/* * * 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 http://www.boost.org/libs/iostreams for documentation. * * Defines the classes operation_sequence and operation, in the namespace * boost::iostreams::test, for verifying that all elements of a sequence of * operations are executed, and that they are executed in the correct order. * * File: libs/iostreams/test/detail/operation_sequence.hpp * Date: Mon Dec 10 18:58:19 MST 2007 * Copyright: 2007-2008 CodeRage, LLC * Author: Jonathan Turkanis * Contact: turkanis at coderage dot com */ #ifndef BOOST_IOSTREAMS_TEST_OPERATION_SEQUENCE_HPP_INCLUDED #define BOOST_IOSTREAMS_TEST_OPERATION_SEQUENCE_HPP_INCLUDED #include // make sure size_t is in namespace std #include #include #include #include #include #include // pair #include #include #include #include #include #include #ifndef BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR # define BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR 20 #endif #define BOOST_CHECK_OPERATION_SEQUENCE(seq) \ BOOST_CHECK_MESSAGE(seq.is_success(), seq.message()) \ /**/ namespace boost { namespace iostreams { namespace test { // Simple exception class with error code built in to type template struct operation_error { }; class operation_sequence; // Represent an operation in a sequence of operations to be executed class operation { public: friend class operation_sequence; operation() : pimpl_() { } void execute(); private: static void remove_operation(operation_sequence& seq, int id); struct impl { impl(operation_sequence& seq, int id, int error_code = -1) : seq(seq), id(id), error_code(error_code) { } ~impl() { remove_operation(seq, id); } impl& operator=(const impl&); // Suppress VC warning 4512 operation_sequence& seq; int id; int error_code; }; friend struct impl; operation(operation_sequence& seq, int id, int error_code = -1) : pimpl_(new impl(seq, id, error_code)) { } shared_ptr pimpl_; }; // Represents a sequence of operations to be executed in a particular order class operation_sequence { public: friend class operation; operation_sequence() { reset(); } // // Returns a new operation. // Parameters: // // id - The operation id, determining the position // of the new operation in the operation sequence // error_code - If supplied, indicates that the new // operation will throw operation_error // when executed. Must be an integer between 0 and // BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR, // inclusive. // operation new_operation(int id, int error_code = INT_MAX); bool is_success() const { return success_; } bool is_failure() const { return failed_; } std::string message() const; void reset(); private: void execute(int id); void remove_operation(int id); operation_sequence(const operation_sequence&); operation_sequence& operator=(const operation_sequence&); typedef weak_ptr ptr_type; typedef std::map map_type; map_type operations_; std::vector log_; std::size_t total_executed_; int last_executed_; bool success_; bool failed_; }; //--------------Implementation of operation-----------------------------------// void operation::execute() { pimpl_->seq.execute(pimpl_->id); switch (pimpl_->error_code) { // Implementation with one or more cleanup operations #define BOOST_PP_LOCAL_MACRO(n) \ case n: throw operation_error(); \ /**/ #define BOOST_PP_LOCAL_LIMITS (1, BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR) #include BOOST_PP_LOCAL_ITERATE() #undef BOOST_PP_LOCAL_MACRO default: break; } } inline void operation::remove_operation(operation_sequence& seq, int id) { seq.remove_operation(id); } //--------------Implementation of operation_sequence--------------------------// inline operation operation_sequence::new_operation(int id, int error_code) { using namespace std; if ( error_code < 0 || (error_code > BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR && error_code != INT_MAX) ) { throw runtime_error( string("The error code ") + lexical_cast(error_code) + " is out of range" ); } if (last_executed_ != INT_MIN) throw runtime_error( "Operations in progress; call reset() " "before creating more operations" ); map_type::const_iterator it = operations_.find(id); if (it != operations_.end()) throw runtime_error( string("The operation ") + lexical_cast(id) + " already exists" ); operation op(*this, id, error_code); operations_.insert(make_pair(id, ptr_type(op.pimpl_))); return op; } inline std::string operation_sequence::message() const { using namespace std; if (success_) return "success"; std::string msg = failed_ ? "operations occurred out of order: " : "operation sequence is incomplete: "; typedef vector::size_type size_type; for (size_type z = 0, n = log_.size(); z < n; ++z) { msg += lexical_cast(log_[z]); if (z < n - 1) msg += ','; } return msg; } inline void operation_sequence::reset() { log_.clear(); total_executed_ = 0; last_executed_ = INT_MIN; success_ = false; failed_ = false; } inline void operation_sequence::execute(int id) { log_.push_back(id); if (!failed_ && last_executed_ < id) { if (++total_executed_ == operations_.size()) success_ = true; last_executed_ = id; } else { success_ = false; failed_ = true; } } inline void operation_sequence::remove_operation(int id) { using namespace std; map_type::iterator it = operations_.find(id); if (it == operations_.end()) throw runtime_error( string("No such operation: ") + lexical_cast(id) ); operations_.erase(it); } } } } // End namespace boost::iostreams::test. #endif // #ifndef BOOST_IOSTREAMS_TEST_OPERATION_SEQUENCE_HPP_INCLUDED