225 lines
6.7 KiB
C++
225 lines
6.7 KiB
C++
/*
|
|
*
|
|
* 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&); // 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<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
|