New unit test system: Unit++.

Added test for header::getAllByName/Type
This commit is contained in:
Laurent Richard 2004-10-07 11:02:31 +00:00
parent 3fb6238933
commit 27fb7a5b96
29 changed files with 6719 additions and 0 deletions

359
tests/lib/unit++/COPYING Normal file
View File

@ -0,0 +1,359 @@
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details. You should have received
a copy of the GNU General Public License along with this program; if not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
The author can be contacted at azhrarn@users.sourceforge.net.
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 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.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, 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 or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
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 give any other recipients of the Program a copy of this License
along with the Program.
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 Program or any portion
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
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 Program, 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 Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) 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; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, 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 executable. However, as a
special exception, the source code 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.
If distribution of executable or 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 counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program 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.
5. 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 Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program 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 to
this License.
7. 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 Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program 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 Program.
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.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program 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.
9. The Free Software Foundation may publish revised and/or new versions
of the 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 Program
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 Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, 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
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), 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 Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@ -0,0 +1,16 @@
1.2:
Added a couple of man pages.
Incorporated some changes from Craig McGeachie to allow compilation on
MicroSoft Visual Studio.
Added a Qt GUI
Added Kenn Knowles' m4 automake configuration file for determining in an
automake file if unit++ is available
Added some typename hints for compilers less large than g++ (thanks to Doug
Philips). Should get it to compile on AIX 4.3.3.
1.1.1:
Before this list started...

59
tests/lib/unit++/INSTALL Normal file
View File

@ -0,0 +1,59 @@
The unit++ framework is meant to be very simple.
To install it:
./configure
make install
This should take care of everything, and install in
/usr/local/{lib,include,man}.
The all target (make all) will make the test for the test framework, unit++.
Try to run it to see that everything is fine. If you want to see a failing
test case, try `unit++ -f'.
Graphics
--------
My experience from junit are that a really great day ends with my test suite
producing that green bar... To gain that joy in C++ I have made a GUI for
unit++ based on Qt. However, it does take longer to run the unGUI version, and
I have therefore taken great care that this will still work without it.
The install instructions for the GUI version is:
./configure --with-qt
make install
This will enable the gui, that is add the GUI classes to the unit++ library.
It still requires that the individual test programs enables the GUI. This is
done by adding the following lines to one of the test modules:
#include <unit++/unit++.h>
unitpp::gui_hook ghook;
This will enable the -g (or --gui) option in the resulting executable.
NOTE:
=====
The tests don't work properly on old C++ compilers, without the <sstream>
header (stringstream classes). That includes the g++ 2.95.3 version found on
SunOS and OpenBSD. However, newer versions of libstd++3 works fine.
This means that some things will not be tested in this case, and some
spurious output will result from running the unit++ program. The final count
of tests is correct, even though the output strongly suggests otherwise.
====
Doc.
----
If you have doc++ available the API interface can be documented by `make
doc'. The DOCDIR variable in the Makefile controls where the files end up.
If you want the documentation without having doc++, I try to keep it update
on the SourceForge page: http://unitpp.sourceforge.net/docs/index.html.
It can be found as a file for download there as well.
Man pages
---------
I have added a man page for unit++(3). This documents the API to a level that
I deem sufficient for writing test cases.

View File

@ -0,0 +1,80 @@
# installation targets
prefix=@prefix@
includedir=@includedir@
exec_prefix=@exec_prefix@
libdir=@libdir@
mandir=@mandir@
docdir=./html/unit++
#various compilers.
CXX=@CXX@
CXXFLAGS=@CXXFLAGS@
# for those that have doc++, otherwise get it
DOC=doc++
#Qt related stuff
QT_LIBDIR = @QT_LDFLAGS@
QT_INCDIR = @QT_INCLUDES@
MOC = @MOC@
GUI_OBJS = @gui_vars@
GUI_DOCSRC = @gui_docsrc@
# Below here should not need modification
TARGETLIB=libunit++.a
TARGETHEADERS=unit++.h optmap.h gui.h guitester.h main.h tester.h
# substituted into GUI_OBJS by autoconf, if GUI is enabled
gui_objs = gui.o guitester.o moc_gui.o moc_guitester.o
gui_docsrc = gui.h guitester.h
OBJS=unit++.o tester.o main.o optmap.o $(GUI_OBJS)
#the files with doc information
DOCSRC=$(TARGETHEADERS) main.h tester.h $(GUI_DOCSRC)
DOCOPTS=-d $(docdir)
TSTOBJS=Test_unit++.o Test_optmap.o Test_gui.o
TSTLIBS=-L. -lunit++
.SUFFIXES: .cc
.cc.o:
$(CXX) -D__UNITPP -c $(CXXFLAGS) $(CPPFLAGS) @QT_INCLUDES@ @DEFS@ -o $@ $<
all: $(TARGETLIB) unit++
$(TARGETLIB): $(OBJS)
ar r $@ $(OBJS)
install: $(TARGETLIB) $(TARGETHEADERS)
[ -d $(libdir) ] || mkdir -p $(libdir)
[ -d $(includedir)/unit++ ] || mkdir -p $(includedir)/unit++
[ -d $(mandir)/man1 ] || mkdir -p $(mandir)/man1
[ -d $(mandir)/man3 ] || mkdir -p $(mandir)/man3
cp $(TARGETLIB) $(libdir)
cp $(TARGETHEADERS) $(includedir)/unit++
cp unit++-compat.h $(includedir)/unit++.h
cp optmap-compat.h $(includedir)/optmap.h
cp unit++.1 $(mandir)/man1
cp unit++.3 $(mandir)/man3
doc: $(DOCSRC)
[ -d $(docdir) ] || mkdir -p $(docdir)
doc++ $(DOCOPTS) $(DOCSRC)
unit++: $(TSTOBJS) $(TARGETLIB)
$(CXX) -o $@ $(TSTOBJS) $(TSTLIBS) @QT_LDFLAGS@ @qt_lib@
moc_gui.cc: gui.h
$(MOC) $< -o $@
moc_guitester.cc: guitester.h
$(MOC) $< -o $@
clean:
rm -f $(OBJS) $(TSTOBJS)
clobber: clean
rm -f $(TARGETLIB) unit++
Test_optmap.o: Test_optmap.cc unit++.h optmap.h
Test_unit++.o: Test_unit++.cc unit++.h optmap.h tester.h main.h
Test_gui.o: Test_gui.cc
gui.o: gui.cc gui.h unit++.h optmap.h
guitester.o: guitester.cc
main.o: main.cc main.h tester.h unit++.h optmap.h
optmap.o: optmap.cc optmap.h
tester.o: tester.cc tester.h unit++.h optmap.h
unit++.o: unit++.cc unit++.h optmap.h

View File

@ -0,0 +1,27 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#ifdef GUI
#include "gui.h"
#include "unit++.h"
using namespace std;
using namespace unitpp;
gui_hook ghook; // ensure linkage of the gui stuff
namespace {
// The test suite for the gui components
class Test : public suite
{
void dummy()
{
}
public:
Test() : suite("GUI test suite")
{
suite::main().add("gui", this);
add("dummy", testcase(this, "Test nothing", &Test::dummy));
}
} * theTest = new Test();
}
#endif

View File

@ -0,0 +1,101 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#include "unit++.h"
#include "optmap.h"
#include <iostream>
#ifdef HAVE_SSTREAM
#include <sstream>
#endif
using namespace std;
using namespace unitpp;
using namespace options_utils;
namespace {
const char* x[] = { "testing", "-i", "120", "-n100", "-t" };
#ifdef HAVE_SSTREAM
class hijack {
ostream& os;
streambuf* sbp;
stringbuf sb;
public:
hijack(ostream& os)
: os(os), sbp(os.rdbuf())
{
os.rdbuf(&sb);
}
~hijack() { os.rdbuf(sbp); }
string str() { return sb.str(); }
};
#endif
// The test suite for the optmap library
class Test : public suite
{
const char** argv;
int argc;
void create()
{
optmap om;
}
void usage()
{
bool t_flg;
int i = 7;
int n = 0;
optmap om;
om.add("t", new opt_flag(t_flg));
om.add("i", new opt_int(i));
om.add("n", new opt_int(n));
om.alias("number", "n");
#ifdef HAVE_SSTREAM
string exp(
"usage: testing [ -t ] [ -i <int> ] [( -n | --number) <int> ]\n");
{
hijack s(cerr);
om.parse(argc, argv);
om.usage(false);
assert_eq("usage", exp, s.str());
}
#endif
}
void args()
{
bool t_flg = false;
int i = 7;
int n = 0;
optmap om;
om.add("t", new opt_flag(t_flg));
om.add("i", new opt_int(i));
om.add("n", new opt_int(n));
assert_true("parse ok", om.parse(argc, argv));
assert_eq("get -i", 120, i);
assert_eq("get -n", 100, n);
assert_eq("got -t", true, t_flg);
}
void tail()
{
const char* argv[] = { "xyzzy", "-abc", "-def", "hij" };
size_t argc = sizeof(argv)/sizeof(argv[0]);
string s;
bool f_d, f_e, f_f;
optmap om;
om.add("a", new opt_string(s));
om.add("d", new opt_flag(f_d));
om.add("e", new opt_flag(f_e));
om.add("f", new opt_flag(f_f));
assert_true("parse", om.parse(static_cast<int>(argc), argv));
assert_eq("n", argc - 1, size_t(om.n()));
assert_eq("a", "bc", s);
}
public:
Test() : suite("optmap test suite")
{
argv = x;
argc = sizeof(x)/sizeof(char*);
suite::main().add("optmap", this);
add("create", testcase(this, "Create an optmap", &Test::create));
add("usage", testcase(this, "Optmap usage", &Test::usage));
add("args", testcase(this, "Optmap args", &Test::args));
add("tail", testcase(this, "Optmap tail", &Test::tail));
}
} * theTest = new Test();
}

View File

