changeset 0:d098192f01d9

Initial commit to the repository.
author Brian Neal <bgneal@gmail.com>
date Sat, 19 Mar 2011 19:53:12 -0500
parents
children 62a54c46da31
files .hgignore README.markdown fructose_gen.py tests/g.cpp tests/g1.h tests/g2.h tests/g3.h tests/g_expected.cpp tests/test.bash tests/x.cpp tests/x1.h tests/x2.h tests/x3.h tests/x_expected.cpp
diffstat 14 files changed, 828 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,3 @@
+syntax: glob
+*.pyc
+*.swp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.markdown	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,93 @@
+fructose_gen.py
+===============
+
+Quick Start
+-----------
+
+`fructose_gen.py` is a Python script for auto-generatng the `main()` function
+for the C++ testing framework [Fructose][1].
+
+Sample usage:
+
+    $ python fructose_gen.py [options] test1.h test2.h ... testN.h > main.cpp
+
+In this example, `fructose_gen.py` will read the Fructose test files `test1.h`
+through `testN.h` and produce on standard output C++ code with a generated
+`main()` function. This auto-generated code will instantiate all the test
+instances, register the appropriate tests with each instance, and then execute
+each test in turn.
+
+To see usage information and a list of options:
+
+    $ python fructose_gen.py --help
+
+
+Code Generation Styles
+----------------------
+
+`fructose_gen.py` supports two styles of code generation, described below.
+
+### xUnit Style ###
+
+The default style is xUnit style. In this form, `fructose_gen.py` will scan
+C++ code looking for classes or structs that inherit from `fructose::test_base<>`.
+Inside those classes or structs, member functions that match the following
+pattern are assumed to be test functions:
+
+      void testXXXX(const std::string&)
+
+Upon finding such a function, `fructose_gen.py` will register that member
+function as a test with the name "testXXXX". 
+
+
+### Generator Style ###
+
+To remain backward compatible with the `generator` program that ships with
+Fructose, invoke `fructose_gen.py` with a `-g` or `--generator` option flag.
+
+In this style, `fructose_gen.py` will scan files for the `FRUCTOSE_CLASS`,
+`FRUCTOSE_STRUCT` and `FRUCTOSE_TEST` macros. See the Fructose documentation
+for more information.
+
+
+Caveats
+-------
+
+`fructose_gen.py` is not a true C++ code parser, and in fact is quite simple
+in how it operates. This is sufficient for most cases, but please be aware of
+the following limitations.
+
+1. Ensure your class (or struct) definition is all on one line:
+
+         class my_unit_test : public fructose::test_base<my_unit_test>
+
+   If you split the above across multiple lines `fructose_gen.py` will not
+   recognize your class and will not generate a test instance for it.
+
+2. `fructose_gen.py` does not understand C-style comments or the preprocessor.
+   To comment out a test, you can either use C++ comments, or change the function
+   name slightly to ensure it won't be recognized. Examples:
+
+          /*
+          ** void test_is_sorted(const std::string& name)  // this won't work
+          */
+
+          #if 0
+          void test_is_sorted(const std::string& name)   // this won't work
+          #endif
+
+          void not_a_test_is_sorted(const std::string& name) // this works
+          // void test_is_sorted(const std::string& name)    // this works
+          // FRUCTOSE_TEST(is_sorted)                        // this works
+
+   The above also applies to commenting out test classes.
+
+
+Support
+-------
+See the [fructose_gen support site][2] hosted at [bitbucket.org][3].
+
+
+[1]: http://fructose.sourceforge.net/
+[2]: https://bitbucket.org/bgneal/fructose_gen
+[3]: https://bitbucket.org
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fructose_gen.py	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,222 @@
+#!/usr/bin/env python
+"""
+Copyright (C) 2011 by Brian Neal <bgneal@gmail.com>
+
+fructose_gen.py - A program to auto-generate the main() routine for the C++
+testing framework Fructose.
+
+"""
+from __future__ import with_statement
+import re
+import optparse
+import sys
+
+
+USAGE = "usage: %prog [options] test1.h test2.h ... > main.cpp"
+DESCRIPTION = "Generates the main() routine for the Fructose C++ testing framework"
+VERSION = "%prog 0.1"
+INDENT_CNT = 4
+INDENT = " " * INDENT_CNT
+
+
+def strip_comments(s):
+    """
+    A simple function to strip out C++ style // comments from a string.
+    This function is really dumb; it doesn't know about string literals, etc.,
+    but it should suit our purposes for finding commented out test classes and
+    cases.
+
+    """
+    i = s.find('//')
+    return s if i == -1 else s[:i]
+
+
+class TestClass(object):
+    """
+    This class represents a Fructose test class.
+    Each test class has a name attribute and a list of test case names
+    (strings).
+
+    """
+    def __init__(self, name):
+        self.name = name
+        self.test_cases = []
+
+
+class TestFile(object):
+    """
+    A class to represent a Fructose unit test file.
+    Each test file has a filename attribute and a list of test classes.
+
+    """
+    def __init__(self, filename):
+        self.filename = filename
+        self.test_classes = []
+
+
+class TestFileParser(object):
+    """
+    Base class for parsing Fructose unit test files.
+    """
+    def __init__(self, filename):
+        self.test_file = TestFile(filename)
+
+    def parse(self):
+        """
+        Parse the file by reading it line by line.
+        Returns a TestFile object that contains the test classes found within.
+
+        """
+        with open(self.test_file.filename, 'r') as f:
+            for line in f:
+                s = strip_comments(line)
+                if s:
+                    self._parse_line(s)
+
+        return self.test_file
+
+    def _parse_line(self, line):
+        """
+        Parses each line of the test file, calling into derived classes to
+        find test classes and test cases.
+
+        """
+        test_class = self._parse_test_class(line)
+        if test_class:
+            self.test_file.test_classes.append(test_class)
+        else:
+            test_case = self._parse_test_case(line)
+            if len(self.test_file.test_classes) and test_case:
+                self.test_file.test_classes[-1].test_cases.append(test_case)
+
+    def _parse_test_class(self, line):
+        """Derived classes override this"""
+        raise NotImplementedError
+
+    def _parse_test_case(self, line):
+        """Derived classes override this"""
+        raise NotImplementedError
+
+
+class GeneratorFileParser(TestFileParser):
+    """
+    This class parses Fructose test files using the generator style of code
+    generation.
+
+    """
+    CLASS_RE = re.compile(r'\bFRUCTOSE_(?:CLASS|STRUCT)\s*\(\s*([a-zA-Z_]\w*)\s*\)')
+    CASE_RE = re.compile(r'\bFRUCTOSE_TEST\s*\(\s*([a-zA-Z_]\w*)\s*\)')
+
+    def _parse_test_class(self, line):
+        m = self.CLASS_RE.search(line)
+        return TestClass(m.group(1)) if m else None
+
+    def _parse_test_case(self, line):
+        m = self.CASE_RE.search(line)
+        return m.group(1) if m else None
+
+
+class XunitFileParser(TestFileParser):
+    """
+    This class parses Fructose test files using the xUnit style of code
+    generation.
+
+    """
+    CLASS_RE = re.compile(r'\b(?:struct|class)\s+([a-zA-Z_]\w*)\s+:\s+public'
+                            r'\s+(?:fructose\s*::\s*)?test_base\s*<\s*\1\s*>')
+
+    CASE_RE = re.compile(r'\bvoid\s+(test\w+)\s*\(const\s+(?:std::)?string\s*&'
+                            r'(?:\s+[a-zA-Z_]\w+)?\s*\)')
+
+    def _parse_test_class(self, line):
+        m = self.CLASS_RE.search(line)
+        return TestClass(m.group(1)) if m else None
+
+    def _parse_test_case(self, line):
+        m = self.CASE_RE.search(line)
+        return m.group(1) if m else None
+
+
+def generate_test_instance(test_class):
+    """
+    Generates the code to instantiate a test instance, register and run the
+    tests.
+
+    """
+    type_name = test_class.name
+    instance = type_name + '_instance'
+
+    print "%s{" % INDENT
+    block_indent = INDENT * 2
+    print "%s%s %s;" % (block_indent, type_name, instance)
+    for test_case in test_class.test_cases:
+        print '%s%s.add_test("%s", &%s::%s);' % (
+                block_indent,
+                instance,
+                test_case,
+                type_name,
+                test_case,
+                )
+    print "%sconst int r = %s.run(argc, argv);" % (block_indent, instance)
+    print "%sretval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;" % block_indent
+    print "%s}" % INDENT
+
+
+
+def generate_main(test_files):
+    """
+    Generates the main() file given a list of TestFile objects.
+
+    """
+    for test_file in test_files:
+        print '#include "%s"' % test_file.filename
+
+    print '\n#include <stdlib.h>\n'
+    print 'int main(int argc, char* argv[])\n{'
+    print '%sint retval = EXIT_SUCCESS;\n' % INDENT
+
+    for test_file in test_files:
+        for test_class in test_file.test_classes:
+            generate_test_instance(test_class)
+
+    print '\n%sreturn retval;\n}' % INDENT
+
+
+def main(argv=None):
+
+    parser = optparse.OptionParser(usage=USAGE, description=DESCRIPTION,
+                                   version=VERSION)
+    parser.set_defaults(
+        generator=False,
+    )
+    parser.add_option("-g", "--generator", action="store_true",
+            help="use generator style code generation [default: %default]")
+
+    opts, args = parser.parse_args(args=argv)
+
+    xunit = not opts.generator
+
+    parser_class = XunitFileParser if xunit else GeneratorFileParser
+
+    if len(args) == 0:
+        sys.exit("No input files")
+
+    test_files = []
+    for test_file in args:
+
+        test_parser = parser_class(test_file)
+        try:
+            test_files.append(test_parser.parse())
+        except IOError, ex:
+            sys.stderr.write("Error parsing %s: %s, skipping" % (test_file, ex))
+
+    generate_main(test_files)
+
+
+if __name__ == '__main__':
+    try:
+        main()
+    except IOError, ex:
+        sys.exit("IO Error: %s" % ex)
+    except KeyboardInterrupt:
+        sys.exit("Control-C interrupt")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/g.cpp	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,41 @@
+#include "g1.h"
+#include "g2.h"
+#include "g3.h"
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[])
+{
+    int retval = EXIT_SUCCESS;
+
+    {
+        sample_test sample_test_instance;
+        sample_test_instance.add_test("test42", &sample_test::test42);
+        sample_test_instance.add_test("beast", &sample_test::beast);
+        sample_test_instance.add_test("fivealive", &sample_test::fivealive);
+        const int r = sample_test_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        misc_tests misc_tests_instance;
+        misc_tests_instance.add_test("exceptions", &misc_tests::exceptions);
+        misc_tests_instance.add_test("loopdata", &misc_tests::loopdata);
+        misc_tests_instance.add_test("floatingpoint", &misc_tests::floatingpoint);
+        const int r = misc_tests_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        exception_test exception_test_instance;
+        exception_test_instance.add_test("array_bounds", &exception_test::array_bounds);
+        exception_test_instance.add_test("should_catch_std_exceptions", &exception_test::should_catch_std_exceptions);
+        const int r = exception_test_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        MyTest MyTest_instance;
+        const int r = MyTest_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+
+    return retval;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/g1.h	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,29 @@
+#ifndef FRUCTOSE_MAIN_TEST_X1_H
+#define FRUCTOSE_MAIN_TEST_X1_H
+
+#include "fructose/fructose.h"
+
+const int life_the_available_tests_and_everything = 42;
+const int the_neighbour_of_the_beast = 668;
+const int is_alive = 6;
+
+FRUCTOSE_CLASS(sample_test)
+{
+public:
+    FRUCTOSE_TEST(test42)
+    {
+        fructose_assert(life_the_available_tests_and_everything == 6*7);
+    }
+
+    FRUCTOSE_TEST(beast)
+    {
+        fructose_assert(the_neighbour_of_the_beast == 668);
+    }
+
+    FRUCTOSE_TEST(fivealive)
+    {
+        const int five = 5;
+        fructose_assert_eq(five, is_alive);
+    }
+};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/g2.h	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,68 @@
+#ifndef FRUCTOSE_MAIN_TEST_X2_H
+#define FRUCTOSE_MAIN_TEST_X2_H
+
+#include <stdexcept>
+#include <cmath>
+
+#include "fructose/fructose.h"
+
+// These tests are rigged so that some of them fail.
+// The tests include exercising floating point comparisons
+// and exception handling.
+
+namespace {
+    void throw_a_runtime_error()
+    {
+        throw std::runtime_error("this is a runtime error");
+    }
+}
+
+FRUCTOSE_STRUCT(misc_tests)
+{
+    FRUCTOSE_TEST(exceptions)
+    {
+        fructose_assert_exception(throw_a_runtime_error(), std::logic_error);
+        fructose_assert_no_exception(throw_a_runtime_error());
+    }
+
+    FRUCTOSE_TEST(loopdata)
+    {
+        static const struct {
+            int line_number;
+            int a;
+            int b;
+            int expected_result;
+        } data[] = {
+            {__LINE__, 3, 4, 12}
+          , {__LINE__, 1, 50, 50}
+          , {__LINE__, 5, 12, 60}
+          , {__LINE__, 6, 6, 37}
+          , {__LINE__, 7, 10, 70}
+        };
+
+        for (std::size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i)
+        {
+            if (verbose())
+            {
+                std::cout << "Testing to see if "
+                          << data[i].a << " * " << data[i].b 
+                          << " = " << data[i].expected_result
+                          << std::endl;
+            }
+            int result = data[i].a * data[i].b;
+            fructose_loop1_assert(data[i].line_number, i,
+                                 result == data[i].expected_result);
+        }
+    }
+
+    FRUCTOSE_TEST(floatingpoint)
+    {
+        double my_pi = 3.141592654;
+        double calc_pi = 4.0 * atan(1.0);
+        fructose_assert_double_eq_rel_abs(my_pi, calc_pi, 0.01, 0.01);
+        fructose_assert_double_eq(my_pi, calc_pi);
+        fructose_assert_double_ne(my_pi, calc_pi);
+        fructose_assert_double_ne_rel_abs(my_pi, calc_pi, 0.01, 0.01);
+    }
+};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/g3.h	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,64 @@
+#ifndef FRUCTOSE_MAIN_TEST_X3_H
+#define FRUCTOSE_MAIN_TEST_X3_H
+
+#include <stdexcept>
+#include <vector>
+
+#include "fructose/fructose.h"
+
+struct my_logic_error : public std::logic_error
+{
+    my_logic_error(const char* message) : std::logic_error(message) {}
+};
+
+struct my_runtime_error : public std::runtime_error
+{
+    my_runtime_error(const char* message) : std::runtime_error(message) {}
+};
+
+FRUCTOSE_CLASS(exception_test)
+{
+public:
+    FRUCTOSE_TEST(array_bounds)
+    {
+      std::vector<int> v;
+      v.push_back(1234);
+      fructose_assert_exception(v.at(2), std::out_of_range);
+    }
+
+    FRUCTOSE_TEST(should_catch_std_exceptions)
+    {
+        fructose_assert_exception(throw my_logic_error("a coding error has been detected"), 
+                                  std::logic_error);
+        fructose_assert_exception(throw my_runtime_error("my runtime error"),
+                                  std::runtime_error);
+        fructose_assert_exception(throw my_logic_error("another coding error has been detected"), 
+                                  std::exception);
+        fructose_assert_exception(throw my_runtime_error("my runtime error"),
+                                  std::exception);
+    }
+
+    //FRUCTOSE_TEST(commented_out)
+    //{
+    //   fructose_assert(false);
+    //}
+};
+
+// FRUCTOSE_CLASS(MyTest)
+// {
+// public:
+//    void testIt(const std::string&)
+//    {
+//       fructose_assert(true);
+//    }
+// };
+
+FRUCTOSE_CLASS(MyTest)
+{
+public:
+   void testIt(const std::string&)
+   {
+      fructose_assert(true);
+   }
+};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/g_expected.cpp	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,41 @@
+#include "g1.h"
+#include "g2.h"
+#include "g3.h"
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[])
+{
+    int retval = EXIT_SUCCESS;
+
+    {
+        sample_test sample_test_instance;
+        sample_test_instance.add_test("test42", &sample_test::test42);
+        sample_test_instance.add_test("beast", &sample_test::beast);
+        sample_test_instance.add_test("fivealive", &sample_test::fivealive);
+        const int r = sample_test_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        misc_tests misc_tests_instance;
+        misc_tests_instance.add_test("exceptions", &misc_tests::exceptions);
+        misc_tests_instance.add_test("loopdata", &misc_tests::loopdata);
+        misc_tests_instance.add_test("floatingpoint", &misc_tests::floatingpoint);
+        const int r = misc_tests_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        exception_test exception_test_instance;
+        exception_test_instance.add_test("array_bounds", &exception_test::array_bounds);
+        exception_test_instance.add_test("should_catch_std_exceptions", &exception_test::should_catch_std_exceptions);
+        const int r = exception_test_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        MyTest MyTest_instance;
+        const int r = MyTest_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+
+    return retval;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test.bash	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+INCLUDES="-I../../fructose/include -I../../tclap-1.2.0/include"
+
+../fructose_gen.py x1.h x2.h x3.h > x.cpp
+#g++ -o x.out $INCLUDES x.cpp
+
+../fructose_gen.py --generator g1.h g2.h g3.h > g.cpp
+#g++ -o g.out $INCLUDES g.cpp
+
+diff x_expected.cpp x.cpp >/dev/null
+if [ $? -eq 0 ]; then
+   echo "xunit style passed"
+else
+   echo "xunit style failed"
+fi
+
+diff g_expected.cpp g.cpp >/dev/null
+if [ $? -eq 0 ]; then
+   echo "generator style passed"
+else
+   echo "generator style failed"
+fi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/x.cpp	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,42 @@
+#include "x1.h"
+#include "x2.h"
+#include "x3.h"
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[])
+{
+    int retval = EXIT_SUCCESS;
+
+    {
+        sample_test sample_test_instance;
+        sample_test_instance.add_test("test42", &sample_test::test42);
+        sample_test_instance.add_test("testbeast", &sample_test::testbeast);
+        sample_test_instance.add_test("testfivealive", &sample_test::testfivealive);
+        const int r = sample_test_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        misc_tests misc_tests_instance;
+        misc_tests_instance.add_test("testexceptions", &misc_tests::testexceptions);
+        misc_tests_instance.add_test("testloopdata", &misc_tests::testloopdata);
+        misc_tests_instance.add_test("testfloatingpoint", &misc_tests::testfloatingpoint);
+        const int r = misc_tests_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        exception_test exception_test_instance;
+        exception_test_instance.add_test("test_array_bounds", &exception_test::test_array_bounds);
+        exception_test_instance.add_test("test_should_catch_std_exceptions", &exception_test::test_should_catch_std_exceptions);
+        const int r = exception_test_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        MyTest MyTest_instance;
+        MyTest_instance.add_test("testIt", &MyTest::testIt);
+        const int r = MyTest_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+
+    return retval;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/x1.h	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,28 @@
+#ifndef FRUCTOSE_MAIN_TEST_X1_H
+#define FRUCTOSE_MAIN_TEST_X1_H
+
+#include "fructose/fructose.h"
+
+const int life_the_available_tests_and_everything = 42;
+const int the_neighbour_of_the_beast = 668;
+const int is_alive = 6;
+
+struct sample_test : public fructose::test_base<sample_test>
+{
+    void test42(const std::string& test_name)
+    {
+        fructose_assert(life_the_available_tests_and_everything == 6*7);
+    }
+
+    void testbeast(const std::string& test_name)
+    {
+        fructose_assert(the_neighbour_of_the_beast == 668);
+    }
+
+    void testfivealive(const std::string& test_name)
+    {
+        const int five = 5;
+        fructose_assert_eq(five, is_alive);
+    }
+};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/x2.h	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,69 @@
+#ifndef FRUCTOSE_MAIN_TEST_X2_H
+#define FRUCTOSE_MAIN_TEST_X2_H
+
+#include <stdexcept>
+#include <cmath>
+
+#include "fructose/fructose.h"
+
+// These tests are rigged so that some of them fail.
+// The tests include exercising floating point comparisons
+// and exception handling.
+
+namespace {
+    void throw_a_runtime_error()
+    {
+        throw std::runtime_error("this is a runtime error");
+    }
+}
+
+class misc_tests : public fructose::test_base<misc_tests>
+{
+public:
+    void testexceptions(const std::string& test_name)
+    {
+        fructose_assert_exception(throw_a_runtime_error(), std::logic_error);
+        fructose_assert_no_exception(throw_a_runtime_error());
+    }
+
+    void testloopdata(const std::string& test_name)
+    {
+        static const struct {
+            int line_number;
+            int a;
+            int b;
+            int expected_result;
+        } data[] = {
+            {__LINE__, 3, 4, 12}
+          , {__LINE__, 1, 50, 50}
+          , {__LINE__, 5, 12, 60}
+          , {__LINE__, 6, 6, 37}
+          , {__LINE__, 7, 10, 70}
+        };
+
+        for (std::size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i)
+        {
+            if (verbose())
+            {
+                std::cout << "Testing to see if "
+                          << data[i].a << " * " << data[i].b 
+                          << " = " << data[i].expected_result
+                          << std::endl;
+            }
+            int result = data[i].a * data[i].b;
+            fructose_loop1_assert(data[i].line_number, i,
+                                 result == data[i].expected_result);
+        }
+    }
+
+    void testfloatingpoint(const std::string& test_name)
+    {
+        double my_pi = 3.141592654;
+        double calc_pi = 4.0 * atan(1.0);
+        fructose_assert_double_eq_rel_abs(my_pi, calc_pi, 0.01, 0.01);
+        fructose_assert_double_eq(my_pi, calc_pi);
+        fructose_assert_double_ne(my_pi, calc_pi);
+        fructose_assert_double_ne_rel_abs(my_pi, calc_pi, 0.01, 0.01);
+    }
+};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/x3.h	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,63 @@
+#ifndef FRUCTOSE_MAIN_TEST_X3_H
+#define FRUCTOSE_MAIN_TEST_X3_H
+
+#include <stdexcept>
+#include <vector>
+
+#include "fructose/fructose.h"
+
+struct my_logic_error : public std::logic_error
+{
+    my_logic_error(const char* message) : std::logic_error(message) {}
+};
+
+struct my_runtime_error : public std::runtime_error
+{
+    my_runtime_error(const char* message) : std::runtime_error(message) {}
+};
+
+struct exception_test : public fructose::test_base<exception_test> 
+{
+    void test_array_bounds(const std::string&) 
+    {
+      std::vector<int> v;
+      v.push_back(1234);
+      fructose_assert_exception(v.at(2), std::out_of_range);
+    }
+
+    void test_should_catch_std_exceptions(const std::string& )
+    {
+        fructose_assert_exception(throw my_logic_error("a coding error has been detected"), 
+                                  std::logic_error);
+        fructose_assert_exception(throw my_runtime_error("my runtime error"),
+                                  std::runtime_error);
+        fructose_assert_exception(throw my_logic_error("another coding error has been detected"), 
+                                  std::exception);
+        fructose_assert_exception(throw my_runtime_error("my runtime error"),
+                                  std::exception);
+    }
+
+    //void test_commented_out(const std::string&)
+    //{
+    //   fructose_assert(false);
+    //}
+};
+
+// class MyTest : public fructose::test_base<MyTest>
+// {
+// public:
+//    void testIt(const std::string&)
+//    {
+//       fructose_assert(true);
+//    }
+//  };
+
+class MyTest : public fructose::test_base<MyTest>
+{
+public:
+   void testIt(const std::string&)
+   {
+      fructose_assert(true);
+   }
+};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/x_expected.cpp	Sat Mar 19 19:53:12 2011 -0500
@@ -0,0 +1,42 @@
+#include "x1.h"
+#include "x2.h"
+#include "x3.h"
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[])
+{
+    int retval = EXIT_SUCCESS;
+
+    {
+        sample_test sample_test_instance;
+        sample_test_instance.add_test("test42", &sample_test::test42);
+        sample_test_instance.add_test("testbeast", &sample_test::testbeast);
+        sample_test_instance.add_test("testfivealive", &sample_test::testfivealive);
+        const int r = sample_test_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        misc_tests misc_tests_instance;
+        misc_tests_instance.add_test("testexceptions", &misc_tests::testexceptions);
+        misc_tests_instance.add_test("testloopdata", &misc_tests::testloopdata);
+        misc_tests_instance.add_test("testfloatingpoint", &misc_tests::testfloatingpoint);
+        const int r = misc_tests_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        exception_test exception_test_instance;
+        exception_test_instance.add_test("test_array_bounds", &exception_test::test_array_bounds);
+        exception_test_instance.add_test("test_should_catch_std_exceptions", &exception_test::test_should_catch_std_exceptions);
+        const int r = exception_test_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+    {
+        MyTest MyTest_instance;
+        MyTest_instance.add_test("testIt", &MyTest::testIt);
+        const int r = MyTest_instance.run(argc, argv);
+        retval = retval == EXIT_SUCCESS ? r : EXIT_FAILURE;
+    }
+
+    return retval;
+}