aboutsummaryrefslogtreecommitdiffstats
path: root/lib/eunit
diff options
context:
space:
mode:
Diffstat (limited to 'lib/eunit')
-rw-r--r--lib/eunit/AUTHORS2
-rw-r--r--lib/eunit/COPYING504
-rw-r--r--lib/eunit/Makefile97
-rw-r--r--lib/eunit/doc/edoc-info3
-rw-r--r--lib/eunit/doc/erlang.pngbin0 -> 2109 bytes
-rw-r--r--lib/eunit/doc/eunit.html71
-rw-r--r--lib/eunit/doc/eunit_surefire.html78
-rw-r--r--lib/eunit/doc/html/.gitignore0
-rw-r--r--lib/eunit/doc/index.html17
-rw-r--r--lib/eunit/doc/man3/.gitignore0
-rw-r--r--lib/eunit/doc/modules-frame.html13
-rw-r--r--lib/eunit/doc/overview-summary.html1032
-rw-r--r--lib/eunit/doc/overview.edoc1028
-rw-r--r--lib/eunit/doc/packages-frame.html11
-rw-r--r--lib/eunit/doc/pdf/.gitignore0
-rw-r--r--lib/eunit/doc/src/Makefile174
-rw-r--r--lib/eunit/doc/src/book.xml51
-rw-r--r--lib/eunit/doc/src/fascicules.xml18
-rw-r--r--lib/eunit/doc/src/make.dep19
-rw-r--r--lib/eunit/doc/src/notes.xml153
-rw-r--r--lib/eunit/doc/src/part.xml38
-rw-r--r--lib/eunit/doc/src/part_notes.xml39
-rw-r--r--lib/eunit/doc/src/ref_man.xml40
-rw-r--r--lib/eunit/doc/stylesheet.css55
-rw-r--r--lib/eunit/ebin/.gitignore0
-rw-r--r--lib/eunit/examples/Makefile58
-rw-r--r--lib/eunit/examples/fib.erl19
-rw-r--r--lib/eunit/examples/tests.txt1
-rw-r--r--lib/eunit/include/eunit.hrl340
-rw-r--r--lib/eunit/info2
-rw-r--r--lib/eunit/src/Makefile113
-rw-r--r--lib/eunit/src/eunit.app.src21
-rw-r--r--lib/eunit/src/eunit.appup.src1
-rw-r--r--lib/eunit/src/eunit.erl250
-rw-r--r--lib/eunit/src/eunit_autoexport.erl104
-rw-r--r--lib/eunit/src/eunit_data.erl732
-rw-r--r--lib/eunit/src/eunit_internal.hrl48
-rw-r--r--lib/eunit/src/eunit_lib.erl576
-rw-r--r--lib/eunit/src/eunit_listener.erl178
-rw-r--r--lib/eunit/src/eunit_proc.erl661
-rw-r--r--lib/eunit/src/eunit_serial.erl186
-rw-r--r--lib/eunit/src/eunit_server.erl341
-rw-r--r--lib/eunit/src/eunit_striptests.erl67
-rw-r--r--lib/eunit/src/eunit_surefire.erl417
-rw-r--r--lib/eunit/src/eunit_test.erl320
-rw-r--r--lib/eunit/src/eunit_tests.erl42
-rw-r--r--lib/eunit/src/eunit_tty.erl257
-rw-r--r--lib/eunit/test/Makefile82
-rw-r--r--lib/eunit/test/eunit.cover3
-rw-r--r--lib/eunit/test/eunit.dynspec6
-rw-r--r--lib/eunit/test/eunit_SUITE.erl31
-rw-r--r--lib/eunit/vsn.mk1
52 files changed, 8300 insertions, 0 deletions
diff --git a/lib/eunit/AUTHORS b/lib/eunit/AUTHORS
new file mode 100644
index 0000000000..b7c1426aff
--- /dev/null
+++ b/lib/eunit/AUTHORS
@@ -0,0 +1,2 @@
+Richard Carlsson <[email protected]>
+Micka�l R�mond <[email protected]>
diff --git a/lib/eunit/COPYING b/lib/eunit/COPYING
new file mode 100644
index 0000000000..223ede7de3
--- /dev/null
+++ b/lib/eunit/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/lib/eunit/Makefile b/lib/eunit/Makefile
new file mode 100644
index 0000000000..ee69d5e8e0
--- /dev/null
+++ b/lib/eunit/Makefile
@@ -0,0 +1,97 @@
+# ``The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved via the world wide web at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id$
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+#
+# Macros
+#
+
+SUB_DIRECTORIES = src examples doc/src
+
+include vsn.mk
+VSN = $(EUNIT_VSN)
+
+DIR_NAME = eunit-$(VSN)
+
+ifndef APP_RELEASE_DIR
+ APP_RELEASE_DIR = /tmp
+endif
+
+ifndef APP_TAR_FILE
+ APP_TAR_FILE = $(APP_RELEASE_DIR)/$(DIR_NAME).tgz
+endif
+
+APP_DIR = $(APP_RELEASE_DIR)/$(DIR_NAME)
+
+APPNAME = eunit
+DOC_OPTS = [{def,{version,"$(VSN)"}}]
+
+SPECIAL_TARGETS =
+
+#
+# Default Subdir Targets
+#
+include $(ERL_TOP)/make/otp_subdir.mk
+
+
+.PHONY: info version
+
+info:
+ @echo "APP_RELEASE_DIR: $(APP_RELEASE_DIR)"
+ @echo "APP_DIR: $(APP_DIR)"
+ @echo "APP_TAR_FILE: $(APP_TAR_FILE)"
+
+version:
+ @echo "$(VSN)"
+
+#docs:
+# erl -noshell -run edoc_run application "'$(APPNAME)'" '"."' '$(DOC_OPTS)' -s init stop
+
+#edocs: docs
+
+# ----------------------------------------------------
+# Application (source) release targets
+# ----------------------------------------------------
+app_release: tar
+
+app_dir: $(APP_DIR)
+
+$(APP_DIR):
+ cat TAR.exclude > TAR.exclude2; \
+ echo "eunit/TAR.exclude2" >> TAR.exclude2; \
+ (cd ..; find eunit -name 'findmerge.*' >> eunit/TAR.exclude2)
+ (cd ..; find eunit -name '*.contrib*' >> eunit/TAR.exclude2)
+ (cd ..; find eunit -name '*.keep*' >> eunit/TAR.exclude2)
+ (cd ..; find eunit -name '*~' >> eunit/TAR.exclude2)
+ (cd ..; find eunit -name 'erl_crash.dump' >> eunit/TAR.exclude2)
+ (cd ..; find eunit -name '*.log' >> eunit/TAR.exclude2)
+ (cd ..; find eunit -name 'core' >> eunit/TAR.exclude2)
+ (cd ..; find eunit -name '.cmake.state' >> eunit/TAR.exclude2)
+ mkdir $(APP_DIR); \
+ (cd ..; tar cfX - eunit/TAR.exclude2 eunit) | \
+ (cd $(APP_DIR); tar xf -); \
+ mv $(APP_DIR)/eunit/* $(APP_DIR)/; \
+ rmdir $(APP_DIR)/eunit
+ mkdir $(APP_DIR)/doc; \
+ (cd doc; tar cf - man3 html) | (cd $(APP_DIR)/doc; tar xf -)
+
+tar: $(APP_TAR_FILE)
+
+$(APP_TAR_FILE): $(APP_DIR)
+ (cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
diff --git a/lib/eunit/doc/edoc-info b/lib/eunit/doc/edoc-info
new file mode 100644
index 0000000000..1c04b2ed1a
--- /dev/null
+++ b/lib/eunit/doc/edoc-info
@@ -0,0 +1,3 @@
+{application,eunit}.
+{packages,[]}.
+{modules,[eunit,eunit_surefire]}.
diff --git a/lib/eunit/doc/erlang.png b/lib/eunit/doc/erlang.png
new file mode 100644
index 0000000000..987a618e24
--- /dev/null
+++ b/lib/eunit/doc/erlang.png
Binary files differ
diff --git a/lib/eunit/doc/eunit.html b/lib/eunit/doc/eunit.html
new file mode 100644
index 0000000000..a181d12ce3
--- /dev/null
+++ b/lib/eunit/doc/eunit.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>Module eunit</title>
+<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
+</head>
+<body bgcolor="white">
+<div class="navbar"><a name="#navbar_top"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
+<hr>
+
+<h1>Module eunit</h1>
+<ul class="index"><li><a href="#description">Description</a></li><li><a href="#index">Function Index</a></li><li><a href="#functions">Function Details</a></li></ul>This module is the main EUnit user interface.
+<p>Copyright � 2004-2009 Micka�l R�mond, Richard Carlsson</p>
+
+<p><b>Version:</b> 2.1.1, Apr 22 2009 22:37:19</p>
+<p><b>Authors:</b> Micka�l R�mond (<a href="mailto:[email protected]"><tt>[email protected]</tt></a>) [<em>web site:</em> <tt><a href="http://www.process-one.net/" target="_top">http://www.process-one.net/</a></tt>], Richard Carlsson (<a href="mailto:[email protected]"><tt>[email protected]</tt></a>) [<em>web site:</em> <tt><a href="http://user.it.uu.se/~richardc/" target="_top">http://user.it.uu.se/~richardc/</a></tt>].</p>
+
+<h2><a name="description">Description</a></h2>This module is the main EUnit user interface.
+<h2><a name="index">Function Index</a></h2>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#start-0">start/0</a></td><td>Starts the EUnit server.</td></tr>
+<tr><td valign="top"><a href="#stop-0">stop/0</a></td><td>Stops the EUnit server.</td></tr>
+<tr><td valign="top"><a href="#test-1">test/1</a></td><td>Equivalent to <a href="#test-2"><tt>test(Tests, [])</tt></a>.
+</td></tr>
+<tr><td valign="top"><a href="#test-2">test/2</a></td><td>Runs a set of tests.</td></tr>
+</table>
+
+<h2><a name="functions">Function Details</a></h2>
+
+<h3 class="function"><a name="start-0">start/0</a></h3>
+<div class="spec">
+<p><tt>start() -&gt; any()</tt></p>
+</div><p>Starts the EUnit server. Normally, you don't need to call this
+ function; it is started automatically.</p>
+
+<h3 class="function"><a name="stop-0">stop/0</a></h3>
+<div class="spec">
+<p><tt>stop() -&gt; any()</tt></p>
+</div><p>Stops the EUnit server. Normally, you don't need to call this
+ function.</p>
+
+<h3 class="function"><a name="test-1">test/1</a></h3>
+<div class="spec">
+<p><tt>test(Tests) -&gt; any()</tt></p>
+</div><p>Equivalent to <a href="#test-2"><tt>test(Tests, [])</tt></a>.</p>
+
+
+<h3 class="function"><a name="test-2">test/2</a></h3>
+<div class="spec">
+<p><tt>test(Tests::term(), Options::[term()]) -&gt; ok | {error, term()}</tt></p>
+</div><p><p>Runs a set of tests. The format of <code>Tests</code> is described in the
+ section <a href="overview-summary.html#EUnit_test_representation">EUnit test
+ representation</a> of the overview.</p>
+
+ Example: <pre> eunit:test(fred)</pre><p> runs all tests in the module <code>fred</code>
+ and also any tests in the module <code>fred_tests</code>, if that module exists.</p>
+
+ Options:
+ <dl>
+ <dt><code>verbose</code></dt>
+ <dd>Displays more details about the running tests.</dd>
+ </dl>
+
+ Options in the environment variable EUNIT are also included last in
+ the option list, i.e., have lower precedence than those in <code>Options</code>.</p>
+<p><b>See also:</b> <a href="#test-1">test/1</a>.</p>
+<hr>
+
+<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
+<p><i>Generated by EDoc, Apr 22 2009, 22:37:19.</i></p>
+</body>
+</html>
diff --git a/lib/eunit/doc/eunit_surefire.html b/lib/eunit/doc/eunit_surefire.html
new file mode 100644
index 0000000000..f2ecbae572
--- /dev/null
+++ b/lib/eunit/doc/eunit_surefire.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>Module eunit_surefire</title>
+<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
+</head>
+<body bgcolor="white">
+<div class="navbar"><a name="#navbar_top"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
+<hr>
+
+<h1>Module eunit_surefire</h1>
+<ul class="index"><li><a href="#description">Description</a></li><li><a href="#index">Function Index</a></li><li><a href="#functions">Function Details</a></li></ul>Surefire reports for EUnit (Format used by Maven and Atlassian
+Bamboo for example to integrate test results).
+<p>Copyright � 2009 Micka�l R�mond, Paul Guyot</p>
+
+<p><b>Behaviours:</b> <a href="eunit_listener.html"><tt>eunit_listener</tt></a>.</p>
+<p><b>Authors:</b> Micka�l R�mond (<a href="mailto:[email protected]"><tt>[email protected]</tt></a>).</p>
+<p><b>See also:</b> <a href="eunit.html">eunit</a>.</p>
+
+<h2><a name="description">Description</a></h2><p>Surefire reports for EUnit (Format used by Maven and Atlassian
+Bamboo for example to integrate test results). Based on initial code
+from Paul Guyot.</p>
+
+ Example: Generate XML result file in the current directory:
+ <pre> eunit:test([fib, eunit_examples],
+ [{report,{eunit_surefire,[{dir,"."}]}}]).</pre>
+<h2><a name="index">Function Index</a></h2>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#handle_begin-3">handle_begin/3</a></td><td></td></tr>
+<tr><td valign="top"><a href="#handle_cancel-3">handle_cancel/3</a></td><td></td></tr>
+<tr><td valign="top"><a href="#handle_end-3">handle_end/3</a></td><td></td></tr>
+<tr><td valign="top"><a href="#init-1">init/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#start-0">start/0</a></td><td></td></tr>
+<tr><td valign="top"><a href="#start-1">start/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#terminate-2">terminate/2</a></td><td></td></tr>
+</table>
+
+<h2><a name="functions">Function Details</a></h2>
+
+<h3 class="function"><a name="handle_begin-3">handle_begin/3</a></h3>
+<div class="spec">
+<p><tt>handle_begin(X1, Data, St) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="handle_cancel-3">handle_cancel/3</a></h3>
+<div class="spec">
+<p><tt>handle_cancel(X1, Data, St) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="handle_end-3">handle_end/3</a></h3>
+<div class="spec">
+<p><tt>handle_end(X1, Data, St) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="init-1">init/1</a></h3>
+<div class="spec">
+<p><tt>init(Options) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="start-0">start/0</a></h3>
+<div class="spec">
+<p><tt>start() -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="start-1">start/1</a></h3>
+<div class="spec">
+<p><tt>start(Options) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="terminate-2">terminate/2</a></h3>
+<div class="spec">
+<p><tt>terminate(X1, St) -&gt; any()</tt></p>
+</div>
+<hr>
+
+<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
+<p><i>Generated by EDoc, Apr 22 2009, 22:37:19.</i></p>
+</body>
+</html>
diff --git a/lib/eunit/doc/html/.gitignore b/lib/eunit/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/eunit/doc/html/.gitignore
diff --git a/lib/eunit/doc/index.html b/lib/eunit/doc/index.html
new file mode 100644
index 0000000000..9bd8e8cf6b
--- /dev/null
+++ b/lib/eunit/doc/index.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>The eunit application</title>
+</head>
+<frameset cols="20%,80%">
+<frame src="modules-frame.html" name="modulesFrame" title="">
+
+<frame src="overview-summary.html" name="overviewFrame" title="">
+<noframes>
+<h2>This page uses frames</h2>
+<p>Your browser does not accept frames.
+<br>You should go to the <a href="overview-summary.html">non-frame version</a> instead.
+</p>
+</noframes>
+</frameset>
+</html> \ No newline at end of file
diff --git a/lib/eunit/doc/man3/.gitignore b/lib/eunit/doc/man3/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/eunit/doc/man3/.gitignore
diff --git a/lib/eunit/doc/modules-frame.html b/lib/eunit/doc/modules-frame.html
new file mode 100644
index 0000000000..a484e99d4c
--- /dev/null
+++ b/lib/eunit/doc/modules-frame.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>The eunit application</title>
+<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
+</head>
+<body bgcolor="white">
+<h2 class="indextitle">Modules</h2>
+<table width="100%" border="0" summary="list of modules">
+<tr><td><a href="eunit.html" target="overviewFrame" class="module">eunit</a></td></tr>
+<tr><td><a href="eunit_surefire.html" target="overviewFrame" class="module">eunit_surefire</a></td></tr></table>
+</body>
+</html> \ No newline at end of file
diff --git a/lib/eunit/doc/overview-summary.html b/lib/eunit/doc/overview-summary.html
new file mode 100644
index 0000000000..ea7beba8b3
--- /dev/null
+++ b/lib/eunit/doc/overview-summary.html
@@ -0,0 +1,1032 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>EUnit - a Lightweight Unit Testing Framework for Erlang
+</title>
+<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
+</head>
+<body bgcolor="white">
+<div class="navbar"><a name="#navbar_top"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
+<h1>EUnit - a Lightweight Unit Testing Framework for Erlang
+</h1>
+<p>Copyright � 2004-2007 Micka�l R�mond, Richard Carlsson</p>
+<p><b>Version:</b> 2.1.1, Apr 22 2009 22:37:19
+</p>
+<p><b>Authors:</b> Richard Carlsson (<a href="mailto:[email protected]"><tt>[email protected]</tt></a>) [<em>web site:</em> <tt><a href="http://user.it.uu.se/~richardc/" target="_top">http://user.it.uu.se/~richardc/</a></tt>], Micka�l R�mond (<a href="mailto:[email protected]"><tt>[email protected]</tt></a>) [<em>web site:</em> <tt><a href="http://www.process-one.net/" target="_top">http://www.process-one.net/</a></tt>].</p>
+<p>EUnit is a unit testing framework for Erlang. It is very powerful
+and flexible, is easy to use, and has small syntactical overhead.</p>
+
+<ul>
+<li><a href="#Unit_testing">Unit testing</a></li>
+<li><a href="#Terminology">Terminology</a></li>
+<li><a href="#Getting_started">Getting started</a></li>
+<li><a href="#EUnit_macros">EUnit macros</a></li>
+<li><a href="#EUnit_test_representation">EUnit test representation</a></li>
+</ul>
+
+<p>EUnit builds on ideas from the family of unit testing frameworks for
+Object Oriented languages that originated with JUnit by Beck and Gamma
+(and Beck's previous framework SUnit for Smalltalk). However, EUnit uses
+techniques more adapted to functional and concurrent programming, and is
+typically less verbose than its relatives.</p>
+
+<p>Although EUnit uses many preprocessor macros, they have been designed to
+be as nonintrusive as possible, and should not cause conflicts with
+existing code. Adding EUnit tests to a module should thus not normally
+require changing existing code. Furthermore, tests that only exercise
+the exported functions of a module can always be placed in a completely
+separate module, avoiding any conflicts entirely.</p>
+
+<h3><a name="Unit_testing">Unit testing</a></h3>
+
+<p>Unit Testing is testing of individual program "units" in relative
+isolation. There is no particular size requirement: a unit can be a
+function, a module, a process, or even a whole application, but the most
+typical testing units are individual functions or modules. In order to
+test a unit, you specify a set of individual tests, set up the smallest
+necessary environment for being able to run those tests (often, you
+don't need to do any setup at all), you run the tests and collect the
+results, and finally you do any necessary cleanup so that the test can
+be run again later. A Unit Testing Framework tries to help you in each
+stage of this process, so that it is easy to write tests, easy to run
+them, and easy to see which tests failed (so you can fix the bugs).</p>
+
+<h4><a name="Advantages_of_unit_testing">Advantages of unit testing</a></h4>
+
+<dl>
+ <dt>Reduces the risks of changing the program</dt>
+ <dd>Most programs will be modified during their lifetime: bugs will be
+ fixed, features will be added, optimizations may become necessary, or
+ the code will need to be refactored or cleaned up in other ways to
+ make it easier to work with. But every change to a working program is
+ a risk of introducing new bugs - or reintroducing bugs that had
+ previously been fixed. Having a set of unit tests that you can run
+ with very little effort makes it easy to know that the code still
+ works as it should (this use is called <em>regression testing</em>;
+ see <a href="#Terminology">Terminology</a>). This goes a long way to reduce the
+ resistance to changing and refactoring code.</dd>
+ <dt>Helps guide and speed up the development process</dt>
+ <dd>By focusing on getting the code to pass the tests, the programmer
+ can become more productive, not overspecify or get lost in premature
+ optimizations, and create code that is correct from the very beginning
+ (so-called <em>test-driven development</em>; see <a href="#Terminology">Terminology</a>).</dd>
+ <dt>Helps separate interface from implementation</dt>
+ <dd>When writing tests, the programmer may discover dependencies
+ (in order to get the tests to run) that ought not to be there, and
+ which need to be abstracted away to get a cleaner design. This helps
+ eliminate bad dependencies before they spread throughout the
+ code.</dd>
+ <dt>Makes component integration easier</dt>
+ <dd>By testing in a bottom-up fashion, beginning with the smallest
+ program units and creating a confidence in that they work as they
+ should, it becomes easier to test that a higher-level component,
+ consisting of several such units, also behaves according to
+ specification (known as <em>integration testing</em>; see <a href="#Terminology">Terminology</a>).</dd>
+ <dt>Is self-documenting</dt>
+ <dd>The tests can be read as documentation, typically showing both
+ examples of correct and incorrect usage, along with the expected
+ consequences.</dd>
+</dl>
+
+<h3><a name="Terminology">Terminology</a></h3>
+
+<dl>
+ <dt>Unit testing</dt>
+ <dd>Testing that a program unit behaves as it is supposed to do (in
+ itself), according to its specifications. Unit tests have an important
+ function as regression tests, when the program later is modified for
+ some reason, since they check that the program still behaves according
+ to specification.</dd>
+ <dt>Regression testing</dt>
+ <dd>Running a set of tests after making changes to a program, to check
+ that the program behaves as it did before the changes (except, of
+ course, for any intentional changes in behaviour). Unit tests are
+ important as regression tests, but regression testing can involve more
+ than just unit testing, and may also test behaviour that might not be
+ part of the normal specification (such as bug-for-bug-compatibility).
+ </dd>
+ <dt>Integration testing</dt>
+ <dd>Testing that a number of individually developed program units
+ (assumed to already have been separately unit tested) work together as
+ expected. Depending on the system being developed, integration testing
+ may be as simple as "just another level of unit testing", but might
+ also involve other kinds of tests (compare <em>system testing</em>).
+</dd>
+ <dt>System testing</dt>
+ <dd>Testing that a complete system behaves according to its
+ specification. Specifically, system testing should not require knowing
+ any details about the implementation. It typically involves testing
+ many different aspects of the system behaviour apart from the basic
+ functionality, such as performance, usability, and reliability.</dd>
+ <dt>Test-driven development</dt>
+ <dd>A program development technique where you continuously write tests
+ <em>before</em> you implement the code that is supposed to pass those
+ tests. This can help you focus on solving the right problems, and not
+ make a more complicated implementation than necessary, by letting the
+ unit tests determine when a program is "done": if it fulfils its
+ specifications, there is no need to keep adding functionality.</dd>
+ <dt>Mock object</dt>
+ <dd>Sometimes, testing some unit <code>A</code> (e.g., a function) requires that
+ it collaborates somehow with some other unit <code>B</code> (perhaps being passed
+ as an argument, or by reference) - but <code>B</code> has not been implemented
+ yet. A "mock object" - an object which, for the purposes of testing
+ <code>A</code>, looks and behaves like a real <code>B</code> - might then be used instead.
+ (This is of course only useful if it would be significantly more work
+ to implement a real <code>B</code> than to create a mock object.)</dd>
+ <dt>Test case</dt>
+ <dd>A single, well-defined test, that somehow can be uniquely
+ identified. When executed, the test case either <em>passes</em> or
+ <em>fails</em>; the test report should identify exactly which test
+ cases failed.</dd>
+ <dt>Test suite</dt>
+ <dd>A collection of test cases, generally with a specific, common
+ target for testing, such as a single function, module, or subsystem. A
+ test suite may also be recursively composed by smaller test
+ suites.</dd>
+</dl>
+
+<h3><a name="Getting_started">Getting started</a></h3>
+<ul>
+ <li><a href="#Including_the_EUnit_header_file">Including the EUnit header file</a></li>
+ <li><a href="#Writing_simple_test_functions">Writing simple test functions</a></li>
+ <li><a href="#Running_EUnit">Running EUnit</a></li>
+ <li><a href="#Writing_test_generating_functions">Writing test generating functions</a></li>
+ <li><a href="#An_example">An example</a></li>
+ <li><a href="#Disabling_testing">Disabling testing</a></li>
+ <li><a href="#Avoiding_compile-time_dependency_on_EUnit">Avoiding compile-time dependency on EUnit</a></li>
+</ul>
+
+<h4><a name="Including_the_EUnit_header_file">Including the EUnit header file</a></h4>
+
+The simplest way to use EUnit in an Erlang module is to add the
+following line at the beginning of the module (after the <code>-module</code>
+declaration, but before any function definitions):
+<pre> -include_lib("eunit/include/eunit.hrl").</pre>
+
+This will have the following effect:
+<ul>
+ <li>Creates an exported function <code>test()</code> (unless testing is turned
+ off, and the module does not already contain a test() function), that
+ can be used to run all the unit tests defined in the module</li>
+ <li>Causes all functions whose names match <code>..._test()</code> or <code>..._test_()</code>
+ to be automatically exported from the module (unless testing is
+ turned off, or the <code>EUNIT_NOAUTO</code> macro is defined)</li>
+ <li>Makes all the preprocessor macros of EUnit available, to help
+ writing tests</li>
+</ul>
+
+<strong>Note:</strong> For <code>-include_lib(...)</code> to work, the Erlang
+module search path <em>must</em> contain a directory whose name ends in
+<code>eunit/ebin</code> (pointing to the <code>ebin</code> subdirectory of the EUnit
+installation directory). If EUnit is installed as <code>lib/eunit</code> under your
+Erlang/OTP system directory, its <code>ebin</code> subdirectory will be
+automatically added to the search path when Erlang starts. Otherwise,
+you need to add the directory explicitly, by passing a <code>-pa</code> flag to the
+<code>erl</code> or <code>erlc</code> command. For example, a Makefile could contain the
+following action for compiling <code>.erl</code> files:
+<pre> erlc -pa "path/to/eunit/ebin" $(ERL_COMPILE_FLAGS) -o$(EBIN) $&lt;</pre>
+or if you want Eunit to always be available when you run Erlang
+interactively, you can add a line like the following to your
+<code>$HOME/.erlang</code> file:
+<pre> code:add_path("/path/to/eunit/ebin").</pre>
+
+<h4><a name="Writing_simple_test_functions">Writing simple test functions</a></h4>
+
+<p>The EUnit framework makes it extremely easy to write unit tests in
+Erlang. There are a few different ways of writing them, though, so we
+start with the simplest:</p>
+
+<p>A function with a name ending in <code>..._test()</code> is recognized by EUnit as
+a simple test function - it takes no arguments, and its execution either
+succeeds (returning some arbitrary value that EUnit will throw away), or
+fails by throwing an exception of some kind (or by not terminating, in
+which case it will be aborted after a while).</p>
+
+An example of a simple test function could be the following:
+<pre> reverse_test() -&gt; lists:reverse([1,2,3]).</pre><p>
+This just tests that the function <code>lists:reverse(List)</code> does not crash
+when <code>List</code> is <code>[1,2,3]</code>. It is not a great test, but many people write
+simple functions like this one to test the basic functionality of their
+code, and those tests can be used directly by EUnit, without changes,
+as long as their function names match.</p>
+
+<h5><a name="Use_exceptions_to_signal_failure">Use exceptions to signal failure</a></h5>
+
+To write more interesting tests, we need to make them crash (throw an
+exception) when they don't get the result they expect. A simple way of
+doing this is to use pattern matching with <code>=</code>, as in the following
+examples:
+<pre> reverse_nil_test() -&gt; [] = lists:reverse([]).
+ reverse_one_test() -&gt; [1] = lists:reverse([1]).
+ reverse_two_test() -&gt; [2,1] = lists:reverse([1,2]).</pre><p>
+If there was some bug in <code>lists:reverse/1</code> that made it return something
+other than <code>[2,1]</code> when it got <code>[1,2]</code> as input, then the last test
+above would throw a <code>badmatch</code> error. The first two (we assume they do
+not get a <code>badmatch</code>) would simply return <code>[]</code> and <code>[1]</code>, respectively,
+so both succeed. (Note that EUnit is not psychic: if you write a test
+that returns a value, even if it is the wrong value, EUnit will consider
+it a success. You must make sure that the test is written so that it
+causes a crash if the result is not what it should be.)</p>
+
+<h5><a name="Using_assert_macros">Using assert macros</a></h5>
+
+If you want to use Boolean operators for your tests, the <code>assert</code>
+macro comes in handy (see <a href="#EUnit_macros">EUnit macros</a> for details):
+<pre> length_test() -&gt; ?assert(length([1,2,3]) =:= 3).</pre><p>
+The <code>?assert(Expression)</code> macro will evaluate <code>Expression</code>, and if that
+does not evaluate to <code>true</code>, it will throw an exception; otherwise it
+just returns <code>ok</code>. In the above example, the test will thus fail if the
+call to <code>length</code> does not return 3.</p>
+
+<h4><a name="Running_EUnit">Running EUnit</a></h4>
+
+<p>If you have added the declaration
+<code>-include_lib("eunit/include/eunit.hrl")</code> to your module, as described
+above, you only need to compile the module, and run the automatically
+exported function <code>test()</code>. For example, if your module was named <code>m</code>,
+then calling <code>m:test()</code> will run EUnit on all the tests defined in the
+module. You do not need to write <code>-export</code> declarations for the test
+functions. This is all done by magic.</p>
+
+<p>You can also use the function <a href="eunit.html#test-1"><code>eunit:test/1</code></a> to run arbitrary
+tests, for example to try out some more advanced test descriptors (see
+<a href="#EUnit_test_representation">EUnit test representation</a>). For example, running
+<code>eunit:test(m)</code> does the same thing as the auto-generated function
+<code>m:test()</code>, while <code>eunit:test({inparallel, m})</code> runs the same test
+cases but executes them all in parallel.</p>
+
+<h5><a name="Putting_tests_in_separate_modules">Putting tests in separate modules</a></h5>
+
+<p>If you want to separate your test code from your normal code (at least
+for testing the exported functions), you can simply write the test
+functions in a module named <code>m_tests</code> (note: not <code>m_test</code>), if your
+module is named <code>m</code>. Then, whenever you ask EUnit to test the module
+<code>m</code>, it will also look for the module <code>m_tests</code> and run those tests as
+well. See <code>ModuleName</code> in the section <a href="#Primitives">Primitives</a> for details.</p>
+
+<h5><a name="EUnit_captures_standard_output">EUnit captures standard output</a></h5>
+
+<p>If your test code writes to the standard output, you may be surprised to
+see that the text does not appear on the console when the tests are
+running. This is because EUnit captures all standard output from test
+functions (this also includes setup and cleanup functions, but not
+generator functions), so that it can be included in the test report if
+errors occur. To bypass EUnit and print text directly to the console
+while testing, you can write to the <code>user</code> output stream, as in
+<code>io:format(user, "~w", [Term])</code>. The recommended way of doing this is to
+use the EUnit <a href="#Debugging_macros">Debugging macros</a>, which make it much simpler.</p>
+
+<h4><a name="Writing_test_generating_functions">Writing test generating functions</a></h4>
+
+<p>A drawback of simple test functions is that you must write a separate
+function (with a separate name) for each test case. A more compact way
+of writing tests (and much more flexible, as we shall see), is to write
+functions that <em>return</em> tests, instead of <em>being</em> tests.</p>
+
+<p>A function with a name ending in <code>..._test_()</code> (note the final
+underscore) is recognized by EUnit as a <em>test generator</em>
+function. Test generators return a <em>representation</em> of a <em>set
+of tests</em> to be executed by EUnit.</p>
+
+<h5><a name="Representing_a_test_as_data">Representing a test as data</a></h5>
+
+The most basic representation of a test is a single fun-expression that
+takes no arguments. For example, the following test generator:
+<pre> basic_test_() -&gt;
+ fun () -&gt; ?assert(1 + 1 =:= 2) end.</pre>
+will have the same effect as the following simple test:
+<pre> simple_test() -&gt;
+ ?assert(1 + 1 =:= 2).</pre><p>
+(in fact, EUnit will handle all simple tests just like it handles
+fun-expressions: it will put them in a list, and run them one by one).</p>
+
+<h5><a name="Using_macros_to_write_tests">Using macros to write tests</a></h5>
+
+To make tests more compact and readable, as well as automatically add
+information about the line number in the source code where a test
+occurred (and reduce the number of characters you have to type), you can
+use the <code>_test</code> macro (note the initial underscore character), like
+this:
+<pre> basic_test_() -&gt;
+ ?_test(?assert(1 + 1 =:= 2)).</pre><p>
+The <code>_test</code> macro takes any expression (the "body") as argument, and
+places it within a fun-expression (along with some extra information).
+The body can be any kind of test expression, just like the body of a
+simple test function.</p>
+
+<h5><a name="Underscore-prefixed_macros_create_test_objects">Underscore-prefixed macros create test objects</a></h5>
+
+But this example can be made even shorter! Most test macros, such as the
+family of <code>assert</code> macros, have a corresponding form with an initial
+underscore character, which automatically adds a <code>?_test(...)</code> wrapper.
+The above example can then simply be written:
+<pre> basic_test_() -&gt;
+ ?_assert(1 + 1 =:= 2).</pre><p>
+which has exactly the same meaning (note the <code>_assert</code> instead of
+<code>assert</code>). You can think of the initial underscore as signalling
+<em>test object</em>.</p>
+
+<h4><a name="An_example">An example</a></h4>
+
+Sometimes, an example says more than a thousand words. The following
+small Erlang module shows how EUnit can be used in practice.
+<pre> -module(fib).
+ -export([fib/1]).
+ -include_lib("eunit/include/eunit.hrl").
+
+ fib(0) -&gt; 1;
+ fib(1) -&gt; 1;
+ fib(N) when N &gt; 1 -&gt; fib(N-1) + fib(N-2).
+
+ fib_test_() -&gt;
+ [?_assert(fib(0) =:= 1),
+ ?_assert(fib(1) =:= 1),
+ ?_assert(fib(2) =:= 2),
+ ?_assert(fib(3) =:= 3),
+ ?_assert(fib(4) =:= 5),
+ ?_assert(fib(5) =:= 8),
+ ?_assertException(error, function_clause, fib(-1)),
+ ?_assert(fib(31) =:= 2178309)
+ ].</pre>
+
+<p>(Author's note: When I first wrote this example, I happened to write a
+<code>*</code> instead of <code>+</code> in the <code>fib</code> function. Of course, this showed up
+immediately when I ran the tests.)</p>
+
+<p>See <a href="#EUnit_test_representation">EUnit test representation</a> for a full list of all the ways
+you can specify test sets in EUnit.</p>
+
+<h4><a name="Disabling_testing">Disabling testing</a></h4>
+
+Testing can be turned off by defining the <code>NOTEST</code> macro when compiling,
+for example as an option to <code>erlc</code>, as in:
+<pre> erlc -DNOTEST my_module.erl</pre>
+or by adding a macro definition to the code, <em>before the EUnit header
+file is included</em>:
+<pre> -define(NOTEST, 1).</pre><p>
+(the value is not important, but should typically be 1 or <code>true</code>).
+Note that unless the <code>EUNIT_NOAUTO</code> macro is defined, disabling testing
+will also automatically strip all test functions from the code, except
+for any that are explicitly declared as exported.</p>
+
+For instance, to use EUnit in your application, but with testing turned
+off by default, put the following lines in a header file:
+<pre> -define(NOTEST, true).
+ -include_lib("eunit/include/eunit.hrl").</pre>
+and then make sure that every module of your application includes that
+header file. This means that you have a only a single place to modify in
+order to change the default setting for testing. To override the <code>NOTEST</code>
+setting without modifying the code, you can define <code>TEST</code> in a compiler
+option, like this:
+<pre> erlc -DTEST my_module.erl</pre>
+
+<p>See <a href="#Compilation_control_macros">Compilation control macros</a> for details about these
+macros.</p>
+
+<h4><a name="Avoiding_compile-time_dependency_on_EUnit">Avoiding compile-time dependency on EUnit</a></h4>
+
+If you are distributing the source code for your application for other
+people to compile and run, you probably want to ensure that the code
+compiles even if EUnit is not available. Like the example in the
+previous section, you can put the following lines in a common header
+file:
+<pre> -ifdef(TEST).
+ -include_lib("eunit/include/eunit.hrl").
+ -endif.</pre><p>
+and, of course, also make sure that you place all test code that uses
+EUnit macros within <code>-ifdef(TEST)</code> or <code>-ifdef(EUNIT)</code> sections.</p>
+
+
+<h3><a name="EUnit_macros">EUnit macros</a></h3>
+
+<p>Although all the functionality of EUnit is avaliable even without the
+use of preprocessor macros, the EUnit header file defines a number of
+such macros in order to make it as easy as possible to write unit tests
+as compactly as possible and without getting too many details in the
+way.</p>
+
+<p>Except where explicitly stated, using EUnit macros will never introduce
+run-time dependencies on the EUnit library code, regardless of whether
+your code is compiled with testing enabled or disabled.</p>
+
+<ul>
+<li><a href="#Basic_macros">Basic macros</a></li>
+<li><a href="#Compilation_control_macros">Compilation control macros</a></li>
+<li><a href="#Utility_macros">Utility macros</a></li>
+<li><a href="#Assert_macros">Assert macros</a></li>
+<li><a href="#Macros_for_running_external_commands">Macros for running external commands</a></li>
+<li><a href="#Debugging_macros">Debugging macros</a></li>
+</ul>
+
+<h4><a name="Basic_macros">Basic macros</a></h4>
+
+<dl>
+<dt><code>_test(Expr)</code></dt>
+<dd>Turns <code>Expr</code> into a "test object", by wrapping it in a
+fun-expression and a source line number. Technically, this is the same
+as <code>{?LINE, fun () -&gt; (Expr) end}</code>.
+</dd>
+</dl>
+
+<h4><a name="Compilation_control_macros">Compilation control macros</a></h4>
+
+<dl>
+<dt><code>EUNIT</code></dt>
+<dd>This macro is always defined to <code>true</code> whenever EUnit is enabled at
+compile time. This is typically used to place testing code within
+conditional compilation, as in:
+<pre> -ifdef(EUNIT).
+ % test code here
+ ...
+ -endif.</pre>
+e.g., to ensure that the code can be compiled without including the
+EUnit header file, when testing is disabled. See also the macros <code>TEST</code>
+and <code>NOTEST</code>.
+</dd>
+
+<dt><code>EUNIT_NOAUTO</code></dt>
+<dd>If this macro is defined, the automatic exporting or stripping of
+test functions will be disabled.
+</dd>
+
+<dt><code>TEST</code></dt>
+<dd><p>This macro is always defined (to <code>true</code>, unless previously defined
+by the user to have another value) whenever EUnit is enabled at compile
+time. This can be used to place testing code within conditional
+compilation; see also the macros <code>NOTEST</code> and <code>EUNIT</code>.</p>
+
+<p>For testing code that is strictly dependent on EUnit, it may be
+preferable to use the <code>EUNIT</code> macro for this purpose, while for code
+that uses more generic testing conventions, using the <code>TEST</code> macro may
+be preferred.</p>
+
+The <code>TEST</code> macro can also be used to override the <code>NOTEST</code> macro. If
+<code>TEST</code> is defined <em>before</em> the EUnit header file is
+included (even if <code>NOTEST</code> is also defined), then the code will be
+compiled with EUnit enabled.
+</dd>
+
+<dt><code>NOTEST</code></dt>
+<dd><p>This macro is always defined (to <code>true</code>, unless previously defined
+by the user to have another value) whenever EUnit is <em>disabled</em>
+at compile time. (Compare the <code>TEST</code> macro.)</p>
+
+This macro can also be used for conditional compilation, but is more
+typically used to disable testing: If <code>NOTEST</code> is defined
+<em>before</em> the EUnit header file is included, and <code>TEST</code>
+is <em>not</em> defined, then the code will be compiled with EUnit
+disabled. See also <a href="#Disabling_testing">Disabling testing</a>.
+</dd>
+
+<dt><code>NOASSERT</code></dt>
+<dd>If this macro is defined, the assert macros will have no effect,
+when testing is also disabled. See <a href="#Assert_macros">Assert macros</a>. When
+testing is enabled, the assert macros are always enabled automatically
+and cannot be disabled.
+</dd>
+
+<dt><code>ASSERT</code></dt>
+<dd>If this macro is defined, it overrides the NOASSERT macro, forcing
+the assert macros to always be enabled regardless of other settings.
+</dd>
+
+<dt><code>NODEBUG</code></dt>
+<dd>If this macro is defined, the debugging macros will have no effect.
+See <a href="#Debugging_macros">Debugging macros</a>. <code>NODEBUG</code> also implies <code>NOASSERT</code>,
+unless testing is enabled.
+</dd>
+
+<dt><code>DEBUG</code></dt>
+<dd>If this macro is defined, it overrides the NODEBUG macro, forcing
+the debugging macros to be enabled.
+</dd>
+</dl>
+
+<h4><a name="Utility_macros">Utility macros</a></h4>
+
+<p>The following macros can make tests more compact and readable:</p>
+
+<dl>
+<dt><code>LET(Var,Arg,Expr)</code></dt>
+<dd>Creates a local binding <code>Var = Arg</code> in <code>Expr</code>. (This is the same as
+<code>(fun(Var)-&gt;(Expr)end)(Arg)</code>.) Note that the binding is not exported
+outside of <code>Expr</code>, and that within <code>Expr</code>, this binding of <code>Var</code> will
+shadow any binding of <code>Var</code> in the surrounding scope.
+</dd>
+<dt><code>IF(Cond,TrueCase,FalseCase)</code></dt>
+<dd>Evaluates <code>TrueCase</code> if <code>Cond</code> evaluates to <code>true</code>, or otherwise
+evaluates <code>FalseCase</code> if <code>Cond</code> evaluates to <code>false</code>. (This is the same
+as <code>(case (Cond) of true-&gt;(TrueCase); false-&gt;(FalseCase) end)</code>.) Note
+that it is an error if <code>Cond</code> does not yield a boolean value.
+</dd>
+</dl>
+
+<h4><a name="Assert_macros">Assert macros</a></h4>
+
+<p>(Note that these macros also have corresponding forms which start with
+an "<code>_</code>" (underscore) character, as in <code>?_assert(BoolExpr)</code>, that create
+a "test object" instead of performing the test immediately. This is
+equivalent to writing <code>?_test(assert(BoolExpr))</code>, etc.)</p>
+
+<p>If the macro <code>NOASSERT</code> is defined before the EUnit header file is
+included, these macros have no effect when testing is also disabled; see
+<a href="#Compilation_control_macros">Compilation control macros</a> for details.</p>
+
+<dl>
+<dt><code>assert(BoolExpr)</code></dt>
+<dd><p>Evaluates the expression <code>BoolExpr</code>, if testing is enabled. Unless
+the result is <code>true</code>, an informative exception will be generated. If
+there is no exception, the result of the macro expression is the atom
+<code>ok</code>, and the value of <code>BoolExpr</code> is discarded. If testing is disabled,
+the macro will not generate any code except the atom <code>ok</code>, and
+<code>BoolExpr</code> will not be evaluated.</p>
+
+Typical usage:
+<pre> ?assert(f(X, Y) =:= [])</pre>
+
+The <code>assert</code> macro can be used anywhere in a program, not just in unit
+tests, to check pre/postconditions and invariants. For example:
+<pre> some_recursive_function(X, Y, Z) -&gt;
+ ?assert(X + Y &gt; Z),
+ ...</pre>
+</dd>
+<dt><code>assertNot(BoolExpr)</code></dt>
+<dd>Equivalent to <code>assert(not (BoolExpr))</code>.
+</dd>
+<dt><code>assertMatch(GuardedPattern, Expr)</code></dt>
+<dd><p>Evaluates <code>Expr</code> and matches the result against <code>GuardedPattern</code>, if
+testing is enabled. If the match fails, an informative exception will be
+generated; see the <code>assert</code> macro for further details. <code>GuardedPattern</code>
+can be anything that you can write on the left hand side of the <code>-&gt;</code>
+symbol in a case-clause, except that it cannot contain comma-separated
+guard tests.</p>
+
+<p>The main reason for using <code>assertMatch</code> also for simple matches, instead
+of matching with <code>=</code>, is that it produces more detailed error messages.</p>
+
+Examples:
+<pre> ?assertMatch({found, {fred, _}}, lookup(bloggs, Table))</pre>
+<pre> ?assertMatch([X|_] when X &gt; 0, binary_to_list(B))</pre>
+</dd>
+<dt><code>assertEqual(Expect, Expr)</code></dt>
+<dd><p>Evaluates the expressions <code>Expect</code> and <code>Expr</code> and compares the
+results for equality, if testing is enabled. If the values are not
+equal, an informative exception will be generated; see the <code>assert</code>
+macro for further details.</p>
+
+<p><code>assertEqual</code> is more suitable than than <code>assertMatch</code> when the
+left-hand side is a computed value rather than a simple pattern, and
+gives more details than <code>?assert(Expect =:= Expr)</code>.</p>
+
+Examples:
+<pre> ?assertEqual("b" ++ "a", lists:reverse("ab"))</pre>
+<pre> ?assertEqual(foo(X), bar(Y))</pre>
+</dd>
+<dt><code>assertException(ClassPattern, TermPattern, Expr)</code></dt>
+<dt><code>assertError(TermPattern, Expr)</code></dt>
+<dt><code>assertExit(TermPattern, Expr)</code></dt>
+<dt><code>assertThrow(TermPattern, Expr)</code></dt>
+<dd><p>Evaluates <code>Expr</code>, catching any exception and testing that it matches
+the expected <code>ClassPattern:TermPattern</code>. If the match fails, or if no
+exception is thrown by <code>Expr</code>, an informative exception will be
+generated; see the <code>assert</code> macro for further details. The
+<code>assertError</code>, <code>assertExit</code>, and <code>assertThrow</code> macros, are equivalent to
+using <code>assertException</code> with a <code>ClassPattern</code> of <code>error</code>, <code>exit</code>, or
+<code>throw</code>, respectively.</p>
+
+Examples:
+<pre> ?assertError(badarith, X/0)</pre>
+<pre> ?assertExit(normal, exit(normal))</pre>
+<pre> ?assertException(throw, {not_found,_}, throw({not_found,42}))</pre>
+</dd>
+</dl>
+
+<h4><a name="Macros_for_running_external_commands">Macros for running external commands</a></h4>
+
+<p>Keep in mind that external commands are highly dependent on the
+operating system. You can use the standard library function <code>os:type()</code>
+in test generator functions, to produce different sets of tests
+depending on the current operating system.</p>
+
+<p>Note: these macros introduce a run-time dependency on the EUnit library
+code, if compiled with testing enabled.</p>
+
+<dl>
+<dt><code>assertCmd(CommandString)</code></dt>
+<dd><p>Runs <code>CommandString</code> as an external command, if testing is enabled.
+Unless the returned status value is 0, an informative exception will be
+generated. If there is no exception, the result of the macro expression
+is the atom <code>ok</code>. If testing is disabled, the macro will not generate
+any code except the atom <code>ok</code>, and the command will not be executed.</p>
+
+Typical usage:
+<pre> ?assertCmd("mkdir foo")</pre>
+</dd>
+<dt><code>assertCmdStatus(N, CommandString)</code></dt>
+<dd>Like the <code>assertCmd(CommandString)</code> macro, but generates an
+exception unless the returned status value is <code>N</code>.
+</dd>
+<dt><code>assertCmdOutput(Text, CommandString)</code></dt>
+<dd>Runs <code>CommandString</code> as an external command, if testing is enabled.
+Unless the output produced by the command exactly matches the specified
+string <code>Text</code>, an informative exception will be generated. (Note that
+the output is normalized to use a single LF character as line break on
+all platforms.) If there is no exception, the result of the macro
+expression is the atom <code>ok</code>. If testing is disabled, the macro will not
+generate any code except the atom <code>ok</code>, and the command will not be
+executed.
+</dd>
+<dt><code>cmd(CommandString)</code></dt>
+<dd><p>Runs <code>CommandString</code> as an external command. Unless the returned
+status value is 0 (indicating success), an informative exception will be
+generated; otherwise, the result of the macro expression is the output
+produced by the command, as a flat string. The output is normalized to
+use a single LF character as line break on all platforms.</p>
+
+<p>This macro is useful in the setup and cleanup sections of fixtures,
+e.g., for creating and deleting files or perform similar operating
+system specific tasks, to make sure that the test system is informed of
+any failures.</p>
+
+A Unix-specific example:
+<pre> {setup,
+ fun () -&gt; ?cmd("mktemp") end,
+ fun (FileName) -&gt; ?cmd("rm " ++ FileName) end,
+ ...}</pre>
+</dd>
+</dl>
+
+<h4><a name="Debugging_macros">Debugging macros</a></h4>
+
+<p>To help with debugging, EUnit defines several useful macros for printing
+messages directly to the console (rather than to the standard output).
+Furthermore, these macros all use the same basic format, which includes
+the file and line number where they occur, making it possible in some
+development environments (e.g., when running Erlang in an Emacs buffer)
+to simply click on the message and jump directly to the corresponding
+line in the code.</p>
+
+<p>If the macro <code>NODEBUG</code> is defined before the EUnit header file is
+included, these macros have no effect; see
+<a href="#Compilation_control_macros">Compilation control macros</a> for details.</p>
+
+<dl>
+<dt><code>debugHere</code></dt>
+<dd>Just prints a marker showing the current file and line number. Note
+that this is an argument-less macro. The result is always <code>ok</code>.</dd>
+<dt><code>debugMsg(Text)</code></dt>
+<dd>Outputs the message <code>Text</code> (which can be a plain string, an IO-list,
+or just an atom). The result is always <code>ok</code>.</dd>
+<dt><code>debugFmt(FmtString, Args)</code></dt>
+<dd>This formats the text like <code>io:format(FmtString, Args)</code> and outputs
+it like <code>debugMsg</code>. The result is always <code>ok</code>.</dd>
+<dt><code>debugVal(Expr)</code></dt>
+<dd>Prints both the source code for <code>Expr</code> and its current value. E.g.,
+<code>?debugVal(f(X))</code> might be displayed as "<code>f(X) = 42</code>". (Large terms are
+shown truncated.) The result is always the value of <code>Expr</code>, so this
+macro can be wrapped around any expression to display its value when
+the code is compiled with debugging enabled.</dd>
+<dt><code>debugTime(Text,Expr)</code></dt>
+<dd>Prints <code>Text</code> and the wall clock time for evaluation of <code>Expr</code>. The
+result is always the value of <code>Expr</code>, so this macro can be wrapped
+around any expression to show its run time when the code is compiled
+with debugging enabled. For example, <code>List1 = ?debugTime("sorting",
+lists:sort(List))</code> might show as "<code>sorting: 0.015 s</code>".</dd>
+
+</dl>
+
+
+<h3><a name="EUnit_test_representation">EUnit test representation</a></h3>
+
+<p>The way EUnit represents tests and test sets as data is flexible,
+powerful, and concise. This section describes the representation in
+detail.</p>
+
+<ul>
+<li><a href="#Simple_test_objects">Simple test objects</a></li>
+<li><a href="#Test_sets_and_deep_lists">Test sets and deep lists</a></li>
+<li><a href="#Titles">Titles</a></li>
+<li><a href="#Primitives">Primitives</a></li>
+<li><a href="#Control">Control</a></li>
+<li><a href="#Fixtures">Fixtures</a></li>
+<li><a href="#Lazy_generators">Lazy generators</a></li>
+</ul>
+
+<h4><a name="Simple_test_objects">Simple test objects</a></h4>
+
+A <em>simple test object</em> is one of the following:
+<ul>
+ <li>A nullary functional value (i.e., a fun that takes zero
+ arguments). Examples:
+<pre> fun () -&gt; ... end</pre>
+<pre> fun some_function/0</pre>
+<pre> fun some_module:some_function/0</pre>
+ </li>
+ <li>A pair of atoms <code>{ModuleName, FunctionName}</code>, referring to the
+ function <code>ModuleName:FunctionName/0</code></li>
+ <li>A pair <code>{LineNumber, SimpleTest}</code>, where <code>LineNumber</code> is a
+ nonnegative integer and <code>SimpleTest</code> is another simple test
+ object. <code>LineNumber</code> should indicate the source line of the test.
+ Pairs like this are usually only created via <code>?_test(...)</code> macros;
+ see <a href="#Basic_macros">Basic macros</a>.</li>
+</ul><p>
+In brief, a simple test object consists of a single function that takes
+no arguments (possibly annotated with some additional metadata, i.e., a
+line number). Evaluation of the function either <em>succeeds</em>, by
+returning some value (which is ignored), or <em>fails</em>, by throwing
+an exception.</p>
+
+<h4><a name="Test_sets_and_deep_lists">Test sets and deep lists</a></h4>
+
+<p>A test set can be easily created by placing a sequence of test objects
+in a list. If <code>T_1</code>, ..., <code>T_N</code> are individual test objects, then <code>[T_1,
+..., T_N]</code> is a test set consisting of those objects (in that order).</p>
+
+<p>Test sets can be joined in the same way: if <code>S_1</code>, ..., <code>S_K</code> are test
+sets, then <code>[S_1, ..., S_K]</code> is also a test set, where the tests of
+<code>S_i</code> are ordered before those of <code>S_(i+1)</code>, for each subset <code>S_i</code>.</p>
+
+<p>Thus, the main representation of test sets is <em>deep lists</em>, and
+a simple test object can be viewed as a test set containing only a
+single test; there is no difference between <code>T</code> and <code>[T]</code>.</p>
+
+<p>A module can also be used to represent a test set; see <code>ModuleName</code>
+under <a href="#Primitives">Primitives</a> below.</p>
+
+<h4><a name="Titles">Titles</a></h4>
+
+<p>Any test or test set <code>T</code> can be annotated with a title, by wrapping it
+in a pair <code>{Title, T}</code>, where <code>Title</code> is a string. For convenience, any
+test which is normally represented using a tuple can simply be given a
+title string as the first element, i.e., writing <code>{"The Title", ...}</code>
+instead of adding an extra tuple wrapper as in <code>{"The Title", {...}}</code>.</p>
+
+
+<h4><a name="Primitives">Primitives</a></h4>
+
+The following are primitives, which do not contain other test sets as
+arguments:
+<dl>
+<dt><code>ModuleName::atom()</code>
+</dt>
+<dd>A single atom represents a module name, and is equivalent to
+<code>{module, ModuleName}</code>. This is often used as in the call
+<code>eunit:test(some_module)</code>.
+</dd>
+<dt><code>{module, ModuleName::atom()}</code>
+</dt>
+<dd><p>This composes a test set from the exported test functions of the
+named module, i.e., those functions with arity zero whose names end
+with <code>_test</code> or <code>_test_</code>. Basically, the <code>..._test()</code> functions become
+simple tests, while the <code>..._test_()</code> functions become generators.</p>
+
+In addition, EUnit will also look for another module whose name is
+<code>ModuleName</code> plus the suffix <code>_tests</code>, and if it exists, all the tests
+from that module will also be added. (If <code>ModuleName</code> already contains
+the suffix <code>_tests</code>, this is not done.) E.g., the specification
+<code>{module, mymodule}</code> will run all tests in the modules <code>mymodule</code> and
+<code>mymodule_tests</code>. Typically, the <code>_tests</code> module should only contain
+test cases that use the public interface of the main module (and no
+other code).
+</dd>
+<dt><code>{application, AppName::atom(), Info::list()}</code>
+</dt>
+<dd>This is a normal Erlang/OTP application descriptor, as found in an
+ <code>.app</code> file. The resulting test set consists of the modules listed in
+ the <code>modules</code> entry in <code>Info</code>.
+</dd>
+<dt><code>{application, AppName::atom()}</code>
+</dt>
+<dd>This creates a test set from all the modules belonging to the
+specified application, by consulting the application's <code>.app</code> file
+(see <code>{file, FileName}</code>), or if no such file exists, by testing all
+object files in the application's <tt>ebin</tt>-directory (see <code>{dir,
+Path}</code>); if that does not exist, the <code>code:lib_dir(AppName)</code> directory
+is used.
+</dd>
+<dt><code>Path::string()</code>
+</dt>
+<dd>A single string represents the path of a file or directory, and is
+equivalent to <code>{file, Path}</code>, or <code>{dir, Path}</code>, respectively, depending
+on what <code>Path</code> refers to in the file system.
+</dd>
+<dt><code>{file, FileName::string()}</code>
+</dt>
+<dd><p>If <code>FileName</code> has a suffix that indicates an object file (<code>.beam</code>),
+EUnit will try to reload the module from the specified file and test it.
+Otherwise, the file is assumed to be a text file containing test
+specifications, which will be read using the standard library function
+<code>file:path_consult/2</code>.</p>
+
+Unless the file name is absolute, the file is first searched for
+relative to the current directory, and then using the normal search path
+(<code>code:get_path()</code>). This means that the names of typical "app" files
+can be used directly, without a path, e.g., <code>"mnesia.app"</code>.
+</dd>
+<dt><code>{dir, Path::string()}</code>
+</dt>
+<dd>This tests all object files in the specified directory, as if they
+had been individually specified using <code>{file, FileName}</code>.
+</dd>
+<dt><code>{generator, GenFun::(() -&gt; Tests)}</code>
+</dt>
+<dd>The generator function <code>GenFun</code> is called to produce a test
+set.
+</dd>
+<dt><code>{generator, ModuleName::atom(), FunctionName::atom()}</code>
+</dt>
+<dd>The function <code>ModuleName:FunctionName()</code> is called to produce a test
+set.
+</dd>
+<dt><code>{with, X::any(), [AbstractTestFun::((any()) -&gt; any())]}</code>
+</dt>
+<dd>Distributes the value <code>X</code> over the unary functions in the list,
+turning them into nullary test functions. An <code>AbstractTestFun</code> is like
+an ordinary test fun, but takes one argument instead of zero - it's
+basically missing some information before it can be a proper test. In
+practice, <code>{with, X, [F_1, ..., F_N]}</code> is equivalent to <code>[fun () -&gt;
+F_1(X) end, ..., fun () -&gt; F_N(X) end]</code>. This is particularly useful if
+your abstract test functions are already implemented as proper
+functions: <code>{with, FD, [fun filetest_a/1, fun filetest_b/1, fun
+filetest_c/1]}</code> is equivalent to <code>[fun () -&gt; filetest_a(FD) end, fun ()
+-&gt; filetest_b(FD) end, fun () -&gt; filetest_c(FD) end]</code>, but much more
+compact. See also <a href="#Fixtures">Fixtures</a>, below.
+</dd>
+</dl>
+
+<h4><a name="Control">Control</a></h4>
+
+The following representations control how and where tests are executed:
+<dl>
+<dt><code>{spawn, Tests}</code></dt>
+<dd>Runs the specified tests in a separate subprocess, while the current
+test process waits for it to finish. This is useful for tests that need
+a fresh, isolated process state. (Note that EUnit always starts at least
+one such a subprocess automatically; tests are never executed by the
+caller's own process.)</dd>
+<dt><code>{spawn, Node::atom(), Tests}</code></dt>
+<dd>Like <code>{spawn, Tests}</code>, but runs the specified tests on the given
+Erlang node.</dd>
+<dt><code>{timeout, Time::number(), Tests}</code></dt>
+<dd>Runs the specified tests under the given timeout. Time is in
+seconds; e.g., 60 means one minute and 0.1 means 1/10th of a second. If
+the timeout is exceeded, the unfinished tests will be forced to
+terminate. Note that if a timeout is set around a fixture, it includes
+the time for setup and cleanup, and if the timeout is triggered, the
+entire fixture is abruptly terminated (without running the
+cleanup).</dd>
+<dt><code>{inorder, Tests}</code></dt>
+<dd>Runs the specified tests in strict order. Also see <code>{inparallel,
+Tests}</code>. By default, tests are neither marked as <code>inorder</code> or
+<code>inparallel</code>, but may be executed as the test framework chooses.</dd>
+<dt><code>{inparallel, Tests}</code></dt>
+<dd>Runs the specified tests in parallel (if possible). Also see
+<code>{inorder, Tests}</code>.</dd>
+<dt><code>{inparallel, N::integer(), Tests}</code></dt>
+<dd>Like <code>{inparallel, Tests}</code>, but running no more than <code>N</code> subtests
+simultaneously.</dd>
+</dl>
+
+<h4><a name="Fixtures">Fixtures</a></h4>
+
+<p>A "fixture" is some state that is necessary for a particular set of
+tests to run. EUnit's support for fixtures makes it easy to set up such
+state locally for a test set, and automatically tear it down again when
+the test set is finished, regardless of the outcome (success, failures,
+timeouts, etc.).</p>
+
+<p>To make the descriptions simpler, we first list some definitions:
+<center>
+<table border="0" cellspacing="4">
+<tr>
+<td><code>Setup</code></td><td><code>() -&gt; (R::any())</code></td>
+</tr>
+<tr>
+<td><code>SetupX</code></td><td><code>(X::any()) -&gt; (R::any())</code></td>
+</tr>
+<tr>
+<td><code>Cleanup</code></td><td><code>(R::any()) -&gt; any()</code></td>
+</tr>
+<tr>
+<td><code>CleanupX</code></td><td><code>(X::any(), R::any()) -&gt; any()</code></td>
+</tr>
+<tr>
+<td><code>Instantiator</code></td><td><code>((R::any()) -&gt; Tests) | {with, [AbstractTestFun::((any()) -&gt; any())]}</code></td>
+</tr>
+<tr>
+<td><code>Where</code></td><td><code>local | spawn | {spawn, Node::atom()}</code></td>
+</tr>
+</table>
+</center>
+(these are explained in more detail further below.)</p>
+
+The following representations specify fixture handling for test sets:
+<dl>
+<dt><code>{setup, Setup, Tests | Instantiator}</code></dt>
+<dt><code>{setup, Setup, Cleanup, Tests | Instantiator}</code></dt>
+<dt><code>{setup, Where, Setup, Tests | Instantiator}</code></dt>
+<dt><code>{setup, Where, Setup, Cleanup, Tests | Instantiator}</code></dt>
+<dd><code>setup</code> sets up a single fixture for running all of the specified
+tests, with optional teardown afterwards. The arguments are described in
+detail below.
+</dd>
+<dt><code>{node, Node::atom(), Tests | Instantiator}</code></dt>
+<dt><code>{node, Node::atom(), Args::string(), Tests | Instantiator}</code></dt>
+<dd><code>node</code> is like <code>setup</code>, but with a built-in behaviour: it starts a
+slave node for the duration of the tests. The atom <code>Node</code> should have
+the format <code>[email protected]</code>, and <code>Args</code> are the optional
+arguments to the new node; see <code>slave:start_link/3</code> for details.
+</dd>
+<dt><code>{foreach, Where, Setup, Cleanup, [Tests | Instantiator]}</code></dt>
+<dt><code>{foreach, Setup, Cleanup, [Tests | Instantiator]}</code></dt>
+<dt><code>{foreach, Where, Setup, [Tests | Instantiator]}</code></dt>
+<dt><code>{foreach, Setup, [Tests | Instantiator]}</code></dt>
+<dd><code>foreach</code> is used to set up a fixture and optionally tear it down
+afterwards, repeated for each single one of the specified test sets.
+</dd>
+<dt><code>{foreachx, Where, SetupX, CleanupX,
+ Pairs::[{X::any(), ((X::any(), R::any()) -&gt; Tests)}]}</code></dt>
+<dt><code>{foreachx, SetupX, CleanupX, Pairs}</code></dt>
+<dt><code>{foreachx, Where, SetupX, Pairs}</code></dt>
+<dt><code>{foreachx, SetupX, Pairs}</code></dt>
+<dd><code>foreachx</code> is like <code>foreach</code>, but uses a list of pairs, each
+containing an extra argument <code>X</code> and an extended instantiator function.
+</dd>
+</dl>
+
+<p>A <code>Setup</code> function is executed just before any of the specified tests
+are run, and a <code>Cleanup</code> function is executed when no more of the
+specified tests will be run, regardless of the reason. A <code>Setup</code>
+function takes no argument, and returns some value which will be passed
+as it is to the <code>Cleanup</code> function. A <code>Cleanup</code> function should do
+whatever necessary and return some arbitrary value, such as the atom
+<code>ok</code>. (<code>SetupX</code> and <code>CleanupX</code> functions are similar, but receive one
+additional argument: some value <code>X</code>, which depends on the context.) When
+no <code>Cleanup</code> function is specified, a dummy function is used which has
+no effect.</p>
+
+<p>An <code>Instantiator</code> function receives the same value as the <code>Cleanup</code>
+function, i.e., the value returned by the <code>Setup</code> function. It should
+then behave much like a generator (see <a href="#Primitives">Primitives</a>), and
+return a test set whose tests have been <em>instantiated</em> with the
+given value. A special case is the syntax <code>{with, [AbstractTestFun]}</code>
+which represents an instantiator function that distributes the value
+over a list of unary functions; see <a href="#Primitives">Primitives</a>: <code>{with, X,
+[...]}</code> for more details.</p>
+
+A <code>Where</code> term controls how the specified tests are executed. The
+default is <code>spawn</code>, which means that the current process handles the
+setup and teardown, while the tests are executed in a subprocess.
+<code>{spawn, Node}</code> is like <code>spawn</code>, but runs the subprocess on the
+specified node. <code>local</code> means that the current process will handle both
+setup/teardown and running the tests - the drawback is that if a test
+times out so that the process is killed, the <em>cleanup will not be
+performed</em>; hence, avoid this for persistent fixtures such as file
+operations. In general, 'local' should only be used when:
+<ul>
+ <li>the setup/teardown needs to be executed by the process that will
+ run the tests;</li>
+ <li>no further teardown needs to be done if the process is killed
+ (i.e., no state outside the process was affected by the setup)</li>
+</ul>
+
+<h4><a name="Lazy_generators">Lazy generators</a></h4>
+
+<p>Sometimes, it can be convenient not to produce the whole set of test
+descriptions before the testing begins; for example, if you want to
+generate a huge amount of tests that would take up too much space to
+keep in memory all at once.</p>
+
+<p>It is fairly easy to write a generator which, each time it is called,
+either produces an empty list if it is done, or otherwise produces a
+list containing a single test case plus a new generator which will
+produce the rest of the tests. This demonstrates the basic pattern:</p>
+
+<pre> lazy_test_() -&gt;
+ lazy_gen(10000).
+
+ lazy_gen(N) -&gt;
+ {generator,
+ fun () -&gt;
+ if N &gt; 0 -&gt;
+ [?_test(...)
+ | lazy_gen(N-1)];
+ true -&gt;
+ []
+ end
+ end}.</pre>
+
+<p>When EUnit traverses the test representation in order to run the tests,
+the new generator will not be called to produce the next test until the
+previous test has been executed.</p>
+
+Note that it is easiest to write this kind of recursive generator using
+a help function, like the <code>lazy_gen/1</code> function above. It can also be
+written using a recursive fun, if you prefer to not clutter your
+function namespace and are comfortable with writing that kind of code.
+
+<hr>
+<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
+<p><i>Generated by EDoc, Apr 22 2009, 22:37:19.</i></p>
+</body>
+</html>
diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc
new file mode 100644
index 0000000000..2583f0be25
--- /dev/null
+++ b/lib/eunit/doc/overview.edoc
@@ -0,0 +1,1028 @@
+
+ -*- html -*-
+
+ EUnit overview page
+
+@title EUnit - a Lightweight Unit Testing Framework for Erlang
+
+@author Richard Carlsson <[email protected]>
+ [http://user.it.uu.se/~richardc/]
+@author Micka�l R�mond <[email protected]>
+ [http://www.process-one.net/]
+@copyright 2004-2007 Micka�l R�mond, Richard Carlsson
+@version {@version}, {@date} {@time}
+
+@doc EUnit is a unit testing framework for Erlang. It is very powerful
+and flexible, is easy to use, and has small syntactical overhead.
+
+<ul>
+<li>{@section Unit testing}</li>
+<li>{@section Terminology}</li>
+<li>{@section Getting started}</li>
+<li>{@section EUnit macros}</li>
+<li>{@section EUnit test representation}</li>
+</ul>
+
+EUnit builds on ideas from the family of unit testing frameworks for
+Object Oriented languages that originated with JUnit by Beck and Gamma
+(and Beck's previous framework SUnit for Smalltalk). However, EUnit uses
+techniques more adapted to functional and concurrent programming, and is
+typically less verbose than its relatives.
+
+Although EUnit uses many preprocessor macros, they have been designed to
+be as nonintrusive as possible, and should not cause conflicts with
+existing code. Adding EUnit tests to a module should thus not normally
+require changing existing code. Furthermore, tests that only exercise
+the exported functions of a module can always be placed in a completely
+separate module, avoiding any conflicts entirely.
+
+== Unit testing ==
+
+Unit Testing is testing of individual program "units" in relative
+isolation. There is no particular size requirement: a unit can be a
+function, a module, a process, or even a whole application, but the most
+typical testing units are individual functions or modules. In order to
+test a unit, you specify a set of individual tests, set up the smallest
+necessary environment for being able to run those tests (often, you
+don't need to do any setup at all), you run the tests and collect the
+results, and finally you do any necessary cleanup so that the test can
+be run again later. A Unit Testing Framework tries to help you in each
+stage of this process, so that it is easy to write tests, easy to run
+them, and easy to see which tests failed (so you can fix the bugs).
+
+=== Advantages of unit testing ===
+
+<dl>
+ <dt>Reduces the risks of changing the program</dt>
+ <dd>Most programs will be modified during their lifetime: bugs will be
+ fixed, features will be added, optimizations may become necessary, or
+ the code will need to be refactored or cleaned up in other ways to
+ make it easier to work with. But every change to a working program is
+ a risk of introducing new bugs - or reintroducing bugs that had
+ previously been fixed. Having a set of unit tests that you can run
+ with very little effort makes it easy to know that the code still
+ works as it should (this use is called <em>regression testing</em>;
+ see {@section Terminology}). This goes a long way to reduce the
+ resistance to changing and refactoring code.</dd>
+ <dt>Helps guide and speed up the development process</dt>
+ <dd>By focusing on getting the code to pass the tests, the programmer
+ can become more productive, not overspecify or get lost in premature
+ optimizations, and create code that is correct from the very beginning
+ (so-called <em>test-driven development</em>; see {@section
+ Terminology}).</dd>
+ <dt>Helps separate interface from implementation</dt>
+ <dd>When writing tests, the programmer may discover dependencies
+ (in order to get the tests to run) that ought not to be there, and
+ which need to be abstracted away to get a cleaner design. This helps
+ eliminate bad dependencies before they spread throughout the
+ code.</dd>
+ <dt>Makes component integration easier</dt>
+ <dd>By testing in a bottom-up fashion, beginning with the smallest
+ program units and creating a confidence in that they work as they
+ should, it becomes easier to test that a higher-level component,
+ consisting of several such units, also behaves according to
+ specification (known as <em>integration testing</em>; see {@section
+ Terminology}).</dd>
+ <dt>Is self-documenting</dt>
+ <dd>The tests can be read as documentation, typically showing both
+ examples of correct and incorrect usage, along with the expected
+ consequences.</dd>
+</dl>
+
+== Terminology ==
+
+<dl>
+ <dt>Unit testing</dt>
+ <dd>Testing that a program unit behaves as it is supposed to do (in
+ itself), according to its specifications. Unit tests have an important
+ function as regression tests, when the program later is modified for
+ some reason, since they check that the program still behaves according
+ to specification.</dd>
+ <dt>Regression testing</dt>
+ <dd>Running a set of tests after making changes to a program, to check
+ that the program behaves as it did before the changes (except, of
+ course, for any intentional changes in behaviour). Unit tests are
+ important as regression tests, but regression testing can involve more
+ than just unit testing, and may also test behaviour that might not be
+ part of the normal specification (such as bug-for-bug-compatibility).
+ </dd>
+ <dt>Integration testing</dt>
+ <dd>Testing that a number of individually developed program units
+ (assumed to already have been separately unit tested) work together as
+ expected. Depending on the system being developed, integration testing
+ may be as simple as "just another level of unit testing", but might
+ also involve other kinds of tests (compare <em>system testing</em>).
+</dd>
+ <dt>System testing</dt>
+ <dd>Testing that a complete system behaves according to its
+ specification. Specifically, system testing should not require knowing
+ any details about the implementation. It typically involves testing
+ many different aspects of the system behaviour apart from the basic
+ functionality, such as performance, usability, and reliability.</dd>
+ <dt>Test-driven development</dt>
+ <dd>A program development technique where you continuously write tests
+ <em>before</em> you implement the code that is supposed to pass those
+ tests. This can help you focus on solving the right problems, and not
+ make a more complicated implementation than necessary, by letting the
+ unit tests determine when a program is "done": if it fulfils its
+ specifications, there is no need to keep adding functionality.</dd>
+ <dt>Mock object</dt>
+ <dd>Sometimes, testing some unit `A' (e.g., a function) requires that
+ it collaborates somehow with some other unit `B' (perhaps being passed
+ as an argument, or by reference) - but `B' has not been implemented
+ yet. A "mock object" - an object which, for the purposes of testing
+ `A', looks and behaves like a real `B' - might then be used instead.
+ (This is of course only useful if it would be significantly more work
+ to implement a real `B' than to create a mock object.)</dd>
+ <dt>Test case</dt>
+ <dd>A single, well-defined test, that somehow can be uniquely
+ identified. When executed, the test case either <em>passes</em> or
+ <em>fails</em>; the test report should identify exactly which test
+ cases failed.</dd>
+ <dt>Test suite</dt>
+ <dd>A collection of test cases, generally with a specific, common
+ target for testing, such as a single function, module, or subsystem. A
+ test suite may also be recursively composed by smaller test
+ suites.</dd>
+</dl>
+
+== Getting started ==
+<ul>
+ <li>{@section Including the EUnit header file}</li>
+ <li>{@section Writing simple test functions}</li>
+ <li>{@section Running EUnit}</li>
+ <li>{@section Writing test generating functions}</li>
+ <li>{@section An example}</li>
+ <li>{@section Disabling testing}</li>
+ <li>{@section Avoiding compile-time dependency on EUnit}</li>
+</ul>
+
+=== Including the EUnit header file ===
+
+The simplest way to use EUnit in an Erlang module is to add the
+following line at the beginning of the module (after the `-module'
+declaration, but before any function definitions):
+```-include_lib("eunit/include/eunit.hrl").'''
+
+This will have the following effect:
+<ul>
+ <li>Creates an exported function `test()' (unless testing is turned
+ off, and the module does not already contain a test() function), that
+ can be used to run all the unit tests defined in the module</li>
+ <li>Causes all functions whose names match `..._test()' or `..._test_()'
+ to be automatically exported from the module (unless testing is
+ turned off, or the `EUNIT_NOAUTO' macro is defined)</li>
+ <li>Makes all the preprocessor macros of EUnit available, to help
+ writing tests</li>
+</ul>
+
+<strong>Note:</strong> For `-include_lib(...)' to work, the Erlang
+module search path <em>must</em> contain a directory whose name ends in
+`eunit/ebin' (pointing to the `ebin' subdirectory of the EUnit
+installation directory). If EUnit is installed as `lib/eunit' under your
+Erlang/OTP system directory, its `ebin' subdirectory will be
+automatically added to the search path when Erlang starts. Otherwise,
+you need to add the directory explicitly, by passing a `-pa' flag to the
+`erl' or `erlc' command. For example, a Makefile could contain the
+following action for compiling `.erl' files:
+```erlc -pa "path/to/eunit/ebin" $(ERL_COMPILE_FLAGS) -o$(EBIN) $<'''
+or if you want Eunit to always be available when you run Erlang
+interactively, you can add a line like the following to your
+`$HOME/.erlang' file:
+```code:add_path("/path/to/eunit/ebin").'''
+
+=== Writing simple test functions ===
+
+The EUnit framework makes it extremely easy to write unit tests in
+Erlang. There are a few different ways of writing them, though, so we
+start with the simplest:
+
+A function with a name ending in `..._test()' is recognized by EUnit as
+a simple test function - it takes no arguments, and its execution either
+succeeds (returning some arbitrary value that EUnit will throw away), or
+fails by throwing an exception of some kind (or by not terminating, in
+which case it will be aborted after a while).
+
+An example of a simple test function could be the following:
+```reverse_test() -> lists:reverse([1,2,3]).'''
+This just tests that the function `lists:reverse(List)' does not crash
+when `List' is `[1,2,3]'. It is not a great test, but many people write
+simple functions like this one to test the basic functionality of their
+code, and those tests can be used directly by EUnit, without changes,
+as long as their function names match.
+
+==== Use exceptions to signal failure ====
+
+To write more interesting tests, we need to make them crash (throw an
+exception) when they don't get the result they expect. A simple way of
+doing this is to use pattern matching with `=', as in the following
+examples:
+```reverse_nil_test() -> [] = lists:reverse([]).
+ reverse_one_test() -> [1] = lists:reverse([1]).
+ reverse_two_test() -> [2,1] = lists:reverse([1,2]).
+'''
+If there was some bug in `lists:reverse/1' that made it return something
+other than `[2,1]' when it got `[1,2]' as input, then the last test
+above would throw a `badmatch' error. The first two (we assume they do
+not get a `badmatch') would simply return `[]' and `[1]', respectively,
+so both succeed. (Note that EUnit is not psychic: if you write a test
+that returns a value, even if it is the wrong value, EUnit will consider
+it a success. You must make sure that the test is written so that it
+causes a crash if the result is not what it should be.)
+
+==== Using assert macros ====
+
+If you want to use Boolean operators for your tests, the `assert'
+macro comes in handy (see {@section EUnit macros} for details):
+```length_test() -> ?assert(length([1,2,3]) =:= 3).'''
+The `?assert(Expression)' macro will evaluate `Expression', and if that
+does not evaluate to `true', it will throw an exception; otherwise it
+just returns `ok'. In the above example, the test will thus fail if the
+call to `length' does not return 3.
+
+=== Running EUnit ===
+
+If you have added the declaration
+`-include_lib("eunit/include/eunit.hrl")' to your module, as described
+above, you only need to compile the module, and run the automatically
+exported function `test()'. For example, if your module was named `m',
+then calling `m:test()' will run EUnit on all the tests defined in the
+module. You do not need to write `-export' declarations for the test
+functions. This is all done by magic.
+
+You can also use the function {@link eunit:test/1} to run arbitrary
+tests, for example to try out some more advanced test descriptors (see
+{@section EUnit test representation}). For example, running
+``eunit:test(m)'' does the same thing as the auto-generated function
+``m:test()'', while ``eunit:test({inparallel, m})'' runs the same test
+cases but executes them all in parallel.
+
+==== Putting tests in separate modules ====
+
+If you want to separate your test code from your normal code (at least
+for testing the exported functions), you can simply write the test
+functions in a module named `m_tests' (note: not `m_test'), if your
+module is named `m'. Then, whenever you ask EUnit to test the module
+`m', it will also look for the module `m_tests' and run those tests as
+well. See `ModuleName' in the section {@section Primitives} for details.
+
+==== EUnit captures standard output ====
+
+If your test code writes to the standard output, you may be surprised to
+see that the text does not appear on the console when the tests are
+running. This is because EUnit captures all standard output from test
+functions (this also includes setup and cleanup functions, but not
+generator functions), so that it can be included in the test report if
+errors occur. To bypass EUnit and print text directly to the console
+while testing, you can write to the `user' output stream, as in
+`io:format(user, "~w", [Term])'. The recommended way of doing this is to
+use the EUnit {@section Debugging macros}, which make it much simpler.
+
+=== Writing test generating functions ===
+
+A drawback of simple test functions is that you must write a separate
+function (with a separate name) for each test case. A more compact way
+of writing tests (and much more flexible, as we shall see), is to write
+functions that <em>return</em> tests, instead of <em>being</em> tests.
+
+A function with a name ending in `..._test_()' (note the final
+underscore) is recognized by EUnit as a <em>test generator</em>
+function. Test generators return a <em>representation</em> of a <em>set
+of tests</em> to be executed by EUnit.
+
+==== Representing a test as data ====
+
+The most basic representation of a test is a single fun-expression that
+takes no arguments. For example, the following test generator:
+```basic_test_() ->
+ fun () -> ?assert(1 + 1 =:= 2) end.'''
+will have the same effect as the following simple test:
+```simple_test() ->
+ ?assert(1 + 1 =:= 2).'''
+(in fact, EUnit will handle all simple tests just like it handles
+fun-expressions: it will put them in a list, and run them one by one).
+
+==== Using macros to write tests ====
+
+To make tests more compact and readable, as well as automatically add
+information about the line number in the source code where a test
+occurred (and reduce the number of characters you have to type), you can
+use the `_test' macro (note the initial underscore character), like
+this:
+```basic_test_() ->
+ ?_test(?assert(1 + 1 =:= 2)).'''
+The `_test' macro takes any expression (the "body") as argument, and
+places it within a fun-expression (along with some extra information).
+The body can be any kind of test expression, just like the body of a
+simple test function.
+
+==== Underscore-prefixed macros create test objects ====
+
+But this example can be made even shorter! Most test macros, such as the
+family of `assert' macros, have a corresponding form with an initial
+underscore character, which automatically adds a `?_test(...)' wrapper.
+The above example can then simply be written:
+```basic_test_() ->
+ ?_assert(1 + 1 =:= 2).'''
+which has exactly the same meaning (note the `_assert' instead of
+`assert'). You can think of the initial underscore as signalling
+<em>test object</em>.
+
+=== An example ===
+
+Sometimes, an example says more than a thousand words. The following
+small Erlang module shows how EUnit can be used in practice.
+```-module(fib).
+ -export([fib/1]).
+ -include_lib("eunit/include/eunit.hrl").
+
+ fib(0) -> 1;
+ fib(1) -> 1;
+ fib(N) when N > 1 -> fib(N-1) + fib(N-2).
+
+ fib_test_() ->
+ [?_assert(fib(0) =:= 1),
+ ?_assert(fib(1) =:= 1),
+ ?_assert(fib(2) =:= 2),
+ ?_assert(fib(3) =:= 3),
+ ?_assert(fib(4) =:= 5),
+ ?_assert(fib(5) =:= 8),
+ ?_assertException(error, function_clause, fib(-1)),
+ ?_assert(fib(31) =:= 2178309)
+ ].'''
+
+(Author's note: When I first wrote this example, I happened to write a
+`*' instead of `+' in the `fib' function. Of course, this showed up
+immediately when I ran the tests.)
+
+See {@section EUnit test representation} for a full list of all the ways
+you can specify test sets in EUnit.
+
+=== Disabling testing ===
+
+Testing can be turned off by defining the `NOTEST' macro when compiling,
+for example as an option to `erlc', as in:
+```erlc -DNOTEST my_module.erl'''
+or by adding a macro definition to the code, <em>before the EUnit header
+file is included</em>:
+```-define(NOTEST, 1).'''
+(the value is not important, but should typically be 1 or `true').
+Note that unless the `EUNIT_NOAUTO' macro is defined, disabling testing
+will also automatically strip all test functions from the code, except
+for any that are explicitly declared as exported.
+
+For instance, to use EUnit in your application, but with testing turned
+off by default, put the following lines in a header file:
+```-define(NOTEST, true).
+ -include_lib("eunit/include/eunit.hrl").'''
+and then make sure that every module of your application includes that
+header file. This means that you have a only a single place to modify in
+order to change the default setting for testing. To override the `NOTEST'
+setting without modifying the code, you can define `TEST' in a compiler
+option, like this:
+```erlc -DTEST my_module.erl'''
+
+See {@section Compilation control macros} for details about these
+macros.
+
+=== Avoiding compile-time dependency on EUnit ===
+
+If you are distributing the source code for your application for other
+people to compile and run, you probably want to ensure that the code
+compiles even if EUnit is not available. Like the example in the
+previous section, you can put the following lines in a common header
+file:
+```-ifdef(TEST).
+ -include_lib("eunit/include/eunit.hrl").
+ -endif.'''
+and, of course, also make sure that you place all test code that uses
+EUnit macros within `-ifdef(TEST)' or `-ifdef(EUNIT)' sections.
+
+
+== EUnit macros ==
+
+Although all the functionality of EUnit is available even without the
+use of preprocessor macros, the EUnit header file defines a number of
+such macros in order to make it as easy as possible to write unit tests
+as compactly as possible and without getting too many details in the
+way.
+
+Except where explicitly stated, using EUnit macros will never introduce
+run-time dependencies on the EUnit library code, regardless of whether
+your code is compiled with testing enabled or disabled.
+
+<ul>
+<li>{@section Basic macros}</li>
+<li>{@section Compilation control macros}</li>
+<li>{@section Utility macros}</li>
+<li>{@section Assert macros}</li>
+<li>{@section Macros for running external commands}</li>
+<li>{@section Debugging macros}</li>
+</ul>
+
+=== Basic macros ===
+
+<dl>
+<dt>`_test(Expr)'</dt>
+<dd>Turns `Expr' into a "test object", by wrapping it in a
+fun-expression and a source line number. Technically, this is the same
+as `{?LINE, fun () -> (Expr) end}'.
+</dd>
+</dl>
+
+=== Compilation control macros ===
+
+<dl>
+<dt>`EUNIT'</dt>
+<dd>This macro is always defined to `true' whenever EUnit is enabled at
+compile time. This is typically used to place testing code within
+conditional compilation, as in:
+```-ifdef(EUNIT).
+ % test code here
+ ...
+ -endif.'''
+e.g., to ensure that the code can be compiled without including the
+EUnit header file, when testing is disabled. See also the macros `TEST'
+and `NOTEST'.
+</dd>
+
+<dt>`EUNIT_NOAUTO'</dt>
+<dd>If this macro is defined, the automatic exporting or stripping of
+test functions will be disabled.
+</dd>
+
+<dt>`TEST'</dt>
+<dd>This macro is always defined (to `true', unless previously defined
+by the user to have another value) whenever EUnit is enabled at compile
+time. This can be used to place testing code within conditional
+compilation; see also the macros `NOTEST' and `EUNIT'.
+
+For testing code that is strictly dependent on EUnit, it may be
+preferable to use the `EUNIT' macro for this purpose, while for code
+that uses more generic testing conventions, using the `TEST' macro may
+be preferred.
+
+The `TEST' macro can also be used to override the `NOTEST' macro. If
+`TEST' is defined <em>before</em> the EUnit header file is
+included (even if `NOTEST' is also defined), then the code will be
+compiled with EUnit enabled.
+</dd>
+
+<dt>`NOTEST'</dt>
+<dd>This macro is always defined (to `true', unless previously defined
+by the user to have another value) whenever EUnit is <em>disabled</em>
+at compile time. (Compare the `TEST' macro.)
+
+This macro can also be used for conditional compilation, but is more
+typically used to disable testing: If `NOTEST' is defined
+<em>before</em> the EUnit header file is included, and `TEST'
+is <em>not</em> defined, then the code will be compiled with EUnit
+disabled. See also {@section Disabling testing}.
+</dd>
+
+<dt>`NOASSERT'</dt>
+<dd>If this macro is defined, the assert macros will have no effect,
+when testing is also disabled. See {@section Assert macros}. When
+testing is enabled, the assert macros are always enabled automatically
+and cannot be disabled.
+</dd>
+
+<dt>`ASSERT'</dt>
+<dd>If this macro is defined, it overrides the NOASSERT macro, forcing
+the assert macros to always be enabled regardless of other settings.
+</dd>
+
+<dt>`NODEBUG'</dt>
+<dd>If this macro is defined, the debugging macros will have no effect.
+See {@section Debugging macros}. `NODEBUG' also implies `NOASSERT',
+unless testing is enabled.
+</dd>
+
+<dt>`DEBUG'</dt>
+<dd>If this macro is defined, it overrides the NODEBUG macro, forcing
+the debugging macros to be enabled.
+</dd>
+</dl>
+
+=== Utility macros ===
+
+The following macros can make tests more compact and readable:
+
+<dl>
+<dt>`LET(Var,Arg,Expr)'</dt>
+<dd>Creates a local binding `Var = Arg' in `Expr'. (This is the same as
+`(fun(Var)->(Expr)end)(Arg)'.) Note that the binding is not exported
+outside of `Expr', and that within `Expr', this binding of `Var' will
+shadow any binding of `Var' in the surrounding scope.
+</dd>
+<dt>`IF(Cond,TrueCase,FalseCase)'</dt>
+<dd>Evaluates `TrueCase' if `Cond' evaluates to `true', or otherwise
+evaluates `FalseCase' if `Cond' evaluates to `false'. (This is the same
+as `(case (Cond) of true->(TrueCase); false->(FalseCase) end)'.) Note
+that it is an error if `Cond' does not yield a boolean value.
+</dd>
+</dl>
+
+=== Assert macros ===
+
+(Note that these macros also have corresponding forms which start with
+an "`_'" (underscore) character, as in `?_assert(BoolExpr)', that create
+a "test object" instead of performing the test immediately. This is
+equivalent to writing `?_test(assert(BoolExpr))', etc.)
+
+If the macro `NOASSERT' is defined before the EUnit header file is
+included, these macros have no effect when testing is also disabled; see
+{@section Compilation control macros} for details.
+
+<dl>
+<dt>`assert(BoolExpr)'</dt>
+<dd>Evaluates the expression `BoolExpr', if testing is enabled. Unless
+the result is `true', an informative exception will be generated. If
+there is no exception, the result of the macro expression is the atom
+`ok', and the value of `BoolExpr' is discarded. If testing is disabled,
+the macro will not generate any code except the atom `ok', and
+`BoolExpr' will not be evaluated.
+
+Typical usage:
+```?assert(f(X, Y) =:= [])'''
+
+The `assert' macro can be used anywhere in a program, not just in unit
+tests, to check pre/postconditions and invariants. For example:
+```some_recursive_function(X, Y, Z) ->
+ ?assert(X + Y > Z),
+ ...'''
+</dd>
+<dt>`assertNot(BoolExpr)'</dt>
+<dd>Equivalent to `assert(not (BoolExpr))'.
+</dd>
+<dt>`assertMatch(GuardedPattern, Expr)'</dt>
+<dd>Evaluates `Expr' and matches the result against `GuardedPattern', if
+testing is enabled. If the match fails, an informative exception will be
+generated; see the `assert' macro for further details. `GuardedPattern'
+can be anything that you can write on the left hand side of the `->'
+symbol in a case-clause, except that it cannot contain comma-separated
+guard tests.
+
+The main reason for using `assertMatch' also for simple matches, instead
+of matching with `=', is that it produces more detailed error messages.
+
+Examples:
+```?assertMatch({found, {fred, _}}, lookup(bloggs, Table))'''
+```?assertMatch([X|_] when X > 0, binary_to_list(B))'''
+</dd>
+<dt>`assertEqual(Expect, Expr)'</dt>
+<dd>Evaluates the expressions `Expect' and `Expr' and compares the
+results for equality, if testing is enabled. If the values are not
+equal, an informative exception will be generated; see the `assert'
+macro for further details.
+
+`assertEqual' is more suitable than than `assertMatch' when the
+left-hand side is a computed value rather than a simple pattern, and
+gives more details than `?assert(Expect =:= Expr)'.
+
+Examples:
+```?assertEqual("b" ++ "a", lists:reverse("ab"))'''
+```?assertEqual(foo(X), bar(Y))'''
+</dd>
+<dt>`assertException(ClassPattern, TermPattern, Expr)'</dt>
+<dt>`assertError(TermPattern, Expr)'</dt>
+<dt>`assertExit(TermPattern, Expr)'</dt>
+<dt>`assertThrow(TermPattern, Expr)'</dt>
+<dd>Evaluates `Expr', catching any exception and testing that it matches
+the expected `ClassPattern:TermPattern'. If the match fails, or if no
+exception is thrown by `Expr', an informative exception will be
+generated; see the `assert' macro for further details. The
+`assertError', `assertExit', and `assertThrow' macros, are equivalent to
+using `assertException' with a `ClassPattern' of `error', `exit', or
+`throw', respectively.
+
+Examples:
+```?assertError(badarith, X/0)'''
+```?assertExit(normal, exit(normal))'''
+```?assertException(throw, {not_found,_}, throw({not_found,42}))'''
+</dd>
+</dl>
+
+=== Macros for running external commands ===
+
+Keep in mind that external commands are highly dependent on the
+operating system. You can use the standard library function `os:type()'
+in test generator functions, to produce different sets of tests
+depending on the current operating system.
+
+Note: these macros introduce a run-time dependency on the EUnit library
+code, if compiled with testing enabled.
+
+<dl>
+<dt>`assertCmd(CommandString)'</dt>
+<dd>Runs `CommandString' as an external command, if testing is enabled.
+Unless the returned status value is 0, an informative exception will be
+generated. If there is no exception, the result of the macro expression
+is the atom `ok'. If testing is disabled, the macro will not generate
+any code except the atom `ok', and the command will not be executed.
+
+Typical usage:
+```?assertCmd("mkdir foo")'''
+</dd>
+<dt>`assertCmdStatus(N, CommandString)'</dt>
+<dd>Like the `assertCmd(CommandString)' macro, but generates an
+exception unless the returned status value is `N'.
+</dd>
+<dt>`assertCmdOutput(Text, CommandString)'</dt>
+<dd>Runs `CommandString' as an external command, if testing is enabled.
+Unless the output produced by the command exactly matches the specified
+string `Text', an informative exception will be generated. (Note that
+the output is normalized to use a single LF character as line break on
+all platforms.) If there is no exception, the result of the macro
+expression is the atom `ok'. If testing is disabled, the macro will not
+generate any code except the atom `ok', and the command will not be
+executed.
+</dd>
+<dt>`cmd(CommandString)'</dt>
+<dd>Runs `CommandString' as an external command. Unless the returned
+status value is 0 (indicating success), an informative exception will be
+generated; otherwise, the result of the macro expression is the output
+produced by the command, as a flat string. The output is normalized to
+use a single LF character as line break on all platforms.
+
+This macro is useful in the setup and cleanup sections of fixtures,
+e.g., for creating and deleting files or perform similar operating
+system specific tasks, to make sure that the test system is informed of
+any failures.
+
+A Unix-specific example:
+```{setup,
+ fun () -> ?cmd("mktemp") end,
+ fun (FileName) -> ?cmd("rm " ++ FileName) end,
+ ...}'''
+</dd>
+</dl>
+
+=== Debugging macros ===
+
+To help with debugging, EUnit defines several useful macros for printing
+messages directly to the console (rather than to the standard output).
+Furthermore, these macros all use the same basic format, which includes
+the file and line number where they occur, making it possible in some
+development environments (e.g., when running Erlang in an Emacs buffer)
+to simply click on the message and jump directly to the corresponding
+line in the code.
+
+If the macro `NODEBUG' is defined before the EUnit header file is
+included, these macros have no effect; see
+{@section Compilation control macros} for details.
+
+<dl>
+<dt>`debugHere'</dt>
+<dd>Just prints a marker showing the current file and line number. Note
+that this is an argument-less macro. The result is always `ok'.</dd>
+<dt>`debugMsg(Text)'</dt>
+<dd>Outputs the message `Text' (which can be a plain string, an IO-list,
+or just an atom). The result is always `ok'.</dd>
+<dt>`debugFmt(FmtString, Args)'</dt>
+<dd>This formats the text like `io:format(FmtString, Args)' and outputs
+it like `debugMsg'. The result is always `ok'.</dd>
+<dt>`debugVal(Expr)'</dt>
+<dd>Prints both the source code for `Expr' and its current value. E.g.,
+`?debugVal(f(X))' might be displayed as "`f(X) = 42'". (Large terms are
+shown truncated.) The result is always the value of `Expr', so this
+macro can be wrapped around any expression to display its value when
+the code is compiled with debugging enabled.</dd>
+<dt>`debugTime(Text,Expr)'</dt>
+<dd>Prints `Text' and the wall clock time for evaluation of `Expr'. The
+result is always the value of `Expr', so this macro can be wrapped
+around any expression to show its run time when the code is compiled
+with debugging enabled. For example, `List1 = ?debugTime("sorting",
+lists:sort(List))' might show as "`sorting: 0.015 s'".</dd>
+
+</dl>
+
+
+== EUnit test representation ==
+
+The way EUnit represents tests and test sets as data is flexible,
+powerful, and concise. This section describes the representation in
+detail.
+
+<ul>
+<li>{@section Simple test objects}</li>
+<li>{@section Test sets and deep lists}</li>
+<li>{@section Titles}</li>
+<li>{@section Primitives}</li>
+<li>{@section Control}</li>
+<li>{@section Fixtures}</li>
+<li>{@section Lazy generators}</li>
+</ul>
+
+=== Simple test objects ===
+
+A <em>simple test object</em> is one of the following:
+<ul>
+ <li>A nullary functional value (i.e., a fun that takes zero
+ arguments). Examples:
+```fun () -> ... end'''
+```fun some_function/0'''
+```fun some_module:some_function/0'''
+ </li>
+ <li>A pair of atoms `{ModuleName, FunctionName}', referring to the
+ function `ModuleName:FunctionName/0'</li>
+ <li>A pair `{LineNumber, SimpleTest}', where `LineNumber' is a
+ nonnegative integer and `SimpleTest' is another simple test
+ object. `LineNumber' should indicate the source line of the test.
+ Pairs like this are usually only created via `?_test(...)' macros;
+ see {@section Basic macros}.</li>
+</ul>
+In brief, a simple test object consists of a single function that takes
+no arguments (possibly annotated with some additional metadata, i.e., a
+line number). Evaluation of the function either <em>succeeds</em>, by
+returning some value (which is ignored), or <em>fails</em>, by throwing
+an exception.
+
+=== Test sets and deep lists ===
+
+A test set can be easily created by placing a sequence of test objects
+in a list. If `T_1', ..., `T_N' are individual test objects, then `[T_1,
+..., T_N]' is a test set consisting of those objects (in that order).
+
+Test sets can be joined in the same way: if `S_1', ..., `S_K' are test
+sets, then `[S_1, ..., S_K]' is also a test set, where the tests of
+`S_i' are ordered before those of `S_(i+1)', for each subset `S_i'.
+
+Thus, the main representation of test sets is <em>deep lists</em>, and
+a simple test object can be viewed as a test set containing only a
+single test; there is no difference between `T' and `[T]'.
+
+A module can also be used to represent a test set; see `ModuleName'
+under {@section Primitives} below.
+
+=== Titles ===
+
+Any test or test set `T' can be annotated with a title, by wrapping it
+in a pair `{Title, T}', where `Title' is a string. For convenience, any
+test which is normally represented using a tuple can simply be given a
+title string as the first element, i.e., writing `{"The Title", ...}'
+instead of adding an extra tuple wrapper as in `{"The Title", {...}}'.
+
+
+=== Primitives ===
+
+The following are primitives, which do not contain other test sets as
+arguments:
+<dl>
+<dt>`ModuleName::atom()'
+</dt>
+<dd>A single atom represents a module name, and is equivalent to
+`{module, ModuleName}'. This is often used as in the call
+`eunit:test(some_module)'.
+</dd>
+<dt>`{module, ModuleName::atom()}'
+</dt>
+<dd>This composes a test set from the exported test functions of the
+named module, i.e., those functions with arity zero whose names end
+with `_test' or `_test_'. Basically, the `..._test()' functions become
+simple tests, while the `..._test_()' functions become generators.
+
+In addition, EUnit will also look for another module whose name is
+`ModuleName' plus the suffix `_tests', and if it exists, all the tests
+from that module will also be added. (If `ModuleName' already contains
+the suffix `_tests', this is not done.) E.g., the specification
+`{module, mymodule}' will run all tests in the modules `mymodule' and
+`mymodule_tests'. Typically, the `_tests' module should only contain
+test cases that use the public interface of the main module (and no
+other code).
+</dd>
+<dt>`{application, AppName::atom(), Info::list()}'
+</dt>
+<dd>This is a normal Erlang/OTP application descriptor, as found in an
+ `.app' file. The resulting test set consists of the modules listed in
+ the `modules' entry in `Info'.
+</dd>
+<dt>`{application, AppName::atom()}'
+</dt>
+<dd>This creates a test set from all the modules belonging to the
+specified application, by consulting the application's `.app' file
+(see `{file, FileName}'), or if no such file exists, by testing all
+object files in the application's <tt>ebin</tt>-directory (see `{dir,
+Path}'); if that does not exist, the `code:lib_dir(AppName)' directory
+is used.
+</dd>
+<dt>`Path::string()'
+</dt>
+<dd>A single string represents the path of a file or directory, and is
+equivalent to `{file, Path}', or `{dir, Path}', respectively, depending
+on what `Path' refers to in the file system.
+</dd>
+<dt>`{file, FileName::string()}'
+</dt>
+<dd>If `FileName' has a suffix that indicates an object file (`.beam'),
+EUnit will try to reload the module from the specified file and test it.
+Otherwise, the file is assumed to be a text file containing test
+specifications, which will be read using the standard library function
+`file:path_consult/2'.
+
+Unless the file name is absolute, the file is first searched for
+relative to the current directory, and then using the normal search path
+(`code:get_path()'). This means that the names of typical "app" files
+can be used directly, without a path, e.g., `"mnesia.app"'.
+</dd>
+<dt>`{dir, Path::string()}'
+</dt>
+<dd>This tests all object files in the specified directory, as if they
+had been individually specified using `{file, FileName}'.
+</dd>
+<dt>`{generator, GenFun::(() -> Tests)}'
+</dt>
+<dd>The generator function `GenFun' is called to produce a test
+set.
+</dd>
+<dt>`{generator, ModuleName::atom(), FunctionName::atom()}'
+</dt>
+<dd>The function `ModuleName:FunctionName()' is called to produce a test
+set.
+</dd>
+<dt>`{with, X::any(), [AbstractTestFun::((any()) -> any())]}'
+</dt>
+<dd>Distributes the value `X' over the unary functions in the list,
+turning them into nullary test functions. An `AbstractTestFun' is like
+an ordinary test fun, but takes one argument instead of zero - it's
+basically missing some information before it can be a proper test. In
+practice, `{with, X, [F_1, ..., F_N]}' is equivalent to `[fun () ->
+F_1(X) end, ..., fun () -> F_N(X) end]'. This is particularly useful if
+your abstract test functions are already implemented as proper
+functions: `{with, FD, [fun filetest_a/1, fun filetest_b/1, fun
+filetest_c/1]}' is equivalent to `[fun () -> filetest_a(FD) end, fun ()
+-> filetest_b(FD) end, fun () -> filetest_c(FD) end]', but much more
+compact. See also {@section Fixtures}, below.
+</dd>
+</dl>
+
+=== Control ===
+
+The following representations control how and where tests are executed:
+<dl>
+<dt>`{spawn, Tests}'</dt>
+<dd>Runs the specified tests in a separate subprocess, while the current
+test process waits for it to finish. This is useful for tests that need
+a fresh, isolated process state. (Note that EUnit always starts at least
+one such a subprocess automatically; tests are never executed by the
+caller's own process.)</dd>
+<dt>`{spawn, Node::atom(), Tests}'</dt>
+<dd>Like `{spawn, Tests}', but runs the specified tests on the given
+Erlang node.</dd>
+<dt>`{timeout, Time::number(), Tests}'</dt>
+<dd>Runs the specified tests under the given timeout. Time is in
+seconds; e.g., 60 means one minute and 0.1 means 1/10th of a second. If
+the timeout is exceeded, the unfinished tests will be forced to
+terminate. Note that if a timeout is set around a fixture, it includes
+the time for setup and cleanup, and if the timeout is triggered, the
+entire fixture is abruptly terminated (without running the
+cleanup).</dd>
+<dt>`{inorder, Tests}'</dt>
+<dd>Runs the specified tests in strict order. Also see `{inparallel,
+Tests}'. By default, tests are neither marked as `inorder' or
+`inparallel', but may be executed as the test framework chooses.</dd>
+<dt>`{inparallel, Tests}'</dt>
+<dd>Runs the specified tests in parallel (if possible). Also see
+`{inorder, Tests}'.</dd>
+<dt>`{inparallel, N::integer(), Tests}'</dt>
+<dd>Like `{inparallel, Tests}', but running no more than `N' subtests
+simultaneously.</dd>
+</dl>
+
+=== Fixtures ===
+
+A "fixture" is some state that is necessary for a particular set of
+tests to run. EUnit's support for fixtures makes it easy to set up such
+state locally for a test set, and automatically tear it down again when
+the test set is finished, regardless of the outcome (success, failures,
+timeouts, etc.).
+
+To make the descriptions simpler, we first list some definitions:
+<center>
+<table border="0" cellspacing="4">
+<tr>
+<td>`Setup'</td><td>`() -> (R::any())'</td>
+</tr>
+<tr>
+<td>`SetupX'</td><td>`(X::any()) -> (R::any())'</td>
+</tr>
+<tr>
+<td>`Cleanup'</td><td>`(R::any()) -> any()'</td>
+</tr>
+<tr>
+<td>`CleanupX'</td><td>`(X::any(), R::any()) -> any()'</td>
+</tr>
+<tr>
+<td>`Instantiator'</td><td>`((R::any()) -> Tests) | {with, [AbstractTestFun::((any()) -> any())]}'</td>
+</tr>
+<tr>
+<td>`Where'</td><td>`local | spawn | {spawn, Node::atom()}'</td>
+</tr>
+</table>
+</center>
+(these are explained in more detail further below.)
+
+The following representations specify fixture handling for test sets:
+<dl>
+<dt>`{setup, Setup, Tests | Instantiator}'</dt>
+<dt>`{setup, Setup, Cleanup, Tests | Instantiator}'</dt>
+<dt>`{setup, Where, Setup, Tests | Instantiator}'</dt>
+<dt>`{setup, Where, Setup, Cleanup, Tests | Instantiator}'</dt>
+<dd>`setup' sets up a single fixture for running all of the specified
+tests, with optional teardown afterwards. The arguments are described in
+detail below.
+</dd>
+<dt>`{node, Node::atom(), Tests | Instantiator}'</dt>
+<dt>`{node, Node::atom(), Args::string(), Tests | Instantiator}'</dt>
+<dd>`node' is like `setup', but with a built-in behaviour: it starts a
+slave node for the duration of the tests. The atom `Node' should have
+the format `[email protected]', and `Args' are the optional
+arguments to the new node; see `slave:start_link/3' for details.
+</dd>
+<dt>`{foreach, Where, Setup, Cleanup, [Tests | Instantiator]}'</dt>
+<dt>`{foreach, Setup, Cleanup, [Tests | Instantiator]}'</dt>
+<dt>`{foreach, Where, Setup, [Tests | Instantiator]}'</dt>
+<dt>`{foreach, Setup, [Tests | Instantiator]}'</dt>
+<dd>`foreach' is used to set up a fixture and optionally tear it down
+afterwards, repeated for each single one of the specified test sets.
+</dd>
+<dt>`{foreachx, Where, SetupX, CleanupX,
+ Pairs::[{X::any(), ((X::any(), R::any()) -> Tests)}]}'</dt>
+<dt>`{foreachx, SetupX, CleanupX, Pairs}'</dt>
+<dt>`{foreachx, Where, SetupX, Pairs}'</dt>
+<dt>`{foreachx, SetupX, Pairs}'</dt>
+<dd>`foreachx' is like `foreach', but uses a list of pairs, each
+containing an extra argument `X' and an extended instantiator function.
+</dd>
+</dl>
+
+A `Setup' function is executed just before any of the specified tests
+are run, and a `Cleanup' function is executed when no more of the
+specified tests will be run, regardless of the reason. A `Setup'
+function takes no argument, and returns some value which will be passed
+as it is to the `Cleanup' function. A `Cleanup' function should do
+whatever necessary and return some arbitrary value, such as the atom
+`ok'. (`SetupX' and `CleanupX' functions are similar, but receive one
+additional argument: some value `X', which depends on the context.) When
+no `Cleanup' function is specified, a dummy function is used which has
+no effect.
+
+An `Instantiator' function receives the same value as the `Cleanup'
+function, i.e., the value returned by the `Setup' function. It should
+then behave much like a generator (see {@section Primitives}), and
+return a test set whose tests have been <em>instantiated</em> with the
+given value. A special case is the syntax `{with, [AbstractTestFun]}'
+which represents an instantiator function that distributes the value
+over a list of unary functions; see {@section Primitives}: `{with, X,
+[...]}' for more details.
+
+A `Where' term controls how the specified tests are executed. The
+default is `spawn', which means that the current process handles the
+setup and teardown, while the tests are executed in a subprocess.
+`{spawn, Node}' is like `spawn', but runs the subprocess on the
+specified node. `local' means that the current process will handle both
+setup/teardown and running the tests - the drawback is that if a test
+times out so that the process is killed, the <em>cleanup will not be
+performed</em>; hence, avoid this for persistent fixtures such as file
+operations. In general, 'local' should only be used when:
+<ul>
+ <li>the setup/teardown needs to be executed by the process that will
+ run the tests;</li>
+ <li>no further teardown needs to be done if the process is killed
+ (i.e., no state outside the process was affected by the setup)</li>
+</ul>
+
+=== Lazy generators ===
+
+Sometimes, it can be convenient not to produce the whole set of test
+descriptions before the testing begins; for example, if you want to
+generate a huge amount of tests that would take up too much space to
+keep in memory all at once.
+
+It is fairly easy to write a generator which, each time it is called,
+either produces an empty list if it is done, or otherwise produces a
+list containing a single test case plus a new generator which will
+produce the rest of the tests. This demonstrates the basic pattern:
+
+```lazy_test_() ->
+ lazy_gen(10000).
+
+ lazy_gen(N) ->
+ {generator,
+ fun () ->
+ if N > 0 ->
+ [?_test(...)
+ | lazy_gen(N-1)];
+ true ->
+ []
+ end
+ end}.'''
+
+When EUnit traverses the test representation in order to run the tests,
+the new generator will not be called to produce the next test until the
+previous test has been executed.
+
+Note that it is easiest to write this kind of recursive generator using
+a help function, like the `lazy_gen/1' function above. It can also be
+written using a recursive fun, if you prefer to not clutter your
+function namespace and are comfortable with writing that kind of code.
diff --git a/lib/eunit/doc/packages-frame.html b/lib/eunit/doc/packages-frame.html
new file mode 100644
index 0000000000..52b45534f5
--- /dev/null
+++ b/lib/eunit/doc/packages-frame.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>The eunit application</title>
+<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
+</head>
+<body bgcolor="white">
+<h2 class="indextitle">Packages</h2>
+<table width="100%" border="0" summary="list of packages"></table>
+</body>
+</html> \ No newline at end of file
diff --git a/lib/eunit/doc/pdf/.gitignore b/lib/eunit/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/eunit/doc/pdf/.gitignore
diff --git a/lib/eunit/doc/src/Makefile b/lib/eunit/doc/src/Makefile
new file mode 100644
index 0000000000..faf2f9a847
--- /dev/null
+++ b/lib/eunit/doc/src/Makefile
@@ -0,0 +1,174 @@
+#<copyright>
+# <year>2004-2007</year>
+# <holder>Ericsson AB, All Rights Reserved</holder>
+#</copyright>
+#<legalnotice>
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson AB.
+#</legalnotice>
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(EUNIT_VSN)
+APPLICATION=eunit
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+# ----------------------------------------------------
+# Help application directory specification
+# ----------------------------------------------------
+
+EDOC_DIR = $(ERL_TOP)/lib/edoc
+SYNTAX_TOOLS_DIR = $(ERL_TOP)/lib/syntax_tools
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+EUNIT_DIR = $(ERL_TOP)/lib/eunit/src
+EUNIT_INC_DIR = $(ERL_TOP)/lib/eunit/include
+
+EUNIT_MODULES = \
+ eunit eunit_surefire
+
+XML_APPLICATION_FILES = ref_man.xml
+
+XML_REF3_FILES = $(EUNIT_MODULES:=.xml)
+
+XML_PART_FILES = \
+ part.xml \
+ part_notes.xml
+
+XML_CHAPTER_FILES = \
+ chapter.xml
+
+XML_NOTES_FILES = \
+ notes.xml
+
+HTML_EXAMPLE_FILES =
+
+HTML_STYLESHEET_FILES = \
+ ../stylesheet.css
+
+BOOK_FILES = book.xml
+
+XML_FILES = \
+ $(BOOK_FILES) $(XML_CHAPTER_FILES) $(XML_NOTES_FILES) \
+ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
+
+
+# ----------------------------------------------------
+INFO_FILE = ../../info
+
+HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+
+EXTRA_FILES = \
+ $(DEFAULT_HTML_FILES) \
+ $(DEFAULT_GIF_FILES) \
+ $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)\
+ $(XML_NOTES_FILES:%.xml=$(HTMLDIR)/%.html)
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+DVIPS_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+
+docs: pdf html man
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: gifs $(HTML_REF_MAN_FILE)
+
+
+man: $(MAN3_FILES)
+
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+
+$(XML_REF3_FILES):
+ docb_gen -def vsn $(EUNIT_VSN) -includes $(EUNIT_INC_DIR) $(EUNIT_DIR)/$(@:%.xml=%.erl)
+
+$(XML_CHAPTER_FILES):
+ docb_gen -chapter -def vsn $(EUNIT_VSN) ../overview.edoc
+
+
+info:
+ @echo "XML_PART_FILES: $(XML_PART_FILES)"
+ @echo "XML_APPLICATION_FILES: $(XML_APPLICATION_FILES)"
+ @echo "EUNIT_XML_FILES: $(EUNIT_XML_FILES)"
+ @echo "EUNIT_MODULES: $(EUNIT_MODULES)"
+ @echo "HTML_FILES: $(HTML_FILES)"
+ @echo "HTMLDIR: $(HTMLDIR)"
+ @echo "DEFAULT_GIF_FILES: $(DEFAULT_GIF_FILES)"
+ @echo "DEFAULT_HTML_FILES: $(DEFAULT_HTML_FILES)"
+ @echo "EXTRA_FILES: $(EXTRA_FILES)"
+
+xml: $(XML_REF3_FILES) $(XML_CHAPTER_FILES)
+
+debug opt:
+
+clean clean_docs:
+ rm -rf $(HTMLDIR)/*
+ rm -f $(MAN3DIR)/*
+ rm -f $(XML_CHAPTER_FILES) *.html
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f errs core *~
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_docs_spec: docs
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(HTMLDIR)/* \
+ $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
+ $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
+
+
+release_spec:
+
+
+
diff --git a/lib/eunit/doc/src/book.xml b/lib/eunit/doc/src/book.xml
new file mode 100644
index 0000000000..4444b1dd7a
--- /dev/null
+++ b/lib/eunit/doc/src/book.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <copyright>
+ <year>2008</year>
+ <year>2008</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>EUnit</title>
+ <prepared>Richard Carlsson, Micka�l R�mond</prepared>
+ <docno></docno>
+ <date>2008-10-29</date>
+ <rev>2.0</rev>
+ <file>book.xml</file>
+ </header>
+ <insidecover>
+ </insidecover>
+ <pagetext>EUnit Application</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <parts lift="no">
+ <xi:include href="part.xml"/>
+ </parts>
+ <applications>
+ <xi:include href="ref_man.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+ <listofterms></listofterms>
+ <index></index>
+</book>
+
diff --git a/lib/eunit/doc/src/fascicules.xml b/lib/eunit/doc/src/fascicules.xml
new file mode 100644
index 0000000000..bbe1e6c5cc
--- /dev/null
+++ b/lib/eunit/doc/src/fascicules.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
+
+<fascicules>
+ <fascicule file="part" href="part_frame.html" entry="no">
+ User's Guide
+ </fascicule>
+ <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
+ Reference Manual
+ </fascicule>
+ <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
+ Release Notes
+ </fascicule>
+ <fascicule file="" href="../../../../doc/print.html" entry="no">
+ Off-Print
+ </fascicule>
+</fascicules>
+
diff --git a/lib/eunit/doc/src/make.dep b/lib/eunit/doc/src/make.dep
new file mode 100644
index 0000000000..d68f888403
--- /dev/null
+++ b/lib/eunit/doc/src/make.dep
@@ -0,0 +1,19 @@
+# ----------------------------------------------------
+# >>>> Do not edit this file <<<<
+# This file was automaticly generated by
+# /home/otp/bin/docdepend
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# TeX files that the DVI file depend on
+# ----------------------------------------------------
+
+book.dvi: book.tex chapter.tex eunit.tex part.tex ref_man.tex
+
+# ----------------------------------------------------
+# Source inlined when transforming from source to LaTeX
+# ----------------------------------------------------
+
+book.tex: ref_man.xml
+
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
new file mode 100644
index 0000000000..ac86448fab
--- /dev/null
+++ b/lib/eunit/doc/src/notes.xml
@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2008</year>
+ <year>2008</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>EUnit Release Notes</title>
+ <prepared>otp_appnotes</prepared>
+ <docno>nil</docno>
+ <date>nil</date>
+ <rev>nil</rev>
+ <file>notes.xml</file>
+ </header>
+ <p>This document describes the changes made to the EUnit application.</p>
+
+<section><title>Eunit 2.1.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation is now built with open source tools
+ (xsltproc and fop) that exists on most platforms. One
+ visible change is that the frames are removed.</p>
+ <p>
+ Own Id: OTP-8201</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Eunit 2.1.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Miscellaneous updates.</p>
+ <p>
+ Own Id: OTP-8190</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Eunit 2.1.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Miscellanous updates.</p>
+ <p>
+ Own Id: OTP-8038</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Eunit 2.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ eunit was broken in R13B.</p>
+ <p>
+ Own Id: OTP-8018</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Eunit 2.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Mostly internal changes, in particular to the event
+ protocol; fixes problems with timeouts that could cause
+ eunit to hang, and makes it much easier to write new
+ reporting back-ends.</p>
+ <p>New "surefire" report backend for Maven and
+ Bamboo.</p>
+ <p>The test representation is no longer traversed twice
+ (the first pass was for enumeration only). This
+ eliminates some strange restrictions on how generators
+ can be written, but it also means that reports cannot be
+ quite as complete as before in the event of skipped
+ tests.</p>
+ <p>
+ Own Id: OTP-7964</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+ <section>
+ <title>EUnit 2.0.1</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ Corrected the documentation build.
+ </p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+
+ <section>
+ <title>EUnit 2.0</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ This is the first version of EUnit (for unit testing of Erlang modules) by Richard Carlsson
+ released in OTP.
+ </p>
+ </item>
+ </list>
+ </section>
+ </section>
+</chapter>
+
diff --git a/lib/eunit/doc/src/part.xml b/lib/eunit/doc/src/part.xml
new file mode 100644
index 0000000000..e31a8d1b78
--- /dev/null
+++ b/lib/eunit/doc/src/part.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2008</year>
+ <year>2008</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>EUnit User's Guide</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ </header>
+ <description>
+ <p>The <em>EUnit</em> application
+ contains modules with support for unit testing.</p>
+ </description>
+ <xi:include href="chapter.xml"/>
+</part>
+
diff --git a/lib/eunit/doc/src/part_notes.xml b/lib/eunit/doc/src/part_notes.xml
new file mode 100644
index 0000000000..28644f961b
--- /dev/null
+++ b/lib/eunit/doc/src/part_notes.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2008</year>
+ <year>2008</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>EUnit Release Notes</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date>2008-10-29</date>
+ <rev></rev>
+
+ </header>
+ <description>
+ <p>The <em>EUnit</em> application
+ contains modules with support for unit testing</p>
+ </description>
+ <xi:include href="notes.xml"/>
+</part>
+
diff --git a/lib/eunit/doc/src/ref_man.xml b/lib/eunit/doc/src/ref_man.xml
new file mode 100644
index 0000000000..02feef5e97
--- /dev/null
+++ b/lib/eunit/doc/src/ref_man.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2008</year>
+ <year>2008</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>EUnit Reference Manual</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date>2008-10-29</date>
+ <rev>2.0</rev>
+ <file>ref_man.xml</file>
+ </header>
+ <description>
+ <p>The <em>EUnit</em> application
+ contains modules with support for unit testing.</p>
+ </description>
+ <xi:include href="eunit.xml"/>
+ <xi:include href="eunit_surefire.xml"/>
+ </application>
+
diff --git a/lib/eunit/doc/stylesheet.css b/lib/eunit/doc/stylesheet.css
new file mode 100644
index 0000000000..e426a90483
--- /dev/null
+++ b/lib/eunit/doc/stylesheet.css
@@ -0,0 +1,55 @@
+/* standard EDoc style sheet */
+body {
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ margin-left: .25in;
+ margin-right: .2in;
+ margin-top: 0.2in;
+ margin-bottom: 0.2in;
+ color: #000000;
+ background-color: #ffffff;
+}
+h1,h2 {
+ margin-left: -0.2in;
+}
+div.navbar {
+ background-color: #add8e6;
+ padding: 0.2em;
+}
+h2.indextitle {
+ padding: 0.4em;
+ background-color: #add8e6;
+}
+h3.function,h3.typedecl {
+ background-color: #add8e6;
+ padding-left: 1em;
+}
+div.spec {
+ margin-left: 2em;
+ background-color: #eeeeee;
+}
+a.module,a.package {
+ text-decoration:none
+}
+a.module:hover,a.package:hover {
+ background-color: #eeeeee;
+}
+ul.definitions {
+ list-style-type: none;
+}
+ul.index {
+ list-style-type: none;
+ background-color: #eeeeee;
+}
+
+/*
+ * Minor style tweaks
+ */
+ul {
+ list-style-type: square;
+}
+table {
+ border-collapse: collapse;
+}
+td {
+ padding: 3
+}
diff --git a/lib/eunit/ebin/.gitignore b/lib/eunit/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/eunit/ebin/.gitignore
diff --git a/lib/eunit/examples/Makefile b/lib/eunit/examples/Makefile
new file mode 100644
index 0000000000..d1b5bac224
--- /dev/null
+++ b/lib/eunit/examples/Makefile
@@ -0,0 +1,58 @@
+# ``The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved via the world wide web at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id$
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(EUNIT_VSN)
+
+# ----------------------------------------------------
+# Release Macros
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/eunit-$(VSN)
+
+# ----------------------------------------------------
+# Macros
+# ----------------------------------------------------
+
+EXAMPLE_FILES = fib.erl tests.txt
+
+# ----------------------------------------------------
+# Make Rules
+# ----------------------------------------------------
+debug opt:
+
+clean:
+
+docs:
+
+
+# ----------------------------------------------------
+# Release Targets
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec:
+ $(INSTALL_DIR) $(RELSYSDIR)/examples
+ $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples
+
+release_docs_spec:
+
diff --git a/lib/eunit/examples/fib.erl b/lib/eunit/examples/fib.erl
new file mode 100644
index 0000000000..1df02cebfc
--- /dev/null
+++ b/lib/eunit/examples/fib.erl
@@ -0,0 +1,19 @@
+
+-module(fib).
+-export([fib/1]).
+-include_lib("eunit/include/eunit.hrl").
+
+fib(0) -> 1;
+fib(1) -> 1;
+fib(N) when N > 1 -> fib(N-1) + fib(N-2).
+
+fib_test_() ->
+ [?_assert(fib(0) =:= 1),
+ ?_assert(fib(1) =:= 1),
+ ?_assert(fib(2) =:= 2),
+ ?_assert(fib(3) =:= 3),
+ ?_assert(fib(4) =:= 5),
+ ?_assert(fib(5) =:= 8),
+ ?_assertException(error, function_clause, fib(-1)),
+ ?_assert(fib(31) =:= 2178309)
+ ].
diff --git a/lib/eunit/examples/tests.txt b/lib/eunit/examples/tests.txt
new file mode 100644
index 0000000000..f514972f1a
--- /dev/null
+++ b/lib/eunit/examples/tests.txt
@@ -0,0 +1 @@
+[eunit_lib].
diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl
new file mode 100644
index 0000000000..82ba982f03
--- /dev/null
+++ b/lib/eunit/include/eunit.hrl
@@ -0,0 +1,340 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: eunit.hrl 337 2009-03-09 08:38:28Z rcarlsson $
+%%
+%% Copyright (C) 2004-2006 Micka�l R�mond, Richard Carlsson
+
+%% Including this file turns on testing and defines TEST, unless NOTEST
+%% is defined before the file is included. If both NOTEST and TEST are
+%% already defined, then TEST takes precedence, and NOTEST will become
+%% undefined.
+%%
+%% If NODEBUG is defined before this file is included, the debug macros
+%% are disabled, unless DEBUG is also defined, in which case NODEBUG
+%% will become undefined. NODEBUG also implies NOASSERT, unless testing
+%% is enabled.
+%%
+%% If including this file causes TEST to be defined, then NOASSERT will
+%% be undefined, even if it was previously defined and even if NODEBUG
+%% is defined. If both ASSERT and NOASSERT are defined before the file
+%% is included, then ASSERT takes precedence, and NOASSERT will become
+%% undefined regardless of TEST.
+%%
+%% After including this file, EUNIT will be defined if and only if TEST
+%% is defined.
+
+-ifndef(EUNIT_HRL).
+-define(EUNIT_HRL, true).
+
+%% allow defining TEST to override NOTEST
+-ifdef(TEST).
+-undef(NOTEST).
+-endif.
+
+%% allow defining DEBUG to override NODEBUG
+-ifdef(DEBUG).
+-undef(NODEBUG).
+-endif.
+
+%% allow NODEBUG to imply NOASSERT, unless overridden below
+-ifdef(NODEBUG).
+-ifndef(NOASSERT).
+-define(NOASSERT, true).
+-endif.
+-endif.
+
+%% note that the main switch used within this file is NOTEST; however,
+%% both TEST and EUNIT may be used to check whether testing is enabled
+-ifndef(NOTEST).
+-undef(NOASSERT). % testing requires that assertions are enabled
+-ifndef(TEST).
+-define(TEST, true).
+-endif.
+-ifndef(EUNIT).
+-define(EUNIT, true).
+-endif.
+-else.
+-undef(EUNIT).
+-endif.
+
+%% allow ASSERT to override NOASSERT (regardless of TEST/NOTEST)
+-ifdef(ASSERT).
+-undef(NOASSERT).
+-endif.
+
+%% Parse transforms for automatic exporting/stripping of test functions.
+%% (Note that although automatic stripping is convenient, it will make
+%% the code dependent on this header file and the eunit_striptests
+%% module for compilation, even when testing is switched off! Using
+%% -ifdef(EUNIT) around all test code makes the program more portable.)
+
+-ifndef(EUNIT_NOAUTO).
+-ifndef(NOTEST).
+-compile({parse_transform, eunit_autoexport}).
+-else.
+-compile({parse_transform, eunit_striptests}).
+-endif.
+-endif.
+
+%% All macros should be available even if testing is turned off, and
+%% should preferably not require EUnit to be present at runtime.
+%%
+%% We must use fun-call wrappers ((fun () -> ... end)()) to avoid
+%% exporting local variables, and furthermore we only use variable names
+%% prefixed with "__", that hopefully will not be bound outside the fun.
+
+%% A generic let-macro is particularly useful when writing test cases.
+%% It is more compact than 'begin X = Y, Z end', and guarantees that
+%% X gets a new, local binding.
+%% (Note that lowercase 'let' is a reserved word.)
+-ifndef(LET).
+-define(LET(X,Y,Z), ((fun(X)->(Z)end)(Y))).
+-endif.
+
+%% It is important that testing code is short and readable.
+%% An if-then-else macro can make some code much more compact.
+%% Compare: case f(X) of true->g(X); false->h(X) end
+%% and: ?IF(f(X), g(Y), h(Z))
+-ifndef(IF).
+-define(IF(B,T,F), (case (B) of true->(T); false->(F) end)).
+-endif.
+
+%% This macro yields 'true' if the value of E matches the guarded
+%% pattern G, otherwise 'false'.
+-ifndef(MATCHES).
+-define(MATCHES(G,E), (case (E) of G -> true; _ -> false end)).
+-endif.
+
+%% This macro can be used at any time to check whether or not the code
+%% is currently running directly under eunit. Note that it does not work
+%% in secondary processes if they have been assigned a new group leader.
+-ifndef(UNDER_EUNIT).
+-define(UNDER_EUNIT,
+ (?MATCHES({current_function,{eunit_proc,_,_}},
+ .erlang:process_info(.erlang:group_leader(),
+ current_function)))).
+-endif.
+
+-ifdef(NOASSERT).
+%% The plain assert macro should be defined to do nothing if this file
+%% is included when debugging/testing is turned off.
+-ifndef(assert).
+-define(assert(BoolExpr),ok).
+-endif.
+-else.
+%% The assert macro is written the way it is so as not to cause warnings
+%% for clauses that cannot match, even if the expression is a constant.
+-undef(assert).
+-define(assert(BoolExpr),
+ ((fun () ->
+ case (BoolExpr) of
+ true -> ok;
+ __V -> .erlang:error({assertion_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??BoolExpr)},
+ {expected, true},
+ {value, case __V of false -> __V;
+ _ -> {not_a_boolean,__V}
+ end}]})
+ end
+ end)())).
+-endif.
+-define(assertNot(BoolExpr), ?assert(not (BoolExpr))).
+
+-define(_test(Expr), {?LINE, fun () -> (Expr) end}).
+
+-define(_assert(BoolExpr), ?_test(?assert(BoolExpr))).
+
+-define(_assertNot(BoolExpr), ?_assert(not (BoolExpr))).
+
+%% This is mostly a convenience which gives more detailed reports.
+%% Note: Guard is a guarded pattern, and can not be used for value.
+-ifdef(NOASSERT).
+-define(assertMatch(Guard,Expr),ok).
+-else.
+-define(assertMatch(Guard, Expr),
+ ((fun () ->
+ case (Expr) of
+ Guard -> ok;
+ __V -> .erlang:error({assertMatch_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??Expr)},
+ {expected, (??Guard)},
+ {value, __V}]})
+ end
+ end)())).
+-endif.
+-define(_assertMatch(Guard, Expr), ?_test(?assertMatch(Guard, Expr))).
+
+%% This is a convenience macro which gives more detailed reports when
+%% the expected LHS value is not a pattern, but a computed value
+-ifdef(NOASSERT).
+-define(assertEqual(Expect,Expr),ok).
+-else.
+-define(assertEqual(Expect, Expr),
+ ((fun (__X) ->
+ case (Expr) of
+ __X -> ok;
+ __V -> .erlang:error({assertEqual_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??Expr)},
+ {expected, __X},
+ {value, __V}]})
+ end
+ end)(Expect))).
+-endif.
+-define(_assertEqual(Expect, Expr), ?_test(?assertEqual(Expect, Expr))).
+
+%% Note: Class and Term are patterns, and can not be used for value.
+-ifdef(NOASSERT).
+-define(assertException(Class, Term, Expr),ok).
+-else.
+-define(assertException(Class, Term, Expr),
+ ((fun () ->
+ try (Expr) of
+ __V -> .erlang:error({assertException_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??Expr)},
+ {expected,
+ "{ "++(??Class)++" , "++(??Term)
+ ++" , [...] }"},
+ {unexpected_success, __V}]})
+ catch
+ Class:Term -> ok;
+ __C:__T ->
+ .erlang:error({assertException_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {expression, (??Expr)},
+ {expected,
+ "{ "++(??Class)++" , "++(??Term)
+ ++" , [...] }"},
+ {unexpected_exception,
+ {__C, __T,
+ .erlang:get_stacktrace()}}]})
+ end
+ end)())).
+-endif.
+
+-define(assertError(Term, Expr), ?assertException(error, Term, Expr)).
+-define(assertExit(Term, Expr), ?assertException(exit, Term, Expr)).
+-define(assertThrow(Term, Expr), ?assertException(throw, Term, Expr)).
+
+-define(_assertException(Class, Term, Expr),
+ ?_test(?assertException(Class, Term, Expr))).
+-define(_assertError(Term, Expr), ?_assertException(error, Term, Expr)).
+-define(_assertExit(Term, Expr), ?_assertException(exit, Term, Expr)).
+-define(_assertThrow(Term, Expr), ?_assertException(throw, Term, Expr)).
+
+%% Macros for running operating system commands. (Note that these
+%% require EUnit to be present at runtime, or at least eunit_lib.)
+
+%% these can be used for simply running commands in a controlled way
+-define(_cmd_(Cmd), (.eunit_lib:command(Cmd))).
+-define(cmdStatus(N, Cmd),
+ ((fun () ->
+ case ?_cmd_(Cmd) of
+ {(N), __Out} -> __Out;
+ {__N, _} -> .erlang:error({command_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {command, (Cmd)},
+ {expected_status,(N)},
+ {status,__N}]})
+ end
+ end)())).
+-define(_cmdStatus(N, Cmd), ?_test(?cmdStatus(N, Cmd))).
+-define(cmd(Cmd), ?cmdStatus(0, Cmd)).
+-define(_cmd(Cmd), ?_test(?cmd(Cmd))).
+
+%% these are only used for testing; they always return 'ok' on success,
+%% and have no effect if debugging/testing is turned off
+-ifdef(NOASSERT).
+-define(assertCmdStatus(N, Cmd),ok).
+-else.
+-define(assertCmdStatus(N, Cmd),
+ ((fun () ->
+ case ?_cmd_(Cmd) of
+ {(N), _} -> ok;
+ {__N, _} -> .erlang:error({assertCmd_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {command, (Cmd)},
+ {expected_status,(N)},
+ {status,__N}]})
+ end
+ end)())).
+-endif.
+-define(assertCmd(Cmd), ?assertCmdStatus(0, Cmd)).
+
+-ifdef(NOASSERT).
+-define(assertCmdOutput(T, Cmd),ok).
+-else.
+-define(assertCmdOutput(T, Cmd),
+ ((fun () ->
+ case ?_cmd_(Cmd) of
+ {_, (T)} -> ok;
+ {_, __T} -> .erlang:error({assertCmdOutput_failed,
+ [{module, ?MODULE},
+ {line, ?LINE},
+ {command,(Cmd)},
+ {expected_output,(T)},
+ {output,__T}]})
+ end
+ end)())).
+-endif.
+
+-define(_assertCmdStatus(N, Cmd), ?_test(?assertCmdStatus(N, Cmd))).
+-define(_assertCmd(Cmd), ?_test(?assertCmd(Cmd))).
+-define(_assertCmdOutput(T, Cmd), ?_test(?assertCmdOutput(T, Cmd))).
+
+%% Macros to simplify debugging (in particular, they work even when the
+%% standard output is being redirected by EUnit while running tests)
+
+-ifdef(NODEBUG).
+-define(debugMsg(S), ok).
+-define(debugHere, ok).
+-define(debugFmt(S, As), ok).
+-define(debugVal(E), (E)).
+-define(debugTime(S,E), (E)).
+-else.
+-define(debugMsg(S),
+ (begin
+ .io:fwrite(user, <<"~s:~w: ~s\n">>, [?FILE, ?LINE, S]),
+ ok
+ end)).
+-define(debugHere, (?debugMsg("<-"))).
+-define(debugFmt(S, As), (?debugMsg(.io_lib:format((S), (As))))).
+-define(debugVal(E),
+ ((fun (__V) ->
+ ?debugFmt(<<"~s = ~P">>, [(??E), __V, 15]),
+ __V
+ end)(E))).
+-define(debugTime(S,E),
+ ((fun () ->
+ {__T0, _} = statistics(wall_clock),
+ __V = (E),
+ {__T1, _} = statistics(wall_clock),
+ ?debugFmt(<<"~s: ~.3f s">>, [(S), (__T1-__T0)/1000]),
+ __V
+ end)())).
+-endif.
+
+-endif. % EUNIT_HRL
diff --git a/lib/eunit/info b/lib/eunit/info
new file mode 100644
index 0000000000..138f4dc040
--- /dev/null
+++ b/lib/eunit/info
@@ -0,0 +1,2 @@
+group: tools
+short: Support for unit testing.
diff --git a/lib/eunit/src/Makefile b/lib/eunit/src/Makefile
new file mode 100644
index 0000000000..4897c20ec1
--- /dev/null
+++ b/lib/eunit/src/Makefile
@@ -0,0 +1,113 @@
+#
+# Copyright (C) 2008, Ericsson Telecommunications
+# Authors: Richard Carlsson, Bertil Karlsson
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(EUNIT_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/eunit-$(VSN)
+
+
+#
+# Common Macros
+#
+
+EBIN = ../ebin
+INCLUDE=../include
+
+ERL_COMPILE_FLAGS += -pa $(EBIN) -I$(INCLUDE) +warn_unused_vars +nowarn_shadow_vars +warn_unused_import +warn_obsolete_guard
+
+SOURCES= \
+ eunit_autoexport.erl \
+ eunit_striptests.erl \
+ eunit.erl \
+ eunit_tests.erl \
+ eunit_server.erl \
+ eunit_proc.erl \
+ eunit_serial.erl \
+ eunit_test.erl \
+ eunit_lib.erl \
+ eunit_data.erl \
+ eunit_tty.erl \
+ eunit_surefire.erl \
+ eunit_listener.erl
+
+INCLUDE_FILES = eunit.hrl
+
+OBJECTS=$(SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+
+INCLUDE_DELIVERABLES = $(INCLUDE_FILES:%=$(INCLUDE)/%)
+
+APP_FILE= eunit.app
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBIN)/$(APP_FILE)
+
+APPUP_FILE= eunit.appup
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(OBJECTS)
+
+docs:
+
+all: $(OBJECTS)
+
+
+clean:
+ rm -f $(OBJECTS)
+ rm -f core *~
+
+distclean: clean
+
+info:
+ @echo "MODULES: $(MODULES)"
+ @echo "EBIN: $(EBIN)"
+ @echo "EMULATOR: $(EMULATOR)"
+ @echo "APP_TARGET: $(APP_TARGET)"
+ @echo "TARGET_FILES: $(TARGET_FILES)"
+ @echo "DOC_TARGET_FILES: $(DOC_TARGET_FILES)"
+ @echo "DOCDIR/%html: $(DOCDIR)/%.html"
+
+realclean: clean
+
+$(EBIN)/%.$(EMULATOR):%.erl
+ erlc -W $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(OBJECTS) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(SOURCES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/include
+ $(INSTALL_DATA) $(INCLUDE_DELIVERABLES) $(RELSYSDIR)/include
+
+release_docs_spec:
+
diff --git a/lib/eunit/src/eunit.app.src b/lib/eunit/src/eunit.app.src
new file mode 100644
index 0000000000..4fd76588c3
--- /dev/null
+++ b/lib/eunit/src/eunit.app.src
@@ -0,0 +1,21 @@
+% This is an -*- erlang -*- file.
+
+{application, eunit,
+ [{description, "EUnit"},
+ {vsn, "%VSN%"},
+ {modules, [eunit,
+ eunit_autoexport,
+ eunit_striptests,
+ eunit_server,
+ eunit_proc,
+ eunit_serial,
+ eunit_test,
+ eunit_tests,
+ eunit_lib,
+ eunit_listener,
+ eunit_data,
+ eunit_tty,
+ eunit_surefire]},
+ {registered,[]},
+ {applications, [stdlib]},
+ {env, []}]}.
diff --git a/lib/eunit/src/eunit.appup.src b/lib/eunit/src/eunit.appup.src
new file mode 100644
index 0000000000..54a63833e6
--- /dev/null
+++ b/lib/eunit/src/eunit.appup.src
@@ -0,0 +1 @@
+{"%VSN%",[],[]}.
diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl
new file mode 100644
index 0000000000..59084a52fb
--- /dev/null
+++ b/lib/eunit/src/eunit.erl
@@ -0,0 +1,250 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: eunit.erl 339 2009-04-05 14:10:47Z rcarlsson $
+%%
+%% @copyright 2004-2009 Micka�l R�mond, Richard Carlsson
+%% @author Micka�l R�mond <[email protected]>
+%% [http://www.process-one.net/]
+%% @author Richard Carlsson <[email protected]>
+%% [http://user.it.uu.se/~richardc/]
+%% @version {@version}, {@date} {@time}
+%% @doc This module is the main EUnit user interface.
+
+-module(eunit).
+
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+%% Official exports
+-export([start/0, stop/0, test/1, test/2]).
+
+%% Experimental; may be removed or relocated
+-export([start/1, stop/1, test/3, submit/1, submit/2, submit/3, watch/1,
+ watch/2, watch/3, watch_path/1, watch_path/2, watch_path/3,
+ watch_regexp/1, watch_regexp/2, watch_regexp/3, watch_app/1,
+ watch_app/2, watch_app/3]).
+
+%% EUnit entry points
+
+%% TODO: Command line interface similar to that of edoc?
+
+%% @doc Starts the EUnit server. Normally, you don't need to call this
+%% function; it is started automatically.
+start() ->
+ start(?SERVER).
+
+%% @private
+%% @doc See {@link start/0}.
+start(Server) ->
+ eunit_server:start(Server).
+
+%% @doc Stops the EUnit server. Normally, you don't need to call this
+%% function.
+stop() ->
+ stop(?SERVER).
+
+%% @private
+%% @doc See {@link stop/0}.
+stop(Server) ->
+ eunit_server:stop(Server).
+
+%% @private
+watch(Target) ->
+ watch(Target, []).
+
+%% @private
+watch(Target, Options) ->
+ watch(?SERVER, Target, Options).
+
+%% @private
+watch(Server, Target, Options) ->
+ eunit_server:watch(Server, Target, Options).
+
+%% @private
+watch_path(Target) ->
+ watch_path(Target, []).
+
+%% @private
+watch_path(Target, Options) ->
+ watch_path(?SERVER, Target, Options).
+
+%% @private
+watch_path(Server, Target, Options) ->
+ eunit_server:watch_path(Server, Target, Options).
+
+%% @private
+watch_regexp(Target) ->
+ watch_regexp(Target, []).
+
+%% @private
+watch_regexp(Target, Options) ->
+ watch_regexp(?SERVER, Target, Options).
+
+%% @private
+watch_regexp(Server, Target, Options) ->
+ eunit_server:watch_regexp(Server, Target, Options).
+
+%% @private
+watch_app(Name) ->
+ watch_app(Name, []).
+
+%% @private
+watch_app(Name, Options) ->
+ watch_app(?SERVER, Name, Options).
+
+%% @private
+watch_app(Server, Name, Options) ->
+ case code:lib_dir(Name) of
+ Path when is_list(Path) ->
+ watch_path(Server, filename:join(Path, "ebin"), Options);
+ _ ->
+ error
+ end.
+
+%% @equiv test(Tests, [])
+test(Tests) ->
+ test(Tests, []).
+
+%% @spec test(Tests::term(), Options::[term()]) -> ok | {error, term()}
+%% @doc Runs a set of tests. The format of `Tests' is described in the
+%% section <a
+%% href="overview-summary.html#EUnit_test_representation">EUnit test
+%% representation</a> of the overview.
+%%
+%% Example: ```eunit:test(fred)''' runs all tests in the module `fred'
+%% and also any tests in the module `fred_tests', if that module exists.
+%%
+%% Options:
+%% <dl>
+%% <dt>`verbose'</dt>
+%% <dd>Displays more details about the running tests.</dd>
+%% </dl>
+%%
+%% Options in the environment variable EUNIT are also included last in
+%% the option list, i.e., have lower precedence than those in `Options'.
+%% @see test/1
+test(Tests, Options) ->
+ test(?SERVER, Tests, all_options(Options)).
+
+%% @private
+%% @doc See {@link test/2}.
+test(Server, Tests, Options) ->
+ Listeners = [eunit_tty:start(Options) | listeners(Options)],
+ Serial = eunit_serial:start(Listeners),
+ case eunit_server:start_test(Server, Serial, Tests, Options) of
+ {ok, Reference} -> test_run(Reference, Listeners);
+ {error, R} -> {error, R}
+ end.
+
+test_run(Reference, Listeners) ->
+ receive
+ {start, Reference} ->
+ cast(Listeners, {start, Reference})
+ end,
+ receive
+ {done, Reference} ->
+ cast(Listeners, {stop, Reference, self()}),
+ receive
+ {result, Reference, Result} ->
+ Result
+ end
+ end.
+
+cast([P | Ps], Msg) ->
+ P ! Msg,
+ cast(Ps, Msg);
+cast([], _Msg) ->
+ ok.
+
+%% TODO: functions that run tests on a given node, not a given server
+%% TODO: maybe some functions could check for a globally registered server?
+%% TODO: some synchronous but completely quiet interface function
+
+%% @private
+submit(T) ->
+ submit(T, []).
+
+%% @private
+submit(T, Options) ->
+ submit(?SERVER, T, Options).
+
+%% @private
+submit(Server, T, Options) ->
+ Dummy = spawn(fun devnull/0),
+ eunit_server:start_test(Server, Dummy, T, Options).
+
+listeners(Options) ->
+ Ps = start_listeners(proplists:get_all_values(report, Options)),
+ %% the event_log option is for debugging, to view the raw events
+ case proplists:get_value(event_log, Options) of
+ undefined ->
+ Ps;
+ X ->
+ LogFile = if is_list(X) -> X;
+ true -> "eunit-events.log"
+ end,
+ [spawn_link(fun () -> event_logger(LogFile) end) | Ps]
+ end.
+
+start_listeners([P | Ps]) when is_pid(P) ; is_atom(P) ->
+ [P | start_listeners(Ps)];
+start_listeners([{Mod, Opts} | Ps]) when is_atom(Mod) ->
+ [Mod:start(Opts) | start_listeners(Ps)];
+start_listeners([]) ->
+ [].
+
+%% TODO: make this report file errors
+event_logger(LogFile) ->
+ case file:open(LogFile, [write]) of
+ {ok, FD} ->
+ receive
+ {start, Reference} ->
+ event_logger_loop(Reference, FD)
+ end;
+ Error ->
+ exit(Error)
+ end.
+
+event_logger_loop(Reference, FD) ->
+ receive
+ {status, _Id, _Info}=Msg ->
+ io:fwrite(FD, "~p.\n", [Msg]),
+ event_logger_loop(Reference, FD);
+ {stop, Reference, _ReplyTo} ->
+ %% no need to reply, just exit
+ file:close(FD),
+ exit(normal)
+ end.
+
+%% TODO: make a proper logger for asynchronous execution with submit/3
+
+devnull() ->
+ receive _ -> devnull() end.
+
+%% including options from EUNIT environment variable
+
+all_options(Opts) ->
+ try os:getenv("EUNIT") of
+ false -> Opts;
+ S ->
+ {ok, Ts, _} = erl_scan:string(S),
+ {ok, V} = erl_parse:parse_term(Ts ++ [{dot,1}]),
+ if is_list(V) -> Opts ++ V;
+ true -> Opts ++ [V]
+ end
+ catch
+ _:_ -> Opts
+ end.
diff --git a/lib/eunit/src/eunit_autoexport.erl b/lib/eunit/src/eunit_autoexport.erl
new file mode 100644
index 0000000000..7b153c1194
--- /dev/null
+++ b/lib/eunit/src/eunit_autoexport.erl
@@ -0,0 +1,104 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: eunit_autoexport.erl 329 2009-03-01 11:23:32Z rcarlsson $
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2006 Richard Carlsson
+%% @private
+%% @see eunit
+%% @doc Parse transform for automatic exporting of test functions.
+
+-module(eunit_autoexport).
+
+-include("eunit_internal.hrl").
+
+-export([parse_transform/2]).
+
+
+parse_transform(Forms, Options) ->
+ TestSuffix = proplists:get_value(eunit_test_suffix, Options,
+ ?DEFAULT_TEST_SUFFIX),
+ GeneratorSuffix = proplists:get_value(eunit_generator_suffix,
+ Options,
+ ?DEFAULT_GENERATOR_SUFFIX),
+ ExportSuffix = proplists:get_value(eunit_export_suffix, Options,
+ ?DEFAULT_EXPORT_SUFFIX),
+ F = fun (Form, Set) ->
+ form(Form, Set, TestSuffix, GeneratorSuffix,
+ ExportSuffix)
+ end,
+ Exports = sets:to_list(lists:foldl(F, sets:new(), Forms)),
+ rewrite(Forms, Exports).
+
+form({function, _L, Name, 0, _Cs}, S, TestSuffix, GeneratorSuffix,
+ ExportSuffix) ->
+ N = atom_to_list(Name),
+ case lists:suffix(TestSuffix, N) of
+ true ->
+ sets:add_element({Name, 0}, S);
+ false ->
+ case lists:suffix(GeneratorSuffix, N) of
+ true ->
+ sets:add_element({Name, 0}, S);
+ false ->
+ case lists:suffix(ExportSuffix, N) of
+ true ->
+ sets:add_element({Name, 0}, S);
+ false ->
+ S
+ end
+ end
+ end;
+form({function, _L, ?DEFAULT_MODULE_WRAPPER_NAME, 1, _Cs}, S, _, _, _) ->
+ sets:add_element({?DEFAULT_MODULE_WRAPPER_NAME,1}, S);
+form(_, S, _, _, _) ->
+ S.
+
+rewrite([{attribute,_,module,{Name,_Ps}}=M | Fs], Exports) ->
+ module_decl(Name, M, Fs, Exports);
+rewrite([{attribute,_,module,Name}=M | Fs], Exports) ->
+ module_decl(Name, M, Fs, Exports);
+rewrite([F | Fs], Exports) ->
+ [F | rewrite(Fs, Exports)];
+rewrite([], _Exports) ->
+ []. %% fail-safe, in case there is no module declaration
+
+rewrite([{function,_,test,0,_}=F | Fs], As, Module, _Test) ->
+ rewrite(Fs, [F | As], Module, false);
+rewrite([F | Fs], As, Module, Test) ->
+ rewrite(Fs, [F | As], Module, Test);
+rewrite([], As, Module, Test) ->
+ {if Test ->
+ EUnit = {record_field,0,{atom,0,''},{atom,0,eunit}},
+ [{function,0,test,0,
+ [{clause,0,[],[],
+ [{call,0,{remote,0,EUnit,{atom,0,test}},
+ [{atom,0,Module}]}]}]}
+ | As];
+ true ->
+ As
+ end,
+ Test}.
+
+module_decl(Name, M, Fs, Exports) ->
+ Module = if is_atom(Name) -> Name;
+ true -> list_to_atom(packages:concat(Name))
+ end,
+ {Fs1, Test} = rewrite(Fs, [], Module, true),
+ Es = if Test -> [{test,0} | Exports];
+ true -> Exports
+ end,
+ [M, {attribute,0,export,Es} | lists:reverse(Fs1)].
diff --git a/lib/eunit/src/eunit_data.erl b/lib/eunit/src/eunit_data.erl
new file mode 100644
index 0000000000..0543b6c543
--- /dev/null
+++ b/lib/eunit/src/eunit_data.erl
@@ -0,0 +1,732 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2006 Richard Carlsson
+%% @private
+%% @see eunit
+%% @doc Interpretation of symbolic test representation
+
+-module(eunit_data).
+
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+-include_lib("kernel/include/file.hrl").
+
+-export([iter_init/2, iter_next/1, iter_prev/1, iter_id/1,
+ enter_context/3, get_module_tests/1]).
+
+-import(lists, [foldr/3]).
+
+-define(TICKS_PER_SECOND, 1000).
+
+%% @type tests() =
+%% SimpleTest
+%% | [tests()]
+%% | moduleName()
+%% | {module, moduleName()}
+%% | {application, appName()}
+%% | {application, appName(), [term()]}
+%% | fileName()
+%% | {file, fileName()}
+%% | {string(), tests()}
+%% | {generator, () -> tests()}
+%% | {generator, M::moduleName(), F::functionName()}
+%% | {spawn, tests()}
+%% | {spawn, Node::atom(), tests()}
+%% | {timeout, T::number(), tests()}
+%% | {inorder, tests()}
+%% | {inparallel, tests()}
+%% | {inparallel, N::integer(), tests()}
+%% | {with, X::any(), [AbstractTestFunction]}
+%% | {setup, Where::local | spawn | {spawn, Node::atom()},
+%% Setup::() -> (R::any()),
+%% Cleanup::(R::any()) -> any(),
+%% tests() | Instantiator
+%% }
+%% | {setup, Setup, Cleanup, tests() | Instantiator}
+%% | {setup, Where, Setup, tests() | Instantiator}
+%% | {setup, Setup, tests() | Instantiator}
+%% | {foreach, Where::local | spawn | {spawn, Node::atom()},
+%% Setup::() -> (R::any()),
+%% Cleanup::(R::any()) -> any(),
+%% [tests() | Instantiator]
+%% }
+%% | {foreach, Setup, Cleanup, [tests() | Instantiator]}
+%% | {foreach, Where, Setup, [tests() | Instantiator]}
+%% | {foreach, Setup, [tests() | Instantiator]}
+%% | {foreachx, Where::local | spawn | {spawn, Node::atom()},
+%% SetupX::(X::any()) -> (R::any()),
+%% CleanupX::(X::any(), R::any()) -> any(),
+%% Pairs::[{X::any(),
+%% (X::any(), R::any()) -> tests()}]
+%% }
+%% | {foreachx, SetupX, CleanupX, Pairs}
+%% | {foreachx, Where, SetupX, Pairs}
+%% | {foreachx, SetupX, Pairs}
+%% | {node, Node::atom(), tests() | Instantiator}
+%% | {node, Node, Args::string(), tests() | Instantiator}
+%%
+%% SimpleTest = TestFunction | {Line::integer(), SimpleTest}
+%%
+%% TestFunction = () -> any()
+%% | {M::moduleName(), F::functionName()}.
+%%
+%% AbstractTestFunction = (X::any()) -> any()
+%%
+%% Instantiator = (R::any()) -> tests()
+%% | {with, [AbstractTestFunction]}
+%%
+%% Note that `{string(), ...}' is a short-hand for `{string(), {...}}'
+%% if the tuple contains more than two elements.
+%%
+%% @type moduleName() = atom()
+%% @type functionName() = atom()
+%% @type arity() = integer()
+%% @type appName() = atom()
+%% @type fileName() = string()
+
+%% TODO: Can we mark up tests as known-failures?
+%% TODO: Is it possible to handle known timout/setup failures?
+%% TODO: Add diagnostic tests which never fail, but may cause warnings?
+
+%% ---------------------------------------------------------------------
+%% Abstract test set iterator
+
+-record(iter,
+ {prev = [],
+ next = [],
+ tests = [],
+ pos = 0,
+ parent = []}).
+
+%% @spec (tests(), [integer()]) -> testIterator()
+%% @type testIterator()
+
+iter_init(Tests, ParentID) ->
+ #iter{tests = Tests, parent = lists:reverse(ParentID)}.
+
+%% @spec (testIterator()) -> [integer()]
+
+iter_id(#iter{pos = N, parent = Ns}) ->
+ lists:reverse(Ns, [N]).
+
+%% @spec (testIterator()) -> none | {testItem(), testIterator()}
+
+iter_next(I = #iter{next = []}) ->
+ case next(I#iter.tests) of
+ {T, Tests} ->
+ {T, I#iter{prev = [T | I#iter.prev],
+ tests = Tests,
+ pos = I#iter.pos + 1}};
+ none ->
+ none
+ end;
+iter_next(I = #iter{next = [T | Ts]}) ->
+ {T, I#iter{next = Ts,
+ prev = [T | I#iter.prev],
+ pos = I#iter.pos + 1}}.
+
+%% @spec (testIterator()) -> none | {testItem(), testIterator()}
+
+iter_prev(#iter{prev = []}) ->
+ none;
+iter_prev(#iter{prev = [T | Ts], next = Next, pos = Pos} = I) ->
+ {T, I#iter{prev = Ts, next = [T | Next], pos = Pos - 1}}.
+
+
+%% ---------------------------------------------------------------------
+%% Concrete test set representation iterator
+
+%% @spec (tests()) -> none | {testItem(), tests()}
+%% @type testItem() = #test{} | #group{}
+%% @throws {bad_test, term()}
+%% | {generator_failed, exception()}
+%% | {no_such_function, eunit_lib:mfa()}
+%% | {module_not_found, moduleName()}
+%% | {application_not_found, appName()}
+%% | {file_read_error, {Reason::atom(), Message::string(),
+%% fileName()}}
+
+next(Tests) ->
+ case eunit_lib:dlist_next(Tests) of
+ [T | Ts] ->
+ case parse(T) of
+ {data, T1} ->
+ next([T1 | Ts]);
+ T1 ->
+ {T1, Ts}
+ end;
+ [] ->
+ none
+ end.
+
+%% this returns either a #test{} or #group{} record, or {data, T} to
+%% signal that T has been substituted for the given representation
+
+parse({foreach, S, Fs}) when is_function(S), is_list(Fs) ->
+ parse({foreach, S, fun ok/1, Fs});
+parse({foreach, S, C, Fs})
+ when is_function(S), is_function(C), is_list(Fs) ->
+ parse({foreach, ?DEFAULT_SETUP_PROCESS, S, C, Fs});
+parse({foreach, P, S, Fs})
+ when is_function(S), is_list(Fs) ->
+ parse({foreach, P, S, fun ok/1, Fs});
+parse({foreach, P, S, C, Fs} = T)
+ when is_function(S), is_function(C), is_list(Fs) ->
+ check_arity(S, 0, T),
+ check_arity(C, 1, T),
+ case Fs of
+ [F | Fs1] ->
+ {data, [{setup, P, S, C, F}, {foreach, P, S, C, Fs1}]};
+ [] ->
+ {data, []}
+ end;
+parse({foreachx, S1, Ps}) when is_function(S1), is_list(Ps) ->
+ parse({foreachx, S1, fun ok/2, Ps});
+parse({foreachx, S1, C1, Ps})
+ when is_function(S1), is_function(C1), is_list(Ps) ->
+ parse({foreachx, ?DEFAULT_SETUP_PROCESS, S1, C1, Ps});
+parse({foreachx, P, S1, Ps})
+ when is_function(S1), is_list(Ps) ->
+ parse({foreachx, P, S1, fun ok/2, Ps});
+parse({foreachx, P, S1, C1, Ps} = T)
+ when is_function(S1), is_function(C1), is_list(Ps) ->
+ check_arity(S1, 1, T),
+ check_arity(C1, 2, T),
+ case Ps of
+ [{X, F1} | Ps1] when is_function(F1) ->
+ check_arity(F1, 2, T),
+ S = fun () -> S1(X) end,
+ C = fun (R) -> C1(X, R) end,
+ F = fun (R) -> F1(X, R) end,
+ {data, [{setup, P, S, C, F}, {foreachx, P, S1, C1, Ps1}]};
+ [_|_] ->
+ bad_test(T);
+ [] ->
+ {data, []}
+ end;
+parse({generator, F} = T) when is_function(F) ->
+ check_arity(F, 0, T),
+ %% use run_testfun/1 to handle wrapper exceptions
+ case eunit_test:run_testfun(F) of
+ {ok, T1} ->
+ {data, T1};
+ {error, {Class, Reason, Trace}} ->
+ throw({generator_failed, {Class, Reason, Trace}})
+ end;
+parse({generator, M, F}) when is_atom(M), is_atom(F) ->
+ parse({generator, eunit_test:function_wrapper(M, F)});
+parse({inorder, T}) ->
+ group(#group{tests = T, order = inorder});
+parse({inparallel, T}) ->
+ parse({inparallel, 0, T});
+parse({inparallel, N, T}) when is_integer(N), N >= 0 ->
+ group(#group{tests = T, order = {inparallel, N}});
+parse({timeout, N, T}) when is_number(N), N >= 0 ->
+ group(#group{tests = T, timeout = round(N * ?TICKS_PER_SECOND)});
+parse({spawn, T}) ->
+ group(#group{tests = T, spawn = local});
+parse({spawn, N, T}) when is_atom(N) ->
+ group(#group{tests = T, spawn = {remote, N}});
+parse({setup, S, I}) when is_function(S); is_list(S) ->
+ parse({setup, ?DEFAULT_SETUP_PROCESS, S, I});
+parse({setup, S, C, I}) when is_function(S), is_function(C) ->
+ parse({setup, ?DEFAULT_SETUP_PROCESS, S, C, I});
+parse({setup, P, S, I}) when is_function(S) ->
+ parse({setup, P, S, fun ok/1, I});
+parse({setup, P, L, I} = T) when is_list(L) ->
+ check_setup_list(L, T),
+ {S, C} = eunit_test:multi_setup(L),
+ parse({setup, P, S, C, I});
+parse({setup, P, S, C, I} = T)
+ when is_function(S), is_function(C), is_function(I) ->
+ check_arity(S, 0, T),
+ check_arity(C, 1, T),
+ case erlang:fun_info(I, arity) of
+ {arity, 0} ->
+ %% if I is nullary, it is a plain test
+ parse({setup, S, C, fun (_) -> I end});
+ _ ->
+ %% otherwise, I must be an instantiator function
+ check_arity(I, 1, T),
+ case P of
+ local -> ok;
+ spawn -> ok;
+ {spawn, N} when is_atom(N) -> ok;
+ _ -> bad_test(T)
+ end,
+ group(#group{tests = I,
+ context = #context{setup = S, cleanup = C,
+ process = P}})
+ end;
+parse({setup, P, S, C, {with, As}}) when is_list(As) ->
+ parse({setup, P, S, C, fun (X) -> {with, X, As} end});
+parse({setup, P, S, C, T}) when is_function(S), is_function(C) ->
+ parse({setup, P, S, C, fun (_) -> T end});
+parse({node, N, T}) when is_atom(N) ->
+ parse({node, N, "", T});
+parse({node, N, A, T1}=T) when is_atom(N) ->
+ case eunit_lib:is_string(A) of
+ true ->
+ %% TODO: better stack traces for internal funs like these
+ parse({setup,
+ fun () ->
+ %% TODO: auto-start net_kernel if needed
+ StartedNet = false,
+%% The following is commented out because of problems when running
+%% eunit as part of the init sequence (from the command line):
+%% StartedNet =
+%% case whereis(net_kernel) of
+%% undefined ->
+%% M = list_to_atom(atom_to_list(N)
+%% ++ "_master"),
+%% case net_kernel:start([M]) of
+%% {ok, _} ->
+%% true;
+%% {error, E} ->
+%% throw({net_kernel_start, E})
+%% end;
+%% _ -> false
+%% end,
+%% ?debugVal({started, StartedNet}),
+ {Name, Host} = eunit_lib:split_node(N),
+ {ok, Node} = slave:start_link(Host, Name, A),
+ {Node, StartedNet}
+ end,
+ fun ({Node, StopNet}) ->
+%% ?debugVal({stop, StopNet}),
+ slave:stop(Node),
+ case StopNet of
+ true -> net_kernel:stop();
+ false -> ok
+ end
+ end,
+ T1});
+ false ->
+ bad_test(T)
+ end;
+parse({module, M}) when is_atom(M) ->
+ {data, {"module '" ++ atom_to_list(M) ++ "'", get_module_tests(M)}};
+parse({application, A}) when is_atom(A) ->
+ try parse({file, atom_to_list(A)++".app"})
+ catch
+ {file_read_error,{enoent,_,_}} ->
+ case code:lib_dir(A) of
+ Dir when is_list(Dir) ->
+ %% add "ebin" if it exists, like code_server does
+ BinDir = filename:join(Dir, "ebin"),
+ case file:read_file_info(BinDir) of
+ {ok, #file_info{type=directory}} ->
+ parse({dir, BinDir});
+ _ ->
+ parse({dir, Dir})
+ end;
+ _ ->
+ throw({application_not_found, A})
+ end
+ end;
+parse({application, A, Info}=T) when is_atom(A) ->
+ case proplists:get_value(modules, Info) of
+ Ms when is_list(Ms) ->
+ case [M || M <- Ms, not is_atom(M)] of
+ [] ->
+ {data, {"application '" ++ atom_to_list(A) ++ "'", Ms}};
+ _ ->
+ bad_test(T)
+ end;
+ _ ->
+ bad_test(T)
+ end;
+parse({file, F} = T) when is_list(F) ->
+ case eunit_lib:is_string(F) of
+ true ->
+ {data, {"file \"" ++ F ++ "\"", get_file_tests(F)}};
+ false ->
+ bad_test(T)
+ end;
+parse({dir, D}=T) when is_list(D) ->
+ case eunit_lib:is_string(D) of
+ true ->
+ {data, {"directory \"" ++ D ++ "\"", get_directory_modules(D)}};
+ false ->
+ bad_test(T)
+ end;
+parse({with, X, As}=T) when is_list(As) ->
+ case As of
+ [A | As1] ->
+ check_arity(A, 1, T),
+ {data, [{eunit_lib:fun_parent(A), fun () -> A(X) end},
+ {with, X, As1}]};
+ [] ->
+ {data, []}
+ end;
+parse({S, T1} = T) when is_list(S) ->
+ case eunit_lib:is_string(S) of
+ true ->
+ group(#group{tests = T1, desc = list_to_binary(S)});
+ false ->
+ bad_test(T)
+ end;
+parse({S, T1}) when is_binary(S) ->
+ group(#group{tests = T1, desc = S});
+parse(T) when tuple_size(T) > 2, is_list(element(1, T)) ->
+ [S | Es] = tuple_to_list(T),
+ parse({S, list_to_tuple(Es)});
+parse(T) when tuple_size(T) > 2, is_binary(element(1, T)) ->
+ [S | Es] = tuple_to_list(T),
+ parse({S, list_to_tuple(Es)});
+parse(M) when is_atom(M) ->
+ parse({module, M});
+parse(T) when is_list(T) ->
+ case eunit_lib:is_string(T) of
+ true ->
+ try parse({dir, T})
+ catch
+ {file_read_error,{R,_,_}}
+ when R =:= enotdir; R =:= enoent ->
+ parse({file, T})
+ end;
+ false ->
+ bad_test(T)
+ end;
+parse(T) ->
+ parse_simple(T).
+
+%% parse_simple always produces a #test{} record
+
+parse_simple({L, F}) when is_integer(L), L >= 0 ->
+ (parse_simple(F))#test{line = L};
+parse_simple({{M,N,A}=Loc, F}) when is_atom(M), is_atom(N), is_integer(A) ->
+ (parse_simple(F))#test{location = Loc};
+parse_simple(F) ->
+ parse_function(F).
+
+parse_function(F) when is_function(F) ->
+ check_arity(F, 0, F),
+ #test{f = F, location = eunit_lib:fun_parent(F)};
+parse_function({M,F}) when is_atom(M), is_atom(F) ->
+ #test{f = eunit_test:function_wrapper(M, F), location = {M, F, 0}};
+parse_function(F) ->
+ bad_test(F).
+
+check_arity(F, N, T) when is_function(F) ->
+ case erlang:fun_info(F, arity) of
+ {arity, N} ->
+ ok;
+ _ ->
+ bad_test(T)
+ end;
+check_arity(_, _, T) ->
+ bad_test(T).
+
+check_setup_list([{Tag, S, C} | Es], T)
+ when is_atom(Tag), is_function(S), is_function(C) ->
+ check_arity(S, 0, T),
+ check_arity(C, 1, T),
+ check_setup_list(Es, T);
+check_setup_list([{Tag, S} | Es], T)
+ when is_atom(Tag), is_function(S) ->
+ check_arity(S, 0, T),
+ check_setup_list(Es, T);
+check_setup_list([], _T) ->
+ ok;
+check_setup_list(_, T) ->
+ bad_test(T).
+
+bad_test(T) ->
+ throw({bad_test, T}).
+
+ok(_) -> ok.
+ok(_, _) -> ok.
+
+%% This does some look-ahead and folds nested groups and tests where
+%% possible. E.g., {String, Test} -> Test#test{desc = String}.
+
+group(#group{context = #context{}} = G) ->
+ %% leave as it is - the test body is an instantiator, which is not
+ %% suitable for lookahead (and anyway, properties of the setup
+ %% should not be merged with properties of its body, e.g. spawn)
+ G;
+group(#group{tests = T0, desc = Desc, order = Order, context = Context,
+ spawn = Spawn, timeout = Timeout} = G) ->
+ {T1, Ts} = lookahead(T0),
+ {T2, _} = lookahead(Ts),
+ case T1 of
+ #test{desc = Desc1, timeout = Timeout1}
+ when T2 =:= none, Spawn =:= undefined, Context =:= undefined,
+ ((Desc =:= undefined) or (Desc1 =:= undefined)),
+ ((Timeout =:= undefined) or (Timeout1 =:= undefined)) ->
+ %% a single test within a non-spawn/setup group: put the
+ %% information directly on the test; drop the order
+ T1#test{desc = join_properties(Desc, Desc1),
+ timeout = join_properties(Timeout, Timeout1)};
+
+ #test{timeout = undefined}
+ when T2 =:= none, Timeout =/= undefined, Context =:= undefined ->
+ %% a single test without timeout, within a non-joinable
+ %% group with a timeout and no fixture: push the timeout to
+ %% the test
+ G#group{tests = {timeout, (Timeout div ?TICKS_PER_SECOND), T0},
+ timeout = undefined};
+
+ #group{desc = Desc1, order = Order1, context = Context1,
+ spawn = Spawn1, timeout = Timeout1}
+ when T2 =:= none,
+ ((Desc =:= undefined) or (Desc1 =:= undefined)),
+ ((Order =:= undefined) or (Order1 =:= undefined)),
+ ((Context =:= undefined) or (Context1 =:= undefined)),
+ ((Spawn =:= undefined) or (Spawn1 =:= undefined)),
+ ((Timeout =:= undefined) or (Timeout1 =:= undefined)) ->
+ %% two nested groups with non-conflicting properties
+ group(T1#group{desc = join_properties(Desc, Desc1),
+ order = join_properties(Order, Order1),
+ context = join_properties(Context, Context1),
+ spawn = join_properties(Spawn, Spawn1),
+ timeout = join_properties(Timeout, Timeout1)});
+
+ #group{order = Order1, timeout = Timeout1}
+ when T2 =:= none ->
+ %% two nested groups that cannot be joined: try to push the
+ %% timeout and ordering properties to the inner group
+ push_order(Order, Order1, push_timeout(Timeout, Timeout1, G));
+
+ _ ->
+ %% leave the group as it is and discard the lookahead
+ G
+ end.
+
+lookahead(T) ->
+ case next(T) of
+ {T1, Ts} -> {T1, Ts};
+ none -> {none, []}
+ end.
+
+join_properties(undefined, X) -> X;
+join_properties(X, undefined) -> X.
+
+push_timeout(Timeout, undefined, G=#group{context=undefined})
+ when Timeout =/= undefined ->
+ %% A timeout on a context (fixture) includes the setup/cleanup time
+ %% and must not be propagated into the body
+ G#group{tests = {timeout, (Timeout div ?TICKS_PER_SECOND), G#group.tests},
+ timeout = undefined};
+push_timeout(_, _, G) ->
+ G.
+
+push_order(inorder, undefined, G) ->
+ G#group{tests = {inorder, G#group.tests}, order = undefined};
+push_order({inparallel, N}, undefined, G) ->
+ G#group{tests = {inparallel, N, G#group.tests}, order = undefined};
+push_order(_, _, G) ->
+ G.
+
+%% ---------------------------------------------------------------------
+%% Extracting test funs from a module
+
+%% @throws {module_not_found, moduleName()}
+
+get_module_tests(M) ->
+ try M:module_info(exports) of
+ Es ->
+ Fs = get_module_tests_1(M, Es),
+ W = ?DEFAULT_MODULE_WRAPPER_NAME,
+ case lists:member({W,1}, Es) of
+ false -> Fs;
+ true -> {generator, fun () -> M:W(Fs) end}
+ end
+ catch
+ error:undef ->
+ throw({module_not_found, M})
+ end.
+
+get_module_tests_1(M, Es) ->
+ Fs = testfuns(Es, M, ?DEFAULT_TEST_SUFFIX,
+ ?DEFAULT_GENERATOR_SUFFIX),
+ Name = atom_to_list(M),
+ case lists:suffix(?DEFAULT_TESTMODULE_SUFFIX, Name) of
+ false ->
+ Name1 = Name ++ ?DEFAULT_TESTMODULE_SUFFIX,
+ M1 = list_to_atom(Name1),
+ try get_module_tests(M1) of
+ Fs1 ->
+ Fs ++ [{"module '" ++ Name1 ++ "'", Fs1}]
+ catch
+ {module_not_found, M1} ->
+ Fs
+ end;
+ true ->
+ Fs
+ end.
+
+testfuns(Es, M, TestSuffix, GeneratorSuffix) ->
+ foldr(fun ({F, 0}, Fs) ->
+ N = atom_to_list(F),
+ case lists:suffix(TestSuffix, N) of
+ true ->
+ [{M,F} | Fs];
+ false ->
+ case lists:suffix(GeneratorSuffix, N) of
+ true ->
+ [{generator, M, F} | Fs];
+ false ->
+ Fs
+ end
+ end;
+ (_, Fs) ->
+ Fs
+ end,
+ [],
+ Es).
+
+
+%% ---------------------------------------------------------------------
+%% Getting a test set from a file
+
+%% @throws {file_read_error, {Reason::atom(), Message::string(),
+%% fileName()}}
+
+get_file_tests(F) ->
+ case is_module_filename(F) of
+ true ->
+ %% look relative to current dir first
+ case file:read_file_info(F) of
+ {ok, #file_info{type=regular}} ->
+ objfile_test(F);
+ _ ->
+ %% (where_is_file/1 does not take a path argument)
+ case code:where_is_file(F) of
+ non_existing ->
+ %% this will produce a suitable error message
+ objfile_test(F);
+ Path ->
+ objfile_test(Path)
+ end
+ end;
+ false ->
+ eunit_lib:consult_file(F)
+ end.
+
+is_module_filename(F) ->
+ filename:extension(F) =:= code:objfile_extension().
+
+objfile_test(File) ->
+ try
+ {module, M} = lists:keyfind(module, 1, beam_lib:info(File)),
+ {setup,
+ fun () ->
+ %% TODO: better error/stacktrace for this internal fun
+ code:purge(M),
+ {module,M} = code:load_abs(filename:rootname(File)),
+ ok
+ end,
+ {module, M}}
+ catch
+ _:_ ->
+ throw({file_read_error,
+ {undefined, "extracting module name failed", File}})
+ end.
+
+
+%% ---------------------------------------------------------------------
+%% Getting a list of module names from object files in a directory
+
+%% @throws {file_read_error, {Reason::atom(), Message::string(),
+%% fileName()}}
+
+%% TODO: handle packages (recursive search for files)
+
+get_directory_modules(D) ->
+ [objfile_test(filename:join(D, F))
+ || F <- eunit_lib:list_dir(D), is_module_filename(F)].
+
+
+
+%% ---------------------------------------------------------------------
+%% Entering a setup-context, with guaranteed cleanup.
+
+%% @spec (Tests::#context{}, Instantiate, Callback) -> any()
+%% Instantiate = (any()) -> tests()
+%% Callback = (tests()) -> any()
+%% @throws {context_error, Error, eunit_lib:exception()}
+%% Error = setup_failed | instantiation_failed | cleanup_failed
+
+enter_context(#context{setup = S, cleanup = C, process = P}, I, F) ->
+ F1 = case P of
+ local -> F;
+ spawn -> fun (X) -> F({spawn, X}) end;
+ {spawn, N} -> fun (T) -> F({spawn, N, T}) end
+ end,
+ eunit_test:enter_context(S, C, I, F1).
+
+
+-ifdef(TEST).
+generator_exported_() ->
+ generator().
+
+generator() ->
+ T = ?_test(ok),
+ [T, T, T].
+
+echo_proc() ->
+ receive {P,X} -> P ! X, echo_proc() end.
+
+ping(P) ->
+ P ! {self(),ping}, receive ping -> ok end.
+
+data_test_() ->
+ Setup = fun () -> spawn(fun echo_proc/0) end,
+ Cleanup = fun (Pid) -> exit(Pid, kill) end,
+ Fail = ?_test(throw(eunit)),
+ T = ?_test(ok),
+ Tests = [T,T,T],
+ [?_assertMatch(ok, eunit:test(T)),
+ ?_assertMatch(error, eunit:test(Fail)),
+ ?_assertMatch(ok, eunit:test({generator, fun () -> Tests end})),
+ ?_assertMatch(ok, eunit:test({generator, fun generator/0})),
+ ?_assertMatch(ok, eunit:test({generator, ?MODULE, generator_exported_})),
+ ?_assertMatch(ok, eunit:test({inorder, Tests})),
+ ?_assertMatch(ok, eunit:test({inparallel, Tests})),
+ ?_assertMatch(ok, eunit:test({timeout, 10, Tests})),
+ ?_assertMatch(ok, eunit:test({spawn, Tests})),
+ ?_assertMatch(ok, eunit:test({setup, Setup, Cleanup,
+ fun (P) -> ?_test(ok = ping(P)) end})),
+ %%?_assertMatch(ok, eunit:test({node, test@localhost, Tests})),
+ ?_assertMatch(ok, eunit:test({module, eunit_lib})),
+ ?_assertMatch(ok, eunit:test(eunit_lib)),
+ ?_assertMatch(ok, eunit:test("examples/tests.txt"))
+
+ %%?_test({foreach, Setup, [T, T, T]})
+ ].
+
+lazy_test_() ->
+ {spawn, [?_test(undefined = put(count, 0)),
+ lazy_gen(7),
+ ?_assertMatch(7, get(count))]}.
+
+lazy_gen(N) ->
+ {generator,
+ fun () ->
+ if N > 0 ->
+ [?_test(put(count,1+get(count)))
+ | lazy_gen(N-1)];
+ true ->
+ []
+ end
+ end}.
+-endif.
diff --git a/lib/eunit/src/eunit_internal.hrl b/lib/eunit/src/eunit_internal.hrl
new file mode 100644
index 0000000000..8d0ac30bd7
--- /dev/null
+++ b/lib/eunit/src/eunit_internal.hrl
@@ -0,0 +1,48 @@
+%% -------------------------------------------------------------------
+%% File: eunit_internal.hrl
+%%
+%% $Id: eunit_internal.hrl 329 2009-03-01 11:23:32Z rcarlsson $
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2006 Richard Carlsson
+%% @doc
+
+-define(SERVER, eunit_server).
+-define(DEFAULT_TEST_SUFFIX, "_test").
+-define(DEFAULT_GENERATOR_SUFFIX, "_test_").
+-define(DEFAULT_EXPORT_SUFFIX, "_exported_").
+-define(DEFAULT_TESTMODULE_SUFFIX, "_tests").
+-define(DEFAULT_GROUP_TIMEOUT, infinity).
+-define(DEFAULT_TEST_TIMEOUT, 5000).
+-define(DEFAULT_SETUP_PROCESS, spawn).
+-define(DEFAULT_MODULE_WRAPPER_NAME, eunit_wrapper_).
+
+-ifdef(DEBUG).
+-define(debugmsg(S),io:fwrite("\n* ~s: ~s\n", [?MODULE,S])).
+-define(debugmsg1(S,As),io:fwrite("\n* ~s: " ++ S ++ "\n", [?MODULE] ++ As)).
+-else.
+-define(debugmsg(S),ok).
+-define(debugmsg1(S,As),ok).
+-endif.
+
+
+%% ---------------------------------------------------------------------
+%% Internal test data representation
+
+-record(test, {f = undefined,
+ desc = undefined,
+ timeout = undefined,
+ location = undefined,
+ line = 0
+ }).
+
+-record(group, {desc = undefined,
+ order = undefined, % run in order or in parallel
+ timeout = undefined,
+ context = undefined, % setup-context record
+ spawn = undefined, % run group in new process
+ tests = undefined}).
+
+-record(context, {setup = undefined,
+ cleanup = undefined,
+ process = local}). % spawn new process for body
diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl
new file mode 100644
index 0000000000..4751f1094a
--- /dev/null
+++ b/lib/eunit/src/eunit_lib.erl
@@ -0,0 +1,576 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: eunit_lib.erl 339 2009-04-05 14:10:47Z rcarlsson $
+%%
+%% @copyright 2004-2007 Micka�l R�mond, Richard Carlsson
+%% @author Micka�l R�mond <[email protected]>
+%% [http://www.process-one.net/]
+%% @author Richard Carlsson <[email protected]>
+%% [http://user.it.uu.se/~richardc/]
+%% @private
+%% @see eunit
+%% @doc Utility functions for eunit
+
+-module(eunit_lib).
+
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+
+-export([dlist_next/1, uniq/1, fun_parent/1, is_string/1, command/1,
+ command/2, command/3, trie_new/0, trie_store/2, trie_match/2,
+ split_node/1, consult_file/1, list_dir/1, format_exit_term/1,
+ format_exception/1, format_error/1]).
+
+
+%% Type definitions for describing exceptions
+%%
+%% @type exception() = {exceptionClass(), Reason::term(), stackTrace()}
+%%
+%% @type exceptionClass() = error | exit | throw
+%%
+%% @type stackTrace() = [{moduleName(), functionName(),
+%% arity() | argList()}]
+%%
+%% @type moduleName() = atom()
+%% @type functionName() = atom()
+%% @type arity() = integer()
+%% @type mfa() = {moduleName(), functionName(), arity()}
+%% @type argList() = [term()]
+%% @type fileName() = string()
+
+
+%% ---------------------------------------------------------------------
+%% Formatting of error descriptors
+
+format_exception({Class,Term,Trace})
+ when is_atom(Class), is_list(Trace) ->
+ case is_stacktrace(Trace) of
+ true ->
+ io_lib:format("~w:~P\n~s",
+ [Class, Term, 20, format_stacktrace(Trace)]);
+ false ->
+ format_term(Term)
+ end;
+format_exception(Term) ->
+ format_term(Term).
+
+format_term(Term) ->
+ io_lib:format("~P\n", [Term, 15]).
+
+format_exit_term(Term) ->
+ {Reason, Trace} = analyze_exit_term(Term),
+ io_lib:format("~P~s", [Reason, 15, Trace]).
+
+analyze_exit_term({Reason, [_|_]=Trace}=Term) ->
+ case is_stacktrace(Trace) of
+ true ->
+ {Reason, format_stacktrace(Trace)};
+ false ->
+ {Term, ""}
+ end;
+analyze_exit_term(Term) ->
+ {Term, ""}.
+
+is_stacktrace([]) ->
+ true;
+is_stacktrace([{M,F,A}|Fs]) when is_atom(M), is_atom(F), is_integer(A) ->
+ is_stacktrace(Fs);
+is_stacktrace([{M,F,As}|Fs]) when is_atom(M), is_atom(F), is_list(As) ->
+ is_stacktrace(Fs);
+is_stacktrace(_) ->
+ false.
+
+format_stacktrace(Trace) ->
+ format_stacktrace(Trace, "in function", "in call from").
+
+format_stacktrace([{M,F,A}|Fs], Pre, Pre1) when is_integer(A) ->
+ [io_lib:fwrite(" ~s ~w:~w/~w\n", [Pre, M, F, A])
+ | format_stacktrace(Fs, Pre1, Pre1)];
+format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) ->
+ A = length(As),
+ C = case is_op(M,F,A) of
+ true when A =:= 1 ->
+ [A1] = As,
+ io_lib:fwrite("~s ~s", [F,format_arg(A1)]);
+ true when A =:= 2 ->
+ [A1, A2] = As,
+ io_lib:fwrite("~s ~s ~s",
+ [format_arg(A1),F,format_arg(A2)]);
+ false ->
+ io_lib:fwrite("~w(~s)", [F,format_arglist(As)])
+ end,
+ [io_lib:fwrite(" ~s ~w:~w/~w\n called as ~s\n",
+ [Pre,M,F,A,C])
+ | format_stacktrace(Fs,Pre1,Pre1)];
+format_stacktrace([],_Pre,_Pre1) ->
+ "".
+
+format_arg(A) ->
+ io_lib:format("~P",[A,15]).
+
+format_arglist([A]) ->
+ format_arg(A);
+format_arglist([A|As]) ->
+ [io_lib:format("~P,",[A,15]) | format_arglist(As)];
+format_arglist([]) ->
+ "".
+
+is_op(erlang, F, A) ->
+ erl_internal:arith_op(F, A)
+ orelse erl_internal:bool_op(F, A)
+ orelse erl_internal:comp_op(F, A)
+ orelse erl_internal:list_op(F, A)
+ orelse erl_internal:send_op(F, A);
+is_op(_M, _F, _A) ->
+ false.
+
+format_error({bad_test, Term}) ->
+ error_msg("bad test descriptor", "~P", [Term, 15]);
+format_error({generator_failed, Exception}) ->
+ error_msg("test generator failed", "~s",
+ [format_exception(Exception)]);
+format_error({no_such_function, {M,F,A}})
+ when is_atom(M), is_atom(F), is_integer(A) ->
+ error_msg(io_lib:format("no such function: ~w:~w/~w", [M,F,A]),
+ "", []);
+format_error({module_not_found, M}) ->
+ error_msg("test module not found", "~p", [M]);
+format_error({application_not_found, A}) when is_atom(A) ->
+ error_msg("application not found", "~w", [A]);
+format_error({file_read_error, {_R, Msg, F}}) ->
+ error_msg("error reading file", "~s: ~s", [Msg, F]);
+format_error({setup_failed, Exception}) ->
+ error_msg("context setup failed", "~s",
+ [format_exception(Exception)]);
+format_error({cleanup_failed, Exception}) ->
+ error_msg("context cleanup failed", "~s",
+ [format_exception(Exception)]);
+format_error({instantiation_failed, Exception}) ->
+ error_msg("instantiation of subtests failed", "~s",
+ [format_exception(Exception)]).
+
+error_msg(Title, Fmt, Args) ->
+ Msg = io_lib:format("::"++Fmt, Args), % gets indentation right
+ io_lib:fwrite("*** ~s ***\n~s\n\n", [Title, Msg]).
+
+
+%% ---------------------------------------------------------------------
+%% Deep list iterator; accepts improper lists/sublists, and also accepts
+%% non-lists on the top level. Nonempty strings (not deep strings) are
+%% recognized as separate elements, even on the top level. (It is not
+%% recommended to include integers in the deep list, since a list of
+%% integers is likely to be interpreted as a string.). The result is
+%% always presented as a list (which may be improper), which is either
+%% empty or otherwise has a non-list head element.
+
+dlist_next([X | Xs] = Xs0) when is_list(X) ->
+ case is_nonempty_string(X) of
+ true -> Xs0;
+ false -> dlist_next(X, Xs)
+ end;
+dlist_next([_|_] = Xs) ->
+ case is_nonempty_string(Xs) of
+ true -> [Xs];
+ false -> Xs
+ end;
+dlist_next([]) ->
+ [];
+dlist_next(X) ->
+ [X].
+
+%% the first two clauses avoid pushing empty lists on the stack
+dlist_next([X], Ys) when is_list(X) ->
+ case is_nonempty_string(X) of
+ true -> [X | Ys];
+ false -> dlist_next(X, Ys)
+ end;
+dlist_next([X], Ys) ->
+ [X | Ys];
+dlist_next([X | Xs], Ys) when is_list(X) ->
+ case is_nonempty_string(X) of
+ true -> [X | [Xs | Ys]];
+ false -> dlist_next(X, [Xs | Ys])
+ end;
+dlist_next([X | Xs], Ys) ->
+ [X | [Xs | Ys]];
+dlist_next([], Xs) ->
+ dlist_next(Xs).
+
+
+-ifdef(TEST).
+dlist_test_() ->
+ {"deep list traversal",
+ [{"non-list term -> singleton list",
+ ?_test([any] = dlist_next(any))},
+ {"empty list -> empty list",
+ ?_test([] = dlist_next([]))},
+ {"singleton list -> singleton list",
+ ?_test([any] = dlist_next([any]))},
+ {"taking the head of a flat list",
+ ?_test([a,b,c] = dlist_next([a,b,c]))},
+ {"skipping an initial empty list",
+ ?_test([a,b,c] = dlist_next([[],a,b,c]))},
+ {"skipping nested initial empty lists",
+ ?_test([a,b,c] = dlist_next([[[[]]],a,b,c]))},
+ {"skipping a final empty list",
+ ?_test([] = dlist_next([[]]))},
+ {"skipping nested final empty lists",
+ ?_test([] = dlist_next([[[[]]]]))},
+ {"the first element is in a sublist",
+ ?_test([a,b,c] = dlist_next([[a],b,c]))},
+ {"recognizing a naked string",
+ ?_test(["abc"] = dlist_next("abc"))},
+ {"recognizing a wrapped string",
+ ?_test(["abc"] = dlist_next(["abc"]))},
+ {"recognizing a leading string",
+ ?_test(["abc",a,b,c] = dlist_next(["abc",a,b,c]))},
+ {"recognizing a nested string",
+ ?_test(["abc"] = dlist_next([["abc"]]))},
+ {"recognizing a leading string in a sublist",
+ ?_test(["abc",a,b,c] = dlist_next([["abc"],a,b,c]))},
+ {"traversing an empty list",
+ ?_test([] = dlist_flatten([]))},
+ {"traversing a flat list",
+ ?_test([a,b,c] = dlist_flatten([a,b,c]))},
+ {"traversing a deep list",
+ ?_test([a,b,c] = dlist_flatten([[],[a,[b,[]],c],[]]))},
+ {"traversing a deep but empty list",
+ ?_test([] = dlist_flatten([[],[[[]]],[]]))}
+ ]}.
+
+%% test support
+dlist_flatten(Xs) ->
+ case dlist_next(Xs) of
+ [X | Xs1] -> [X | dlist_flatten(Xs1)];
+ [] -> []
+ end.
+-endif.
+
+
+%% ---------------------------------------------------------------------
+%% Check for proper Unicode-stringness.
+
+is_string([C | Cs]) when is_integer(C), C >= 0, C =< 16#10ffff ->
+ is_string(Cs);
+is_string([_ | _]) ->
+ false;
+is_string([]) ->
+ true;
+is_string(_) ->
+ false.
+
+is_nonempty_string([]) -> false;
+is_nonempty_string(Cs) -> is_string(Cs).
+
+-ifdef(TEST).
+is_string_test_() ->
+ {"is_string",
+ [{"no non-lists", ?_assert(not is_string($A))},
+ {"no non-integer lists", ?_assert(not is_string([true]))},
+ {"empty string", ?_assert(is_string(""))},
+ {"ascii string", ?_assert(is_string(lists:seq(0, 127)))},
+ {"latin-1 string", ?_assert(is_string(lists:seq(0, 255)))},
+ {"unicode string",
+ ?_assert(is_string([0, $A, 16#10fffe, 16#10ffff]))},
+ {"not above unicode range",
+ ?_assert(not is_string([0, $A, 16#110000]))},
+ {"no negative codepoints", ?_assert(not is_string([$A, -1, 0]))}
+ ]}.
+-endif.
+
+
+%% ---------------------------------------------------------------------
+%% Splitting a full node name into basename and hostname,
+%% using 'localhost' as the default hostname
+
+split_node(N) when is_atom(N) -> split_node(atom_to_list(N));
+split_node(Cs) -> split_node_1(Cs, []).
+
+split_node_1([$@ | Cs], As) -> split_node_2(As, Cs);
+split_node_1([C | Cs], As) -> split_node_1(Cs, [C | As]);
+split_node_1([], As) -> split_node_2(As, "localhost").
+
+split_node_2(As, Cs) ->
+ {list_to_atom(lists:reverse(As)), list_to_atom(Cs)}.
+
+%% ---------------------------------------------------------------------
+%% Get the name of the containing function for a fun. (This is encoded
+%% in the name of the generated function that implements the fun.)
+fun_parent(F) ->
+ {module, M} = erlang:fun_info(F, module),
+ {name, N} = erlang:fun_info(F, name),
+ case erlang:fun_info(F, type) of
+ {type, external} ->
+ {arity, A} = erlang:fun_info(F, arity),
+ {M, N, A};
+ {type, local} ->
+ [$-|S] = atom_to_list(N),
+ C1 = string:chr(S, $/),
+ C2 = string:chr(S, $-),
+ {M, list_to_atom(string:sub_string(S, 1, C1 - 1)),
+ list_to_integer(string:sub_string(S, C1 + 1, C2 - 1))}
+ end.
+
+-ifdef(TEST).
+fun_parent_test() ->
+ {?MODULE,fun_parent_test,0} = fun_parent(fun () -> ok end).
+-endif.
+
+%% ---------------------------------------------------------------------
+%% Ye olde uniq function
+
+uniq([X, X | Xs]) -> uniq([X | Xs]);
+uniq([X | Xs]) -> [X | uniq(Xs)];
+uniq([]) -> [].
+
+-ifdef(TEST).
+uniq_test_() ->
+ {"uniq",
+ [?_assertError(function_clause, uniq(ok)),
+ ?_assertError(function_clause, uniq([1|2])),
+ ?_test([] = uniq([])),
+ ?_test([1,2,3] = uniq([1,2,3])),
+ ?_test([1,2,3] = uniq([1,2,2,3])),
+ ?_test([1,2,3,2,1] = uniq([1,2,2,3,2,2,1])),
+ ?_test([1,2,3] = uniq([1,1,1,2,2,2,3,3,3])),
+ ?_test(["1","2","3"] = uniq(["1","1","2","2","3","3"]))
+ ]}.
+-endif.
+
+%% ---------------------------------------------------------------------
+%% Replacement for os:cmd
+
+%% TODO: Better cmd support, especially on Windows (not much tested)
+%% TODO: Can we capture stderr separately somehow?
+
+command(Cmd) ->
+ command(Cmd, "").
+
+command(Cmd, Dir) ->
+ command(Cmd, Dir, []).
+
+command(Cmd, Dir, Env) ->
+ CD = if Dir =:= "" -> [];
+ true -> [{cd, Dir}]
+ end,
+ SetEnv = if Env =:= [] -> [];
+ true -> [{env, Env}]
+ end,
+ Opt = CD ++ SetEnv ++ [stream, exit_status, use_stdio,
+ stderr_to_stdout, in, eof],
+ P = open_port({spawn, Cmd}, Opt),
+ get_data(P, []).
+
+get_data(P, D) ->
+ receive
+ {P, {data, D1}} ->
+ get_data(P, [D1|D]);
+ {P, eof} ->
+ port_close(P),
+ receive
+ {P, {exit_status, N}} ->
+ {N, normalize(lists:flatten(lists:reverse(D)))}
+ end
+ end.
+
+normalize([$\r, $\n | Cs]) ->
+ [$\n | normalize(Cs)];
+normalize([$\r | Cs]) ->
+ [$\n | normalize(Cs)];
+normalize([C | Cs]) ->
+ [C | normalize(Cs)];
+normalize([]) ->
+ [].
+
+-ifdef(TEST).
+
+cmd_test_() ->
+ ([?_test({0, "hello\n"} = ?_cmd_("echo hello"))]
+ ++ case os:type() of
+ {unix, _} ->
+ unix_cmd_tests();
+ {win32, _} ->
+ win32_cmd_tests();
+ _ ->
+ []
+ end).
+
+unix_cmd_tests() ->
+ [{"command execution, status, and output",
+ [?_cmd("echo hello"),
+ ?_assertCmdStatus(0, "true"),
+ ?_assertCmdStatus(1, "false"),
+ ?_assertCmd("true"),
+ ?_assertCmdOutput("hello\n", "echo hello"),
+ ?_assertCmdOutput("hello", "echo -n hello")
+ ]},
+ {"file setup and cleanup",
+ setup,
+ fun () -> ?cmd("mktemp tmp.XXXXXXXX") end,
+ fun (File) -> ?cmd("rm " ++ File) end,
+ fun (File) ->
+ [?_assertCmd("echo xyzzy >" ++ File),
+ ?_assertCmdOutput("xyzzy\n", "cat " ++ File)]
+ end}
+ ].
+
+win32_cmd_tests() ->
+ [{"command execution, status, and output",
+ [?_cmd("echo hello"),
+ ?_assertCmdOutput("hello\n", "echo hello")
+ ]}
+ ].
+
+-endif. % TEST
+
+
+%% ---------------------------------------------------------------------
+%% Wrapper around file:path_consult
+
+%% @throws {file_read_error, {Reason::atom(), Message::string(),
+%% fileName()}}
+
+consult_file(File) ->
+ case file:path_consult(["."]++code:get_path(), File) of
+ {ok, Data, _Path} ->
+ Data;
+ {error, Reason} ->
+ Msg = file:format_error(Reason),
+ throw({file_read_error, {Reason, Msg, File}})
+ end.
+
+%% ---------------------------------------------------------------------
+%% Wrapper around file:list_dir
+
+%% @throws {file_read_error, {Reason::atom(), Message::string(),
+%% fileName()}}
+
+list_dir(Dir) ->
+ case file:list_dir(Dir) of
+ {ok, Fs} ->
+ Fs;
+ {error, Reason} ->
+ Msg = file:format_error(Reason),
+ throw({file_read_error, {Reason, Msg, Dir}})
+ end.
+
+%% ---------------------------------------------------------------------
+%% A trie for remembering and checking least specific cancelled events
+%% (an empty list `[]' simply represents a stored empty list, i.e., all
+%% events will match, while an empty tree means that no events match).
+
+trie_new() ->
+ gb_trees:empty().
+
+trie_store([_ | _], []) ->
+ [];
+trie_store([E | Es], T) ->
+ case gb_trees:lookup(E, T) of
+ none ->
+ if Es =:= [] ->
+ gb_trees:insert(E, [], T);
+ true ->
+ gb_trees:insert(E, trie_store(Es, gb_trees:empty()),
+ T)
+ end;
+ {value, []} ->
+ T; %% prefix already stored
+ {value, T1} ->
+ gb_trees:update(E, trie_store(Es, T1), T)
+ end;
+trie_store([], _T) ->
+ [].
+
+trie_match([_ | _], []) ->
+ prefix;
+trie_match([E | Es], T) ->
+ case gb_trees:lookup(E, T) of
+ none ->
+ no;
+ {value, []} ->
+ if Es =:= [] -> exact;
+ true -> prefix
+ end;
+ {value, T1} ->
+ trie_match(Es, T1)
+ end;
+trie_match([], []) ->
+ exact;
+trie_match([], _T) ->
+ no.
+
+-ifdef(TEST).
+
+trie_test_() ->
+ [{"basic representation",
+ [?_assert(trie_new() =:= gb_trees:empty()),
+ ?_assert(trie_store([1], trie_new())
+ =:= gb_trees:insert(1, [], gb_trees:empty())),
+ ?_assert(trie_store([1,2], trie_new())
+ =:= gb_trees:insert(1,
+ gb_trees:insert(2, [],
+ gb_trees:empty()),
+ gb_trees:empty())),
+ ?_assert([] =:= trie_store([1], [])),
+ ?_assert([] =:= trie_store([], gb_trees:empty()))
+ ]},
+ {"basic storing and matching",
+ [?_test(no = trie_match([], trie_new())),
+ ?_test(exact = trie_match([], trie_store([], trie_new()))),
+ ?_test(no = trie_match([], trie_store([1], trie_new()))),
+ ?_test(exact = trie_match([1], trie_store([1], trie_new()))),
+ ?_test(prefix = trie_match([1,2], trie_store([1], trie_new()))),
+ ?_test(no = trie_match([1], trie_store([1,2], trie_new()))),
+ ?_test(no = trie_match([1,3], trie_store([1,2], trie_new()))),
+ ?_test(exact = trie_match([1,2,3,4,5],
+ trie_store([1,2,3,4,5], trie_new()))),
+ ?_test(prefix = trie_match([1,2,3,4,5],
+ trie_store([1,2,3], trie_new()))),
+ ?_test(no = trie_match([1,2,2,4,5],
+ trie_store([1,2,3], trie_new())))
+ ]},
+ {"matching with partially overlapping patterns",
+ setup,
+ fun () ->
+ trie_store([1,3,2], trie_store([1,2,3], trie_new()))
+ end,
+ fun (T) ->
+ [?_test(no = trie_match([], T)),
+ ?_test(no = trie_match([1], T)),
+ ?_test(no = trie_match([1,2], T)),
+ ?_test(no = trie_match([1,3], T)),
+ ?_test(exact = trie_match([1,2,3], T)),
+ ?_test(exact = trie_match([1,3,2], T)),
+ ?_test(no = trie_match([1,2,2], T)),
+ ?_test(no = trie_match([1,3,3], T)),
+ ?_test(prefix = trie_match([1,2,3,4], T)),
+ ?_test(prefix = trie_match([1,3,2,1], T))]
+ end},
+ {"matching with more general pattern overriding less general",
+ setup,
+ fun () -> trie_store([1], trie_store([1,2,3], trie_new())) end,
+ fun (_) -> ok end,
+ fun (T) ->
+ [?_test(no = trie_match([], T)),
+ ?_test(exact = trie_match([1], T)),
+ ?_test(prefix = trie_match([1,2], T)),
+ ?_test(prefix = trie_match([1,2,3], T)),
+ ?_test(prefix = trie_match([1,2,3,4], T))]
+ end}
+ ].
+
+-endif. % TEST
diff --git a/lib/eunit/src/eunit_listener.erl b/lib/eunit/src/eunit_listener.erl
new file mode 100644
index 0000000000..20faecbf01
--- /dev/null
+++ b/lib/eunit/src/eunit_listener.erl
@@ -0,0 +1,178 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2009 Richard Carlsson
+%% @private
+%% @see eunit
+%% @doc Generic listener process for eunit.
+
+-module(eunit_listener).
+
+-define(NODEBUG, true).
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+-export([start/1, start/2]).
+
+-export([behaviour_info/1]).
+
+
+behaviour_info(callbacks) ->
+ [{init,1},{handle_begin,3},{handle_end,3},{handle_cancel,3},
+ {terminate,2}];
+behaviour_info(_Other) ->
+ undefined.
+
+
+-record(state, {callback, % callback module
+ pass = 0,
+ fail = 0,
+ skip = 0,
+ cancel = 0,
+ state % substate
+ }).
+
+start(Callback) ->
+ start(Callback, []).
+
+start(Callback, Options) ->
+ St = #state{callback = Callback},
+ spawn_opt(fun () -> init(St, Options) end,
+ proplists:get_all_values(spawn, Options)).
+
+init(St0, Options) ->
+ St1 = call(init, [Options], St0),
+ St2 = expect([], undefined, St1),
+ Data = [{pass, St2#state.pass},
+ {fail, St2#state.fail},
+ {skip, St2#state.skip},
+ {cancel, St2#state.cancel}],
+ call(terminate, [{ok, Data}, St2#state.state], St2),
+ exit(normal).
+
+expect(Id, ParentId, St) ->
+ case wait_for(Id, 'begin', ParentId) of
+ {done, Data} ->
+ {done, Data, St};
+ {ok, Msg} ->
+ case Msg of
+ {group, Data} ->
+ group(Id, Data, St);
+ {test, Data} ->
+ St1 = handle_begin(test, Id, Data, St),
+ case wait_for(Id, 'end', ParentId) of
+ {cancel, Reason} ->
+ handle_cancel(test, Id, Data, Reason, St1);
+ {ok, Result} ->
+ handle_end(test, Id, Data, Result, St1)
+ end
+ end
+ end.
+
+%% collect group items in order until group is done
+group(Id, Data, St) ->
+ St1 = handle_begin(group, Id, Data, St),
+ group_loop(0, Id, Data, St1).
+
+group_loop(N, Id, Data, St) ->
+ N1 = N + 1,
+ case expect(Id ++ [N1], Id, St) of
+ {done, {cancel, Reason}, St1} ->
+ handle_cancel(group, Id, Data, Reason, St1);
+ {done, Result, St1} ->
+ handle_end(group, Id, Data, Result, St1);
+ St1 ->
+ group_loop(N1, Id, Data, St1)
+ end.
+
+%% waiting for [..., M, N] begin
+%% get:
+%% [..., M, N] begin test -> expect [..., M, N] end (test begin)
+%% [..., M, N] begin group -> expect [..., M, N, 1] end (group begin)
+%% [..., M] end -> expect [..., M+1] begin (parent end)
+%% cancel([..., M]) (parent cancel)
+%%
+%% waiting for [..., M, N] end
+%% get:
+%% [..., M, N] end -> expect [..., M, N+1] begin (seen end)
+%% cancel([..., M, N]) (cancelled)
+
+wait_for(Id, Type, ParentId) ->
+ ?debugFmt("waiting for ~w ~w", [Id, Type]),
+ receive
+ {status, Id, {progress, Type, Data}} ->
+ ?debugFmt("got status ~w ~w", [Id, Data]),
+ {ok, Data};
+ {status, ParentId, {progress, 'end', Data}} when Type =:= 'begin' ->
+ ?debugFmt("got parent end ~w ~w", [ParentId, Data]),
+ {done, Data};
+ {status, Id, {cancel, Reason}} when Type =:= 'end' ->
+ ?debugFmt("got cancel ~w ~w", [Id, Reason]),
+ {cancel, Reason};
+ {status, ParentId, {cancel, _Reason}} ->
+ ?debugFmt("got parent cancel ~w ~w", [ParentId, _Reason]),
+ {done, {cancel, _Reason}}
+ end.
+
+call(F, As, St) when is_atom(F) ->
+ try apply(St#state.callback, F, As) of
+ Substate -> St#state{state = Substate}
+ catch
+ Class:Term ->
+ Trace = erlang:get_stacktrace(),
+ if F =/= terminate ->
+ call(terminate, [{error, {Class, Term, Trace}},
+ St#state.state], St);
+ true -> ok
+ end,
+ erlang:raise(Class, Term, Trace)
+ end.
+
+handle_begin(group, Id, Data0, St) ->
+ Data = [{id, Id} | Data0],
+ ?debugFmt("handle_begin group ~w ~w", [Id, Data0]),
+ call(handle_begin, [group, Data, St#state.state], St);
+handle_begin(test, Id, Data0, St) ->
+ Data = [{id, Id} | Data0],
+ ?debugFmt("handle_begin test ~w ~w", [Id, Data0]),
+ call(handle_begin, [test, Data, St#state.state], St).
+
+handle_end(group, Id, Data0, {Count, Data1}, St) ->
+ Data = [{id, Id}, {size, Count} | Data0 ++ Data1],
+ ?debugFmt("handle_end group ~w ~w", [Id, {Count, Data1}]),
+ call(handle_end, [group, Data, St#state.state], St);
+handle_end(test, Id, Data0, {Status, Data1}, St) ->
+ Data = [{id, Id}, {status, Status} | Data0 ++ Data1],
+ ?debugFmt("handle_end test ~w ~w", [Id, {Status, Data1}]),
+ St1 = case Status of
+ ok -> St#state{pass = St#state.pass + 1};
+ {skipped,_} -> St#state{skip = St#state.skip + 1};
+ {error,_} -> St#state{fail = St#state.fail + 1}
+ end,
+ call(handle_end, [test, Data, St#state.state], St1).
+
+handle_cancel(group, Id, Data0, Reason, St) ->
+ Data = [{id, Id}, {reason, Reason} | Data0],
+ ?debugFmt("handle_cancel group ~w ~w", [Id, Reason]),
+ call(handle_cancel, [group, Data, St#state.state],
+ St#state{cancel = St#state.cancel + 1});
+handle_cancel(test, Id, Data0, Reason, St) ->
+ Data = [{id, Id}, {reason, Reason} | Data0],
+ ?debugFmt("handle_cancel test ~w ~w", [Id, Reason]),
+ call(handle_cancel, [test, Data, St#state.state],
+ St#state{cancel = St#state.cancel + 1}).
diff --git a/lib/eunit/src/eunit_proc.erl b/lib/eunit/src/eunit_proc.erl
new file mode 100644
index 0000000000..e2d51d8bd5
--- /dev/null
+++ b/lib/eunit/src/eunit_proc.erl
@@ -0,0 +1,661 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2006 Richard Carlsson
+%% @private
+%% @see eunit
+%% @doc Test runner process tree functions
+
+-module(eunit_proc).
+
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+-export([start/4]).
+
+%% This must be exported; see new_group_leader/1 for details.
+-export([group_leader_process/1]).
+
+-record(procstate, {ref, id, super, insulator, parent, order}).
+
+
+%% Spawns test process and returns the process Pid; sends {done,
+%% Reference, Pid} to caller when finished. See the function
+%% wait_for_task/2 for details about the need for the reference.
+%%
+%% The `Super' process receives a stream of status messages; see
+%% message_super/3 for details.
+
+start(Tests, Order, Super, Reference)
+ when is_pid(Super), is_reference(Reference) ->
+ St = #procstate{ref = Reference,
+ id = [],
+ super = Super,
+ order = Order},
+ spawn_group(local, #group{tests = Tests}, St).
+
+
+%% Status messages sent to the supervisor process. (A supervisor does
+%% not have to act on these messages - it can e.g. just log them, or
+%% even discard them.) Each status message has the following form:
+%%
+%% {status, Id, Info}
+%%
+%% where Id identifies the item that the message pertains to, and the
+%% Info part can be one of:
+%%
+%% {progress, 'begin', {test | group, Data}}
+%% indicates that the item has been entered, and what type it is;
+%% Data is [{desc,binary()}, {source,Source}, {line,integer()}] for
+%% a test, and [{desc,binary()}, {spawn,SpawnType},
+%% {order,OrderType}] for a group.
+%%
+%% {progress, 'end', {Status, Data}}
+%% Status = 'ok' | {error, Exception} | {skipped, Cause} | integer()
+%% Data = [{time,integer()}, {output,binary()}]
+%%
+%% where Time is measured in milliseconds and Output is the data
+%% written to the standard output stream during the test; if
+%% Status is {skipped, Cause}, then Cause is a term thrown from
+%% eunit_test:run_testfun/1. For a group item, the Status field is
+%% the number of immediate subitems of the group; this helps the
+%% collation of results. Failure for groups is always signalled
+%% through a cancel message, not through the Status field.
+%%
+%% {cancel, Descriptor}
+%% where Descriptor can be:
+%% timeout a timeout occurred
+%% {blame, Id} forced to terminate because of item `Id'
+%% {abort, Cause} the test or group failed to execute
+%% {exit, Reason} the test process terminated unexpectedly
+%% {startup, Reason} failed to start a remote test process
+%%
+%% where Cause is a term thrown from eunit_data:enter_context/4 or
+%% from eunit_data:iter_next/2, and Reason is an exit term from a
+%% crashed process
+%%
+%% Note that due to concurrent (and possibly distributed) execution,
+%% there are *no* strict ordering guarantees on the status messages,
+%% with one exception: a 'begin' message will always arrive before its
+%% corresponding 'end' message.
+
+message_super(Id, Info, St) ->
+ St#procstate.super ! {status, Id, Info}.
+
+
+%% @TODO implement synchronized mode for insulator/child execution
+
+%% Ideas for synchronized mode:
+%%
+%% * At each "program point", i.e., before entering a test, entering a
+%% group, or leaving a group, the child will synchronize with the
+%% insulator to make sure it is ok to proceed.
+%%
+%% * The insulator can receive controlling messages from higher up in
+%% the hierarchy, telling it to pause, resume, single-step, repeat, etc.
+%%
+%% * Synchronization on entering/leaving groups is necessary in order to
+%% get control over things such as subprocess creation/termination and
+%% setup/cleanup, making it possible to, e.g., repeat all the tests
+%% within a particular subprocess without terminating and restarting it,
+%% or repeating tests without repeating the setup/cleanup.
+%%
+%% * Some tests that depend on state will not be possible to repeat, but
+%% require a fresh context setup. There is nothing that can be done
+%% about this, and the many tests that are repeatable should not be
+%% punished because of it. The user must decide which level to restart.
+%%
+%% * Question: How propagate control messages down the hierarchy
+%% (preferably only to the correct insulator process)? An insulator does
+%% not currenctly know whether its child process has spawned subtasks.
+%% (The "supervisor" process does not know the Pids of the controlling
+%% insulator processes in the tree, and it probably should not be
+%% responsible for this anyway.)
+
+
+%% ---------------------------------------------------------------------
+%% Process tree primitives
+
+%% A "task" consists of an insulator process and a child process which
+%% handles the actual work. When the child terminates, the insulator
+%% process sends {done, Reference, self()} to the process which started
+%% the task (the "parent"). The child process is given a State record
+%% which contains the process id:s of the parent, the insulator, and the
+%% supervisor.
+
+%% @spec (Type, (#procstate{}) -> () -> term(), #procstate{}) -> pid()
+%% Type = local | {remote, Node::atom()}
+
+start_task(Type, Fun, St0) ->
+ St = St0#procstate{parent = self()},
+ %% (note: the link here is mainly to propagate signals *downwards*,
+ %% so that the insulator can detect if the process that started the
+ %% task dies before the task is done)
+ F = fun () -> insulator_process(Type, Fun, St) end,
+ case Type of
+ local ->
+ %% we assume (at least for now) that local spawns can never
+ %% fail in such a way that the process does not start, so a
+ %% new local insulator does not need to synchronize here
+ spawn_link(F);
+ {remote, Node} ->
+ Pid = spawn_link(Node, F),
+ %% See below for the need for the {ok, Reference, Pid}
+ %% message.
+ Reference = St#procstate.ref,
+ Monitor = erlang:monitor(process, Pid),
+ %% (the DOWN message is guaranteed to arrive after any
+ %% messages sent by the process itself)
+ receive
+ {ok, Reference, Pid} ->
+ Pid;
+ {'DOWN', Monitor, process, Pid, Reason} ->
+ %% send messages as if the insulator process was
+ %% started, but terminated on its own accord
+ Msg = {startup, Reason},
+ message_super(St#procstate.id, {cancel, Msg}, St),
+ self() ! {done, Reference, Pid}
+ end,
+ erlang:demonitor(Monitor, [flush]),
+ Pid
+ end.
+
+%% Relatively simple, and hopefully failure-proof insulator process
+%% (This is cleaner than temporarily setting up the caller to trap
+%% signals, and does not affect the caller's mailbox or other state.)
+%%
+%% We assume that nobody does a 'kill' on an insulator process - if that
+%% should happen, the test framework will hang since the insulator will
+%% never send a reply; see below for more.
+%%
+%% Note that even if the insulator process itself never fails, it is
+%% still possible that it does not start properly, if it is spawned
+%% remotely (e.g., if the remote node is down). Therefore, remote
+%% insulators must always immediately send an {ok, Reference, self()}
+%% message to the parent as soon as it is spawned.
+
+%% @spec (Type, Fun::() -> term(), St::#procstate{}) -> ok
+%% Type = local | {remote, Node::atom()}
+
+insulator_process(Type, Fun, St0) ->
+ process_flag(trap_exit, true),
+ Parent = St0#procstate.parent,
+ if Type =:= local -> ok;
+ true -> Parent ! {ok, St0#procstate.ref, self()}
+ end,
+ St = St0#procstate{insulator = self()},
+ Child = spawn_link(fun () -> child_process(Fun(St), St) end),
+ insulator_wait(Child, Parent, [], St).
+
+%% Normally, child processes exit with the reason 'normal' even if the
+%% executed tests failed (by throwing exceptions), since the tests are
+%% executed within a try-block. Child processes can terminate abnormally
+%% by the following reasons:
+%% 1) an error in the processing of the test descriptors (a malformed
+%% descriptor, failure in a setup, cleanup or initialization, a
+%% missing module or function, or a failing generator function);
+%% 2) an internal error in the test running framework itself;
+%% 3) receiving a non-trapped error signal as a consequence of running
+%% test code.
+%% Those under point 1 are "expected errors", handled specially in the
+%% protocol, while the other two are unexpected errors. (Since alt. 3
+%% implies that the test neither reported success nor failure, it can
+%% never be considered "proper" behaviour of a test.) Abnormal
+%% termination is reported to the supervisor process but otherwise does
+%% not affect the insulator compared to normal termination. Child
+%% processes can also be killed abruptly by their insulators, in case of
+%% a timeout or if a parent process dies.
+%%
+%% The insulator is the group leader for the child process, and gets all
+%% of its standard I/O. The output is buffered and associated with the
+%% currently active test or group, and is sent along with the 'end'
+%% progress message when the test or group has finished.
+
+insulator_wait(Child, Parent, Buf, St) ->
+ receive
+ {child, Child, Id, {'begin', Type, Data}} ->
+ message_super(Id, {progress, 'begin', {Type, Data}}, St),
+ insulator_wait(Child, Parent, [[] | Buf], St);
+ {child, Child, Id, {'end', Status, Time}} ->
+ Data = [{time, Time}, {output, buffer_to_binary(hd(Buf))}],
+ message_super(Id, {progress, 'end', {Status, Data}}, St),
+ insulator_wait(Child, Parent, tl(Buf), St);
+ {child, Child, Id, {skipped, Reason}} ->
+ %% this happens when a subgroup fails to enter the context
+ message_super(Id, {cancel, {abort, Reason}}, St),
+ insulator_wait(Child, Parent, Buf, St);
+ {child, Child, Id, {abort, Cause}} ->
+ %% this happens when the child code threw an internal
+ %% eunit_abort; the child process has already exited
+ exit_messages(Id, {abort, Cause}, St),
+ %% no need to wait for the {'EXIT',Child,_} message
+ terminate_insulator(St);
+ {io_request, Child, ReplyAs, Req} ->
+ %% we only collect output from the child process itself, not
+ %% from secondary processes, otherwise we get race problems;
+ %% however, each test runs its personal group leader that
+ %% funnels all output - see the run_test() function
+ Buf1 = io_request(Child, ReplyAs, Req, hd(Buf)),
+ insulator_wait(Child, Parent, [Buf1 | tl(Buf)], St);
+ {io_request, From, ReplyAs, Req} when is_pid(From) ->
+ %% (this shouldn't happen anymore, but we keep it safe)
+ %% just ensure the sender gets a reply; ignore the data
+ io_request(From, ReplyAs, Req, []),
+ insulator_wait(Child, Parent, Buf, St);
+ {timeout, Child, Id} ->
+ exit_messages(Id, timeout, St),
+ kill_task(Child, St);
+ {'EXIT', Child, normal} ->
+ terminate_insulator(St);
+ {'EXIT', Child, Reason} ->
+ exit_messages(St#procstate.id, {exit, Reason}, St),
+ terminate_insulator(St);
+ {'EXIT', Parent, _} ->
+ %% make sure child processes are cleaned up recursively
+ kill_task(Child, St)
+ end.
+
+kill_task(Child, St) ->
+ exit(Child, kill),
+ terminate_insulator(St).
+
+buffer_to_binary([B]) when is_binary(B) -> B; % avoid unnecessary copying
+buffer_to_binary(Buf) -> list_to_binary(lists:reverse(Buf)).
+
+%% Unlinking before exit avoids polluting the parent process with exit
+%% signals from the insulator. The child process is already dead here.
+
+terminate_insulator(St) ->
+ %% messaging/unlinking is ok even if the parent is already dead
+ Parent = St#procstate.parent,
+ Parent ! {done, St#procstate.ref, self()},
+ unlink(Parent),
+ exit(normal).
+
+%% send cancel messages for the Id of the "causing" item, and also for
+%% the Id of the insulator itself, if they are different
+exit_messages(Id, Cause, St) ->
+ %% the message for the most specific Id is always sent first
+ message_super(Id, {cancel, Cause}, St),
+ case St#procstate.id of
+ Id -> ok;
+ Id1 -> message_super(Id1, {cancel, {blame, Id}}, St)
+ end.
+
+%% Child processes send all messages via the insulator to ensure proper
+%% sequencing with timeouts and exit signals.
+
+message_insulator(Data, St) ->
+ St#procstate.insulator ! {child, self(), St#procstate.id, Data}.
+
+%% Timeout handling
+
+set_timeout(Time, St) ->
+ erlang:send_after(Time, St#procstate.insulator,
+ {timeout, self(), St#procstate.id}).
+
+clear_timeout(Ref) ->
+ erlang:cancel_timer(Ref).
+
+with_timeout(undefined, Default, F, St) ->
+ with_timeout(Default, F, St);
+with_timeout(Time, _Default, F, St) ->
+ with_timeout(Time, F, St).
+
+with_timeout(infinity, F, _St) ->
+ %% don't start timers unnecessarily
+ {T0, _} = statistics(wall_clock),
+ Value = F(),
+ {T1, _} = statistics(wall_clock),
+ {Value, T1 - T0};
+with_timeout(Time, F, St) when is_integer(Time), Time > 16#FFFFffff ->
+ with_timeout(16#FFFFffff, F, St);
+with_timeout(Time, F, St) when is_integer(Time), Time < 0 ->
+ with_timeout(0, F, St);
+with_timeout(Time, F, St) when is_integer(Time) ->
+ Ref = set_timeout(Time, St),
+ {T0, _} = statistics(wall_clock),
+ try F() of
+ Value ->
+ %% we could also read the timer, but this is simpler
+ {T1, _} = statistics(wall_clock),
+ {Value, T1 - T0}
+ after
+ clear_timeout(Ref)
+ end.
+
+%% The normal behaviour of a child process is not to trap exit
+%% signals. The testing framework is not dependent on this, however, so
+%% the test code is allowed to enable signal trapping as it pleases.
+%% Note that I/O is redirected to the insulator process.
+
+%% @spec (() -> term(), #procstate{}) -> ok
+
+child_process(Fun, St) ->
+ group_leader(St#procstate.insulator, self()),
+ try Fun() of
+ _ -> ok
+ catch
+ %% the only "normal" way for a child process to bail out (e.g,
+ %% when not being able to parse the test descriptor) is to throw
+ %% an {eunit_abort, Reason} exception; any other exception will
+ %% be reported as an unexpected termination of the test
+ {eunit_abort, Cause} ->
+ message_insulator({abort, Cause}, St),
+ exit(aborted)
+ end.
+
+-ifdef(TEST).
+child_test_() ->
+ [{"test processes do not trap exit signals",
+ ?_assertMatch(false, process_flag(trap_exit, false))}].
+-endif.
+
+%% @throws abortException()
+%% @type abortException() = {eunit_abort, Cause::term()}
+
+abort_task(Cause) ->
+ throw({eunit_abort, Cause}).
+
+%% Typically, the process that executes this code is not trapping
+%% signals, but it might be - it is outside of our control, since test
+%% code can enable or disable trapping at will. That we cannot rely on
+%% process links here, is why the insulator process of a task must be
+%% guaranteed to always send a reply before it terminates.
+%%
+%% The unique reference guarantees that we don't extract any message
+%% from the mailbox unless it belongs to the test framework (and not to
+%% the running tests) - it is not possible to use selective receive to
+%% match only messages that are tagged with some pid out of a
+%% dynamically varying set of pids. When the wait-loop terminates, no
+%% such message should remain in the mailbox.
+
+wait_for_task(Pid, St) ->
+ wait_for_tasks(sets:from_list([Pid]), St).
+
+wait_for_tasks(PidSet, St) ->
+ case sets:size(PidSet) of
+ 0 ->
+ ok;
+ _ ->
+ %% (note that when we receive this message for some task, we
+ %% are guaranteed that the insulator process of the task has
+ %% already informed the supervisor about any anomalies)
+ Reference = St#procstate.ref,
+ receive
+ {done, Reference, Pid} ->
+ %% (if Pid is not in the set, del_element has no
+ %% effect, so this is always safe)
+ Rest = sets:del_element(Pid, PidSet),
+ wait_for_tasks(Rest, St)
+ end
+ end.
+
+%% ---------------------------------------------------------------------
+%% Separate testing process
+
+%% TODO: Ability to stop after N failures.
+%% TODO: Flow control, starting new job as soon as slot is available
+
+tests(T, St) ->
+ I = eunit_data:iter_init(T, St#procstate.id),
+ case St#procstate.order of
+ inorder -> tests_inorder(I, St);
+ inparallel -> tests_inparallel(I, 0, St);
+ {inparallel, N} when is_integer(N), N >= 0 ->
+ tests_inparallel(I, N, St)
+ end.
+
+set_id(I, St) ->
+ St#procstate{id = eunit_data:iter_id(I)}.
+
+tests_inorder(I, St) ->
+ tests_inorder(I, 0, St).
+
+tests_inorder(I, N, St) ->
+ case get_next_item(I) of
+ {T, I1} ->
+ handle_item(T, set_id(I1, St)),
+ tests_inorder(I1, N+1, St);
+ none ->
+ N % the return status of a group is the subtest count
+ end.
+
+tests_inparallel(I, K0, St) ->
+ tests_inparallel(I, 0, St, K0, K0, sets:new()).
+
+tests_inparallel(I, N, St, K, K0, Children) when K =< 0, K0 > 0 ->
+ wait_for_tasks(Children, St),
+ tests_inparallel(I, N, St, K0, K0, sets:new());
+tests_inparallel(I, N, St, K, K0, Children) ->
+ case get_next_item(I) of
+ {T, I1} ->
+ Child = spawn_item(T, set_id(I1, St)),
+ tests_inparallel(I1, N+1, St, K - 1, K0,
+ sets:add_element(Child, Children));
+ none ->
+ wait_for_tasks(Children, St),
+ N % the return status of a group is the subtest count
+ end.
+
+%% this starts a new separate task for an inparallel-item (which might
+%% be a group and in that case might cause yet another spawn in the
+%% handle_group() function, but it might also be just a single test)
+spawn_item(T, St0) ->
+ Fun = fun (St) ->
+ fun () -> handle_item(T, St) end
+ end,
+ %% inparallel-items are always spawned locally
+ start_task(local, Fun, St0).
+
+get_next_item(I) ->
+ try eunit_data:iter_next(I)
+ catch
+ Term -> abort_task(Term)
+ end.
+
+handle_item(T, St) ->
+ case T of
+ #test{} -> handle_test(T, St);
+ #group{} -> handle_group(T, St)
+ end.
+
+handle_test(T, St) ->
+ Data = [{desc, T#test.desc}, {source, T#test.location},
+ {line, T#test.line}],
+ message_insulator({'begin', test, Data}, St),
+
+ %% each test case runs under a fresh group leader process
+ G0 = group_leader(),
+ Runner = self(),
+ G1 = new_group_leader(Runner),
+ group_leader(G1, self()),
+
+ %% run the actual test, handling timeouts and getting the total run
+ %% time of the test code (and nothing else)
+ {Status, Time} = with_timeout(T#test.timeout, ?DEFAULT_TEST_TIMEOUT,
+ fun () -> run_test(T) end, St),
+
+ %% restore group leader, get the collected output, and re-emit it so
+ %% that it all seems to come from this process, and always comes
+ %% before the 'end' message for this test
+ group_leader(G0, self()),
+ Output = group_leader_sync(G1),
+ io:put_chars(Output),
+
+ message_insulator({'end', Status, Time}, St),
+ ok.
+
+%% @spec (#test{}) -> ok | {error, eunit_lib:exception()}
+%% | {skipped, eunit_test:wrapperError()}
+
+run_test(#test{f = F}) ->
+ try eunit_test:run_testfun(F) of
+ {ok, _Value} ->
+ %% just discard the return value
+ ok;
+ {error, Exception} ->
+ {error, Exception}
+ catch
+ throw:WrapperError -> {skipped, WrapperError}
+ end.
+
+set_group_order(#group{order = undefined}, St) ->
+ St;
+set_group_order(#group{order = Order}, St) ->
+ St#procstate{order = Order}.
+
+handle_group(T, St0) ->
+ St = set_group_order(T, St0),
+ case T#group.spawn of
+ undefined ->
+ run_group(T, St);
+ Type ->
+ Child = spawn_group(Type, T, St),
+ wait_for_task(Child, St)
+ end.
+
+spawn_group(Type, T, St0) ->
+ Fun = fun (St) ->
+ fun () -> run_group(T, St) end
+ end,
+ start_task(Type, Fun, St0).
+
+run_group(T, St) ->
+ %% note that the setup/cleanup is outside the group timeout; if the
+ %% setup fails, we do not start any timers
+ Timeout = T#group.timeout,
+ Data = [{desc, T#group.desc}, {spawn, T#group.spawn},
+ {order, T#group.order}],
+ message_insulator({'begin', group, Data}, St),
+ F = fun (G) -> enter_group(G, Timeout, St) end,
+ try with_context(T, F) of
+ {Status, Time} ->
+ message_insulator({'end', Status, Time}, St)
+ catch
+ %% a throw here can come from eunit_data:enter_context/4 or from
+ %% get_next_item/1; for context errors, report group as aborted,
+ %% but continue processing tests
+ {context_error, Why, Trace} ->
+ message_insulator({skipped, {Why, Trace}}, St)
+ end,
+ ok.
+
+enter_group(T, Timeout, St) ->
+ with_timeout(Timeout, ?DEFAULT_GROUP_TIMEOUT,
+ fun () -> tests(T, St) end, St).
+
+with_context(#group{context = undefined, tests = T}, F) ->
+ F(T);
+with_context(#group{context = #context{} = C, tests = I}, F) ->
+ eunit_data:enter_context(C, I, F).
+
+%% Group leader process for test cases - collects I/O output requests.
+
+new_group_leader(Runner) ->
+ %% We must use spawn/3 here (with explicit module and function
+ %% name), because the 'current function' status of the group leader
+ %% is used by the UNDER_EUNIT macro (in eunit.hrl). If we spawn
+ %% using a fun, the current function will be 'erlang:apply/2' during
+ %% early process startup, which will fool the macro.
+ spawn_link(?MODULE, group_leader_process, [Runner]).
+
+group_leader_process(Runner) ->
+ group_leader_loop(Runner, infinity, []).
+
+group_leader_loop(Runner, Wait, Buf) ->
+ receive
+ {io_request, From, ReplyAs, Req} ->
+ P = process_flag(priority, normal),
+ %% run this part under normal priority always
+ Buf1 = io_request(From, ReplyAs, Req, Buf),
+ process_flag(priority, P),
+ group_leader_loop(Runner, Wait, Buf1);
+ stop ->
+ %% quitting time: make a minimal pause, go low on priority,
+ %% set receive-timeout to zero and schedule out again
+ receive after 2 -> ok end,
+ process_flag(priority, low),
+ group_leader_loop(Runner, 0, Buf);
+ _ ->
+ %% discard any other messages
+ group_leader_loop(Runner, Wait, Buf)
+ after Wait ->
+ %% no more messages and nothing to wait for; we ought to
+ %% have collected all immediately pending output now
+ process_flag(priority, normal),
+ Runner ! {self(), buffer_to_binary(Buf)}
+ end.
+
+group_leader_sync(G) ->
+ G ! stop,
+ receive
+ {G, Buf} -> Buf
+ end.
+
+%% Implementation of buffering I/O for group leader processes. (Note that
+%% each batch of characters is just pushed on the buffer, so it needs to
+%% be reversed when it is flushed.)
+
+io_request(From, ReplyAs, Req, Buf) ->
+ {Reply, Buf1} = io_request(Req, Buf),
+ io_reply(From, ReplyAs, Reply),
+ Buf1.
+
+io_reply(From, ReplyAs, Reply) ->
+ From ! {io_reply, ReplyAs, Reply}.
+
+io_request({put_chars, Chars}, Buf) ->
+ {ok, [Chars | Buf]};
+io_request({put_chars, M, F, As}, Buf) ->
+ try apply(M, F, As) of
+ Chars -> {ok, [Chars | Buf]}
+ catch
+ C:T -> {{error, {C,T,erlang:get_stacktrace()}}, Buf}
+ end;
+io_request({put_chars, _Enc, Chars}, Buf) ->
+ io_request({put_chars, Chars}, Buf);
+io_request({put_chars, _Enc, Mod, Func, Args}, Buf) ->
+ io_request({put_chars, Mod, Func, Args}, Buf);
+io_request({get_chars, _Enc, _Prompt, _N}, Buf) ->
+ {eof, Buf};
+io_request({get_chars, _Prompt, _N}, Buf) ->
+ {eof, Buf};
+io_request({get_line, _Prompt}, Buf) ->
+ {eof, Buf};
+io_request({get_line, _Enc, _Prompt}, Buf) ->
+ {eof, Buf};
+io_request({get_until, _Prompt, _M, _F, _As}, Buf) ->
+ {eof, Buf};
+io_request({setopts, _Opts}, Buf) ->
+ {ok, Buf};
+io_request(getopts, Buf) ->
+ {error, {error, enotsup}, Buf};
+io_request({get_geometry,columns}, Buf) ->
+ {error, {error, enotsup}, Buf};
+io_request({get_geometry,rows}, Buf) ->
+ {error, {error, enotsup}, Buf};
+io_request({requests, Reqs}, Buf) ->
+ io_requests(Reqs, {ok, Buf});
+io_request(_, Buf) ->
+ {{error, request}, Buf}.
+
+io_requests([R | Rs], {ok, Buf}) ->
+ io_requests(Rs, io_request(R, Buf));
+io_requests(_, Result) ->
+ Result.
diff --git a/lib/eunit/src/eunit_serial.erl b/lib/eunit/src/eunit_serial.erl
new file mode 100644
index 0000000000..d9ccae86f9
--- /dev/null
+++ b/lib/eunit/src/eunit_serial.erl
@@ -0,0 +1,186 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2006 Richard Carlsson
+%% @private
+%% @see eunit
+%% @doc Event serializing and multiplexing process, to be used as the
+%% main "supervisor" process for en EUnit test runner. See eunit_proc
+%% for details about the events that will be sent to the listeners
+%% (provided to this process at startup). This process guarantees that
+%% listeners will receive events in order, even if tests execute in
+%% parallel. For every received 'begin' event, there will be exactly one
+%% 'end' or 'cancel' event. For a cancelling event with identifier Id,
+%% no further events will arrive whose identifiers have Id as prefix.
+
+-module(eunit_serial).
+
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+-export([start/1]).
+
+%% Notes:
+%% * Due to concurrency, there are no guarantees that we will receive
+%% all status messages for the items within a group before we receive
+%% the 'end' message of the group itself.
+%%
+%% * A cancelling event may arrive at any time, and may concern items we
+%% are not yet expecting (if tests are executed in parallel), or may
+%% concern not only the current item but possibly a group ancestor of
+%% the current item (as in the case of a group timeout).
+%%
+%% * It is not possible to use selective receive to extract only those
+%% cancelling messages that affect the current item and its parents;
+%% basically, because we cannot have a dynamically computed prefix as a
+%% pattern in a receive. Hence, we must extract each cancelling event as
+%% it arrives and keep track of them separately.
+%%
+%% * Before we wait for a new item, we must check whether it (and thus
+%% also all its subitems, if any) is already cancelled.
+%%
+%% * When a new cancelling event arrives, we must either store it for
+%% future use, and/or cancel the current item and possibly one or more
+%% of its parent groups.
+
+-record(state, {listeners :: set(),
+ cancelled = eunit_lib:trie_new() :: gb_tree(),
+ messages = dict:new() :: dict()}).
+
+start(Pids) ->
+ spawn(fun () -> serializer(Pids) end).
+
+serializer(Pids) ->
+ St = #state{listeners = sets:from_list(Pids),
+ cancelled = eunit_lib:trie_new(),
+ messages = dict:new()},
+ expect([], undefined, 0, St),
+ exit(normal).
+
+%% collect beginning and end of an expected item; return {Done, NewSt}
+%% where Done is true if there are no more items of this group
+expect(Id, ParentId, GroupMinSize, St0) ->
+ case wait(Id, 'begin', ParentId, GroupMinSize, St0) of
+ {done, St1} ->
+ {true, St1};
+ {cancel, prefix, _Msg, St1} ->
+ %% if a parent caused the cancel, signal done with group and
+ %% cast no cancel event (since the item might not exist)
+ {true, St1};
+ {cancel, exact, Msg, St1} ->
+ cast_cancel(Id, Msg, St1),
+ {false, St1};
+ {ok, Msg, St1} ->
+ %%?debugVal({got_begin, Id, Msg}),
+ cast(Msg, St1),
+ St2 = case Msg of
+ {status, _, {progress, 'begin', {group, _Info}}} ->
+ group(Id, 0, St1);
+ _ ->
+ St1
+ end,
+ case wait(Id, 'end', ParentId, GroupMinSize, St2) of
+ {cancel, Why, Msg1, St3} ->
+ %% we know the item exists, so always cast a cancel
+ %% event, and signal done with the group if a parent
+ %% caused the cancel
+ cast_cancel(Id, Msg1, St3),
+ {(Why =:= prefix), St3};
+ {ok, Msg1, St3} ->
+ %%?debugVal({got_end, Id, Msg1}),
+ cast(Msg1, St3),
+ {false, St3}
+ end
+ end.
+
+%% collect group items in order until group is done
+group(ParentId, GroupMinSize, St) ->
+ N = GroupMinSize + 1,
+ case expect(ParentId ++ [N], ParentId, GroupMinSize, St) of
+ {false, St1} ->
+ group(ParentId, N, St1);
+ {true, St1} ->
+ St1
+ end.
+
+cast_cancel(Id, undefined, St) ->
+ %% reasonable message for implicitly cancelled events
+ cast({status, Id, {cancel, undefined}}, St);
+cast_cancel(_Id, Msg, St) ->
+ cast(Msg, St).
+
+cast(Msg, St) ->
+ sets:fold(fun (L, M) -> L ! M end, Msg, St#state.listeners),
+ ok.
+
+%% wait for a particular begin or end event, that might have arrived or
+%% been cancelled already, or might become cancelled later, or might not
+%% even exist (for the last+1 element of a group)
+wait(Id, Type, ParentId, GroupMinSize, St) ->
+ %%?debugVal({wait, Id, Type}),
+ case check_cancelled(Id, St) of
+ no ->
+ case recall(Id, St) of
+ undefined ->
+ wait_1(Id, Type, ParentId, GroupMinSize, St);
+ Msg ->
+ {ok, Msg, forget(Id, St)}
+ end;
+ Why ->
+ %%?debugVal({cancelled, Why, Id, ParentId}),
+ {cancel, Why, recall(Id, St), forget(Id, St)}
+ end.
+
+%% the event has not yet arrived or been cancelled - wait for more info
+wait_1(Id, Type, ParentId, GroupMinSize, St) ->
+ receive
+ {status, Id, {progress, Type, _}}=Msg ->
+ %%?debugVal({Type, ParentId, Id}),
+ {ok, Msg, St};
+ {status, ParentId, {progress, 'end', {GroupMinSize, _}}}=Msg ->
+ %% the parent group ended (the final status of a group is
+ %% the count of its subitems), and we have seen all of its
+ %% subtests, so the currently expected event does not exist
+ %%?debugVal({end_group, ParentId, Id, GroupMinSize}),
+ {done, remember(ParentId, Msg, St)};
+ {status, SomeId, {cancel, _Cause}}=Msg ->
+ %%?debugVal({got_cancel, SomeId, _Cause}),
+ St1 = set_cancelled(SomeId, Msg, St),
+ wait(Id, Type, ParentId, GroupMinSize, St1)
+ end.
+
+set_cancelled(Id, Msg, St0) ->
+ St = remember(Id, Msg, St0),
+ St#state{cancelled = eunit_lib:trie_store(Id, St0#state.cancelled)}.
+
+check_cancelled(Id, St) ->
+ %% returns 'no', 'exact', or 'prefix'
+ eunit_lib:trie_match(Id, St#state.cancelled).
+
+remember(Id, Msg, St) ->
+ St#state{messages = dict:store(Id, Msg, St#state.messages)}.
+
+forget(Id, St) ->
+ %% this is just to enable garbage collection of old messages
+ St#state{messages = dict:store(Id, undefined, St#state.messages)}.
+
+recall(Id, St) ->
+ case dict:find(Id, St#state.messages) of
+ {ok, Msg} -> Msg;
+ error -> undefined
+ end.
diff --git a/lib/eunit/src/eunit_server.erl b/lib/eunit/src/eunit_server.erl
new file mode 100644
index 0000000000..bf1bb9bcef
--- /dev/null
+++ b/lib/eunit/src/eunit_server.erl
@@ -0,0 +1,341 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: eunit_server.erl 267 2008-10-19 18:48:03Z rcarlsson $
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2006 Richard Carlsson
+%% @private
+%% @see eunit
+%% @doc EUnit server process
+
+-module(eunit_server).
+
+-export([start/1, stop/1, start_test/4, watch/3, watch_path/3,
+ watch_regexp/3]).
+
+-export([main/1]). % private
+
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+
+-define(AUTO_TIMEOUT, 60000). %% auto test time limit
+
+%% TODO: pass options to server, such as default timeout?
+
+start(Server) when is_atom(Server) ->
+ ensure_started(Server).
+
+stop(Server) ->
+ command(Server, stop).
+
+
+-record(job, {super, test, options}).
+
+%% The `Super' process will receive a stream of status messages; see
+%% eunit_proc:status_message/3 for details.
+
+start_test(Server, Super, T, Options) ->
+ command(Server, {start, #job{super = Super,
+ test = T,
+ options = Options}}).
+
+watch(Server, Module, Opts) when is_atom(Module) ->
+ command(Server, {watch, {module, Module}, Opts}).
+
+watch_path(Server, Path, Opts) ->
+ command(Server, {watch, {path, filename:flatten(Path)}, Opts}).
+
+watch_regexp(Server, Regex, Opts) ->
+ case regexp:parse(Regex) of
+ {ok, R} ->
+ command(Server, {watch, {regexp, R}, Opts});
+ {error, _}=Error ->
+ Error
+ end.
+
+%% This makes sure the server is started before sending the command, and
+%% returns {ok, Result} if the server accepted the command or {error,
+%% server_down} if the server process crashes. If the server does not
+%% reply, this function will wait until the server is killed.
+
+command(Server, Cmd) ->
+ if is_atom(Server), Cmd /= stop -> ensure_started(Server);
+ true -> ok
+ end,
+ if is_pid(Server) -> command_1(Server, Cmd);
+ true ->
+ case whereis(Server) of
+ undefined -> {error, server_down};
+ Pid -> command_1(Pid, Cmd)
+ end
+ end.
+
+command_1(Pid, Cmd) when is_pid(Pid) ->
+ Pid ! {command, self(), Cmd},
+ command_wait(Pid, 1000, undefined).
+
+command_wait(Pid, Timeout, Monitor) ->
+ receive
+ {Pid, Result} -> Result;
+ {'DOWN', Monitor, process, Pid, _R} -> {error, server_down}
+ after Timeout ->
+ %% avoid creating a monitor unless some time has passed
+ command_wait(Pid, infinity, erlang:monitor(process, Pid))
+ end.
+
+%% Starting the server
+
+ensure_started(Name) ->
+ ensure_started(Name, 5).
+
+ensure_started(Name, N) when N > 0 ->
+ case whereis(Name) of
+ undefined ->
+ Parent = self(),
+ Pid = spawn(fun () -> server_start(Name, Parent) end),
+ receive
+ {Pid, ok} ->
+ Pid;
+ {Pid, error} ->
+ receive after 200 -> ensure_started(Name, N - 1) end
+ end;
+ Pid ->
+ Pid
+ end;
+ensure_started(_, _) ->
+ throw(no_server).
+
+server_start(undefined = Name, Parent) ->
+ %% anonymous server
+ server_start_1(Name, Parent);
+server_start(Name, Parent) ->
+ try register(Name, self()) of
+ true -> server_start_1(Name, Parent)
+ catch
+ _:_ ->
+ Parent ! {self(), error},
+ exit(error)
+ end.
+
+server_start_1(Name, Parent) ->
+ Parent ! {self(), ok},
+ server_init(Name).
+
+-record(state, {name,
+ stopped,
+ jobs,
+ queue,
+ auto_test,
+ modules,
+ paths,
+ regexps}).
+
+server_init(Name) ->
+ server(#state{name = Name,
+ stopped = false,
+ jobs = dict:new(),
+ queue = queue:new(),
+ auto_test = queue:new(),
+ modules = sets:new(),
+ paths = sets:new(),
+ regexps = sets:new()}).
+
+server(St) ->
+ server_check_exit(St),
+ ?MODULE:main(St).
+
+%% @private
+main(St) ->
+ receive
+ {done, auto_test, _Pid} ->
+ server(auto_test_done(St));
+ {done, Reference, _Pid} ->
+ server(handle_done(Reference, St));
+ {command, From, _Cmd} when St#state.stopped ->
+ From ! {self(), stopped};
+ {command, From, Cmd} ->
+ server_command(From, Cmd, St);
+ {code_monitor, {loaded, M, _Time}} ->
+ case is_watched(M, St) of
+ true ->
+ server(new_auto_test(self(), M, St));
+ false ->
+ server(St)
+ end
+ end.
+
+server_check_exit(St) ->
+ case dict:size(St#state.jobs) of
+ 0 when St#state.stopped -> exit(normal);
+ _ -> ok
+ end.
+
+server_command(From, {start, Job}, St) ->
+ Reference = make_ref(),
+ St1 = case proplists:get_bool(enqueue, Job#job.options) of
+ true ->
+ enqueue(Job, From, Reference, St);
+ false ->
+ start_job(Job, From, Reference, St)
+ end,
+ server_command_reply(From, {ok, Reference}),
+ server(St1);
+server_command(From, stop, St) ->
+ %% unregister the server name and let remaining jobs finish
+ server_command_reply(From, {error, stopped}),
+ catch unregister(St#state.name),
+ server(St#state{stopped = true});
+server_command(From, {watch, Target, _Opts}, St) ->
+ %% the code watcher is only started on demand
+ %% FIXME: this is disabled for now in the OTP distribution
+ %%code_monitor:monitor(self()),
+ %% TODO: propagate options to testing stage
+ St1 = add_watch(Target, St),
+ server_command_reply(From, ok),
+ server(St1);
+server_command(From, {forget, Target}, St) ->
+ St1 = delete_watch(Target, St),
+ server_command_reply(From, ok),
+ server(St1);
+server_command(From, Cmd, St) ->
+ server_command_reply(From, {error, {unknown_command, Cmd}}),
+ server(St).
+
+server_command_reply(From, Result) ->
+ From ! {self(), Result}.
+
+enqueue(Job, From, Reference, St) ->
+ case dict:size(St#state.jobs) of
+ 0 ->
+ start_job(Job, From, Reference, St);
+ _ ->
+ St#state{queue = queue:in({Job, From, Reference},
+ St#state.queue)}
+ end.
+
+dequeue(St) ->
+ case queue:out(St#state.queue) of
+ {empty, _} ->
+ St;
+ {{value, {Job, From, Reference}}, Queue} ->
+ start_job(Job, From, Reference, St#state{queue = Queue})
+ end.
+
+start_job(Job, From, Reference, St) ->
+ From ! {start, Reference},
+ %% The default is to run tests in order unless otherwise specified
+ Order = proplists:get_value(order, Job#job.options, inorder),
+ eunit_proc:start(Job#job.test, Order, Job#job.super, Reference),
+ St#state{jobs = dict:store(Reference, From, St#state.jobs)}.
+
+handle_done(Reference, St) ->
+ case dict:find(Reference, St#state.jobs) of
+ {ok, From} ->
+ From ! {done, Reference},
+ dequeue(St#state{jobs = dict:erase(Reference,
+ St#state.jobs)});
+ error ->
+ St
+ end.
+
+%% Adding and removing watched modules or paths
+
+add_watch({module, M}, St) ->
+ St#state{modules = sets:add_element(M, St#state.modules)};
+add_watch({path, P}, St) ->
+ St#state{paths = sets:add_element(P, St#state.paths)};
+add_watch({regexp, R}, St) ->
+ St#state{regexps = sets:add_element(R, St#state.regexps)}.
+
+delete_watch({module, M}, St) ->
+ St#state{modules = sets:del_element(M, St#state.modules)};
+delete_watch({path, P}, St) ->
+ St#state{paths = sets:del_element(P, St#state.paths)};
+delete_watch({regexp, R}, St) ->
+ St#state{regexps = sets:del_element(R, St#state.regexps)}.
+
+%% Checking if a module is being watched
+
+is_watched(M, St) when is_atom(M) ->
+ sets:is_element(M, St#state.modules) orelse
+ is_watched(code:which(M), St);
+is_watched(Path, St) ->
+ sets:is_element(filename:dirname(Path), St#state.paths) orelse
+ match_any(sets:to_list(St#state.regexps), Path).
+
+match_any([R | Rs], Str) ->
+ case regexp:first_match(Str, R) of
+ {match, _, _} -> true;
+ _ -> match_any(Rs, Str)
+ end;
+match_any([], _Str) -> false.
+
+%% Running automatic tests when a watched module is loaded.
+%% Uses a queue in order to avoid overlapping output when several
+%% watched modules are loaded simultaneously. (The currently running
+%% automatic test is kept in the queue until it is done. An empty queue
+%% means that no automatic test is running.)
+
+new_auto_test(Server, M, St) ->
+ case queue:is_empty(St#state.auto_test) of
+ true ->
+ start_auto_test(Server, M);
+ false ->
+ ok
+ end,
+ St#state{auto_test = queue:in({Server, M}, St#state.auto_test)}.
+
+auto_test_done(St) ->
+ %% remove finished test from queue before checking for more
+ {_, Queue} = queue:out(St#state.auto_test),
+ case queue:out(Queue) of
+ {{value, {Server, M}}, _} ->
+ %% this is just lookahead - the item is not removed
+ start_auto_test(Server, M);
+ {empty, _} ->
+ ok
+ end,
+ St#state{auto_test = Queue}.
+
+start_auto_test(Server, M) ->
+ spawn(fun () -> auto_super(Server, M) end).
+
+auto_super(Server, M) ->
+ process_flag(trap_exit, true),
+ %% Give the user a short delay before any output is produced
+ receive after 333 -> ok end,
+ %% Make sure output is sent to console on server node
+ group_leader(whereis(user), self()),
+ Pid = spawn_link(fun () -> auto_proc(Server, M) end),
+ receive
+ {'EXIT', Pid, _} ->
+ ok
+ after ?AUTO_TIMEOUT ->
+ exit(Pid, kill),
+ io:put_chars("\n== EUnit: automatic test was aborted ==\n"),
+ io:put_chars("\n> ")
+ end,
+ Server ! {done, auto_test, self()}.
+
+auto_proc(Server, M) ->
+ %% Make the output start on a new line instead of on the same line
+ %% as the current shell prompt.
+ io:fwrite("\n== EUnit: testing module ~w ==\n", [M]),
+ eunit:test(Server, M, [enqueue]),
+ %% Make sure to print a dummy prompt at the end of the output, most
+ %% of all so that the Emacs mode realizes that input is active.
+ io:put_chars("\n-> ").
diff --git a/lib/eunit/src/eunit_striptests.erl b/lib/eunit/src/eunit_striptests.erl
new file mode 100644
index 0000000000..606e44b286
--- /dev/null
+++ b/lib/eunit/src/eunit_striptests.erl
@@ -0,0 +1,67 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: eunit_striptests.erl 329 2009-03-01 11:23:32Z rcarlsson $
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @author Eric Merritt <[email protected]>
+%% @copyright 2006 Richard Carlsson, Eric Merritt
+%% @private
+%% @see eunit
+%% @doc Parse transform for stripping EUnit test functions.
+
+-module(eunit_striptests).
+
+-include("eunit_internal.hrl").
+
+-export([parse_transform/2]).
+
+parse_transform(Forms, Options) ->
+ TestSuffix = proplists:get_value(eunit_test_suffix, Options,
+ ?DEFAULT_TEST_SUFFIX),
+ GeneratorSuffix = proplists:get_value(eunit_generator_suffix,
+ Options,
+ ?DEFAULT_GENERATOR_SUFFIX),
+ ExportSuffix = proplists:get_value(eunit_export_suffix, Options,
+ ?DEFAULT_EXPORT_SUFFIX),
+ Exports = lists:foldl(fun ({attribute,_,export,Es}, S) ->
+ sets:union(sets:from_list(Es), S);
+ (_F, S) -> S
+ end,
+ sets:new(), Forms),
+ F = fun (Form, Acc) ->
+ form(Form, Acc, Exports, TestSuffix, GeneratorSuffix,
+ ExportSuffix)
+ end,
+ lists:reverse(lists:foldl(F, [], Forms)).
+
+form({function, _L, Name, 0, _Cs}=Form, Acc, Exports, TestSuffix,
+ GeneratorSuffix, ExportSuffix) ->
+ N = atom_to_list(Name),
+ case not sets:is_element({Name, 0}, Exports)
+ andalso (lists:suffix(TestSuffix, N)
+ orelse lists:suffix(GeneratorSuffix, N)
+ orelse lists:suffix(ExportSuffix, N))
+ of
+ true ->
+ Acc;
+ false ->
+ [Form | Acc]
+ end;
+form({function, _L, ?DEFAULT_MODULE_WRAPPER_NAME, 1, _Cs}, Acc, _, _, _,
+ _) ->
+ Acc;
+form(Form, Acc, _, _, _, _) ->
+ [Form | Acc].
diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl
new file mode 100644
index 0000000000..aeda31d251
--- /dev/null
+++ b/lib/eunit/src/eunit_surefire.erl
@@ -0,0 +1,417 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: $
+%%
+%% @author Micka�l R�mond <[email protected]>
+%% @copyright 2009 Micka�l R�mond, Paul Guyot
+%% @see eunit
+%% @doc Surefire reports for EUnit (Format used by Maven and Atlassian
+%% Bamboo for example to integrate test results). Based on initial code
+%% from Paul Guyot.
+%%
+%% Example: Generate XML result file in the current directory:
+%% ```eunit:test([fib, eunit_examples],
+%% [{report,{eunit_surefire,[{dir,"."}]}}]).'''
+
+-module(eunit_surefire).
+
+-behaviour(eunit_listener).
+
+-define(NODEBUG, true).
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+-export([start/0, start/1]).
+
+-export([init/1, handle_begin/3, handle_end/3, handle_cancel/3,
+ terminate/2]).
+
+%% ============================================================================
+%% MACROS
+%% ============================================================================
+-define(XMLDIR, ".").
+-define(INDENT, <<" ">>).
+-define(NEWLINE, <<"\n">>).
+
+%% ============================================================================
+%% TYPES
+%% ============================================================================
+-type(chars() :: [char() | any()]). % chars()
+
+%% ============================================================================
+%% RECORDS
+%% ============================================================================
+-record(testcase,
+ {
+ name :: chars(),
+ description :: chars(),
+ result :: ok | {failed, tuple()} | {aborted, tuple()} | {skipped, tuple()},
+ time :: integer(),
+ output :: binary()
+ }).
+-record(testsuite,
+ {
+ name = <<>> :: binary(),
+ time = 0 :: integer(),
+ output = <<>> :: binary(),
+ succeeded = 0 :: integer(),
+ failed = 0 :: integer(),
+ aborted = 0 :: integer(),
+ skipped = 0 :: integer(),
+ testcases = [] :: [#testcase{}]
+ }).
+-record(state, {verbose = false,
+ indent = 0,
+ xmldir = ".",
+ testsuite = #testsuite{}
+ }).
+
+start() ->
+ start([]).
+
+start(Options) ->
+ eunit_listener:start(?MODULE, Options).
+
+init(Options) ->
+ XMLDir = proplists:get_value(dir, Options, ?XMLDIR),
+ St = #state{verbose = proplists:get_bool(verbose, Options),
+ xmldir = XMLDir,
+ testsuite = #testsuite{}},
+ receive
+ {start, _Reference} ->
+ St
+ end.
+
+terminate({ok, _Data}, St) ->
+ TestSuite = St#state.testsuite,
+ XmlDir = St#state.xmldir,
+ write_report(TestSuite, XmlDir),
+ ok;
+terminate({error, Reason}, _St) ->
+ io:fwrite("Internal error: ~P.\n", [Reason, 25]),
+ sync_end(error).
+
+sync_end(Result) ->
+ receive
+ {stop, Reference, ReplyTo} ->
+ ReplyTo ! {result, Reference, Result},
+ ok
+ end.
+
+handle_begin(group, Data, St) ->
+ NewId = proplists:get_value(id, Data),
+ case NewId of
+ [] ->
+ St;
+ [_GroupId] ->
+ Desc = proplists:get_value(desc, Data),
+ TestSuite = St#state.testsuite,
+ NewTestSuite = TestSuite#testsuite{name = Desc},
+ St#state{testsuite=NewTestSuite};
+ %% Surefire format is not hierarchic: Ignore subgroups:
+ _ ->
+ St
+ end;
+handle_begin(test, _Data, St) ->
+ St.
+handle_end(group, Data, St) ->
+ %% Retrieve existing test suite:
+ case proplists:get_value(id, Data) of
+ [] ->
+ St;
+ [_GroupId|_] ->
+ TestSuite = St#state.testsuite,
+
+ %% Update TestSuite data:
+ Time = proplists:get_value(time, Data),
+ Output = proplists:get_value(output, Data),
+ NewTestSuite = TestSuite#testsuite{ time = Time, output = Output },
+ St#state{testsuite=NewTestSuite}
+ end;
+handle_end(test, Data, St) ->
+ %% Retrieve existing test suite:
+ TestSuite = St#state.testsuite,
+
+ %% Create test case:
+ Name = format_name(proplists:get_value(source, Data),
+ proplists:get_value(line, Data)),
+ Desc = format_desc(proplists:get_value(desc, Data)),
+ Result = proplists:get_value(status, Data),
+ Time = proplists:get_value(time, Data),
+ Output = proplists:get_value(output, Data),
+ TestCase = #testcase{name = Name, description = Desc,
+ time = Time,output = Output},
+ NewTestSuite = add_testcase_to_testsuite(Result, TestCase, TestSuite),
+ St#state{testsuite=NewTestSuite}.
+
+%% Cancel group does not give information on the individual cancelled test case
+%% We ignore this event
+handle_cancel(group, _Data, St) ->
+ St;
+handle_cancel(test, Data, St) ->
+ %% Retrieve existing test suite:
+ TestSuite = St#state.testsuite,
+
+ %% Create test case:
+ Name = format_name(proplists:get_value(source, Data),
+ proplists:get_value(line, Data)),
+ Desc = format_desc(proplists:get_value(desc, Data)),
+ Reason = proplists:get_value(reason, Data),
+ TestCase = #testcase{
+ name = Name, description = Desc,
+ result = {skipped, Reason}, time = 0,
+ output = <<>>},
+ NewTestSuite = TestSuite#testsuite{
+ skipped = TestSuite#testsuite.skipped+1,
+ testcases=[TestCase|TestSuite#testsuite.testcases] },
+ St#state{testsuite=NewTestSuite}.
+
+format_name({Module, Function, Arity}, Line) ->
+ lists:flatten([atom_to_list(Module), ":", atom_to_list(Function), "/",
+ integer_to_list(Arity), "_", integer_to_list(Line)]).
+format_desc(undefined) ->
+ "";
+format_desc(Desc) when is_binary(Desc) ->
+ binary_to_list(Desc);
+format_desc(Desc) when is_list(Desc) ->
+ Desc.
+
+%% Add testcase to testsuite depending on the result of the test.
+add_testcase_to_testsuite(ok, TestCaseTmp, TestSuite) ->
+ TestCase = TestCaseTmp#testcase{ result = ok },
+ TestSuite#testsuite{
+ succeeded = TestSuite#testsuite.succeeded+1,
+ testcases=[TestCase|TestSuite#testsuite.testcases] };
+add_testcase_to_testsuite({error, Exception}, TestCaseTmp, TestSuite) ->
+ case Exception of
+ {error,{AssertionException,_},_} when
+ AssertionException == assertion_failed;
+ AssertionException == assertMatch_failed;
+ AssertionException == assertEqual_failed;
+ AssertionException == assertException_failed;
+ AssertionException == assertCmd_failed;
+ AssertionException == assertCmdOutput_failed
+ ->
+ TestCase = TestCaseTmp#testcase{ result = {failed, Exception} },
+ TestSuite#testsuite{
+ failed = TestSuite#testsuite.failed+1,
+ testcases = [TestCase|TestSuite#testsuite.testcases] };
+ _ ->
+ TestCase = TestCaseTmp#testcase{ result = {aborted, Exception} },
+ TestSuite#testsuite{
+ aborted = TestSuite#testsuite.aborted+1,
+ testcases = [TestCase|TestSuite#testsuite.testcases] }
+ end.
+
+%% ----------------------------------------------------------------------------
+%% Write a report to the XML directory.
+%% This function opens the report file, calls write_report_to/2 and closes the file.
+%% ----------------------------------------------------------------------------
+write_report(#testsuite{name = Name} = TestSuite, XmlDir) ->
+ Filename = filename:join(XmlDir, lists:flatten(["TEST-", escape_suitename(Name)], ".xml")),
+ case file:open(Filename, [write, raw]) of
+ {ok, FileDescriptor} ->
+ try
+ write_report_to(TestSuite, FileDescriptor)
+ after
+ file:close(FileDescriptor)
+ end;
+ {error, _Reason} = Error -> throw(Error)
+ end.
+
+%% ----------------------------------------------------------------------------
+%% Actually write a report.
+%% ----------------------------------------------------------------------------
+write_report_to(TestSuite, FileDescriptor) ->
+ write_header(FileDescriptor),
+ write_start_tag(TestSuite, FileDescriptor),
+ write_testcases(lists:reverse(TestSuite#testsuite.testcases), FileDescriptor),
+ write_end_tag(FileDescriptor).
+
+%% ----------------------------------------------------------------------------
+%% Write the XML header.
+%% ----------------------------------------------------------------------------
+write_header(FileDescriptor) ->
+ file:write(FileDescriptor, [<<"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>">>, ?NEWLINE]).
+
+%% ----------------------------------------------------------------------------
+%% Write the testsuite start tag, with attributes describing the statistics
+%% of the test suite.
+%% ----------------------------------------------------------------------------
+write_start_tag(
+ #testsuite{
+ name = Name,
+ time = Time,
+ succeeded = Succeeded,
+ failed = Failed,
+ skipped = Skipped,
+ aborted = Aborted},
+ FileDescriptor) ->
+ Total = Succeeded + Failed + Skipped + Aborted,
+ StartTag = [
+ <<"<testsuite tests=\"">>, integer_to_list(Total),
+ <<"\" failures=\"">>, integer_to_list(Failed),
+ <<"\" errors=\"">>, integer_to_list(Aborted),
+ <<"\" skipped=\"">>, integer_to_list(Skipped),
+ <<"\" time=\"">>, format_time(Time),
+ <<"\" name=\"">>, escape_attr(Name),
+ <<"\">">>, ?NEWLINE],
+ file:write(FileDescriptor, StartTag).
+
+%% ----------------------------------------------------------------------------
+%% Recursive function to write the test cases.
+%% ----------------------------------------------------------------------------
+write_testcases([], _FileDescriptor) -> void;
+write_testcases([TestCase| Tail], FileDescriptor) ->
+ write_testcase(TestCase, FileDescriptor),
+ write_testcases(Tail, FileDescriptor).
+
+%% ----------------------------------------------------------------------------
+%% Write the testsuite end tag.
+%% ----------------------------------------------------------------------------
+write_end_tag(FileDescriptor) ->
+ file:write(FileDescriptor, [<<"</testsuite>">>, ?NEWLINE]).
+
+%% ----------------------------------------------------------------------------
+%% Write a test case, as a testcase tag.
+%% If the test case was successful and if there was no output, we write an empty
+%% tag.
+%% ----------------------------------------------------------------------------
+write_testcase(
+ #testcase{
+ name = Name,
+ description = Description,
+ result = Result,
+ time = Time,
+ output = Output},
+ FileDescriptor) ->
+ DescriptionAttr = case Description of
+ <<>> -> [];
+ [] -> [];
+ _ -> [<<" description=\"">>, escape_attr(Description), <<"\"">>]
+ end,
+ StartTag = [
+ ?INDENT, <<"<testcase time=\"">>, format_time(Time),
+ <<"\" name=\"">>, escape_attr(Name), <<"\"">>,
+ DescriptionAttr],
+ ContentAndEndTag = case {Result, Output} of
+ {ok, []} -> [<<"/>">>, ?NEWLINE];
+ {ok, <<>>} -> [<<"/>">>, ?NEWLINE];
+ _ -> [<<">">>, ?NEWLINE, format_testcase_result(Result), format_testcase_output(Output), ?INDENT, <<"</testcase>">>, ?NEWLINE]
+ end,
+ file:write(FileDescriptor, [StartTag, ContentAndEndTag]).
+
+%% ----------------------------------------------------------------------------
+%% Format the result of the test.
+%% Failed tests are represented with a failure tag.
+%% Aborted tests are represented with an error tag.
+%% Skipped tests are represented with a skipped tag.
+%% ----------------------------------------------------------------------------
+format_testcase_result(ok) -> [<<>>];
+format_testcase_result({failed, {error, {Type, _}, _} = Exception}) when is_atom(Type) ->
+ [?INDENT, ?INDENT, <<"<failure type=\"">>, escape_attr(atom_to_list(Type)), <<"\">">>, ?NEWLINE,
+ <<"::">>, escape_text(eunit_lib:format_exception(Exception)),
+ ?INDENT, ?INDENT, <<"</failure>">>, ?NEWLINE];
+format_testcase_result({failed, Term}) ->
+ [?INDENT, ?INDENT, <<"<failure type=\"unknown\">">>, ?NEWLINE,
+ escape_text(io_lib:write(Term)),
+ ?INDENT, ?INDENT, <<"</failure>">>, ?NEWLINE];
+format_testcase_result({aborted, {Class, _Term, _Trace} = Exception}) when is_atom(Class) ->
+ [?INDENT, ?INDENT, <<"<error type=\"">>, escape_attr(atom_to_list(Class)), <<"\">">>, ?NEWLINE,
+ <<"::">>, escape_text(eunit_lib:format_exception(Exception)),
+ ?INDENT, ?INDENT, <<"</error>">>, ?NEWLINE];
+format_testcase_result({aborted, Term}) ->
+ [?INDENT, ?INDENT, <<"<error type=\"unknown\">">>, ?NEWLINE,
+ escape_text(io_lib:write(Term)),
+ ?INDENT, ?INDENT, <<"</error>">>, ?NEWLINE];
+format_testcase_result({skipped, {abort, Error}}) when is_tuple(Error) ->
+ [?INDENT, ?INDENT, <<"<skipped type=\"">>, escape_attr(atom_to_list(element(1, Error))), <<"\">">>, ?NEWLINE,
+ escape_text(eunit_lib:format_error(Error)),
+ ?INDENT, ?INDENT, <<"</skipped>">>, ?NEWLINE];
+format_testcase_result({skipped, {Type, Term}}) when is_atom(Type) ->
+ [?INDENT, ?INDENT, <<"<skipped type=\"">>, escape_attr(atom_to_list(Type)), <<"\">">>, ?NEWLINE,
+ escape_text(io_lib:write(Term)),
+ ?INDENT, ?INDENT, <<"</skipped>">>, ?NEWLINE];
+format_testcase_result({skipped, timeout}) ->
+ [?INDENT, ?INDENT, <<"<skipped type=\"timeout\"/>">>, ?NEWLINE];
+format_testcase_result({skipped, Term}) ->
+ [?INDENT, ?INDENT, <<"<skipped type=\"unknown\">">>, ?NEWLINE,
+ escape_text(io_lib:write(Term)),
+ ?INDENT, ?INDENT, <<"</skipped>">>, ?NEWLINE].
+
+%% ----------------------------------------------------------------------------
+%% Format the output of a test case in xml.
+%% Empty output is simply the empty string.
+%% Other output is inside a <system-out> xml tag.
+%% ----------------------------------------------------------------------------
+format_testcase_output([]) -> [];
+format_testcase_output(Output) ->
+ [?INDENT, ?INDENT, <<"<system-out>">>, escape_text(Output), ?NEWLINE, ?INDENT, ?INDENT, <<"</system-out>">>, ?NEWLINE].
+
+%% ----------------------------------------------------------------------------
+%% Return the time in the SECS.MILLISECS format.
+%% ----------------------------------------------------------------------------
+format_time(Time) ->
+ format_time_s(lists:reverse(integer_to_list(Time))).
+format_time_s([Digit]) -> ["0.00", Digit];
+format_time_s([Digit1, Digit2]) -> ["0.0", Digit2, Digit1];
+format_time_s([Digit1, Digit2, Digit3]) -> ["0.", Digit3, Digit2, Digit1];
+format_time_s([Digit1, Digit2, Digit3 | Tail]) -> [lists:reverse(Tail), $., Digit3, Digit2, Digit1].
+
+%% ----------------------------------------------------------------------------
+%% Escape a suite's name to generate the filename.
+%% Remark: we might overwrite another testsuite's file.
+%% ----------------------------------------------------------------------------
+escape_suitename([Head | _T] = List) when is_list(Head) ->
+ escape_suitename(lists:flatten(List));
+escape_suitename(Binary) when is_binary(Binary) ->
+ escape_suitename(binary_to_list(Binary));
+escape_suitename("module '" ++ String) ->
+ escape_suitename(String);
+escape_suitename(String) ->
+ escape_suitename(String, []).
+
+escape_suitename(Binary, Acc) when is_binary(Binary) -> escape_suitename(binary_to_list(Binary), Acc);
+escape_suitename([], Acc) -> lists:reverse(Acc);
+escape_suitename([$ | Tail], Acc) -> escape_suitename(Tail, [$_ | Acc]);
+escape_suitename([$' | Tail], Acc) -> escape_suitename(Tail, Acc);
+escape_suitename([$/ | Tail], Acc) -> escape_suitename(Tail, [$: | Acc]);
+escape_suitename([$\\ | Tail], Acc) -> escape_suitename(Tail, [$: | Acc]);
+escape_suitename([Char | Tail], Acc) when Char < $! -> escape_suitename(Tail, Acc);
+escape_suitename([Char | Tail], Acc) when Char > $~ -> escape_suitename(Tail, Acc);
+escape_suitename([Char | Tail], Acc) -> escape_suitename(Tail, [Char | Acc]).
+
+%% ----------------------------------------------------------------------------
+%% Escape text for XML text nodes.
+%% Replace < with &lt;, > with &gt; and & with &amp;
+%% ----------------------------------------------------------------------------
+escape_text(Text) when is_binary(Text) -> escape_text(binary_to_list(Text));
+escape_text(Text) -> escape_xml(lists:flatten(Text), [], false).
+
+
+%% ----------------------------------------------------------------------------
+%% Escape text for XML attribute nodes.
+%% Replace < with &lt;, > with &gt; and & with &amp;
+%% ----------------------------------------------------------------------------
+escape_attr(Text) when is_binary(Text) -> escape_attr(binary_to_list(Text));
+escape_attr(Text) -> escape_xml(lists:flatten(Text), [], true).
+
+escape_xml([], Acc, _ForAttr) -> lists:reverse(Acc);
+escape_xml([$< | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $t, $l, $& | Acc], ForAttr);
+escape_xml([$> | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $t, $g, $& | Acc], ForAttr);
+escape_xml([$& | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $p, $m, $a, $& | Acc], ForAttr);
+escape_xml([$" | Tail], Acc, true) -> escape_xml(Tail, [$;, $t, $o, $u, $q, $& | Acc], true); % "
+escape_xml([Char | Tail], Acc, ForAttr) when is_integer(Char) -> escape_xml(Tail, [Char | Acc], ForAttr).
diff --git a/lib/eunit/src/eunit_test.erl b/lib/eunit/src/eunit_test.erl
new file mode 100644
index 0000000000..d322c4b420
--- /dev/null
+++ b/lib/eunit/src/eunit_test.erl
@@ -0,0 +1,320 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: eunit_test.erl 336 2009-03-06 14:43:21Z rcarlsson $
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2006 Richard Carlsson
+%% @private
+%% @see eunit
+%% @doc Test running functionality
+
+-module(eunit_test).
+
+-export([run_testfun/1, function_wrapper/2, enter_context/4,
+ multi_setup/1]).
+
+
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+
+%% ---------------------------------------------------------------------
+%% Getting a cleaned up stack trace. (We don't want it to include
+%% eunit's own internal functions. This complicates self-testing
+%% somewhat, but you can't have everything.) Note that we assume that
+%% this particular module is the boundary between eunit and user code.
+
+get_stacktrace() ->
+ get_stacktrace([]).
+
+get_stacktrace(Ts) ->
+ eunit_lib:uniq(prune_trace(erlang:get_stacktrace(), Ts)).
+
+prune_trace([{eunit_data, _, _} | Rest], Tail) ->
+ prune_trace(Rest, Tail);
+prune_trace([{?MODULE, _, _} | _Rest], Tail) ->
+ Tail;
+prune_trace([T | Ts], Tail) ->
+ [T | prune_trace(Ts, Tail)];
+prune_trace([], Tail) ->
+ Tail.
+
+
+%% ---------------------------------------------------------------------
+%% Test runner
+
+%% @spec ((any()) -> any()) -> {ok, Value} | {error, eunit_lib:exception()}
+%% @throws wrapperError()
+
+run_testfun(F) ->
+ try
+ F()
+ of Value ->
+ {ok, Value}
+ catch
+ {eunit_internal, Term} ->
+ %% Internally generated: re-throw Term (lose the trace)
+ throw(Term);
+ Class:Reason ->
+ {error, {Class, Reason, get_stacktrace()}}
+ end.
+
+
+-ifdef(TEST).
+macro_test_() ->
+ {"macro definitions",
+ [{?LINE, fun () ->
+ {?LINE, F} = ?_test(undefined),
+ {ok, undefined} = run_testfun(F)
+ end},
+ ?_test(begin
+ {?LINE, F} = ?_assert(true),
+ {ok, ok} = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assert(false),
+ {error,{error,{assertion_failed,
+ [{module,_},
+ {line,_},
+ {expression,_},
+ {expected,true},
+ {value,false}]},
+ _}}
+ = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assert([]),
+ {error,{error,{assertion_failed,
+ [{module,_},
+ {line,_},
+ {expression,_},
+ {expected,true},
+ {value,{not_a_boolean,[]}}]},
+ _}}
+ = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assertNot(false),
+ {ok, ok} = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assertNot(true),
+ {error,{error,{assertion_failed,
+ [{module,_},
+ {line,_},
+ {expression,_},
+ {expected,true},
+ {value,false}]},
+ _}}
+ = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assertMatch(ok, ok),
+ {ok, ok} = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assertMatch([_], []),
+ {error,{error,{assertMatch_failed,
+ [{module,_},
+ {line,_},
+ {expression,_},
+ {expected,"[ _ ]"},
+ {value,[]}]},
+ _}}
+ = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assertEqual(ok, ok),
+ {ok, ok} = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assertEqual(3, 1+1),
+ {error,{error,{assertEqual_failed,
+ [{module,_},
+ {line,_},
+ {expression,_},
+ {expected,3},
+ {value,2}]},
+ _}}
+ = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assertException(error, badarith,
+ erlang:error(badarith)),
+ {ok, ok} = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assertException(error, badarith, ok),
+ {error,{error,{assertException_failed,
+ [{module,_},
+ {line,_},
+ {expression,_},
+ {expected,_},
+ {unexpected_success,ok}]},
+ _}}
+ = run_testfun(F)
+ end),
+ ?_test(begin
+ {?LINE, F} = ?_assertException(error, badarg,
+ erlang:error(badarith)),
+ {error,{error,{assertException_failed,
+ [{module,_},
+ {line,_},
+ {expression,_},
+ {expected,_},
+ {unexpected_exception,
+ {error,badarith,_}}]},
+ _}}
+ = run_testfun(F)
+ end)
+ ]}.
+
+under_eunit_test() -> ?assert(?UNDER_EUNIT).
+-endif.
+
+
+%% ---------------------------------------------------------------------
+%% Wrapper for simple "named function" tests ({M,F}), which provides
+%% better error reporting when the function is missing at test time.
+%%
+%% Note that the wrapper fun is usually called by run_testfun/1, and the
+%% special exceptions thrown here are expected to be handled there.
+%%
+%% @throws {eunit_internal, wrapperError()}
+%%
+%% @type wrapperError() = {no_such_function, mfa()}
+%% | {module_not_found, moduleName()}
+
+function_wrapper(M, F) ->
+ fun () ->
+ try M:F()
+ catch
+ error:undef ->
+ %% Check if it was M:F/0 that was undefined
+ case erlang:module_loaded(M) of
+ false ->
+ fail({module_not_found, M});
+ true ->
+ case erlang:function_exported(M, F, 0) of
+ false ->
+ fail({no_such_function, {M,F,0}});
+ true ->
+ rethrow(error, undef, [{M,F,0}])
+ end
+ end
+ end
+ end.
+
+rethrow(Class, Reason, Trace) ->
+ erlang:raise(Class, Reason, get_stacktrace(Trace)).
+
+fail(Term) ->
+ throw({eunit_internal, Term}).
+
+
+-ifdef(TEST).
+wrapper_test_() ->
+ {"error handling in function wrapper",
+ [?_assertException(throw, {module_not_found, eunit_nonexisting},
+ run_testfun(function_wrapper(eunit_nonexisting,test))),
+ ?_assertException(throw,
+ {no_such_function, {?MODULE,nonexisting_test,0}},
+ run_testfun(function_wrapper(?MODULE,nonexisting_test))),
+ ?_test({error, {error, undef, _T}}
+ = run_testfun(function_wrapper(?MODULE,wrapper_test_exported_)))
+ ]}.
+
+%% this must be exported (done automatically by the autoexport transform)
+wrapper_test_exported_() ->
+ {ok, ?MODULE:nonexisting_function()}.
+-endif.
+
+
+%% ---------------------------------------------------------------------
+%% Entering a setup-context, with guaranteed cleanup.
+
+%% @spec (Setup, Cleanup, Instantiate, Callback) -> any()
+%% Setup = () -> any()
+%% Cleanup = (any()) -> any()
+%% Instantiate = (any()) -> tests()
+%% Callback = (tests()) -> any()
+%% @throws {context_error, Error, eunit_lib:exception()}
+%% Error = setup_failed | instantiation_failed | cleanup_failed
+
+enter_context(Setup, Cleanup, Instantiate, Callback) ->
+ try Setup() of
+ R ->
+ try Instantiate(R) of
+ T ->
+ try Callback(T) %% call back to client code
+ after
+ %% Always run cleanup; client may be an idiot
+ try Cleanup(R)
+ catch
+ Class:Term ->
+ context_error(cleanup_failed, Class, Term)
+ end
+ end
+ catch
+ Class:Term ->
+ context_error(instantiation_failed, Class, Term)
+ end
+ catch
+ Class:Term ->
+ context_error(setup_failed, Class, Term)
+ end.
+
+context_error(Type, Class, Term) ->
+ throw({context_error, Type, {Class, Term, get_stacktrace()}}).
+
+%% This generates single setup/cleanup functions from a list of tuples
+%% on the form {Tag, Setup, Cleanup}, where the setup function always
+%% backs out correctly from partial completion.
+
+multi_setup(List) ->
+ {SetupAll, CleanupAll} = multi_setup(List, fun ok/1),
+ %% must reverse back and forth here in order to present the list in
+ %% "natural" order to the test instantiation function
+ {fun () -> lists:reverse(SetupAll([])) end,
+ fun (Rs) -> CleanupAll(lists:reverse(Rs)) end}.
+
+multi_setup([{Tag, S, C} | Es], CleanupPrev) ->
+ Cleanup = fun ([R | Rs]) ->
+ try C(R) of
+ _ -> CleanupPrev(Rs)
+ catch
+ Class:Term ->
+ throw({Tag, {Class, Term, get_stacktrace()}})
+ end
+ end,
+ {SetupRest, CleanupAll} = multi_setup(Es, Cleanup),
+ {fun (Rs) ->
+ try S() of
+ R ->
+ SetupRest([R|Rs])
+ catch
+ Class:Term ->
+ CleanupPrev(Rs),
+ throw({Tag, {Class, Term, get_stacktrace()}})
+ end
+ end,
+ CleanupAll};
+multi_setup([{Tag, S} | Es], CleanupPrev) ->
+ multi_setup([{Tag, S, fun ok/1} | Es], CleanupPrev);
+multi_setup([], CleanupAll) ->
+ {fun (Rs) -> Rs end, CleanupAll}.
+
+ok(_) -> ok.
diff --git a/lib/eunit/src/eunit_tests.erl b/lib/eunit/src/eunit_tests.erl
new file mode 100644
index 0000000000..37c0b4d6ae
--- /dev/null
+++ b/lib/eunit/src/eunit_tests.erl
@@ -0,0 +1,42 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: eunit_tests.erl 238 2007-11-15 10:23:54Z mremond $
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2007 Richard Carlsson
+%% @private
+%% @see eunit
+%% @doc External tests for eunit.erl
+
+-module(eunit_tests).
+
+-include("eunit.hrl").
+
+-ifdef(TEST).
+%% Cause all the other modules to be tested as well as this one.
+full_test_() ->
+ %%{application, eunit}. % this currently causes a loop
+ %% We use the below until loop detection is implemented
+ [eunit_autoexport,
+ eunit_striptests,
+ eunit_server,
+ eunit_proc,
+ eunit_serial,
+ eunit_test,
+ eunit_lib,
+ eunit_data,
+ eunit_tty].
+-endif.
diff --git a/lib/eunit/src/eunit_tty.erl b/lib/eunit/src/eunit_tty.erl
new file mode 100644
index 0000000000..5fe0140559
--- /dev/null
+++ b/lib/eunit/src/eunit_tty.erl
@@ -0,0 +1,257 @@
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library 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
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: eunit_tty.erl 330 2009-03-01 16:28:02Z rcarlsson $
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2006-2009 Richard Carlsson
+%% @private
+%% @see eunit
+%% @doc Text-based frontend for EUnit
+
+-module(eunit_tty).
+
+-behaviour(eunit_listener).
+
+-define(NODEBUG, true).
+-include("eunit.hrl").
+-include("eunit_internal.hrl").
+
+-export([start/0, start/1]).
+
+-export([init/1, handle_begin/3, handle_end/3, handle_cancel/3,
+ terminate/2]).
+
+-record(state, {verbose = false,
+ indent = 0
+ }).
+
+start() ->
+ start([]).
+
+start(Options) ->
+ eunit_listener:start(?MODULE, Options).
+
+init(Options) ->
+ St = #state{verbose = proplists:get_bool(verbose, Options)},
+ receive
+ {start, _Reference} ->
+ if St#state.verbose -> print_header();
+ true -> ok
+ end,
+ St
+ end.
+
+terminate({ok, Data}, St) ->
+ Pass = proplists:get_value(pass, Data, 0),
+ Fail = proplists:get_value(fail, Data, 0),
+ Skip = proplists:get_value(skip, Data, 0),
+ Cancel = proplists:get_value(cancel, Data, 0),
+ if Fail =:= 0, Skip =:= 0, Cancel =:= 0 ->
+ if Pass =:= 0 ->
+ io:fwrite(" There were no tests to run.\n");
+ true ->
+ if St#state.verbose -> print_bar();
+ true -> ok
+ end,
+ if Pass =:= 1 ->
+ io:fwrite(" Test passed.\n");
+ true ->
+ io:fwrite(" All ~w tests passed.\n", [Pass])
+ end
+ end,
+ sync_end(ok);
+ true ->
+ print_bar(),
+ io:fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n",
+ [Fail, Skip, Pass]),
+ if Cancel =/= 0 ->
+ io:fwrite("One or more tests were cancelled.\n");
+ true -> ok
+ end,
+ sync_end(error)
+ end;
+terminate({error, Reason}, _St) ->
+ io:fwrite("Internal error: ~P.\n", [Reason, 25]),
+ sync_end(error).
+
+sync_end(Result) ->
+ receive
+ {stop, Reference, ReplyTo} ->
+ ReplyTo ! {result, Reference, Result},
+ ok
+ end.
+
+print_header() ->
+ io:fwrite("======================== EUnit ========================\n").
+
+print_bar() ->
+ io:fwrite("=======================================================\n").
+
+
+handle_begin(group, Data, St) ->
+ ?debugFmt("handle_begin group ~w", [Data]),
+ Desc = proplists:get_value(desc, Data),
+ if Desc =/= "", Desc =/= undefined, St#state.verbose ->
+ I = St#state.indent,
+ print_group_start(I, Desc),
+ St#state{indent = I + 1};
+ true ->
+ St
+ end;
+handle_begin(test, Data, St) ->
+ ?debugFmt("handle_begin test ~w", [Data]),
+ if St#state.verbose -> print_test_begin(St#state.indent, Data);
+ true -> ok
+ end,
+ St.
+
+handle_end(group, Data, St) ->
+ ?debugFmt("handle_end group ~w", [Data]),
+ Desc = proplists:get_value(desc, Data),
+ if Desc =/= "", Desc =/= undefined, St#state.verbose ->
+ Time = proplists:get_value(time, Data),
+ I = St#state.indent,
+ print_group_end(I, Time),
+ St#state{indent = I - 1};
+ true ->
+ St
+ end;
+handle_end(test, Data, St) ->
+ ?debugFmt("handle_end test ~w", [Data]),
+ case proplists:get_value(status, Data) of
+ ok ->
+ if St#state.verbose -> print_test_end(Data);
+ true -> ok
+ end,
+ St;
+ Status ->
+ if St#state.verbose -> ok;
+ true -> print_test_begin(St#state.indent, Data)
+ end,
+ print_test_error(Status, Data),
+ St
+ end.
+
+handle_cancel(group, Data, St) ->
+ ?debugFmt("handle_cancel group ~w", [Data]),
+ I = St#state.indent,
+ case proplists:get_value(reason, Data) of
+ undefined ->
+ %% "skipped" message is not interesting here
+ St#state{indent = I - 1};
+ Reason ->
+ Desc = proplists:get_value(desc, Data),
+ if Desc =/= "", Desc =/= undefined, St#state.verbose ->
+ print_group_cancel(I, Reason);
+ true ->
+ print_group_start(I, Desc),
+ print_group_cancel(I, Reason)
+ end,
+ St#state{indent = I - 1}
+ end;
+handle_cancel(test, Data, St) ->
+ ?debugFmt("handle_cancel test ~w", [Data]),
+ if St#state.verbose -> ok;
+ true -> print_test_begin(St#state.indent, Data)
+ end,
+ print_test_cancel(proplists:get_value(reason, Data)),
+ St.
+
+
+indent(N) when is_integer(N), N >= 1 ->
+ io:put_chars(lists:duplicate(N * 2, $\s));
+indent(_N) ->
+ ok.
+
+print_group_start(I, Desc) ->
+ indent(I),
+ io:fwrite("~s\n", [Desc]).
+
+print_group_end(I, Time) ->
+ if Time > 0 ->
+ indent(I),
+ io:fwrite("[done in ~.3f s]\n", [Time/1000]);
+ true ->
+ ok
+ end.
+
+print_test_begin(I, Data) ->
+ Desc = proplists:get_value(desc, Data),
+ Line = proplists:get_value(line, Data, 0),
+ indent(I),
+ L = if Line =:= 0 -> "";
+ true -> io_lib:fwrite("~w:", [Line])
+ end,
+ D = if Desc =:= "" ; Desc =:= undefined -> "";
+ true -> io_lib:fwrite(" (~s)", [Desc])
+ end,
+ case proplists:get_value(source, Data) of
+ {Module, Name, _Arity} ->
+ io:fwrite("~s:~s ~s~s...", [Module, L, Name, D]);
+ _ ->
+ io:fwrite("~s~s...", [L, D])
+ end.
+
+print_test_end(Data) ->
+ Time = proplists:get_value(time, Data, 0),
+ T = if Time > 0 -> io_lib:fwrite("[~.3f s] ", [Time/1000]);
+ true -> ""
+ end,
+ io:fwrite("~sok\n", [T]).
+
+print_test_error({error, Exception}, Data) ->
+ Output = proplists:get_value(output, Data),
+ io:fwrite("*failed*\n::~s",
+ [eunit_lib:format_exception(Exception)]),
+ case Output of
+ <<>> ->
+ io:put_chars("\n\n");
+ <<Text:800/binary, _:1/binary, _/binary>> ->
+ io:fwrite(" output:<<\"~s\">>...\n\n", [Text]);
+ _ ->
+ io:fwrite(" output:<<\"~s\">>\n\n", [Output])
+ end;
+print_test_error({skipped, Reason}, _) ->
+ io:fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]).
+
+format_skipped({module_not_found, M}) ->
+ io_lib:format("missing module: ~w", [M]);
+format_skipped({no_such_function, {M,F,A}}) ->
+ io_lib:format("no such function: ~w:~w/~w", [M,F,A]).
+
+print_test_cancel(Reason) ->
+ io:fwrite(format_cancel(Reason)).
+
+print_group_cancel(_I, {blame, _}) ->
+ ok;
+print_group_cancel(I, Reason) ->
+ indent(I),
+ io:fwrite(format_cancel(Reason)).
+
+format_cancel(undefined) ->
+ "*skipped*\n";
+format_cancel(timeout) ->
+ "*timed out*\n";
+format_cancel({startup, Reason}) ->
+ io_lib:fwrite("*could not start test process*\n::~P\n\n",
+ [Reason, 15]);
+format_cancel({blame, _SubId}) ->
+ "*cancelled because of subtask*\n";
+format_cancel({exit, Reason}) ->
+ io_lib:fwrite("*unexpected termination of test process*\n::~P\n\n",
+ [Reason, 15]);
+format_cancel({abort, Reason}) ->
+ eunit_lib:format_error(Reason).
diff --git a/lib/eunit/test/Makefile b/lib/eunit/test/Makefile
new file mode 100644
index 0000000000..83fca0ade4
--- /dev/null
+++ b/lib/eunit/test/Makefile
@@ -0,0 +1,82 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+MODULES = \
+ eunit_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+INSTALL_PROGS= $(TARGET_FILES)
+
+EMAKEFILE=Emakefile
+COVERFILE=eunit.cover
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/eunit_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_MAKE_FLAGS += -pa $(ERL_TOP)/lib/test_server/ebin
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+.PHONY: make_emakefile
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) \
+ $(MODULES) >> $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Special targets
+# ----------------------------------------------------
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) eunit.dynspec $(EMAKEFILE) \
+ $(COVERFILE) $(ERL_FILES) \
+ $(RELSYSDIR)
+
+release_docs_spec:
diff --git a/lib/eunit/test/eunit.cover b/lib/eunit/test/eunit.cover
new file mode 100644
index 0000000000..d1eaf770b6
--- /dev/null
+++ b/lib/eunit/test/eunit.cover
@@ -0,0 +1,3 @@
+%% -*- erlang -*-
+{exclude,[eunit_test]}.
+
diff --git a/lib/eunit/test/eunit.dynspec b/lib/eunit/test/eunit.dynspec
new file mode 100644
index 0000000000..c1d345ac14
--- /dev/null
+++ b/lib/eunit/test/eunit.dynspec
@@ -0,0 +1,6 @@
+%% -*- erlang -*-
+%% You can test this file using this command.
+%% file:script("eunit.dynspec", [{'Os',"Unix"}]).
+
+[].
+
diff --git a/lib/eunit/test/eunit_SUITE.erl b/lib/eunit/test/eunit_SUITE.erl
new file mode 100644
index 0000000000..4ebcec6f5d
--- /dev/null
+++ b/lib/eunit/test/eunit_SUITE.erl
@@ -0,0 +1,31 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(eunit_SUITE).
+
+-export([all/1,eunit_test/1]).
+
+-include("test_server.hrl").
+
+all(suite) ->
+ [eunit_test].
+
+eunit_test(Config) when is_list(Config) ->
+ ok = file:set_cwd(code:lib_dir(eunit)),
+ ok = eunit:test(eunit).
+
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
new file mode 100644
index 0000000000..002703b1b3
--- /dev/null
+++ b/lib/eunit/vsn.mk
@@ -0,0 +1 @@
+EUNIT_VSN = 2.1.4