blob: 15aaecfc74c7ac6019bc8398f76beadcd54dd7f7 [file] [log] [blame]
/*
*
* 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 <boost/config.hpp> // make sure size_t is in namespace std
#include <cstddef>
#include <climits>
#include <map>
#include <stdexcept>
#include <string>
#include <utility> // pair
#include <vector>
#include <boost/lexical_cast.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/test/test_tools.hpp>
#include <boost/weak_ptr.hpp>
#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<int Code>
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&); // Supress 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<impl> 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<error_code>
// 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<operation::impl> ptr_type;
typedef std::map<int, ptr_type> map_type;
map_type operations_;
std::vector<int> 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<n>(); \
/**/
#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<string>(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<string>(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<int>::size_type size_type;
for (size_type z = 0, n = log_.size(); z < n; ++z) {
msg += lexical_cast<string>(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<string>(id) );
operations_.erase(it);
}
} } } // End namespace boost::iostreams::test.
#endif // #ifndef BOOST_IOSTREAMS_TEST_OPERATION_SEQUENCE_HPP_INCLUDED