From 55b88bead7a1a178fbfad57b7f6bd0c236d98a6d Mon Sep 17 00:00:00 2001 From: Leonid Shalupov Date: Tue, 6 Dec 2011 17:50:23 +0400 Subject: [PATCH] initial import --- Makefile | 52 ++++++++ VERSION | 1 + boost/PARAM_TEST_CASES.txt | 134 ++++++++++++++++++++ boost/README.txt | 32 +++++ boost/boost_test.cpp | 84 +++++++++++++ boost/boost_test.output.flowId.gold | 102 +++++++++++++++ boost/boost_test.output.gold | 102 +++++++++++++++ boost/teamcity_boost.cpp | 157 ++++++++++++++++++++++++ common/teamcity_messages.cpp | 150 ++++++++++++++++++++++ common/teamcity_messages.h | 55 +++++++++ cppunit/README.txt | 30 +++++ cppunit/cppunit_test.cpp | 92 ++++++++++++++ cppunit/cppunit_test.output.flowId.gold | 46 +++++++ cppunit/cppunit_test.output.gold | 46 +++++++ cppunit/example.cpp | 67 ++++++++++ cppunit/teamcity_cppunit.cpp | 86 +++++++++++++ cppunit/teamcity_cppunit.h | 55 +++++++++ 17 files changed, 1291 insertions(+) create mode 100644 Makefile create mode 100644 VERSION create mode 100644 boost/PARAM_TEST_CASES.txt create mode 100644 boost/README.txt create mode 100644 boost/boost_test.cpp create mode 100644 boost/boost_test.output.flowId.gold create mode 100644 boost/boost_test.output.gold create mode 100644 boost/teamcity_boost.cpp create mode 100644 common/teamcity_messages.cpp create mode 100644 common/teamcity_messages.h create mode 100644 cppunit/README.txt create mode 100644 cppunit/cppunit_test.cpp create mode 100644 cppunit/cppunit_test.output.flowId.gold create mode 100644 cppunit/cppunit_test.output.gold create mode 100644 cppunit/example.cpp create mode 100644 cppunit/teamcity_cppunit.cpp create mode 100644 cppunit/teamcity_cppunit.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2b837dc --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ +TEAMCITY_PROJECT_NAME = test +export TEAMCITY_PROJECT_NAME + +COMMON_FILES = common/teamcity_messages.cpp common/teamcity_messages.h +BOOST_FILES = $(COMMON_FILES) \ + boost/teamcity_boost.cpp \ + boost/boost_test.cpp +CPPUNIT_FILES = $(COMMON_FILES) \ + cppunit/teamcity_cppunit.cpp cppunit/teamcity_cppunit.h \ + cppunit/cppunit_test.cpp + +.PHONY: all test dist + +all: test + +CXXFLAGS := -Icommon -g -O0 + +boost_test: $(BOOST_FILES) + g++ $(CXXFLAGS) -DBOOST_TEST_DYN_LINK -o \ + $@ $(filter %.cpp, $(BOOST_FILES)) -lboost_unit_test_framework-mt + +cppunit_test: $(CPPUNIT_FILES) + g++ $(CXXFLAGS) -o \ + $@ $(filter %.cpp, $(CPPUNIT_FILES)) -lcppunit + +BOOST_OUTPUT = boost/boost_test.output +CPPUNIT_OUTPUT = cppunit/cppunit_test.output + +test: boost_test cppunit_test + ./boost_test >$(BOOST_OUTPUT).tmp 2>&1 ||: + diff -Nru $(BOOST_OUTPUT).gold $(BOOST_OUTPUT).tmp && rm -f $(BOOST_OUTPUT).tmp + + TEAMCITY_PROCESS_FLOW_ID=myFlowId ./boost_test >$(BOOST_OUTPUT).flowId.tmp 2>&1 ||: + diff -Nru $(BOOST_OUTPUT).flowId.gold $(BOOST_OUTPUT).flowId.tmp && rm -f $(BOOST_OUTPUT).flowId.tmp + + ./cppunit_test >$(CPPUNIT_OUTPUT).tmp 2>&1 ||: + diff -Nru $(CPPUNIT_OUTPUT).gold $(CPPUNIT_OUTPUT).tmp && rm -f $(CPPUNIT_OUTPUT).tmp + + TEAMCITY_PROCESS_FLOW_ID=myFlowId ./cppunit_test >$(CPPUNIT_OUTPUT).flowId.tmp 2>&1 ||: + diff -Nru $(CPPUNIT_OUTPUT).flowId.gold $(CPPUNIT_OUTPUT).flowId.tmp && rm -f $(CPPUNIT_OUTPUT).flowId.tmp + + @echo "<<< Tests OK >>>" + +VERSION = $(shell cat VERSION) + +dist: + rm -f teamcity-*-$(VERSION).zip + zip -rj9 teamcity-cppunit-$(VERSION).zip \ + cppunit/teamcity_cppunit.* common/teamcity_messages.* \ + cppunit/example.cpp cppunit/README.txt + zip -rj9 teamcity-boost-$(VERSION).zip \ + boost/teamcity_boost.* common/teamcity_messages.* boost/README.txt diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..7e32cd5 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.3 diff --git a/boost/PARAM_TEST_CASES.txt b/boost/PARAM_TEST_CASES.txt new file mode 100644 index 0000000..9bbdce8 --- /dev/null +++ b/boost/PARAM_TEST_CASES.txt @@ -0,0 +1,134 @@ +If you have tests with test parameters, +look at http://jetbrains.net/tracker/issue/TW-7043 for quick solution. + +It's proposed to replace standard parameterized_test.hpp with modified one - it +creates a unique name for each test from the range if you use +BOOST_NAMED_PARAM_TEST_CASE macro. + +By default a per-param type sequence is used to create name, +e.g. test(#1), test(#2), ... but you can alter name generation by +providing your our compose_test_case_name() function, or by providing +to_string overloads. Note that any of your custom overloads should reside +in the boost::unit_test namespace. + +Thanks Kirill Kovalenko for the workaround. + +parameterized_test.hpp +---------------------- + +#pragma once + +#include +#include + +#define BOOST_NAMED_PARAM_TEST_CASE( function, begin, end ) \ + boost::unit_test::make_named_test_case( function, \ + BOOST_TEST_STRINGIZE( function ), \ + (begin), (end) ) \ + +namespace boost { + +namespace unit_test { + +template +inline std::string to_string(const ParamType& t) +{ + static unsigned int counter = 0; + std::stringstream ss; + ss << "#" << ++counter; + return ss.str(); +} + +template +inline std::string compose_test_case_name(boost::unit_test::const_string name, const ParamType& t) +{ + std::stringstream ss; + ss << name << "(\"" << to_string(t) << "\")"; + return ss.str(); +} + +namespace ut_detail { + +template +class named_param_test_case_generator : public test_unit_generator { +public: + named_param_test_case_generator( + callback1 const& test_func, + const_string tc_name, + ParamIter par_begin, + ParamIter par_end ) + : m_test_func( test_func ) + , m_tc_name( ut_detail::normalize_test_case_name( tc_name ) ) + , m_par_begin( par_begin ) + , m_par_end( par_end ) + {} + + boost::unit_test::test_unit* next() const + { + if( m_par_begin == m_par_end ) + return (test_unit*)0; + + boost::unit_test::ut_detail::test_func_with_bound_param bound_test_func( m_test_func, *m_par_begin ); + test_unit* res = new test_case( compose_test_case_name(m_tc_name, *m_par_begin), bound_test_func ); + + ++m_par_begin; + + return res; + } + +private: + // Data members + callback1 m_test_func; + std::string m_tc_name; + mutable ParamIter m_par_begin; + ParamIter m_par_end; +}; + +} // ut_detail + +template +inline ut_detail::named_param_test_case_generator +make_named_test_case( callback1 const& test_func, + const_string tc_name, + ParamIter par_begin, + ParamIter par_end ) +{ + return ut_detail::named_param_test_case_generator( test_func, tc_name, par_begin, par_end ); +} + +//____________________________________________________________________________// + +template +inline ut_detail::named_param_test_case_generator< + BOOST_DEDUCED_TYPENAME remove_const::type>::type,ParamIter> +make_named_test_case( void (*test_func)( ParamType ), + const_string tc_name, + ParamIter par_begin, + ParamIter par_end ) +{ + typedef BOOST_DEDUCED_TYPENAME remove_const::type>::type param_value_type; + return ut_detail::named_param_test_case_generator( test_func, tc_name, par_begin, par_end ); +} + +//____________________________________________________________________________// + +template +inline ut_detail::named_param_test_case_generator< + BOOST_DEDUCED_TYPENAME remove_const::type>::type,ParamIter> +make_named_test_case( void (UserTestCase::*test_method )( ParamType ), + const_string tc_name, + boost::shared_ptr const& user_test_case, + ParamIter par_begin, + ParamIter par_end ) +{ + typedef BOOST_DEDUCED_TYPENAME remove_const::type>::type param_value_type; + return ut_detail::named_param_test_case_generator( + ut_detail::user_param_tc_method_invoker( user_test_case, test_method ), + tc_name, + par_begin, + par_end ); +} + +} // namespace unit_test + +} // boost diff --git a/boost/README.txt b/boost/README.txt new file mode 100644 index 0000000..d96b23c --- /dev/null +++ b/boost/README.txt @@ -0,0 +1,32 @@ +Boost listener for TeamCity +--------------------------- + +To report your tests result to TeamCity server +just include teamcity_messages.* teamcity_boost.cpp +to your project. + +That code will register global fixture +( http://www.boost.org/doc/libs/1_38_0/libs/test/doc/html/utf/user-guide/fixture/global.html ) +to replace output formatter if run under TeamCity. + +If you have tests with test parameters, see PARAM_TEST_CASES.txt for quick solution. + +Technical details +----------------- + +Reporting implemented as writing TeamCity service messages to stdout. + +See +http://www.jetbrains.net/confluence/display/TCD3/Build+Script+Interaction+with+TeamCity +for more details. + +Contact information +------------------- + +See http://www.jetbrains.com/support/teamcity + +License +------- + +Apache, version 2.0 +http://www.apache.org/licenses/LICENSE-2.0 diff --git a/boost/boost_test.cpp b/boost/boost_test.cpp new file mode 100644 index 0000000..a136de5 --- /dev/null +++ b/boost/boost_test.cpp @@ -0,0 +1,84 @@ +#define BOOST_TEST_MAIN + +#include + +#include + +using namespace std; + +/* Suite tree tests */ +BOOST_AUTO_TEST_SUITE(my_suite1) + +BOOST_AUTO_TEST_CASE(my_test1) { + BOOST_CHECK(2 == 1); +} + +BOOST_AUTO_TEST_CASE(my_test2) { + int i = 0; + + BOOST_CHECK_EQUAL(i, 2); + BOOST_CHECK_EQUAL(i, 0); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_CASE(my_test3) { + int i = 0; + + BOOST_CHECK_EQUAL(i, 0); +} + +BOOST_AUTO_TEST_SUITE(my_suite2) + +BOOST_AUTO_TEST_CASE(my_test4) { + int i = 0; + + BOOST_CHECK_EQUAL(i, 1); +} + +BOOST_AUTO_TEST_SUITE(internal_suite) + +BOOST_AUTO_TEST_CASE(my_test5) { + int i = 0; + + BOOST_CHECK_EQUAL( i, 1 ); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() + +/* Other */ +BOOST_AUTO_TEST_CASE(testCerr) { + cerr << "Hello from cerr" << endl; +} + +BOOST_AUTO_TEST_CASE(testCout) { + cerr << "Hello from cout" << endl; +} + +static void ThrowRuntimeError() { + throw runtime_error("runtime exception text"); +} + +BOOST_AUTO_TEST_CASE(testException) { + ThrowRuntimeError(); +} + +BOOST_AUTO_TEST_CASE(testAssertExceptionGood) { + BOOST_CHECK_THROW(ThrowRuntimeError(), runtime_error); +} + +BOOST_AUTO_TEST_CASE(testAssertExceptionFail) { + BOOST_CHECK_THROW(ThrowRuntimeError(), logic_error); +} + +BOOST_AUTO_TEST_CASE(testFatal) { + BOOST_FAIL("bfail"); +} + +BOOST_AUTO_TEST_CASE(testError) { + BOOST_ERROR("berror"); +} + +BOOST_AUTO_TEST_CASE(testNothing) {} diff --git a/boost/boost_test.output.flowId.gold b/boost/boost_test.output.flowId.gold new file mode 100644 index 0000000..fa25157 --- /dev/null +++ b/boost/boost_test.output.flowId.gold @@ -0,0 +1,102 @@ + +##teamcity[testSuiteStarted name='Master Test Suite' flowId='myFlowId'] + +##teamcity[testSuiteStarted name='my_suite1' flowId='myFlowId'] + +##teamcity[testStarted name='my_test1' flowId='myFlowId'] +check 2 == 1 failed + +##teamcity[testFailed name='my_test1' message='failed' details='check 2 == 1 failed|n' flowId='myFlowId'] + +##teamcity[testFinished name='my_test1' flowId='myFlowId' duration='0'] + +##teamcity[testStarted name='my_test2' flowId='myFlowId'] +check i == 2 failed [0 != 2] +check i == 0 passed + +##teamcity[testFailed name='my_test2' message='failed' details='check i == 2 failed [0 != 2|]|ncheck i == 0 passed|n' flowId='myFlowId'] + +##teamcity[testFinished name='my_test2' flowId='myFlowId' duration='0'] + +##teamcity[testSuiteFinished name='my_suite1' flowId='myFlowId'] + +##teamcity[testStarted name='my_test3' flowId='myFlowId'] +check i == 0 passed + +##teamcity[testFinished name='my_test3' flowId='myFlowId' duration='0'] + +##teamcity[testSuiteStarted name='my_suite2' flowId='myFlowId'] + +##teamcity[testStarted name='my_test4' flowId='myFlowId'] +check i == 1 failed [0 != 1] + +##teamcity[testFailed name='my_test4' message='failed' details='check i == 1 failed [0 != 1|]|n' flowId='myFlowId'] + +##teamcity[testFinished name='my_test4' flowId='myFlowId' duration='0'] + +##teamcity[testSuiteStarted name='internal_suite' flowId='myFlowId'] + +##teamcity[testStarted name='my_test5' flowId='myFlowId'] +check i == 1 failed [0 != 1] + +##teamcity[testFailed name='my_test5' message='failed' details='check i == 1 failed [0 != 1|]|n' flowId='myFlowId'] + +##teamcity[testFinished name='my_test5' flowId='myFlowId' duration='0'] + +##teamcity[testSuiteFinished name='internal_suite' flowId='myFlowId'] + +##teamcity[testSuiteFinished name='my_suite2' flowId='myFlowId'] + +##teamcity[testStarted name='testCerr' flowId='myFlowId'] +Hello from cerr +Test case testCerr did not check any assertions + +##teamcity[testFinished name='testCerr' flowId='myFlowId' duration='0'] + +##teamcity[testStarted name='testCout' flowId='myFlowId'] +Hello from cout +Test case testCout did not check any assertions + +##teamcity[testFinished name='testCout' flowId='myFlowId' duration='0'] + +##teamcity[testStarted name='testException' flowId='myFlowId'] +std::runtime_error: runtime exception text + +##teamcity[testFailed name='testException' message='aborted' details='std::runtime_error: runtime exception text|n' flowId='myFlowId'] + +##teamcity[testFinished name='testException' flowId='myFlowId' duration='0'] + +##teamcity[testStarted name='testAssertExceptionGood' flowId='myFlowId'] +check 'exception runtime_error is caught' passed + +##teamcity[testFinished name='testAssertExceptionGood' flowId='myFlowId' duration='0'] + +##teamcity[testStarted name='testAssertExceptionFail' flowId='myFlowId'] +std::runtime_error: runtime exception text + +##teamcity[testFailed name='testAssertExceptionFail' message='aborted' details='std::runtime_error: runtime exception text|n' flowId='myFlowId'] + +##teamcity[testFinished name='testAssertExceptionFail' flowId='myFlowId' duration='0'] + +##teamcity[testStarted name='testFatal' flowId='myFlowId'] +bfail + +##teamcity[testFailed name='testFatal' message='aborted' details='bfail|n' flowId='myFlowId'] + +##teamcity[testFinished name='testFatal' flowId='myFlowId' duration='0'] + +##teamcity[testStarted name='testError' flowId='myFlowId'] +berror + +##teamcity[testFailed name='testError' message='failed' details='berror|n' flowId='myFlowId'] + +##teamcity[testFinished name='testError' flowId='myFlowId' duration='0'] + +##teamcity[testStarted name='testNothing' flowId='myFlowId'] +Test case testNothing did not check any assertions + +##teamcity[testFinished name='testNothing' flowId='myFlowId' duration='0'] + +##teamcity[testSuiteFinished name='Master Test Suite' flowId='myFlowId'] + +*** 8 failures detected in test suite "Master Test Suite" diff --git a/boost/boost_test.output.gold b/boost/boost_test.output.gold new file mode 100644 index 0000000..f636129 --- /dev/null +++ b/boost/boost_test.output.gold @@ -0,0 +1,102 @@ + +##teamcity[testSuiteStarted name='Master Test Suite'] + +##teamcity[testSuiteStarted name='my_suite1'] + +##teamcity[testStarted name='my_test1'] +check 2 == 1 failed + +##teamcity[testFailed name='my_test1' message='failed' details='check 2 == 1 failed|n'] + +##teamcity[testFinished name='my_test1' duration='0'] + +##teamcity[testStarted name='my_test2'] +check i == 2 failed [0 != 2] +check i == 0 passed + +##teamcity[testFailed name='my_test2' message='failed' details='check i == 2 failed [0 != 2|]|ncheck i == 0 passed|n'] + +##teamcity[testFinished name='my_test2' duration='0'] + +##teamcity[testSuiteFinished name='my_suite1'] + +##teamcity[testStarted name='my_test3'] +check i == 0 passed + +##teamcity[testFinished name='my_test3' duration='0'] + +##teamcity[testSuiteStarted name='my_suite2'] + +##teamcity[testStarted name='my_test4'] +check i == 1 failed [0 != 1] + +##teamcity[testFailed name='my_test4' message='failed' details='check i == 1 failed [0 != 1|]|n'] + +##teamcity[testFinished name='my_test4' duration='0'] + +##teamcity[testSuiteStarted name='internal_suite'] + +##teamcity[testStarted name='my_test5'] +check i == 1 failed [0 != 1] + +##teamcity[testFailed name='my_test5' message='failed' details='check i == 1 failed [0 != 1|]|n'] + +##teamcity[testFinished name='my_test5' duration='0'] + +##teamcity[testSuiteFinished name='internal_suite'] + +##teamcity[testSuiteFinished name='my_suite2'] + +##teamcity[testStarted name='testCerr'] +Hello from cerr +Test case testCerr did not check any assertions + +##teamcity[testFinished name='testCerr' duration='0'] + +##teamcity[testStarted name='testCout'] +Hello from cout +Test case testCout did not check any assertions + +##teamcity[testFinished name='testCout' duration='0'] + +##teamcity[testStarted name='testException'] +std::runtime_error: runtime exception text + +##teamcity[testFailed name='testException' message='aborted' details='std::runtime_error: runtime exception text|n'] + +##teamcity[testFinished name='testException' duration='0'] + +##teamcity[testStarted name='testAssertExceptionGood'] +check 'exception runtime_error is caught' passed + +##teamcity[testFinished name='testAssertExceptionGood' duration='0'] + +##teamcity[testStarted name='testAssertExceptionFail'] +std::runtime_error: runtime exception text + +##teamcity[testFailed name='testAssertExceptionFail' message='aborted' details='std::runtime_error: runtime exception text|n'] + +##teamcity[testFinished name='testAssertExceptionFail' duration='0'] + +##teamcity[testStarted name='testFatal'] +bfail + +##teamcity[testFailed name='testFatal' message='aborted' details='bfail|n'] + +##teamcity[testFinished name='testFatal' duration='0'] + +##teamcity[testStarted name='testError'] +berror + +##teamcity[testFailed name='testError' message='failed' details='berror|n'] + +##teamcity[testFinished name='testError' duration='0'] + +##teamcity[testStarted name='testNothing'] +Test case testNothing did not check any assertions + +##teamcity[testFinished name='testNothing' duration='0'] + +##teamcity[testSuiteFinished name='Master Test Suite'] + +*** 8 failures detected in test suite "Master Test Suite" diff --git a/boost/teamcity_boost.cpp b/boost/teamcity_boost.cpp new file mode 100644 index 0000000..4b107fb --- /dev/null +++ b/boost/teamcity_boost.cpp @@ -0,0 +1,157 @@ +/* Copyright 2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Revision: 88625 $ +*/ + +#include + +#include +#include +#include +#include +#include + +#include "teamcity_messages.h" + +using namespace boost::unit_test; +using namespace std; + +namespace JetBrains { + +// Custom formatter for TeamCity messages +class TeamcityBoostLogFormatter: public boost::unit_test::unit_test_log_formatter { + TeamcityMessages messages; + std::string currentDetails; + std::string flowId; + +public: + TeamcityBoostLogFormatter(const std::string &_flowId); + TeamcityBoostLogFormatter(); + + void log_start(std::ostream&, boost::unit_test::counter_t test_cases_amount); + void log_finish(std::ostream&); + void log_build_info(std::ostream&); + + void test_unit_start(std::ostream&, boost::unit_test::test_unit const& tu); + void test_unit_finish(std::ostream&, + boost::unit_test::test_unit const& tu, + unsigned long elapsed); + void test_unit_skipped(std::ostream&, boost::unit_test::test_unit const& tu); + + void log_exception(std::ostream&, + boost::unit_test::log_checkpoint_data const&, + boost::unit_test::const_string explanation); + + void log_entry_start(std::ostream&, + boost::unit_test::log_entry_data const&, + log_entry_types let); + void log_entry_value(std::ostream&, boost::unit_test::const_string value); + void log_entry_finish(std::ostream&); +}; + +// Fake fixture to register formatter +struct TeamcityFormatterRegistrar { + TeamcityFormatterRegistrar() { + if (JetBrains::underTeamcity()) { + boost::unit_test::unit_test_log.set_formatter(new JetBrains::TeamcityBoostLogFormatter()); + boost::unit_test::unit_test_log.set_threshold_level(boost::unit_test::log_successful_tests); + } + } +}; +BOOST_GLOBAL_FIXTURE(TeamcityFormatterRegistrar); + +// Formatter implementation +string toString(const_string bstr) { + stringstream ss; + + ss << bstr; + + return ss.str(); +} + +TeamcityBoostLogFormatter::TeamcityBoostLogFormatter(const std::string &_flowId) +: flowId(_flowId) +{} + +TeamcityBoostLogFormatter::TeamcityBoostLogFormatter() +: flowId(getFlowIdFromEnvironment()) +{} + +void TeamcityBoostLogFormatter::log_start(ostream &out, counter_t test_cases_amount) +{} + +void TeamcityBoostLogFormatter::log_finish(ostream &out) +{} + +void TeamcityBoostLogFormatter::log_build_info(ostream &out) +{} + +void TeamcityBoostLogFormatter::test_unit_start(ostream &out, test_unit const& tu) { + messages.setOutput(out); + + if (tu.p_type == tut_case) { + messages.testStarted(tu.p_name, flowId); + } else { + messages.suiteStarted(tu.p_name, flowId); + } + + currentDetails.clear(); +} + +void TeamcityBoostLogFormatter::test_unit_finish(ostream &out, test_unit const& tu, unsigned long elapsed) { + messages.setOutput(out); + + test_results const& tr = results_collector.results(tu.p_id); + if (tu.p_type == tut_case) { + if(!tr.passed()) { + if(tr.p_skipped) { + messages.testIgnored(tu.p_name, "ignored", flowId); + } else if (tr.p_aborted) { + messages.testFailed(tu.p_name, "aborted", currentDetails, flowId); + } else { + messages.testFailed(tu.p_name, "failed", currentDetails, flowId); + } + } + + messages.testFinished(tu.p_name, elapsed / 1000, flowId); + } else { + messages.suiteFinished(tu.p_name, flowId); + } +} + +void TeamcityBoostLogFormatter::test_unit_skipped(ostream &out, test_unit const& tu) +{} + +void TeamcityBoostLogFormatter::log_exception(ostream &out, log_checkpoint_data const&, const_string explanation) { + string what = toString(explanation); + + out << what << endl; + currentDetails += what + "\n"; +} + +void TeamcityBoostLogFormatter::log_entry_start(ostream&, log_entry_data const&, log_entry_types let) +{} + +void TeamcityBoostLogFormatter::log_entry_value(ostream &out, const_string value) { + out << value; + currentDetails += toString(value); +} + +void TeamcityBoostLogFormatter::log_entry_finish(ostream &out) { + out << endl; + currentDetails += "\n"; +} + +} diff --git a/common/teamcity_messages.cpp b/common/teamcity_messages.cpp new file mode 100644 index 0000000..e6d2639 --- /dev/null +++ b/common/teamcity_messages.cpp @@ -0,0 +1,150 @@ +/* Copyright 2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Revision: 88625 $ +*/ + +#include +#include + +#include "teamcity_messages.h" + +using namespace std; + +namespace JetBrains { + +std::string getFlowIdFromEnvironment() { + const char *flowId = getenv("TEAMCITY_PROCESS_FLOW_ID"); + return flowId == NULL ? "" : flowId; +} + +bool underTeamcity() { + return getenv("TEAMCITY_PROJECT_NAME") != NULL; +} + +TeamcityMessages::TeamcityMessages() +: m_out(&cout) +{} + +void TeamcityMessages::setOutput(ostream &out) { + m_out = &out; +} + +string TeamcityMessages::escape(string s) { + string result; + + for (size_t i = 0; i < s.length(); i++) { + char c = s[i]; + + switch (c) { + case '\n': result.append("|n"); break; + case '\r': result.append("|r"); break; + case '\'': result.append("|'"); break; + case '|': result.append("||"); break; + case ']': result.append("|]"); break; + default: result.append(&c, 1); + } + } + + return result; +} + +void TeamcityMessages::openMsg(const string &name) { + // endl for http://jetbrains.net/tracker/issue/TW-4412 + *m_out << endl << "##teamcity[" << name; +} + +void TeamcityMessages::closeMsg() { + *m_out << "]"; + // endl for http://jetbrains.net/tracker/issue/TW-4412 + *m_out << endl; + m_out->flush(); +} + +void TeamcityMessages::writeProperty(string name, string value) { + *m_out << " " << name << "='" << escape(value) << "'"; +} + +void TeamcityMessages::suiteStarted(string name, string flowid) { + openMsg("testSuiteStarted"); + writeProperty("name", name); + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + closeMsg(); +} + +void TeamcityMessages::suiteFinished(string name, string flowid) { + openMsg("testSuiteFinished"); + writeProperty("name", name); + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + closeMsg(); +} + +void TeamcityMessages::testStarted(string name, string flowid) { + openMsg("testStarted"); + writeProperty("name", name); + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + closeMsg(); +} + +void TeamcityMessages::testFinished(string name, int durationMs, string flowid) { + openMsg("testFinished"); + + writeProperty("name", name); + + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + if(durationMs >= 0) { + stringstream out; + out << durationMs; + writeProperty("duration", out.str()); + } + + closeMsg(); +} + +void TeamcityMessages::testFailed(string name, string message, string details, string flowid) { + openMsg("testFailed"); + writeProperty("name", name); + writeProperty("message", message); + writeProperty("details", details); + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + closeMsg(); +} + +void TeamcityMessages::testIgnored(std::string name, std::string message, string flowid) { + openMsg("testIgnored"); + writeProperty("name", name); + writeProperty("message", message); + if(flowid.length() > 0) { + writeProperty("flowId", flowid); + } + + closeMsg(); +} + +} diff --git a/common/teamcity_messages.h b/common/teamcity_messages.h new file mode 100644 index 0000000..5d5d291 --- /dev/null +++ b/common/teamcity_messages.h @@ -0,0 +1,55 @@ +/* Copyright 2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Revision: 88625 $ +*/ + +#ifndef H_TEAMCITY_MESSAGES +#define H_TEAMCITY_MESSAGES + +#include +#include + +namespace JetBrains { + +std::string getFlowIdFromEnvironment(); +bool underTeamcity(); + +class TeamcityMessages { + std::ostream *m_out; + +protected: + std::string escape(std::string s); + + void openMsg(const std::string &name); + void writeProperty(std::string name, std::string value); + void closeMsg(); + +public: + TeamcityMessages(); + + void setOutput(std::ostream &); + + void suiteStarted(std::string name, std::string flowid = ""); + void suiteFinished(std::string name, std::string flowid = ""); + + void testStarted(std::string name, std::string flowid = ""); + void testFailed(std::string name, std::string message, std::string details, std::string flowid = ""); + void testIgnored(std::string name, std::string message, std::string flowid = ""); + void testFinished(std::string name, int durationMs = -1, std::string flowid = ""); +}; + +} + +#endif /* H_TEAMCITY_MESSAGES */ diff --git a/cppunit/README.txt b/cppunit/README.txt new file mode 100644 index 0000000..04f7914 --- /dev/null +++ b/cppunit/README.txt @@ -0,0 +1,30 @@ +CppUnit listener for TeamCity +----------------------------- + +To report your tests result to TeamCity server +include teamcity_messages.* teamcity_cppunit.* +to your project and modify "main" function +as shown in example.cpp +(around JetBrains::underTeamcity and JetBrains::TeamcityProgressListener) + +Technical details +----------------- + +Reporting implemented by writing TeamCity service messages to stdout. + +See +http://www.jetbrains.net/confluence/display/TCD3/Build+Script+Interaction+with+TeamCity +for more details. + +Contact information +------------------- + +Mail to teamcity-feedback@jetbrains.com or see other options at + +http://www.jetbrains.com/support/teamcity + +License +------- + +Apache, version 2.0 +http://www.apache.org/licenses/LICENSE-2.0 diff --git a/cppunit/cppunit_test.cpp b/cppunit/cppunit_test.cpp new file mode 100644 index 0000000..cb50afe --- /dev/null +++ b/cppunit/cppunit_test.cpp @@ -0,0 +1,92 @@ +/* Copyright 2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Revision: 88625 $ +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "teamcity_cppunit.h" + +using namespace CPPUNIT_NS; +using namespace std; + +class MyTest : public TestCase { + CPPUNIT_TEST_SUITE(MyTest); + CPPUNIT_TEST(testHelloWorld); + CPPUNIT_TEST(testException); + CPPUNIT_TEST(testCerr); + CPPUNIT_TEST(testAssert); + CPPUNIT_TEST(testAssertThrow); + CPPUNIT_TEST(testAssertEqual); + CPPUNIT_TEST(testNothing); + CPPUNIT_TEST_SUITE_END(); + +public: + void testHelloWorld() { + cout << "Hello, cout" << endl; + } + + void testCerr() { + cerr << "Hello, cerr!" << endl; + } + + void testException() { + throwRuntimeException(); + } + + void testAssertEqual() { + CPPUNIT_ASSERT_EQUAL(2, 1); + } + + void testNothing() {} + + void testAssertThrow() { + CPPUNIT_ASSERT_THROW(throwRuntimeException(), logic_error); + } + + void testAssert() { + CPPUNIT_ASSERT(false); + } + + void throwRuntimeException() { + throw runtime_error("runtime exception text"); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(MyTest); + +static void run() { + CPPUNIT_NS::TestResult controller; + + JetBrains::TeamcityProgressListener tc; + controller.addListener(&tc); + + CPPUNIT_NS::TestRunner runner; + runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest()); + runner.run(controller); +} + +int main(int argc, char **argv) { + run(); + return 0; +} diff --git a/cppunit/cppunit_test.output.flowId.gold b/cppunit/cppunit_test.output.flowId.gold new file mode 100644 index 0000000..31341db --- /dev/null +++ b/cppunit/cppunit_test.output.flowId.gold @@ -0,0 +1,46 @@ + +##teamcity[testSuiteStarted name='All Tests' flowId='myFlowId'] + +##teamcity[testSuiteStarted name='MyTest' flowId='myFlowId'] + +##teamcity[testStarted name='MyTest::testHelloWorld' flowId='myFlowId'] +Hello, cout + +##teamcity[testFinished name='MyTest::testHelloWorld' flowId='myFlowId'] + +##teamcity[testStarted name='MyTest::testException' flowId='myFlowId'] + +##teamcity[testFailed name='MyTest::testException' message='uncaught exception of type St13runtime_error' details='- runtime exception text|n' flowId='myFlowId'] + +##teamcity[testFinished name='MyTest::testException' flowId='myFlowId'] + +##teamcity[testStarted name='MyTest::testCerr' flowId='myFlowId'] +Hello, cerr! + +##teamcity[testFinished name='MyTest::testCerr' flowId='myFlowId'] + +##teamcity[testStarted name='MyTest::testAssert' flowId='myFlowId'] + +##teamcity[testFailed name='MyTest::testAssert' message='assertion failed' details='- Expression: false|n at cppunit/cppunit_test.cpp:68|n' flowId='myFlowId'] + +##teamcity[testFinished name='MyTest::testAssert' flowId='myFlowId'] + +##teamcity[testStarted name='MyTest::testAssertThrow' flowId='myFlowId'] + +##teamcity[testFailed name='MyTest::testAssertThrow' message='expected exception not thrown' details='- Expected: logic_error|n- Actual : St13runtime_error|n- What() : runtime exception text|n at cppunit/cppunit_test.cpp:64|n' flowId='myFlowId'] + +##teamcity[testFinished name='MyTest::testAssertThrow' flowId='myFlowId'] + +##teamcity[testStarted name='MyTest::testAssertEqual' flowId='myFlowId'] + +##teamcity[testFailed name='MyTest::testAssertEqual' message='equality assertion failed' details='- Expected: 2|n- Actual : 1|n at cppunit/cppunit_test.cpp:58|n' flowId='myFlowId'] + +##teamcity[testFinished name='MyTest::testAssertEqual' flowId='myFlowId'] + +##teamcity[testStarted name='MyTest::testNothing' flowId='myFlowId'] + +##teamcity[testFinished name='MyTest::testNothing' flowId='myFlowId'] + +##teamcity[testSuiteFinished name='MyTest' flowId='myFlowId'] + +##teamcity[testSuiteFinished name='All Tests' flowId='myFlowId'] diff --git a/cppunit/cppunit_test.output.gold b/cppunit/cppunit_test.output.gold new file mode 100644 index 0000000..dc64cd7 --- /dev/null +++ b/cppunit/cppunit_test.output.gold @@ -0,0 +1,46 @@ + +##teamcity[testSuiteStarted name='All Tests'] + +##teamcity[testSuiteStarted name='MyTest'] + +##teamcity[testStarted name='MyTest::testHelloWorld'] +Hello, cout + +##teamcity[testFinished name='MyTest::testHelloWorld'] + +##teamcity[testStarted name='MyTest::testException'] + +##teamcity[testFailed name='MyTest::testException' message='uncaught exception of type St13runtime_error' details='- runtime exception text|n'] + +##teamcity[testFinished name='MyTest::testException'] + +##teamcity[testStarted name='MyTest::testCerr'] +Hello, cerr! + +##teamcity[testFinished name='MyTest::testCerr'] + +##teamcity[testStarted name='MyTest::testAssert'] + +##teamcity[testFailed name='MyTest::testAssert' message='assertion failed' details='- Expression: false|n at cppunit/cppunit_test.cpp:68|n'] + +##teamcity[testFinished name='MyTest::testAssert'] + +##teamcity[testStarted name='MyTest::testAssertThrow'] + +##teamcity[testFailed name='MyTest::testAssertThrow' message='expected exception not thrown' details='- Expected: logic_error|n- Actual : St13runtime_error|n- What() : runtime exception text|n at cppunit/cppunit_test.cpp:64|n'] + +##teamcity[testFinished name='MyTest::testAssertThrow'] + +##teamcity[testStarted name='MyTest::testAssertEqual'] + +##teamcity[testFailed name='MyTest::testAssertEqual' message='equality assertion failed' details='- Expected: 2|n- Actual : 1|n at cppunit/cppunit_test.cpp:58|n'] + +##teamcity[testFinished name='MyTest::testAssertEqual'] + +##teamcity[testStarted name='MyTest::testNothing'] + +##teamcity[testFinished name='MyTest::testNothing'] + +##teamcity[testSuiteFinished name='MyTest'] + +##teamcity[testSuiteFinished name='All Tests'] diff --git a/cppunit/example.cpp b/cppunit/example.cpp new file mode 100644 index 0000000..5386680 --- /dev/null +++ b/cppunit/example.cpp @@ -0,0 +1,67 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "teamcity_cppunit.h" + +using namespace CppUnit; +using namespace std; + +class MyTest : public TestCase { + CPPUNIT_TEST_SUITE(MyTest); + CPPUNIT_TEST(testHelloWorld); + CPPUNIT_TEST(testAssertEqual); + CPPUNIT_TEST(testNothing); + CPPUNIT_TEST_SUITE_END(); + +public: + void testHelloWorld() { + cout << "Hello, world" << endl; + } + + void testAssertEqual() { + CPPUNIT_ASSERT_EQUAL(2, 1); + } + + void testNothing() {} +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(MyTest); + +int main(int argc, char **argv) { + // Create the event manager and test controller + TestResult controller; + + // Add a listener that collects test result + TestResultCollector result; + controller.addListener(&result); + + // Add the top suite to the test runner + TestRunner runner; + runner.addTest(TestFactoryRegistry::getRegistry().makeTest()); + + // Listen to progress + TestListener *listener; + + if (JetBrains::underTeamcity()) { + // Add unique flowId parameter if you want to run test processes in parallel + // See http://confluence.jetbrains.net/display/TCD6/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-MessageFlowId + listener = new JetBrains::TeamcityProgressListener(); + } else { + listener = new BriefTestProgressListener(); + } + controller.addListener(listener); + + // Run tests + runner.run(controller); + + delete listener; + + return result.wasSuccessful() ? 0 : 1; +} diff --git a/cppunit/teamcity_cppunit.cpp b/cppunit/teamcity_cppunit.cpp new file mode 100644 index 0000000..8c5111a --- /dev/null +++ b/cppunit/teamcity_cppunit.cpp @@ -0,0 +1,86 @@ +/* Copyright 2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Revision: 88625 $ +*/ + +#include + +#include "cppunit/Test.h" +#include "cppunit/Exception.h" + +#include "teamcity_cppunit.h" + +using namespace CPPUNIT_NS; +using namespace std; + +namespace JetBrains { + +TeamcityProgressListener::TeamcityProgressListener() +{ + flowid = getFlowIdFromEnvironment(); +} + +TeamcityProgressListener::TeamcityProgressListener(const std::string& _flowid) +{ + flowid = _flowid; +} + +TeamcityProgressListener::~TeamcityProgressListener() +{} + +void TeamcityProgressListener::startTest(Test *test) { + messages.testStarted(test->getName(), flowid); +} + +static string sourceLine2string(const SourceLine &sline) { + stringstream ss; + + ss << sline.fileName() << ":" << sline.lineNumber(); + + return ss.str(); +} + +void TeamcityProgressListener::addFailure(const TestFailure &failure) { + const Exception *e = failure.thrownException(); + + string details = e->message().details(); + + if (e->sourceLine().isValid()) { + details.append(" at "); + details.append(sourceLine2string(e->sourceLine())); + details.append("\n"); + } + + messages.testFailed( + failure.failedTest()->getName(), + e->message().shortDescription(), + details, + flowid + ); +} + +void TeamcityProgressListener::endTest(Test *test) { + messages.testFinished(test->getName(), -1, flowid); +} + +void TeamcityProgressListener::startSuite(Test *test) { + messages.suiteStarted(test->getName(), flowid); +} + +void TeamcityProgressListener::endSuite(Test *test) { + messages.suiteFinished(test->getName(), flowid); +} + +} diff --git a/cppunit/teamcity_cppunit.h b/cppunit/teamcity_cppunit.h new file mode 100644 index 0000000..d0f180c --- /dev/null +++ b/cppunit/teamcity_cppunit.h @@ -0,0 +1,55 @@ +/* Copyright 2011 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Revision: 88625 $ +*/ + +#ifndef H_TEAMCITY_CPPUNIT +#define H_TEAMCITY_CPPUNIT + +#include +#include +#include + +#include "teamcity_messages.h" + +namespace JetBrains { + +class TeamcityProgressListener: public CPPUNIT_NS::TestListener { + TeamcityMessages messages; + +public: + TeamcityProgressListener(const std::string& _flowid); + TeamcityProgressListener(); + ~TeamcityProgressListener(); + + void startTest(CPPUNIT_NS::Test *test); + void addFailure(const CPPUNIT_NS::TestFailure &failure); + void endTest(CPPUNIT_NS::Test *test); + void startSuite(CPPUNIT_NS::Test *test); + void endSuite(CPPUNIT_NS::Test *test); + +private: + std::string flowid; + + // Prevents the use of the copy constructor. + TeamcityProgressListener(const TeamcityProgressListener ©); + + // Prevents the use of the copy operator. + void operator =(const TeamcityProgressListener ©); +}; + +} + +#endif /* H_TEAMCITY_CPPUNIT */