@ -0,0 +1,194 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#include "unit++.h"
#include "tester.h"
#include "main.h"
#ifdef HAVE_SSTREAM
#include <sstream>
#else
#include <iostream>
#endif
using namespace std;
using namespace unitpp;
namespace {
// a test case that can fail with any exception
class test_test : public test
{
public:
enum result { succes, fail, error, exotic };
test_test(string name, result res = succes) : test(name), res(res) {}
virtual void operator()()
{
switch (res) {
case succes: break;
case fail: ::fail("test_test");
case error: throw out_of_range("ranged");
case exotic: throw 4711;
}
}
private:
result res;
};
// The test suite for the unit++ library
class Test : public suite
{
void create()
{
test_test a_loc_test("local");
}
void assert_ok()
{
string s("ok");
assert_true("assert_true(true)", true);
assert_eq("assert_eq(int)", 7, 7);
assert_eq("assert_eq(char*, string)", "ok", s);
}
void assert_fail()
{
string s("fejl");
bool ok = true;
try {
assert_true("assert_true(false)", false);
ok = false;
} catch (assertion_error e) {
#ifdef HAVE_SSTREAM
ostringstream oss;
oss << e;
assert_eq("assert_true(false) output",
"assert_true(false) [assertion failed]", oss.str());
#endif
}
if (!ok)
fail("no exception from assert_true(false)");
try {
assert_eq("assert_eq(int)", 5, 7);
ok = false;
} catch (assert_value_error<int,int> e) {
#ifdef HAVE_SSTREAM
ostringstream oss;
oss << e;
assert_eq("assert_eq(int) output",
"assert_eq(int) [expected: `5' got: `7']", oss.str());
#endif
}
if (!ok)
fail("no exception from assert_eq(int)");
try {
assert_eq("assert_eq(char*, string)", "ok", s);
ok = false;
} catch (assert_value_error<const char*, string> e) {
} catch (assert_value_error<char*, string> e) { // MSVC++ bug
}
if (!ok)
fail("no exception from assert_eq(const char*, string)");
}
void tester_visit()
{
out_of_range oor("negative");
assertion_error ae("test");
#ifdef HAVE_SSTREAM
ostringstream os;
tester tst(os);
#else
tester tst(cerr);
#endif
root.visit(&tst);
assert_eq("tests ok", 3, tst.res_tests().n_ok());
assert_eq("tests error", 2, tst.res_tests().n_err());
assert_eq("tests fail", 1, tst.res_tests().n_fail());
assert_eq("suites ok", 1, tst.res_suites().n_ok());
assert_eq("suites error", 2, tst.res_suites().n_err());
assert_eq("suites fail", 1, tst.res_suites().n_fail());
}
void ex_test()
{
throw out_of_range("expected");
}
void get_by_id()
{
test* p = root.get_child("s2");
assert_true("found s2", p != 0);
suite* sp = dynamic_cast<suite*>(p);
assert_true("s2 was suite", sp != 0);
assert_eq("right s2", "S2", sp->name());
p = sp->get_child("t20");
assert_true("found t20", p != 0);
assert_eq("not suite", static_cast<suite*>(0),dynamic_cast<suite*>(p));
}
void vec()
{
string s = "three.blind.mice";
vector<string> v(vectorize(s,'.'));
assert_eq("v[0]", string("three"), v[0]);
assert_eq("v[1]", string("blind"), v[1]);
assert_eq("v[2]", string("mice"), v[2]);
assert_eq("size", size_t(3), v.size());
v = vectorize(s,'-');
assert_eq("no match", s, v[0]);
assert_eq("no match size", size_t(1), v.size());
}
void empty_vec()
{
string s("");
vector<string> v(vectorize(s,'.'));
assert_eq("size", size_t(0), v.size());
s = "one..three";
v = vectorize(s,'.');
assert_eq("v[0]", string("one"), v[0]);
assert_eq("v[1]", string(""), v[1]);
assert_eq("v[2]", string("three"), v[2]);
assert_eq("size", size_t(3), v.size());
}
void find()
{
test* tp = root.find("s2.s21.t210");
assert_eq("t210", t210, tp);
tp = root.find("s1.s21");
assert_eq("bad mid", static_cast<test*>(0), tp);
}
suite root;
test* t210;
bool do_fail;
void fail_on_flag()
{
assert_true("Fail option not set", !do_fail);
}
public:
Test() : suite("Unit++ test suite"), root("The root")
{
do_fail = false;
options().add("f", new options_utils::opt_flag(do_fail));
options().alias("fail", "f");
suite* s1;
suite* s2;
suite* s21;
root.add("s1", s1 = new suite("S1"));
root.add("s2", s2 = new suite("S2"));
s2->add("s21", s21 = new suite("S21"));
s1->add("t10", new test_test("T10"));
s1->add("t11", new test_test("T11"));
s2->add("t20", new test_test("T20", test_test::error));
s2->add("t22", new test_test("T22", test_test::exotic));
s21->add("t210", t210 = new test_test("T210"));
s21->add("t211", new test_test("T211", test_test::fail));
//
// Adding testcases
suite::main().add("unitpp", this);
add("create", testcase(this, "Create a test", &Test::create));
add("assert_ok", testcase(this, "Assert ok", &Test::assert_ok));
add("assert_fail", testcase(this, "Assert fail", &Test::assert_fail));
add("tester_visit", testcase(this, "Visit", &Test::tester_visit));
add("exception", testcase(new exception_test<out_of_range>(
testcase(this, "gen ex", &Test::ex_test))));
add("id_get", testcase(this, "Get by id", &Test::get_by_id));
add("vec", testcase(this, "Vectorize", &Test::vec));
add("empty_vec", testcase(this, "Vectorize empty", &Test::empty_vec));
add("find", testcase(this, "find", &Test::find));
add("fail", testcase(this, "fail on option", &Test::fail_on_flag));
}
} * theTest = new Test();
}

314
tests/lib/unit++/aclocal.m4 vendored Normal file
View File

