// Copyright 2013 by Martin Moene // // lest is based on ideas by Kevlin Henney, see video at // http://skillsmatter.com/podcast/agile-testing/kevlin-henney-rethinking-unit-testing-in-c-plus-plus // // Distributed under the Boost Software License, Version 1.0: // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #ifndef LEST_LEST_H_INCLUDED #define LEST_LEST_H_INCLUDED #include #include #include #include #include #ifndef lest_NO_SHORT_ASSERTION_NAMES # define EXPECT lest_EXPECT # define EXPECT_THROWS lest_EXPECT_THROWS # define EXPECT_THROWS_AS lest_EXPECT_THROWS_AS #endif #define lest_EXPECT( expr ) \ try \ { \ if ( ! (expr) ) \ throw lest::failure{ lest_LOCATION, #expr }; \ } \ catch( lest::failure const & ) \ { \ throw ; \ } \ catch( std::exception const & e ) \ { \ throw lest::unexpected{ lest_LOCATION, #expr, lest::with_message( e.what() ) }; \ } \ catch(...) \ { \ throw lest::unexpected{ lest_LOCATION, #expr, "of unknown type" }; \ } #define lest_EXPECT_THROWS( expr ) \ for (;;) \ { \ try { lest::serum( expr ); } catch (...) { break; } \ throw lest::expected{ lest_LOCATION, #expr }; \ } #define lest_EXPECT_THROWS_AS( expr, excpt ) \ for (;;) \ { \ try { lest::serum( expr ); } catch ( excpt & ) { break; } catch (...) {} \ throw lest::expected{ lest_LOCATION, #expr, lest::of_type( #excpt ) }; \ } #define lest_LOCATION lest::location{__FILE__, __LINE__} namespace lest { struct test { const std::string name; const std::function behaviour; }; struct location { const std::string file; const int line; location( std::string file, int line ) : file{ file }, line{ line } {} }; struct comment { const std::string text; comment( std::string text ) : text{ text } {} explicit operator bool() { return ! text.empty(); } }; struct message : std::runtime_error { const std::string kind; const location where; const comment note; ~message() throw() {} // GCC 4.6 message( std::string kind, location where, std::string expr, std::string note = "" ) : std::runtime_error{ expr }, kind{ kind }, where{ where }, note{ note } {} }; struct failure : message { failure( location where, std::string expr ) : message{ "failed", where, expr } {} }; struct expected : message { expected( location where, std::string expr, std::string excpt = "" ) : message{ "failed: didn't get exception", where, expr, excpt } {} }; struct unexpected : message { unexpected( location where, std::string expr, std::string note ) : message{ "failed: got unexpected exception", where, expr, note } {} }; inline bool serum( bool verum ) { return verum; } inline std::string with_message( std::string text ) { return "with message \"" + text + "\""; } inline std::string of_type( std::string text ) { return "of type " + text; } inline std::string pluralise( int n, std::string text ) { return n == 1 ? text : text + "s"; } inline std::ostream & operator<<( std::ostream & os, comment note ) { return os << (note ? " " + note.text : "" ); } inline std::ostream & operator<<( std::ostream & os, location where ) { #ifdef __GNUG__ return os << where.file << ":" << where.line; #else return os << where.file << "(" << where.line << ")"; #endif } inline void report( std::ostream & os, message const & e, std::string test ) { os << e.where << ": " << e.kind << e.note << ": " << test << ": " << e.what() << std::endl; } template int run( test const (&specification)[N], std::ostream & os = std::cout ) { int failures = 0; for ( auto & testing : specification ) { try { testing.behaviour(); } catch( message const & e ) { ++failures; report( os, e, testing.name ); } } if ( failures > 0 ) { os << failures << " out of " << N << " " << pluralise(N, "test") << " failed." << std::endl; } return failures; } } // namespace lest #endif // LEST_LEST_H_INCLUDED