BoostTestUi

BoostTestUi is a freeware Windows based unit test runner for the boost.test, Google Test and NUnit frameworks. boost.test and Google Test project require an alternative header file to support BoostTestUi, NUnit test assemblies can run unmodified.
BoostTestUi

Features

  • Runs boost.test, Google Test and
    NUnit based unit tests
  • Runs both 32 bit and 64 bit unit tests
  • Option to wait for attaching a debugger at test executable startup
  • Repeat until fail
  • Run test cases in random order
  • Auto run tests after rebuild
  • Double click log line to open source file reference in Visual Studio editor

Toolbar

Open Load a unit test executable. Unit tests can also be loaded from the File -> Open, File -> Recent Tests or by dragging a unit test executable file onto the BoostTestUi window.

SaveSave the test log as a text file.

ClearClear the test log.

ReloadReset the test case selection.

ClockToggle test log time stamps between test run time or clock time.

CopyCopy the test log to the clipboard.

RandomizeWhen enabled, randomises the test case execution order.

RepeatWhen enabled, runs all selected test cases until a test fails.

WaitWhen enabled, the test executable allows you to attach a debugger before executing the tests.

RunRun all selected tests in the unit test. Choose Run from the test case tree view context menu to run single test suites or test cases.

AbortAbort the running test.

LogLevelSelect the amount of test log detail.

Changelog

1.0.0 28 September 2013

  • Initial release

1.0.1 16 February 2015

  • Added support for NUnit TestFixture attributes with constructor arguments
  • Sort NUnit tests alphetically

1.0.2 21 February 2015

  • Added support for NUnit Ignored tests
  • NUnit help dialog added

1.0.3 28 April 2015

  • Find in toolbar
  • NUnit: Don’t interpret setUp/tearDown methods named TestXxx as testcases

1.0.4 2 June 2015

  • Support for GTest parameterised test cases

1.0.5 7 October 2015

  • Support for GTest 1.7.0

1.0.6 20 October 2015

  • Running an empty test tree now runs nothing instead of all tests

1.0.7 27 December 2015

  • Support for updated boost.test in boost 1.59.

1.0.8 7 February 2016

  • unit_test_gui.hpp in 1.0.7 didn’t work any more with boost 1.58 and before. Fixed
  • Fixed Unsupported Unit Test Type on startup
  • Improved error message when test executable can not start

1.0.9 8 September 2016

  • Fixed problems with NUnit assemblies in a path with spaces.

1.1.0 26 November 2016

  • Added support for the Catch C++ test framework.

1.1.1 20 December 2016

  • Report failures during gtest test setup.

1.1.2 25 June 2017

  • Auto attach debugger if available.
  • Visual Studio selection dialog added.
  • Fixed logging of &.

1.1.3 25 June 2017

  • Support gtest 1.8.0.

1.1.4 14 September 2017

  • Add NUnit TestCaseAttribute support.
  • Add debug type selection.

1.1.5 17 January 2018

  • Select single test case.

Download

BoostTestUi runs on 32 bit and 64 bit versions of Windows XP and later. The installer installs BoostTestUi in the Start menu. The zip file download contains just the executables that you can copy and run anywhere.

Win32 zip file download: BoostTestUi-win32-1.1.5.zip (434 kB)

Win32 installer download: BoostTestUi-win32-1.1.5.msi (492 kB)

Author

Gert-Jan de Vos