@ -0,0 +1,314 @@
dnl aclocal.m4 generated automatically by aclocal 1.4-p4
dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
dnl This program is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
dnl PARTICULAR PURPOSE.
dnl This file is part of QXi, the Qt XInput Extension.
dnl
dnl Copyright (C) 2000 Daniel Schmitt <pnambic@unu.nu>
dnl
dnl This file may be distributed under the terms of the Q Public License as
dnl appearing in the file COPYING.QPL included in the packaging of this file.
dnl
dnl The portions of this file that deal with the detection of Qt are
dnl stripped-down versions of macros used in KDE (http://www.kde.org/).
dnl
dnl The originals are copyrighted by
dnl Copyright (C) 1997 Janos Farkas (chexum@shadow.banki.hu)
dnl (C) 1997,98,99 Stephan Kulow (coolo@kde.org)
dnl ------------------------------------------------------------------------
dnl Find a file (or one of more files in a list of dirs)
dnl ------------------------------------------------------------------------
dnl
AC_DEFUN(AC_FIND_FILE,
[
$3=NO
for i in $2;
do
for j in $1;
do
if test -r "$i/$j"; then
$3=$i
break 2
fi
done
done
])
dnl ------------------------------------------------------------------------
dnl Find the meta object compiler in the PATH, in $QTDIR/bin, and some
dnl more usual places
dnl ------------------------------------------------------------------------
dnl
AC_DEFUN(AC_PATH_QT_MOC,
[
AC_PATH_PROG(MOC, moc, "", $ac_qt_bindir:$QTDIR/bin:$QTDIR/src/moc:/usr/bin /usr/X11R6/bin:/usr/lib/qt/bin:/usr/lib/qt2/bin:/usr/local/qt/bin:$PATH)
if test -z "$MOC"; then
if test -n "$ac_cv_path_moc"; then
output=`eval "$ac_cv_path_moc --help 2>&1 | sed -e '1q' | grep Qt"`
fi
echo "configure:__oline__: tried to call $ac_cv_path_moc --help 2>&1 | sed -e '1q' | grep Qt" >&AC_FD_CC
echo "configure:__oline__: moc output: $output" >&AC_FD_CC
if test -z "$output"; then
AC_MSG_ERROR([No Qt meta object compiler (moc) found!
Please check whether you installed Qt correctly.
You need to have a running moc binary.
configure tried to run $ac_cv_path_moc and the test didn't
succeed. If configure shouldn't have tried this one, set
the environment variable MOC to the right one before running
configure.
])
fi
fi
AC_SUBST(MOC)
])
AC_DEFUN(AC_PRINT_QT_PROGRAM,
[
cat > conftest.$ac_ext <<EOF
#include "confdefs.h"
#include <qglobal.h>
#include <qapplication.h>
#include <qstyle.h>
int main() {
QStringList *t = new QStringList();
return 0;
}
EOF
])
dnl ------------------------------------------------------------------------
dnl Try to find the Qt headers and libraries.
dnl $(QT_LDFLAGS) will be -Lqtliblocation (if needed)
dnl and $(QT_INCLUDES) will be -Iqthdrlocation (if needed)
dnl ------------------------------------------------------------------------
dnl
AC_DEFUN(AC_PATH_QT,
[
LIBQT="-lqt"
AC_MSG_CHECKING([for Qt])
ac_qt_includes=NO ac_qt_libraries=NO ac_qt_bindir=NO
qt_libraries=""
qt_includes=""
AC_ARG_WITH(qt-dir,
[ --with-qt-dir=DIR where the root of Qt is installed ],
[ ac_qt_includes="$withval"/include
ac_qt_libraries="$withval"/lib
ac_qt_bindir="$withval"/bin
])
AC_ARG_WITH(qt-includes,
[ --with-qt-includes=DIR where the Qt includes are. ],
[
ac_qt_includes="$withval"
])
ac_qt_libs_given=no
AC_ARG_WITH(qt-libraries,
[ --with-qt-libraries=DIR where the Qt library is installed.],
[ ac_qt_libraries="$withval"
ac_qt_libs_given=yes
])
AC_CACHE_VAL(ac_cv_have_qt,
[#try to guess Qt locations
qt_incdirs="$QTINC /usr/lib/qt/include /usr/local/qt/include /usr/include/qt3 /usr/include/qt /usr/include /usr/lib/qt2/include /usr/X11R6/include/X11/qt $x_includes"
test -n "$QTDIR" && qt_incdirs="$QTDIR/include $QTDIR $qt_incdirs"
qt_incdirs="$ac_qt_includes $qt_incdirs"
AC_FIND_FILE(qstyle.h, $qt_incdirs, qt_incdir)
ac_qt_includes="$qt_incdir"
qt_libdirs="$QTLIB /usr/lib/qt/lib /usr/X11R6/lib /usr/lib /usr/local/qt/lib /usr/lib/qt /usr/lib/qt2/lib $x_libraries"
test -n "$QTDIR" && qt_libdirs="$QTDIR/lib $QTDIR $qt_libdirs"
if test ! "$ac_qt_libraries" = "NO"; then
qt_libdirs="$ac_qt_libraries $qt_libdirs"
fi
test=NONE
qt_libdir=NONE
for dir in $qt_libdirs; do
try="ls -1 $dir/libqt*"
if test=`eval $try 2> /dev/null`; then qt_libdir=$dir; break; else echo "tried $dir" >&AC_FD_CC ; fi
done
ac_qt_libraries="$qt_libdir"
AC_LANG_SAVE
AC_LANG_CPLUSPLUS
ac_cxxflags_safe="$CXXFLAGS"
ac_ldflags_safe="$LDFLAGS"
ac_libs_safe="$LIBS"
CXXFLAGS="$CXXFLAGS -I$qt_incdir $all_includes"
LDFLAGS="-L$x_libraries -L$qt_libdir $all_libraries"
LIBS="$LIBS $LIBQT"
AC_PRINT_QT_PROGRAM
if AC_TRY_EVAL(ac_link) && test -s conftest; then
rm -f conftest*
else
echo "configure: failed program was:" >&AC_FD_CC
cat conftest.$ac_ext >&AC_FD_CC
ac_qt_libraries="NO"
fi
rm -f conftest*
CXXFLAGS="$ac_cxxflags_safe"
LDFLAGS="$ac_ldflags_safe"
LIBS="$ac_libs_safe"
AC_LANG_RESTORE
if test "$ac_qt_includes" = NO || test "$ac_qt_libraries" = NO; then
ac_cv_have_qt="have_qt=no"
ac_qt_notfound=""
if test "$ac_qt_includes" = NO; then
if test "$ac_qt_libraries" = NO; then
ac_qt_notfound="(headers and libraries)";
else
ac_qt_notfound="(headers)";
fi
else
ac_qt_notfound="(libraries)";
fi
AC_MSG_ERROR([Qt 2 not found. Please check your installation! ]);
else
have_qt="yes"
fi
])
eval "$ac_cv_have_qt"
if test "$have_qt" != yes; then
AC_MSG_RESULT([$have_qt]);
else
ac_cv_have_qt="have_qt=yes \
ac_qt_includes=$ac_qt_includes ac_qt_libraries=$ac_qt_libraries"
AC_MSG_RESULT([libraries $ac_qt_libraries, headers $ac_qt_includes])
qt_libraries="$ac_qt_libraries"
qt_includes="$ac_qt_includes"
fi
AC_SUBST(qt_libraries)
AC_SUBST(qt_includes)
if test "$qt_includes" = "$x_includes" || test -z "$qt_includes"; then
QT_INCLUDES="";
else
QT_INCLUDES="-I$qt_includes"
all_includes="$QT_INCLUDES $all_includes"
fi
if test "$qt_libraries" = "$x_libraries" || test -z "$qt_libraries"; then
QT_LDFLAGS=""
else
QT_LDFLAGS="-L$qt_libraries"
all_libraries="$QT_LDFLAGS $all_libraries"
fi
AC_SUBST(QT_INCLUDES)
AC_SUBST(QT_LDFLAGS)
AC_PATH_QT_MOC
LIB_QT='-lqt'
AC_SUBST(LIB_QT)
])
dnl ------------------------------------------------------------------------
dnl Find libz (required by libpng)
dnl ------------------------------------------------------------------------
dnl
AC_DEFUN(AC_FIND_ZLIB,
[
AC_MSG_CHECKING([for libz])
AC_CACHE_VAL(ac_cv_lib_z,
[
AC_LANG_C
ac_save_LIBS="$LIBS"
LIBS="$all_libraries -lz $LIBSOCKET"
ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $all_includes"
AC_TRY_LINK(dnl
[
#include<zlib.h>
],
[return (zlibVersion() == ZLIB_VERSION); ],
eval "ac_cv_lib_z='-lz'",
eval "ac_cv_lib_z=no")
LIBS="$ac_save_LIBS"
CFLAGS="$ac_save_CFLAGS"
])dnl
if eval "test ! \"`echo $ac_cv_lib_z`\" = no"; then
AC_DEFINE_UNQUOTED(HAVE_LIBZ, 1, [Define if you have libz])
LIBZ="$ac_cv_lib_z"
AC_SUBST(LIBZ)
AC_MSG_RESULT($ac_cv_lib_z)
else
AC_MSG_RESULT(no)
LIBZ=""
AC_SUBST(LIBZ)
fi
])
dnl ------------------------------------------------------------------------
dnl Find libpng (required by Qt 2)
dnl ------------------------------------------------------------------------
dnl
AC_DEFUN(AC_FIND_PNG,
[
AC_REQUIRE([AC_FIND_ZLIB])
AC_MSG_CHECKING([for libpng])
AC_CACHE_VAL(ac_cv_lib_png,
[
ac_save_LIBS="$LIBS"
LIBS="$LIBS $all_libraries -lpng $LIBZ -lm -lX11 $LIBSOCKET"
ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $all_includes"
AC_LANG_C
AC_TRY_LINK(dnl
[
#include<png.h>
],
[
png_structp png_ptr = png_create_read_struct( /* image ptr */
PNG_LIBPNG_VER_STRING, 0, 0, 0 );
return( png_ptr != 0 );
],
eval "ac_cv_lib_png='-lpng $LIBZ -lm'",
eval "ac_cv_lib_png=no"
)
LIBS="$ac_save_LIBS"
CFLAGS="$ac_save_CFLAGS"
])dnl
if eval "test ! \"`echo $ac_cv_lib_png`\" = no"; then
AC_DEFINE_UNQUOTED(HAVE_LIBPNG, 1, [Define if you have libpng])
LIBPNG="$ac_cv_lib_png"
AC_SUBST(LIBPNG)
AC_MSG_RESULT($ac_cv_lib_png)
else
AC_MSG_RESULT(no)
LIBPNG=""
AC_SUBST(LIBPNG)
fi
])

3599
tests/lib/unit++/configure vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
# Process this file with autoconf to produce a configure script.
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
AC_LANG(C++)
AC_CONFIG_SRCDIR([Test_optmap.cc])
AC_ARG_WITH(qt, AC_HELP_STRING([--with-qt], [use Qt gui (default no)]),
[ac_use_qt=yes], [ac_use_qt=no])
# Checks for programs.
AC_PROG_CXX
# AC_PROG_INSTALL
# Checks for libraries.
if test "$ac_use_qt" = yes; then
AC_PATH_QT
AC_DEFINE([GUI])
gui_vars='$(gui_objs)'
gui_docsrc='$(gui_docsrc)'
qt_lib='-lqt'
fi
AC_SUBST(gui_vars)
AC_SUBST(gui_docsrc)
AC_SUBST(qt_lib)
# Checks for header files.
AC_CHECK_HEADERS([sstream])
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

313
tests/lib/unit++/gui.cc Normal file
View File

@ -0,0 +1,313 @@
#include <algorithm>
#include "gui.h"
#include "unit++.h"
#ifdef HAVE_SSTREAM
#include <sstream>
#endif
using namespace unitpp;
using namespace std;
#ifdef GUI
static const char* const img_error[] = {
"14 14 2 1",
". c #ff0000",
"# c #ffffff",
"...########...",
"....######....",
".....####.....",
"#.....##.....#",
"##..........##",
"###........###",
"####......####",
"####......####",
"###........###",
"##..........##",
"#.....##.....#",
".....####.....",
"....######....",
"...########..."};
static const char* const img_fail[] = {
"14 14 2 1",
"# c #ff0000",
". c #ffffff",
"..............",
".##........##.",
".###......###.",
"..###....###..",
"...###..###...",
"....######....",
".....####.....",
".....####.....",
"....######....",
"...###..###...",
"..###....###..",
".###......###.",
".##........##.",
".............."};
static const char* const img_ok[] = {
"14 14 3 1",
"# c #00ff00",
"a c #00ff6a",
". c #ffffff",
"..........###.",
".........###..",
"........#a#...",
"........aa#...",
".......#aa....",
".......#a.....",
"......#aa.....",
"#.....aa#.....",
"###..#a#......",
".##.##a.......",
"..#aaa#.......",
"...#aa#.......",
"....##........",
".....#........"};
static const char* const img_empty[] = {
"14 14 2 1",
". c #f0f0f0",
"# c #ffffff",
"..............",
".############.",
".############.",
".############.",
".############.",
".############.",
".############.",
".############.",
".############.",
".############.",
".############.",
".############.",
".############.",
".............."};
cnt_item::cnt_item(QWidget* par, const QString& txt, const QColor& col,
const char* name)
: QHBox(par, name), v(0)
{
setSpacing(3);
setMargin(5);
val = new QLabel("0", this);
val->setAlignment(QLabel::AlignVCenter | QLabel::AlignRight);
QPalette pal(val->palette());
QColorGroup cg(pal.active());
cg.setColor(QColorGroup::Foreground, col);
pal.setActive(cg);
val->setPalette(pal);
label = new QLabel(txt, this);
}
void cnt_item::value(int iv)
{
v = iv;
val->setNum(v);
}
void cnt_item::inc()
{
value(v+1);
}
cnt_line::cnt_line(const QString& txt, QWidget* par, const char* name)
: QHBox(par, name)
{
label = new QLabel(txt, this);
QFont font(label->font());
font.setBold(true);
label->setFont(font);
cnts[0] = new cnt_item(this, "Total");
cnts[1] = new cnt_item(this, "OK", green);
cnts[2] = new cnt_item(this, "Fail", red);
cnts[3] = new cnt_item(this, "Error", red);
setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
}
void cnt_line::max(int m) { cnts[id_max]->value(m); }
void cnt_line::inc_ok() { cnts[id_ok]->inc(); }
void cnt_line::inc_fail() { cnts[id_fail]->inc(); }
void cnt_line::inc_error() { cnts[id_error]->inc(); }
void cnt_line::reset()
{
for (int i = id_ok; i < n_id; ++i)
cnts[i]->value(0);
}
res_stack::res_stack(const QString& txt, QWidget* par, const char* name)
: QVBox(par, name)
{
cnts = new cnt_line(txt, this, name);
bar = new QProgressBar(this);
setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
}
void res_stack::max(int v)
{
cnts->max(v);
bar->setTotalSteps(v);
}
void res_stack::inc_progress(bool err)
{
QPalette pal(bar->palette());
QColorGroup cg(pal.active());
QColor red(255,0,0);
QColor green(0,244,0);
cg.setColor(QColorGroup::Highlight, err ? red : green);
cg.setColor(QColorGroup::HighlightedText, black);
pal.setActive(cg);
pal.setInactive(cg);
pal.setDisabled(cg);
bar->setPalette(pal);
bar->setProgress(bar->progress()+1);
}
void res_stack::reset() {
cnts->reset();
bar->setProgress(0);
}
void res_stack::inc_ok() { cnts->inc_ok(); inc_progress(false); }
void res_stack::inc_fail() { cnts->inc_fail(); inc_progress(true); }
void res_stack::inc_error() { cnts->inc_error(); inc_progress(true); }
QHBox* behave(QHBox* box, bool x_expand, bool y_expand)
{
box->setSizePolicy(QSizePolicy(
x_expand ? QSizePolicy::Preferred : QSizePolicy::Maximum,
y_expand ? QSizePolicy::Preferred : QSizePolicy::Maximum));
box->setFrameShape(QFrame::StyledPanel);
box->setFrameShadow(QFrame::Raised);
box->setMargin(11);
box->setSpacing(6);
return box;
}
void node::setImg()
{
static QPixmap i_emp((const char**)img_empty);
static QPixmap i_ok((const char**)img_ok);
static QPixmap i_fail((const char**)img_fail);
static QPixmap i_err((const char**)img_error);
static QPixmap* imgs[] = { &i_emp, &i_ok, &i_fail, &i_err };
item->setPixmap(0, *(imgs[st]));
if (st > is_ok)
for (QListViewItem* ip = item; ip != 0; ip = ip->parent())
ip->setOpen(true);
};
node::node(suite_node* par, test& t)
: item(new QListViewItem(par->lvi(), t.name().c_str())), t(t), st(none)
{
par->add_child(this);
setImg();
}
node::node(gui* gp, test& t)
: item(new QListViewItem(gp->test_tree(), t.name().c_str())), t(t), st(none)
{
setImg();
item->setOpen(true);
}
void node::run()
{
for (QListViewItem* ip=item->firstChild(); ip != 0; ip=item->firstChild())
delete ip;
try {
t();
status(is_ok);
emit ok();
} catch (assertion_error& e) {
status(is_fail);
show_error(e);
emit fail();
} catch (exception& e) {
status(is_error);
show_error(e.what());
emit error();
} catch (...) {
status(is_error);
show_error("unknown ... exception");
emit error();
}
}
void node::show_error(assertion_error& e)
{
#ifdef HAVE_SSTREAM
ostringstream oss;
oss << e;
show_error(oss.str().c_str());
#else
show_error(e.what()); // not well, but some sign
#endif
}
void node::show_error(const char* msg)
{
QListViewItem* elvi = new QListViewItem(item, msg);
elvi->setSelectable(false);
}
suite_node::suite_node(suite_node* par, suite& t) : node(par, t) { }
suite_node::suite_node(gui* par, suite& t) : node(par, t) { }
void suite_node::run()
{
status(is_ok);
for (cctyp::iterator p = cc.begin(); p != cc.end(); ++p) {
(*p)->run();
status(max(status(), (*p)->status()));
}
switch (status()) {
case is_ok: emit ok(); break;
case is_fail: emit fail(); break;
case is_error: emit error(); break;
}
}
gui::gui(QApplication& app, QWidget* par, const char* name)
: QVBox(par, name), app(app)
{
setMargin(6);
setSpacing(3);
tree = new QListView(behave(new QVBox(this), true, true), "tree");
tree->addColumn("Test");
tree->setFrameShadow(QListView::Sunken);
tree->setResizePolicy(QScrollView::Manual);
tree->setRootIsDecorated(true);
QVBox* f_cnts = new QVBox(this);
behave(f_cnts, true, false);
suites = new res_stack("Suites", f_cnts);
tests = new res_stack("Tests", f_cnts);
QHBox* hbox = behave(new QHBox(this), true, false);
b_run = new QPushButton("Run", hbox, "run");
b_stop = new QPushButton("Stop", hbox, "stop");
b_quit = new QPushButton("Quit", hbox, "quit");
connect(b_quit, SIGNAL(clicked()), &app, SLOT(quit()));
connect(b_run, SIGNAL(clicked()), this, SLOT(run_pressed()));
connect(b_stop, SIGNAL(clicked()), this, SLOT(stop_pressed()));
}
gui::~gui() { }
void gui::processEvents(int t)
{
app.processEvents(t);
}
void gui::reset()
{
tests->reset();
suites->reset();
}
void gui::nconnect(node* n, res_stack* rs)
{
connect(n, SIGNAL(ok()), rs, SLOT(inc_ok()));
connect(n, SIGNAL(fail()), rs, SLOT(inc_fail()));
connect(n, SIGNAL(error()), rs, SLOT(inc_error()));
}
void gui::add_test(node* n)
{
nconnect(n, tests);
}
void gui::add_suite(node* n)
{
nconnect(n, suites);
}
void gui::totSuites(int v) { suites->max(v); }
void gui::totTests(int v) { tests->max(v); }
#endif

166
tests/lib/unit++/gui.h Normal file
View File

@ -0,0 +1,166 @@
#ifndef __UNITPP_GUI_H
#define __UNITPP_GUI_H
#ifdef GUI
#include "tester.h"
#include <exception>
#include <vector>
#include <qwidget.h>
#include <qcolor.h>
#include <qframe.h>
#include <qlabel.h>
#include <qlistview.h>
#include <qprogressbar.h>
#include <qhbox.h>
#include <qvbox.h>
#include <qpixmap.h>
#include <qpushbutton.h>
#include <qapplication.h>
/// \name unitpp
namespace unitpp {
/// A colored count with a unit.
class cnt_item : public QHBox
{
Q_OBJECT
private:
int v;
QLabel* val;
QLabel* label;
public:
cnt_item(QWidget* par, const QString& txt, const QColor& col = black,
const char* nam = 0);
public slots:
void value(int v);
void inc();
};
/// A line with total, ok, fail, and error counts.
class cnt_line : public QHBox
{
Q_OBJECT
private:
enum fields { id_max, id_ok, id_fail, id_error, n_id };
QLabel* label;
cnt_item* cnts[n_id];
public slots:
void max(int v);
void reset();
void inc_ok();
void inc_fail();
void inc_error();
public:
cnt_line(const QString& txt, QWidget* par = 0, const char* name = 0);
};
/// A cnt_line stacked with a progress bar.
class res_stack : public QVBox
{
Q_OBJECT
private:
cnt_line* cnts;
QProgressBar* bar;
void inc_progress(bool red);
public slots:
void max(int max);
void reset();
void inc_ok();
void inc_fail();
void inc_error();
public:
res_stack(const QString& txt, QWidget* par=0, const char* name=0);
};
class node;
/// The whole GUI box with test tree, results, and buttons.
class gui : public QVBox
{
Q_OBJECT
public:
gui(QApplication& app, QWidget* par = 0, const char* name = 0);
virtual ~gui();
QListView* test_tree() { return tree; }
void add_test(node* n);
void add_suite(node* n);
void processEvents(int t);
signals:
void run();
void stop();
public slots:
void totSuites(int v);
void totTests(int v);
void reset();
private slots:
void run_pressed() { emit run(); }
void stop_pressed() { emit stop(); }
private:
void nconnect(node* node, res_stack*);
QApplication& app;
QListView* tree;
res_stack* suites;
res_stack* tests;
QPushButton* b_run;
QPushButton* b_stop;
QPushButton* b_quit;
};
class suite_node;
// a Qt error prevents this from being a ListViewItem...
/**
* A node in the test tree. An error in Qt prevents this to be derived from
* QListViewItem, hence the separation.
*/
class node : public QObject
{
Q_OBJECT
public:
enum state { none, is_ok, is_fail, is_error };
/// Create this node under par.
node(suite_node* par, test&);
/// Get the associated QListViewItem.
QListViewItem* lvi() { return item; }
///
state status() { return st; }
signals:
/// [signal] emitted when the test succedes
void ok();
/// [signal] emitted when the test fails
void fail();
/// [signal] emitted when the test throws an exception
void error();
public slots:
/// [slot] Make the test run, and emit appropriate signals.
virtual void run();
protected:
/// Make a top level test, directly under the gui.
node(gui* par, test&);
/// Set the status of the node, including update of the displayed icon.
void status(state s) {
st = s;
setImg();
}
private:
void show_error(assertion_error& e);
void show_error(const char*);
QListViewItem* item;
test& t;
state st;
void setImg();
};
/**
* A specialized node representing a test suite.
*/
class suite_node : public node
{
typedef std::vector<node*> cctyp;
cctyp cc; // child container
public:
/// Inner suite creation.
suite_node(suite_node* par, suite&);
/// Top level suite_node.
suite_node(gui* par, suite&);
/// Test.
virtual void run();
/// Register a node below this.
void add_child(node* n) { cc.push_back(n); }
};
}
#endif
#endif

View File

@ -0,0 +1,100 @@
#ifdef GUI
#include "optmap.h"
#include "main.h"
#include "guitester.h"
#include <qapplication.h>
using namespace unitpp;
using namespace std;
class gui_test_runner : public test_runner {
virtual bool run_tests(int argc, const char** argv)
{
QApplication a(argc, const_cast<char**>(argv));
gui g(a);
g_setup setup(&g);
suite::main().visit(&setup);
a.setMainWidget(&g);
g.show();
return a.exec();
}
};
class gui_flag : public options_utils::optmap::cmd {
bool do_cmd(options_utils::optmap* om)
{
static gui_test_runner gtr;
set_tester(&gtr);
return true;
}
};
gui_hook::gui_hook()
{
options().add("g", new gui_flag());
options().alias("gui", "g");
}
g_setup::g_setup(gui* gp) : gp(gp), running(false), n_suites(0), n_tests(0)
{
connect(gp, SIGNAL(run()), this, SLOT(run()));
}
void g_setup::add_node(node* np)
{
nodes.push_back(np);
rev[np->lvi()] = np;
}
void g_setup::visit(test& t)
{
++n_tests;
node* np = new node(branch.top(), t);
add_node(np);
gp->add_test(np);
}
void g_setup::visit(suite& t)
{
++n_suites;
suite_node* np = branch.size() ? new suite_node(branch.top(), t)
: new suite_node(gp, t);
branch.push(np);
add_node(np);
gp->add_suite(np);
}
void g_setup::visit(suite& t, int)
{
branch.pop();
if (!branch.size()) {
gp->totSuites(n_suites);
gp->totTests(n_tests);
}
}
void g_setup::run()
{
if (running)
return;
running = true;
selected.clear();
find_selected(gp->test_tree()->firstChild());
if (!selected.size())
selected.push_back(rev[gp->test_tree()->firstChild()]);
gp->reset();
for (vector<node*>::iterator p = selected.begin(); p!=selected.end(); ++p) {
(*p)->run();
gp->processEvents(20);
if (!running)
break;
}
running = false;
}
void g_setup::find_selected(QListViewItem* lvi)
{
if (!lvi)
return;
if (lvi->isSelected())
selected.push_back(rev[lvi]);
else
find_selected(lvi->firstChild());
find_selected(lvi->nextSibling());
}
#endif

View File

@ -0,0 +1,40 @@
#ifndef __UNITPP__GUITESTER_H
#define __UNITPP__GUITESTER_H
#ifdef GUI
#include "tester.h"
#include "gui.h"
#include <stack>
#include <vector>
#include <qobject.h>
/// \name unitpp
namespace unitpp {
/**
* The class for setting up the GUI. It visites all the tests, and create
* nodes for them in the test tree, count them and adjust all the GUI
* elements appropriately.
*/
class g_setup : public QObject, public visitor {
Q_OBJECT
private:
gui* gp;
bool running;
int n_suites;
int n_tests;
std::stack<suite_node*> branch;
std::vector<node*> nodes, selected;
std::map<QListViewItem*, node*> rev;
void find_selected(QListViewItem*);
void add_node(node* np);
public:
g_setup(gui* gp);
virtual void visit(test&);
virtual void visit(suite&);
virtual void visit(suite&, int);
public slots:
///
void run();
};
}
#endif
#endif

69
tests/lib/unit++/main.cc Normal file
View File

@ -0,0 +1,69 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#include "main.h"
#include <algorithm>
using namespace std;
using namespace unitpp;
bool unitpp::verbose = false;
test_runner* runner = 0;
test_runner::~test_runner()
{
}
void unitpp::set_tester(test_runner* tr)
{
runner = tr;
}
int main(int argc, const char* argv[])
{
options().add("v", new options_utils::opt_flag(verbose));
options().alias("verbose", "v");
if (!options().parse(argc, argv))
options().usage();
plain_runner plain;
if (!runner)
runner = &plain;
return runner->run_tests(argc, argv) ? 0 : 1;
}
namespace unitpp {
options_utils::optmap& options()
{
static options_utils::optmap opts("[ testids... ]");
return opts;
}
bool plain_runner::run_tests(int argc, const char** argv)
{
bool res = true;
if (options().n() < argc)
for (int i = options().n(); i < argc; ++i)
res = res && run_test(argv[i]);
else
res = run_test();
return res;
}
bool plain_runner::run_test(const string& id)
{
test* tp = suite::main().find(id);
if (!tp) {
return false;
}
return run_test(tp);
}
bool plain_runner::run_test(test* tp)
{
tester tst(cout, verbose);
tp->visit(&tst);
tst.summary();
res_cnt res(tst.res_tests());
return res.n_err() == 0 && res.n_fail() == 0;
}
}

63
tests/lib/unit++/main.h Normal file
View File

@ -0,0 +1,63 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#include <iostream>
#include "tester.h"
#include "optmap.h"
/**
* The main of a test program that executes the main test suite and then
* reports the summary.
*
* A #-v# or #--verbose# will turn on verbose, that reports succesful test
* cases; the default behaviour is to report only those that fails.
*/
int main(int argc, const char* argv[]);
/// @name{unitpp}
namespace unitpp {
/**
* The verbose flag, in case somebody wants to piggyback it with more
* meaning.
*/
extern bool verbose;
/**
* A runner is the base class for the objects that actually processes the
* tests from main. Main simply invokes the run_tests method of the current
* test runner.
* \Ref{main}
*/
class test_runner {
public:
virtual ~test_runner();
/**
* run all the tests with arguments in the argc, argv set
*/
virtual bool run_tests(int argc, const char** argv) = 0;
};
/**
* Sets the test_runner to be used in testing. This hook allows another
* tester to hook into the main function and replace the traditional tester.
*/
void set_tester(test_runner*);
/// A plain test runner for the ordinary text version.
class plain_runner : public test_runner {
public:
/// Run the tests specified in argv, starting at i.
virtual bool run_tests(int argc, const char** argv);
private:
/**
* Run a test found in the suite::main() test by id. If id is empty run the
* main test.
* @name run_test-id
* @return true, if the test was totally succesful.
*/
bool run_test(const std::string& id = "");
/// Run the test and return true if succesful. @see{run_test-id}
bool run_test(test*);
/// find the test with the given id
test* find_test(const std::string& id);
};
}

View File

@ -0,0 +1,4 @@
// This header preserves compatibility with old unit++ versions.
//
// Use of this is deprecated, use unit++/optmap.h directly
#include <unit++/optmap.h>

145
tests/lib/unit++/optmap.cc Normal file
View File

@ -0,0 +1,145 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#include <iostream>
#include <cstdlib>
#include "optmap.h"
using namespace std;
using namespace options_utils;
optmap::optmap(const char* usage)
: i(1), prog("program"), tail(usage)
{
}
optmap::~optmap()
{
for (group_t::iterator p = group.begin(); p != group.end(); ++p)
delete p->first;
}
optmap& optmap::add(const char* c, cmd* h)
{
if (cmds.find(c) != cmds.end())
throw invalid_argument(string("duplicated option: ")+c);
cmds[c] = h;
if (group[h].size() == 0)
gvec.push_back(h);
group[h].push_back(c);
return *this;
}
optmap& optmap::alias(const char* new_opt, const char* old_opt)
{
cmd* h = cmds[old_opt];
if (!h)
throw invalid_argument(string("no alias: ")+old_opt);
return add(new_opt, h);
}
bool optmap::parse(int c, const char** v)
{
argc = c;
argv = v;
prog = argv[0];
for (; i < argc; ++i) {
multichar = false;
const char* s = argv[i];
size_t l = strlen(s);
if (*s != '-' || l == 1)
return true;
if (s[1] == '-') {
if (l == 2) { // end of options marker `--'
++i;
return true;
}
if (!do_cmd(s+2))
return false;
} else {
char cmd[2];
cmd[1] = '\0';
multichar = l > 2;
first_multi = true;
for (const char* p = s+1; *p; ++p) {
cmd[0] = *p;
if (!do_cmd(cmd))
return false;;
first_multi = false;
if (!multichar) // get_arg used it
break;
}
}
}
return true;
}
const char* optmap::get_arg()
{
if (multichar) {
if (!first_multi) {
cerr << "internal option requires argument " << argv[i] << endl;
return 0;
}
multichar = false;
return argv[i]+2;
}
return i < argc - 1 ? argv[++i] : 0;
}
void optmap::usage(bool abort)
{
cerr << "usage: " << prog;
for (gvec_t::iterator p = gvec.begin(); p != gvec.end(); ++p) {
cmd* h = *p;
vector<string>& v(group[h]);
string arg = h->arg();
bool need_par = arg.size() > 0 && v.size() > 1;
bool first = true;
cerr << " [";
if (need_par)
cerr << "(";
for (vector<string>::iterator s = v.begin(); s != v.end(); ++s) {
cerr << (first ? " " : " | ") << (s->size() != 1 ? "--":"-") << *s;
first = false;
}
if (need_par)
cerr << ")";
if (arg.size())
cerr << ' ' << arg;
cerr << " ]";
}
cerr << (tail.size() ? " " : "") << tail << endl;
if (abort)
exit(1);
}
bool optmap::do_cmd(const string& opt)
{
cmd* c = cmds[opt];
if (!c) {
cerr << "unknown option: " << opt << endl;
return false;
}
return c->do_cmd(this);
}
bool opt_int::do_cmd(optmap* om)
{
const char* arg = om->get_arg();
if (!arg)
return false;
char* end;
int v = strtol(arg, &end, 10);
if (*end) {
cerr << "failed to parse int argument: " << arg << endl;
return false;
}
val = v;
return true;
}
bool opt_string::do_cmd(optmap* om)
{
const char* arg = om->get_arg();
if (!arg)
return false;
val = arg;
return true;
}

125
tests/lib/unit++/optmap.h Normal file
View File

@ -0,0 +1,125 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#ifndef __UNITPP__OPTMAP_H
#define __UNITPP__OPTMAP_H
#include <string>
#include <map>
#include <vector>
#include <stdexcept>
/**
* The home of \Ref{optmap}. This is a unified way of handling commang line
* arguments, and suitable usage replies when things goes badly.
*/
namespace options_utils {
/**
* A argument option handler, based on the Command pattern. Each option is
* associated with a handling command object.
*
* The map keeps the reverse association as well, to allow printing of a
* using line.
*/
class optmap {
public:
/**
* The base for all option handlers.
*
* To handle a new kind of commands, just derive a class from this an
* overload the methods to do the work.
*/
struct cmd {
/**
* Performing the work associated with the option.
*
* To obtain an argument see \Ref{get_arg}
* @return true if the parsing was succesfull
*/
virtual bool do_cmd(optmap* om) = 0;
/// return the name of the argument needed by \Ref{usage}
virtual std::string arg() { return std::string(); }
virtual ~cmd() {}
};
/**
* The very simple constructor for an optmap object. Please note that
* copying of optmaps are not supported.
* @param usage The tail of the usage string to follow the options
*/
optmap(const char* usage = "");
~optmap();
/**
* Adds an option that invokes the command handler.
*
* This is the low level work horse for all the add methods. All the
* other adds works by adding appropriate cmd objects for doing the work.
*
* @param o
* The option name. If the length of the option is 1 it will be an old
* fashion option, otherwise it will be a GNU stype long option
* (--long-opt).
* @param h
* The pointer to the handler. The optmap assumes ownership of the
* pointer and delete it in the destructor.
*
* @return *this
*/
optmap& add(const char* o, cmd* h);
/// Adds an alias for an option.
optmap& alias(const char* new_opt, const char* old_opt);
/**
* Parse the command line.
* @return true if the parse is valid, false otherwise.
*/
bool parse(int argc, const char** argv);
/// Gets the index for the first non option argument
int n() { return i; }
/// display a usage string and abort()
void usage(bool abort = true);
/// get the argument of an option; mainly for cmd implementations.
const char* get_arg();
private:
int i;
const char* prog;
int argc;
const char** argv;
bool multichar; // doing a multichar, -pdf --> -p -d -f
bool first_multi;
optmap(const optmap& o);
optmap& operator=(const optmap&);
bool do_cmd(const std::string& opt);
std::string tail;
typedef std::map<std::string, cmd*> cmds_t;
cmds_t cmds;
typedef std::vector<cmd*> gvec_t;
gvec_t gvec;
typedef std::map<cmd*, std::vector<std::string> > group_t;
group_t group;
};
/// A cmd handler that can set a boolean flag.
class opt_flag : public optmap::cmd {
bool& flg;
public:
/// Just give it the bool you want set if the flag is present.
opt_flag(bool& flg) : flg(flg) {}
virtual bool do_cmd(optmap*) { flg = true; return true; }
};
/// A cmd handler that can get an integer value.
class opt_int : public optmap::cmd {
int& val;
public:
/// just give it an int variable that the flag argument can be assigned to.
opt_int(int& val) : val(val) {}
virtual bool do_cmd(optmap* om);
virtual std::string arg() { return std::string("<int>"); }
};
/// A cmd handler for a string
class opt_string : public optmap::cmd {
std::string& val;
public:
/// Give it a string that the flag arg should be assigned to.
opt_string(std::string& val) : val(val) {}
virtual bool do_cmd(optmap* om);
virtual std::string arg() { return std::string("<string>"); }
};
}
#endif

View File

@ -0,0 +1,74 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#include <typeinfo>
#include <iostream>
#include "tester.h"
using namespace std;
using namespace unitpp;
void tester::summary()
{
os << "Tests [Ok-Fail-Error]: [" << n_test.n_ok() << '-'
<< n_test.n_fail() << '-' << n_test.n_err() << "]\n";
}
void tester::visit(test& t)
{
try {
t();
n_test.add_ok();
write(t);
} catch (assertion_error& e) {
n_test.add_fail();
write(t, e);
} catch (exception& e) {
n_test.add_err();
write(t, e);
} catch (...) {
n_test.add_err();
write(t, 0);
}
}
void tester::visit(suite& t)
{
if (verbose)
os << "****** " << t.name() << " ******" << endl;
accu.push(n_test);
}
void tester::visit(suite& , int)
{
res_cnt r(accu.top());
accu.pop();
if (n_test.n_err() != r.n_err())
n_suite.add_err();
else if (n_test.n_fail() != r.n_fail())
n_suite.add_fail();
else
n_suite.add_ok();
}
void tester::write(test& t)
{
if (verbose)
disp(t, "OK");
}
void tester::disp(test& t, const string& status)
{
os << status << ": " << t.name() << endl;
}
void tester::write(test& t, assertion_error& e)
{
disp(t, "FAIL");
os << e << '\n';
}
void tester::write(test& t, std::exception& e)
{
disp(t, "ERROR");
os << " : [" << typeid(e).name() << "] " << e.what() << '\n';
}
void tester::write(test& t, int )
{
disp(t, "ERROR");
os << " : " << "unknown exception" << '\n';
}

77
tests/lib/unit++/tester.h Normal file
View File

@ -0,0 +1,77 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#ifndef _UNITPP_TESTER_H
#define _UNITPP_TESTER_H
#include <stack>
#ifdef __UNITPP
#include "unit++.h"
#else
#include <unit++/unit++.h>
#endif
namespace unitpp {
/// A mostly internal class for keeping score.
class res_cnt {
int ok, fail, err;
public:
/// Create a 0 count.
res_cnt() : ok(0), fail(0), err(0) {}
/// Count one ok.
void add_ok() { ++ok; }
/// Count one fail.
void add_fail() { ++fail; }
/// Count one error.
void add_err() { ++err; }
/// get ok count.
int n_ok() { return ok; }
/// get fail count.
int n_fail() { return fail; }
/// get error count.
int n_err() { return err; }
/// get total count.
int n() { return ok+fail+err; }
};
/**
* The standard text based tester. It implements the visitor pattern for the
* test and suite classes, and executes each test case in a depth first
* traversal, toting the score of test cases.
*
* The class might be used for test executers aimed at other environments,
* e.g. a GUI based version.
*
* Please note that this class is automagically instantiated by the main
* method supplied in the library. This means that you might very well do all
* your testing without directly laying hans on this fellow.
*/
class tester : public visitor {
std::ostream& os;
bool verbose; // list succeded tests
std::stack<res_cnt> accu;
res_cnt n_suite, n_test;
void disp(test& t, const std::string&);
void write(test& t);
void write(test& t, assertion_error& e);
void write(test& t, std::exception& e);
void write(test& t, int dummy);
public:
/**
* Create a text tester.
* \param os the stream to write results to.
* \param verbose whether to report on succesful tests
*/
tester(std::ostream& os, bool verbose = false) : os(os), verbose(verbose) {}
/// Get the score for tests
res_cnt res_tests() { return n_test; }
/// Get the score for suites
res_cnt res_suites() { return n_suite; }
/// Write the summary
virtual void summary();
/// Part of the visitor pattern.
virtual void visit(test&);
/// Part of the visitor pattern.
virtual void visit(suite&);
/// Part of the visitor pattern; visit to suite after children.
virtual void visit(suite& t, int);
};
}
#endif

View File

@ -0,0 +1,4 @@
// This header preserves compatibility with old unit++ versions.
//
// Use of this is deprecated, use unit++/unit++.h directly
#include <unit++/unit++.h>

27
tests/lib/unit++/unit++.1 Normal file
View File

@ -0,0 +1,27 @@
.hy 0
.if n .na
.TH UNIT++ 1 2002-02-23 "Unit++ 1.2"
.UC
.SH NAME
\fBunit++\fB \- run a set of tests
.SH SYNOPSIS
.BI "unit++ [-v|--verbose] [-g|--gui] [testid...]
.SH DESCRIPTION
.B unit++
(the usual name) is the resulting program from compiling a set of unit++
tests, and linking them with the
.B unit++(3)
library supplied main function to obtain an executable test suite.
Each test in the test tree has an id, and it is possible to start only some
of the tests by naming them in the form id1.id2.id3.
The GUI option is only available if the library was compiled with GUI
support, and the tests has actually linked the GUI stuff as well. Otherwise
the -g option will provoke an error. Otherwise it will give a Qt based GUI
that allows running of individual tests, with a nice green (or less nice red)
bar that shows success (or failure).
.SH AUTHOR
Claus Dræby <azhrarn@users.sourceforge.net>
.SH SEE ALSO
.BR unit++ (3).

130
tests/lib/unit++/unit++.3 Normal file
View File

@ -0,0 +1,130 @@
.hy 0
.if n .na
.TH UNIT++ 3 2002-02-23 "Unit++ 1.2"
.UC
.SH NAME
\fBunit++\fB: \fBtest\fB, \fBsuite\fb, \fBtestcase\fB \- test framework for C++.
.SH SYNOPSIS
.B #include <unit++/unit++.h>
.br
.B using namespace unitpp;
.br
.B using namespace std;
.sp
.BI "test::test(string " name );
.sp
.B virtual void test::operator()();
.sp
.B class suite : public test
.sp
.BI "suite::suite(string " name );
.sp
.BI "static suite& suite::main();"
.sp
.BI "void suite::add(const string& " id ", const testcase& " t );
.sp
.BI "template<typename " C "> "
.br
.BI " testcase::testcase(" C "* " par ", const string& " name ", "
.br
.BI " typename test_mfun<" C ">::mfp " fp ");"
.sp
.BI "testcase::testcase(test* " t );
.sp
.BI "fail(const string& " msg );
.sp
.BI "template<typename " A "> "
.br
.BI " void assert_true(const string& " msg ", " A " " assertion );
.sp
.BI "template<typename " T1 ", typename " T2 "> "
.br
.BI " void assert_eq(const string& " msg ", " T1 " " exp ", " T2 " " got );
.sp
.BI "gui_hook::gui_hook();"
.SH LIBRARIES
.B -lunit++
.SH DESCRIPTION
.B unit++
is a fremework that allows creation and execution of C++ unit tests. Each
test is an instance of the
.B test
class, and is managed by an instance of the
.B testcase
class. The actual takes place by invoking each test as function, that is
calling the virtual operator(). However, the usual way of making tests is by
using a class derived from the
.B suite
class to wrap a number of releated tests, Each test then
becomes a member function in the class, and the testcase instance is
constructed by using the
.B member function template constructor.
Each test suite class is usually placed in an anonymous namespace to allow
the all to be named Test, as in the example below. Since the
.B main
method of the library runs a global test suite each suite must add itself to
this global tree. This is done by invoking
.B add
on the test suite object obtained by
.B suite::main().
The assert templates allows the tests to check conditions;
.B assert_true
checks a truth value, that is it fails if !
.I assertion
evaluates to true;
.B assert_eq asserts equality by use of == between two objects, both of which
must be of types that can be written to a stream by use of <<.
.B fail
simply fails a test.
The
.B gui_hook
class is the hook for allowing a test to use the Qt gui for the test program.
A test program must have exactly one global object of type gui_hook in order
to enable the gui, Even when compiled without gui support, creating a
gui_hook is ok, but it will only have effect if gui support was enabled when
the library was compiled.
.SH EXAMPLES
.nf
#include <unit++/unit++.h>
using namespace std;
using namespace unitpp;
namespace {
class Test : public suite
{
char* p = "Hej";
string s("mor");
void t1()
{
assert_true("pointer is not 0", p);
}
void t2()
{
assert_eq("Texts match", string("Mor"), s);
}
public:
Test() : suite("Yet another test suite")
{
suite::main().add("demo", this);
add("t1", testcase(this, "T1", &Test::t1));
add("t2", testcase(this, "T2", &Test::t2));
}
} * theTest = new Test();
}
.fi
.SH NOTE
Each test suite runs all its tests in the same instance. That means that the
above example will invoke first t1 and then t2 in the same object. Hence any
side effects on the Test object that a test function has survives to the
subsequent tests in the same object. This behaviour is directly opposite to
how the original unit test framework junit treats test suites. There are
however compelling C++ reasons why it must be so, and once known it might
even be used to an advantage from time to time.
.SH AUTHOR
Claus Dræby <azhrarn@users.sourceforge.net>
.SH SEE ALSO
.BR unit++ (1).

