operation_sequence.hpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /*
  2. *
  3. * Distributed under the Boost Software License, Version 1.0.(See accompanying
  4. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
  5. *
  6. * See http://www.boost.org/libs/iostreams for documentation.
  7. *
  8. * Defines the classes operation_sequence and operation, in the namespace
  9. * boost::iostreams::test, for verifying that all elements of a sequence of
  10. * operations are executed, and that they are executed in the correct order.
  11. *
  12. * File: libs/iostreams/test/detail/operation_sequence.hpp
  13. * Date: Mon Dec 10 18:58:19 MST 2007
  14. * Copyright: 2007-2008 CodeRage, LLC
  15. * Author: Jonathan Turkanis
  16. * Contact: turkanis at coderage dot com
  17. */
  18. #ifndef BOOST_IOSTREAMS_TEST_OPERATION_SEQUENCE_HPP_INCLUDED
  19. #define BOOST_IOSTREAMS_TEST_OPERATION_SEQUENCE_HPP_INCLUDED
  20. #include <boost/config.hpp> // make sure size_t is in namespace std
  21. #include <cstddef>
  22. #include <climits>
  23. #include <map>
  24. #include <stdexcept>
  25. #include <string>
  26. #include <utility> // pair
  27. #include <vector>
  28. #include <boost/lexical_cast.hpp>
  29. #include <boost/preprocessor/iteration/local.hpp>
  30. #include <boost/shared_ptr.hpp>
  31. #include <boost/test/test_tools.hpp>
  32. #include <boost/weak_ptr.hpp>
  33. #ifndef BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR
  34. # define BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR 20
  35. #endif
  36. #define BOOST_CHECK_OPERATION_SEQUENCE(seq) \
  37. BOOST_CHECK_MESSAGE(seq.is_success(), seq.message()) \
  38. /**/
  39. namespace boost { namespace iostreams { namespace test {
  40. // Simple exception class with error code built in to type
  41. template<int Code>
  42. struct operation_error { };
  43. class operation_sequence;
  44. // Represent an operation in a sequence of operations to be executed
  45. class operation {
  46. public:
  47. friend class operation_sequence;
  48. operation() : pimpl_() { }
  49. void execute();
  50. private:
  51. static void remove_operation(operation_sequence& seq, int id);
  52. struct impl {
  53. impl(operation_sequence& seq, int id, int error_code = -1)
  54. : seq(seq), id(id), error_code(error_code)
  55. { }
  56. ~impl() { remove_operation(seq, id); }
  57. impl& operator=(const impl&); // Suppress VC warning 4512
  58. operation_sequence& seq;
  59. int id;
  60. int error_code;
  61. };
  62. friend struct impl;
  63. operation(operation_sequence& seq, int id, int error_code = -1)
  64. : pimpl_(new impl(seq, id, error_code))
  65. { }
  66. shared_ptr<impl> pimpl_;
  67. };
  68. // Represents a sequence of operations to be executed in a particular order
  69. class operation_sequence {
  70. public:
  71. friend class operation;
  72. operation_sequence() { reset(); }
  73. //
  74. // Returns a new operation.
  75. // Parameters:
  76. //
  77. // id - The operation id, determining the position
  78. // of the new operation in the operation sequence
  79. // error_code - If supplied, indicates that the new
  80. // operation will throw operation_error<error_code>
  81. // when executed. Must be an integer between 0 and
  82. // BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR,
  83. // inclusive.
  84. //
  85. operation new_operation(int id, int error_code = INT_MAX);
  86. bool is_success() const { return success_; }
  87. bool is_failure() const { return failed_; }
  88. std::string message() const;
  89. void reset();
  90. private:
  91. void execute(int id);
  92. void remove_operation(int id);
  93. operation_sequence(const operation_sequence&);
  94. operation_sequence& operator=(const operation_sequence&);
  95. typedef weak_ptr<operation::impl> ptr_type;
  96. typedef std::map<int, ptr_type> map_type;
  97. map_type operations_;
  98. std::vector<int> log_;
  99. std::size_t total_executed_;
  100. int last_executed_;
  101. bool success_;
  102. bool failed_;
  103. };
  104. //--------------Implementation of operation-----------------------------------//
  105. void operation::execute()
  106. {
  107. pimpl_->seq.execute(pimpl_->id);
  108. switch (pimpl_->error_code) {
  109. // Implementation with one or more cleanup operations
  110. #define BOOST_PP_LOCAL_MACRO(n) \
  111. case n: throw operation_error<n>(); \
  112. /**/
  113. #define BOOST_PP_LOCAL_LIMITS (1, BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR)
  114. #include BOOST_PP_LOCAL_ITERATE()
  115. #undef BOOST_PP_LOCAL_MACRO
  116. default:
  117. break;
  118. }
  119. }
  120. inline void operation::remove_operation(operation_sequence& seq, int id)
  121. {
  122. seq.remove_operation(id);
  123. }
  124. //--------------Implementation of operation_sequence--------------------------//
  125. inline operation operation_sequence::new_operation(int id, int error_code)
  126. {
  127. using namespace std;
  128. if ( error_code < 0 ||
  129. (error_code > BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR &&
  130. error_code != INT_MAX) )
  131. {
  132. throw runtime_error( string("The error code ") +
  133. lexical_cast<string>(error_code) +
  134. " is out of range" );
  135. }
  136. if (last_executed_ != INT_MIN)
  137. throw runtime_error( "Operations in progress; call reset() "
  138. "before creating more operations" );
  139. map_type::const_iterator it = operations_.find(id);
  140. if (it != operations_.end())
  141. throw runtime_error( string("The operation ") +
  142. lexical_cast<string>(id) +
  143. " already exists" );
  144. operation op(*this, id, error_code);
  145. operations_.insert(make_pair(id, ptr_type(op.pimpl_)));
  146. return op;
  147. }
  148. inline std::string operation_sequence::message() const
  149. {
  150. using namespace std;
  151. if (success_)
  152. return "success";
  153. std::string msg = failed_ ?
  154. "operations occurred out of order: " :
  155. "operation sequence is incomplete: ";
  156. typedef vector<int>::size_type size_type;
  157. for (size_type z = 0, n = log_.size(); z < n; ++z) {
  158. msg += lexical_cast<string>(log_[z]);
  159. if (z < n - 1)
  160. msg += ',';
  161. }
  162. return msg;
  163. }
  164. inline void operation_sequence::reset()
  165. {
  166. log_.clear();
  167. total_executed_ = 0;
  168. last_executed_ = INT_MIN;
  169. success_ = false;
  170. failed_ = false;
  171. }
  172. inline void operation_sequence::execute(int id)
  173. {
  174. log_.push_back(id);
  175. if (!failed_ && last_executed_ < id) {
  176. if (++total_executed_ == operations_.size())
  177. success_ = true;
  178. last_executed_ = id;
  179. } else {
  180. success_ = false;
  181. failed_ = true;
  182. }
  183. }
  184. inline void operation_sequence::remove_operation(int id)
  185. {
  186. using namespace std;
  187. map_type::iterator it = operations_.find(id);
  188. if (it == operations_.end())
  189. throw runtime_error( string("No such operation: ") +
  190. lexical_cast<string>(id) );
  191. operations_.erase(it);
  192. }
  193. } } } // End namespace boost::iostreams::test.
  194. #endif // #ifndef BOOST_IOSTREAMS_TEST_OPERATION_SEQUENCE_HPP_INCLUDED