2 thoughts on “BoostTestUi

  1. Rainer Kottenhoff

    Hi Jan,
    i appreciate your work – thanx a lot.
    Below, you will find an adapted Boost header “unit_test_gui.hpp” to be able to link against Boost’s dynamic link library versions also.
    Feel free to distribute it 🙂

    Best regards,
    Rainer

    // (C) Copyright Gert-Jan de Vos 2012.
    // Distributed under the Boost Software License, Version 1.0.
    // (See accompanying file LICENSE_1_0.txt or copy at
    // http://www.boost.org/LICENSE_1_0.txt)

    // See https://boosttestui.wordpress.com/ for the boosttestui home page.

    #ifndef BOOST_TEST_UNIT_TEST_GUI_HPP
    #define BOOST_TEST_UNIT_TEST_GUI_HPP

    // auto-link against dynamic libraries
    //#define BOOST_ALL_DYN_LINK
    #ifdef _DEBUG
    #define BOOST_LIB_DIAGNOSTIC
    #endif

    // we will define our own main() to support dynamic linking with boost_test_included
    #define BOOST_TEST_NO_MAIN

    #ifndef BOOST_TEST_NO_GUI_INIT
    #ifdef BOOST_TEST_MODULE
    #define BOOST_TEST_MODULE_GUI BOOST_TEST_MODULE
    // BOOST_TEST_MODULE must be undefined, otherwise
    // boost implements internal init_unit_test_suite(int argc, *argv[])
    #undef BOOST_TEST_MODULE
    #endif

    #endif //BOOST_TEST_NO_GUI_INIT

    // standard unit test include
    #include

    #ifdef BOOST_TEST_MODULE_GUI

    #if BOOST_VERSION > 105400
    # include
    #endif
    #include
    #include
    #include

    #include
    #include

    namespace boost {
    namespace unit_test {
    namespace gui {

    #if BOOST_VERSION <= 105400

    const test_unit_type TUT_ANY = tut_any;
    const test_unit_type TUT_CASE = tut_case;
    const test_unit_type TUT_SUITE = tut_suite;

    #endif

    class test_suite_access : public test_suite
    {
    public:
    const std::vector& members() const
    {
    return m_members;
    }

    private:
    test_suite_access();
    };

    class test_tree_reporter : public test_tree_visitor
    {
    public:
    test_tree_reporter() : m_indent(0)
    {
    }

    private:
    virtual void visit(test_case const& tc)
    {
    std::cout << std::setw(m_indent) << "" << (tc.p_enabled? 'C': 'c') << tc.p_id << ":" << tc.p_name <= 0)
    std::cout << std::setw(m_indent) << "" << (ts.p_enabled? 'S': 's') << ts.p_id << ":" << ts.p_name << "\n";
    m_indent += 4;
    return true;
    }

    virtual void test_suite_finish(test_suite const&)
    {
    m_indent -= 4;
    }

    int m_indent;
    };

    class gui_observer : public test_observer
    {
    public:
    virtual void test_start(counter_t test_cases_amount)
    {
    std::cout << "#start " << test_cases_amount << std::endl;
    }

    virtual void test_finish()
    {
    std::cout << "#finish" << std::endl;
    }

    virtual void test_aborted()
    {
    std::cout << "#aborted" << std::endl;
    }

    virtual void test_unit_start(test_unit const& tu)
    {
    std::cout << "#unit_start " << tu.p_id << std::endl;
    }

    virtual void test_unit_finish(test_unit const& tu, unsigned long elapsed)
    {
    std::cout << "#unit_finish " << tu.p_id << " " << elapsed << std::endl;
    }

    virtual void test_unit_skipped(test_unit const& tu)
    {
    std::cout << "#unit_skipped " << tu.p_id << std::endl;
    }

    virtual void test_unit_aborted(test_unit const& tu)
    {
    std::cout << "#unit_aborted " << tu.p_id << std::endl;
    }

    virtual void assertion_result(bool passed)
    {
    std::cout << "#assertion " << passed << std::endl;
    }

    virtual void exception_caught(boost::execution_exception const& e)
    {
    std::cout << "#exception " << e.what() << std::endl;
    }
    };

    class test_tree_enabler : public test_tree_visitor
    {
    public:
    explicit test_tree_enabler(const std::string& enableArg) :
    m_arg(enableArg), m_it(m_arg.begin())
    {
    }

    private:
    void enable(test_unit_id id)
    {
    bool enable = m_it != m_arg.end() && (*m_it++ == '1');
    framework::get(id, TUT_ANY).p_enabled.set(enable);
    }

    virtual void visit(test_case const& tc)
    {
    enable(tc.p_id);
    }

    virtual bool test_suite_start(test_suite const& ts)
    {
    enable(ts.p_id);
    return true;
    }

    private:
    std::string m_arg;
    std::string::const_iterator m_it;
    };

    static const char* gui_list = "gui_list";
    static const char* gui_run = "gui_run";
    static const char* gui_wait = "gui_wait";

    class runner
    {
    public:
    runner(int& argc, char** argv)
    {
    namespace cla = boost::runtime::cla;

    m_args – cla::ignore_mismatch
    << cla::named_parameter(gui_list) – (cla::prefix = “–“, cla::optional)
    << cla::named_parameter(gui_run) – (cla::prefix = “–“, cla::separator = “=”, cla::optional)
    << cla::named_parameter(gui_wait) – (cla::prefix = “–“, cla::optional);
    m_args.parse(argc, argv);
    }

    void operator()()
    {
    if (m_args[gui_list])
    list();
    if (m_args[gui_run])
    run(m_args.get(gui_run));
    if (m_args[gui_wait])
    wait();
    }

    static void list()
    {
    test_tree_reporter reporter;
    traverse_test_tree(framework::master_test_suite(), reporter);
    // throw std::runtime_error(“No tests”); // “Test setup error: std::runtime_error: No tests”
    throw framework::nothing_to_test(); // What –help does: “Test setup error: unknown type”
    }

    static void run(const std::string& enableArg)
    {
    test_tree_enabler enabler(enableArg);
    traverse_test_tree(framework::master_test_suite(), enabler);

    static gui_observer observer;
    framework::register_observer(observer);
    }

    static void wait()
    {
    std::cout << "#waiting" << std::endl;
    std::getchar();
    }

    static void traverse_test_tree(test_case const& tc, test_tree_visitor& v)
    {
    v.visit(tc);
    }

    static void traverse_test_tree(test_suite const& suite, test_tree_visitor& v)
    {
    if (!v.test_suite_start(suite))
    return;

    BOOST_TEST_FOREACH(test_unit_id, id, static_cast(suite).members())
    traverse_test_tree(id, v);

    v.test_suite_finish(suite);
    }

    static void traverse_test_tree(test_unit_id id, test_tree_visitor& v)
    {
    if (ut_detail::test_id_2_unit_type(id) == TUT_CASE)
    traverse_test_tree(framework::get(id), v);
    else
    traverse_test_tree(framework::get(id), v);
    }

    private:
    boost::runtime::cla::parser m_args;
    };

    } // namespace gui

    inline test_case* enable_test_case(test_case* tc, bool enable)
    {
    tc->p_enabled.value = enable;
    return tc;
    }

    } // namespace unit_test
    } // namespace boost

    #ifndef BOOST_TEST_NO_GUI_INIT

    #include
    boost::unit_test::gui::runner gGuiRunner(__argc, __argv);

    #pragma warning(push)
    #pragma warning(disable: 4100) // unreferenced formal parameter

    #ifdef BOOST_TEST_ALTERNATIVE_INIT_API
    bool init_unit_test()
    #else
    boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[])
    #endif
    {
    using namespace boost::unit_test;
    //unit_test_log.set_stream( gTestOutputStream );

    assign_op( framework::master_test_suite().p_name.value, BOOST_TEST_STRINGIZE( BOOST_TEST_MODULE_GUI ).trim( “\”” ), 0 );
    gGuiRunner();

    #ifdef BOOST_TEST_ALTERNATIVE_INIT_API
    return true;
    #else
    return nullptr;
    #endif
    }

    // —————————————————————————
    // the test module main() entry point
    // —————————————————————————
    #if defined(BOOST_TEST_DYN_LINK) && defined(BOOST_TEST_NO_MAIN)

    int main( int argc, char* argv[] )
    {
    #ifdef BOOST_TEST_ALTERNATIVE_INIT_API
    return ::boost::unit_test::unit_test_main( &init_unit_test, argc, argv );
    #else
    return ::boost::unit_test::unit_test_main( &init_unit_test_suite, argc, argv );
    #endif
    }
    #endif //BOOST_TEST_DYN_LINK && BOOST_TEST_NO_MAIN

    #pragma warning(pop)

    // —————————————————————————

    extern “C” __declspec(dllexport) inline void unit_test_type_boost()
    {
    }

    #endif //BOOST_TEST_NO_GUI_INIT

    #endif //BOOST_TEST_MODULE_GUI

    // —————————————————————————

    #define BOOST_FIXTURE_TEST_CASE_ENABLE(test_name, F, enable) \
    struct test_name : public F { void test_method(); }; \
    \
    static void BOOST_AUTO_TC_INVOKER( test_name )() \
    { \
    test_name t; \
    t.test_method(); \
    } \
    \
    struct BOOST_AUTO_TC_UNIQUE_ID( test_name ) {}; \
    \
    BOOST_AUTO_TU_REGISTRAR( test_name )( \
    boost::unit_test::enable_test_case( \
    boost::unit_test::make_test_case( \
    &BOOST_AUTO_TC_INVOKER( test_name ), #test_name ), enable ), \
    boost::unit_test::ut_detail::auto_tc_exp_fail::instance()->value() ); \
    \
    void test_name::test_method() \

    #define BOOST_AUTO_TEST_CASE_ENABLE(test_name, enable) \
    BOOST_FIXTURE_TEST_CASE_ENABLE(test_name, BOOST_AUTO_TEST_CASE_FIXTURE, enable)

    // —————————————————————————

    #endif // BOOST_TEST_UNIT_TEST_GUI_HPP

    Reply
  2. Tim Benke

    Thanks a lot for your work, Gert-Jan! I’ve been searching for a long time for a decent GUI to use with google test, because we’re using VS 2008 and you can’t use the google test adapter with that version.

    Reply

Leave a comment