View File

@ -0,0 +1,93 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#include <algorithm>
#include <functional>
#include "unit++.h"
using namespace unitpp;
using namespace std;
#ifndef GUI
gui_hook::gui_hook() { }
#endif
void test::visit(visitor* v)
{
v->visit(*this);
}
testcase::testcase(test* t)
: cnt(new size_t(1)), tst(t)
{
}
testcase::testcase(const testcase& t)
: cnt(t.cnt), tst(t.tst)
{
(*cnt)++;
}
void testcase::dec_cnt()
{
if (--(*cnt) == 0) {
delete cnt;
delete tst;
}
}
testcase::~testcase()
{
dec_cnt();
}
testcase& testcase::operator=(const testcase& t)
{
++*(t.cnt);
dec_cnt();
cnt = t.cnt;
tst = t.tst;
return *this;
}
suite& suite::main()
{
static suite instance("top");
return instance;
}
test* suite::get_child(const string& id)
{
vector<string>::iterator p = std::find(ids.begin(), ids.end(), id);
if (p != ids.end())
return &(static_cast<test&>(tests[p - ids.begin()]));
return 0;
}
vector<string> unitpp::vectorize(const string& str, char c)
{
vector<string> res;
string::const_iterator s, p;
for (s = str.begin(); s != str.end(); ) {
p = find(s, str.end(), c);
res.push_back(string(s, p));
s = (p == str.end()) ? p : p + 1;
}
return res;
}
test* suite::find(const string& id)
{
vector<string> ss(vectorize(id, '.'));
test* tp = this;
for (vector<string>::iterator p = ss.begin(); p != ss.end(); ++p)
if (!(tp = tp->get_child(*p)))
break;
return tp;
}
void suite::add(const string& id, const testcase& t)
{
ids.push_back(id);
tests.push_back(t);
}
void suite::visit(visitor* v)
{
v->visit(*this);
for_each(tests.begin(), tests.end(),
bind2nd(mem_fun_ref(&testcase::visit), v));
v->visit(*this, 0);
}
void unitpp::assertion_error::out(ostream& os) const
{
os << msg << string(" [assertion failed]");
}

344
tests/lib/unit++/unit++.h Normal file
View File

@ -0,0 +1,344 @@
// Copyright (C) 2001 Claus Dræby
// Terms of use are in the file COPYING
#ifndef __TEST_FW_H_
#define __TEST_FW_H_
#include <vector>
#include <string>
#include <map>
#include <iostream>
#include "optmap.h"
/**
* The unitpp name space holds all the stuff needed to use the unit++ testing
* framework.
*
* The normal way to make a test is like this:
*
\begin{verbatim}
#include<unit++/unit++.h>
using namespace unitpp;
// use anonymous namespace so all test classes can be named Test
namespace {
class Test : public suite {
void test1()
{
// do test stuff
assert_true("message", exp1); // exp1 should be true
assert_eq("another msg", 123456, exp2); // exp2 should be 123456
// ...
}
void test2()
{
// do something that provokes exception out_of_range
}
public:
Test() : suite("appropriate name for test suite")
{
// any setup you need
add("id1", testcase(this, "Test 1", &Test::test1));
// make a testcase from the method
testcase tc(this, "Test 2", &Test::test2);
// add a testcase that expects the exception
add("id2", exception_case<out_of_range>(tc));
// add the suite to the global test suite
suite::main().add("id", this);
}
} * theTest = new Test(); // by new, since testcase claims ownership
}
\end{verbatim}
*
* In order to make an executable test, simply link the above code against
* libunit++, something like
*
* #g++ -o test++ mytest.cc -L <location of libunit++> -lunit++#
*
* This will generate a test called #test++# and the standard behaviour for a
* test. Note that most shells have #test# defined as a shell builtin which
* makes it a moderately bad name for a program, since it is rather hard to
* get executed, hence #test++#.
* @see main
*/
namespace unitpp {
class visitor;
/**
* The heart of a test system: A test. The test is meant as a base class for
* the tests that a client want performed. This means that all tests are to
* be pointers dynamically allocated. However, the test system takes
* responsibilities for freeing them again.
*
* The function call overload mechanism is used for the executable part of
* the test.
*/
class test {
std::string nam;
public:
/// A test just needs a name
test(const std::string& name) : nam(name) {}
virtual ~test() {}
/// The execution of the test
virtual void operator()() = 0;
virtual void visit(visitor*);
virtual test* get_child(const std::string&) { return 0; }
std::string name() const { return nam; }
};
/**
* A test that is implemented by a member function.
*/
template<typename C>
class test_mfun : public test {
public:
typedef void (C::*mfp)();
/// An object, a name, and a pointer to a member function.
test_mfun(C* par, const std::string& name, mfp fp)
: test(name), par(par), fp(fp)
{}
/// Executed by invoking the function in the object.
virtual void operator()()
{
(par->*fp)();
}
private:
C* par;
mfp fp;
};
/**
* A ref counted reference to a test. This is what test suites are composed
* of, and what ensures destruction.
*/
class testcase {
size_t* cnt;
test* tst;
void dec_cnt();
public:
/// Simply wrap -- and own -- a test.
testcase(test* t);
/// Keep the ref count
testcase(const testcase& tr);
/**
* Make a testcase from a class and a member function.
*
* The normal usage is inside some test suite class Test:
*
* #add("id", testcase(this, "Testing this and that", &Test::test))#
*
* to make a test that invokes the test method on the instance of the
* suite class.
* \Ref{test_mfun}
*/
template<typename C>
testcase(C* par, const std::string& name, typename test_mfun<C>::mfp fp)
: cnt(new size_t(1)), tst(new test_mfun<C>(par, name, fp))
{ }
~testcase();
/// Assignment that maintains reference count.
testcase& operator=(const testcase&);
void visit(visitor* vp) const { tst->visit(vp); }
operator test& () { return *tst; }
operator const test& () const { return *tst; }
};
/**
* A wrapper class for the testcase class that succedes if the correct
* exception is generated.
*/
template<typename E>
class exception_test : public test {
public:
/**
* The constructor needs a testcase to wrap. This exception_test will
* fail unless the wrapped testcase generates the exception.
*
* The name of the exception_test is copied from the wrapped test.
*/
exception_test(const testcase& tc)
: test(static_cast<const test&>(tc).name()), tc(tc) {}
~exception_test() {}
/// Runs the wrapped test, and fails unless the correct exception is thrown.
virtual void operator()();
private:
testcase tc;
};
/**
* Generate a testcase that expects a specific exception from the testcase it
* wraps. It can be used something like
*
* #testcase tc(this, "Test name", &Test::test);#
*
* #add("ex", exception_case<out_of_range>(tc));#
*
* The name of the exception_case is copied from the wrapped testcase, and
* the exception_case will execute the tc test case and report a failure
* unless the #out_of_range# exception is generated.
*/
template<typename E>
testcase exception_case(const testcase& tc)
{
return testcase(new exception_test<E>(tc));
}
/**
* Splits the string by char c. Each c will generate a new element in the
* vector, including leading and trailing c.
*/
extern std::vector<std::string> vectorize(const std::string& str, char c);
/**
* A suite is a test that happens to be a collection of tests. This is an
* implementation of the Composite pattern.
*/
class suite : public test {
std::vector<std::string> ids;
std::vector<testcase> tests;
public:
/// Make an empty test suite.
suite(const std::string& name) : test(name) {}
virtual ~suite() {};
/// Add a testcase to the suite.
void add(const std::string& id, const testcase& t);
/**
* Get a child with the specified id.
* @return 0 if not found.
*/
virtual test* get_child(const std::string& id);
/// An empty implementation.
virtual void operator()() {}
/// Allow a visitor to visit a suite node of the test tree.
void visit(visitor*);
/// Get a reference to the main test suite that the main program will run.
static suite& main();
// Splits the string by dots, and use each id to find a suite or test.
test* find(const std::string& id);
};
/**
* The visitor class is a base class for classes that wants to participate in
* the visitor pattern with the test hierarchi.
*
* This is a slightly extended visitor pattern implementation, intended for
* collaboration with the Composite pattern. The aggregate node (here the
* suite node) is visited twice, before and after the children are visited.
* This allows different algorithms to be implemented.
*/
class visitor {
public:
virtual ~visitor() {}
/// Visit a test case, that is not a suite.
virtual void visit(test&) = 0;
/// Visit a suite node before the children are visited.
virtual void visit(suite&) {};
/**
* Visit a suite after the children are visited
*/
virtual void visit(suite&, int dummy) = 0; // post childs
};
/// The basic for all failed assert statements.
class assertion_error : public std::exception
{
std::string msg;
public:
/// An assertion error with the given message.
assertion_error(const std::string& msg) : msg(msg) {}
///
std::string message() const { return msg; }
virtual ~assertion_error() throw () {}
/**
* The virtual method used for operator<<.
*/
virtual void out(std::ostream& os) const;
};
/**
* This exception represents a failed comparison between two values of types
* T1 and T2. Both the expected and the actually value are kept.
*/
template<class T1, class T2>
class assert_value_error : public assertion_error
{
T1 exp;
T2 got;
public:
/// Construct by message, expected and gotten.
assert_value_error(const std::string& msg, T1& exp, T2& got)
: assertion_error(msg), exp(exp), got(got)
{
}
virtual ~assert_value_error() throw () {}
/**
* Specialized version that requires both T1 and T2 to support
* operator<<(ostream&, Tn).
*/
virtual void out(std::ostream& os) const
{
os << message() << " [expected: `" << exp << "' got: `" << got << "']";
}
};
/// The test was not succesful.
inline void fail(const std::string& msg)
{
throw assertion_error(msg);
}
template<typename E>
void exception_test<E>::operator()()
{
try {
(static_cast<test&>(tc))();
fail("unexpected lack of exception");
} catch (E& ) {
// fine!
}
}
/// Assert that the assertion is true, that is fail #if (!assertion) ...#
template<class A> inline void assert_true(const std::string& msg, A assertion)
{
if (!assertion)
throw assertion_error(msg);
}
/// Assert that the two arguments are equal in the #==# sense.
template<class T1, class T2>
inline void assert_eq(const std::string& msg, T1 exp, T2 got)
{
if (!(exp == got))
throw assert_value_error<T1,T2>(msg, exp, got);
}
/*
* Put an assertion error to a stream, using the out method. The out method
* is virtual.
*/
inline std::ostream& operator<<(std::ostream& os, const unitpp::assertion_error& a)
{
a.out(os);
return os;
}
/**
* The singleton instance of the option handler of main.
*
* This allows a test to add its own flags to the resulting test program, in
* the following way.
*
* #bool x_flg = false;#
* #unitpp::options().add("x", new options_utils::opt_flag(x_flg));#
*
* If a -x is now given to the resulting test it will set the #x_flg#
* variable;
*/
options_utils::optmap& options();
/**
* An instance of this class hooks the GUI code into the test executable.
* Hence, make a global variable of class gui_hook to allow the -g option to
* a test.
*
* If the library is compiled without GUI support, it is still legal to
* create an instance of gui_hook, but it will not add the -g option.
*/
class gui_hook {
public:
gui_hook();
};
}
#endif

View File

@ -0,0 +1,59 @@
dnl AM_PATH_UNITPP([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
dnl note to self - reference vars with $1, $2 etc
AC_DEFUN(AM_PATH_UNITPP,
[
AC_LANG_PUSH(C++)
AC_ARG_ENABLE(unitpp,
AC_HELP_STRING([--enable-unitpp-test],
[enable unit tests (default is YES if you have libunit++, else NO)]),
,
[enable_unitpp="yes"] )
AC_ARG_WITH(unitpp-prefix,
AC_HELP_STRING([--with-unitpp-prefix=DIR],
[specify the location of unit++ for unit tests]) )
if test "x$with_unitpp_prefix"="x"; then
UNITPP_CXXFLAGS="-I$with_unitpp_prefix/include"
UNITPP_LIBS="-L$with_unitpp_prefix/lib -lunit++"
else
# UNITPP_CXXFLAGS="-I/usr/local/include"
UNITPP_LIBS="-lunit++"
fi
CXXFLAGS="$CXXFLAGS $UNITPP_CXXFLAGS"
LIBS="$LIBS $UNITPP_LIBS"
if test $enable_unitpp="yes"; then
AC_CHECK_HEADER(unit++/unit++.h,
[ac_check_unitpp="yes"],
[ac_check_unitpp="no"] )
if test $ac_check_unitpp="yes"; then
AC_MSG_CHECKING(if unit++ test program compiles)
AC_TRY_LINK(
[ #include<unit++/unit++.h> ],
[ unitpp::test* test_test = new unitpp::suite("Test Suite"); ],
[ac_check_unitpp="yes"
AC_MSG_RESULT(yes) ],
[ac_check_unitpp="no"
AC_MSG_RESULT(no) ] )
fi
else
ac_check_unitpp="yes"
fi
if test $ac_check_unitpp="yes"; then
ifelse([$1], , :, [$1])
else
UNITPP_CXXFLAGS=""
UNITPP_LIBS=""
ifelse([$2], , :, [$2])
fi
AC_LANG_POP(C++)
AC_SUBST(UNITPP_CXXFLAGS)
AC_SUBST(UNITPP_LIBS)
])

4
tests/parser/Makefile Normal file
View File

@ -0,0 +1,4 @@
main: headerTest.cpp ../../libvmime-debug.a
g++ -o headerTest headerTest.cpp ../../libvmime-debug.a ../lib/unit++/unit++.cc ../lib/unit++/main.cc ../lib/unit++/optmap.cc ../lib/unit++/tester.cc -Wall

101
tests/parser/headerTest.cpp Normal file
View File

@ -0,0 +1,101 @@
#include "../lib/unit++/unit++.h"
#include <iostream>
#include <ostream>
#include "../../src/vmime"
#include "../../examples/common.inc"
using namespace unitpp;
namespace
{
class headerTest : public suite
{
static const std::string getFieldValue(const vmime::headerField& field)
{
std::ostringstream oss;
vmime::utility::outputStreamAdapter voss(oss);
field.generate(voss);
return (oss.str());
}
// getAllByType function tests
void testGetAllByType1()
{
vmime::header hdr;
hdr.parse("To: a\r\nFrom: b\r\n");
std::vector <vmime::headerField*> res = hdr.fields.findAllByType(vmime::headerField::Subject);
assert_eq("Count", (unsigned int) 0, res.size());
}
void testGetAllByType2()
{
vmime::header hdr;
hdr.parse("To: b\r\nTo : a\r\nFrom: c\r\n");
std::vector <vmime::headerField*> res = hdr.fields.findAllByType(vmime::headerField::To);
assert_eq("Count", (unsigned int) 2, res.size());
assert_eq("First value", "To: b", headerTest::getFieldValue(*res[0]));
assert_eq("First value", "To: a", headerTest::getFieldValue(*res[1]));
}
// getAllByName function tests
void testGetAllByName1()
{
vmime::header hdr;
hdr.parse("A: a1\nC: c1\n");
std::vector <vmime::headerField*> res = hdr.fields.findAllByName("B");
assert_eq("Count", (unsigned int) 0, res.size());
}
void testGetAllByName2()
{
vmime::header hdr;
hdr.parse("A: a1\nB: b1\nB: b2\nC: c1\n");
std::vector <vmime::headerField*> res = hdr.fields.findAllByName("B");
assert_eq("Count", (unsigned int) 2, res.size());
assert_eq("First value", "B: b1", headerTest::getFieldValue(*res[0]));
assert_eq("Second value", "B: b2", headerTest::getFieldValue(*res[1]));
}
void testGetAllByName3()
{
vmime::header hdr;
hdr.parse("A: a1\nB: b1\nB: b2\nC: c1\nC: c3\nC: c2\n");
std::vector <vmime::headerField*> res = hdr.fields.findAllByName("C");
assert_eq("Count", (unsigned int) 3, res.size());
assert_eq("First value", "C: c1", headerTest::getFieldValue(*res[0]));
assert_eq("Second value", "C: c3", headerTest::getFieldValue(*res[1]));
assert_eq("Second value", "C: c2", headerTest::getFieldValue(*res[2]));
}
public:
headerTest() : suite("vmime::header")
{
// VMime initialization
vmime::platformDependant::setHandler<my_handler>();
add("GetAllByType", testcase(this, "GetAllByType1", &headerTest::testGetAllByType1));
add("GetAllByType", testcase(this, "GetAllByType2", &headerTest::testGetAllByType2));
add("GetAllByName", testcase(this, "GetAllByName1", &headerTest::testGetAllByName1));
add("GetAllByName", testcase(this, "GetAllByName2", &headerTest::testGetAllByName2));
add("GetAllByName", testcase(this, "GetAllByName3", &headerTest::testGetAllByName3));
suite::main().add("vmime::header", this);
}
};
headerTest* theTest = new headerTest();
}