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
19 #ifndef BOOST_IOSTREAMS_TEST_OPERATION_SEQUENCE_HPP_INCLUDED
20 #define BOOST_IOSTREAMS_TEST_OPERATION_SEQUENCE_HPP_INCLUDED
21
22 #include <boost/config.hpp> // make sure size_t is in namespace std
23 #include <cstddef>
24 #include <climits>
25 #include <map>
26 #include <stdexcept>
27 #include <string>
28 #include <utility> // pair
29 #include <vector>
30 #include <boost/lexical_cast.hpp>
31 #include <boost/preprocessor/iteration/local.hpp>
32 #include <boost/shared_ptr.hpp>
33 #include <boost/test/test_tools.hpp>
34 #include <boost/weak_ptr.hpp>
35
36 #ifndef BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR
37 # define BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR 20
38 #endif
39
40 #define BOOST_CHECK_OPERATION_SEQUENCE(seq) \
41 BOOST_CHECK_MESSAGE(seq.is_success(), seq.message()) \
42 /**/
43
44 namespace boost { namespace iostreams { namespace test {
45
46 // Simple exception class with error code built in to type
47 template<int Code>
48 struct operation_error { };
49
50 class operation_sequence;
51
52 // Represent an operation in a sequence of operations to be executed
53 class operation {
54 public:
55 friend class operation_sequence;
operation()56 operation() : pimpl_() { }
57 void execute();
58 private:
59 static void remove_operation(operation_sequence& seq, int id);
60
61 struct impl {
implboost::iostreams::test::operation::impl62 impl(operation_sequence& seq, int id, int error_code = -1)
63 : seq(seq), id(id), error_code(error_code)
64 { }
~implboost::iostreams::test::operation::impl65 ~impl() { remove_operation(seq, id); }
66 impl& operator=(const impl&); // Suppress VC warning 4512
67 operation_sequence& seq;
68 int id;
69 int error_code;
70 };
71 friend struct impl;
72
operation(operation_sequence & seq,int id,int error_code=-1)73 operation(operation_sequence& seq, int id, int error_code = -1)
74 : pimpl_(new impl(seq, id, error_code))
75 { }
76
77 shared_ptr<impl> pimpl_;
78 };
79
80 // Represents a sequence of operations to be executed in a particular order
81 class operation_sequence {
82 public:
83 friend class operation;
operation_sequence()84 operation_sequence() { reset(); }
85
86 //
87 // Returns a new operation.
88 // Parameters:
89 //
90 // id - The operation id, determining the position
91 // of the new operation in the operation sequence
92 // error_code - If supplied, indicates that the new
93 // operation will throw operation_error<error_code>
94 // when executed. Must be an integer between 0 and
95 // BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR,
96 // inclusive.
97 //
98 operation new_operation(int id, int error_code = INT_MAX);
99
is_success() const100 bool is_success() const { return success_; }
is_failure() const101 bool is_failure() const { return failed_; }
102 std::string message() const;
103 void reset();
104 private:
105 void execute(int id);
106 void remove_operation(int id);
107 operation_sequence(const operation_sequence&);
108 operation_sequence& operator=(const operation_sequence&);
109
110 typedef weak_ptr<operation::impl> ptr_type;
111 typedef std::map<int, ptr_type> map_type;
112
113 map_type operations_;
114 std::vector<int> log_;
115 std::size_t total_executed_;
116 int last_executed_;
117 bool success_;
118 bool failed_;
119 };
120
121 //--------------Implementation of operation-----------------------------------//
122
execute()123 void operation::execute()
124 {
125 pimpl_->seq.execute(pimpl_->id);
126 switch (pimpl_->error_code) {
127
128 // Implementation with one or more cleanup operations
129 #define BOOST_PP_LOCAL_MACRO(n) \
130 case n: throw operation_error<n>(); \
131 /**/
132
133 #define BOOST_PP_LOCAL_LIMITS (1, BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR)
134 #include BOOST_PP_LOCAL_ITERATE()
135 #undef BOOST_PP_LOCAL_MACRO
136
137 default:
138 break;
139 }
140 }
141
remove_operation(operation_sequence & seq,int id)142 inline void operation::remove_operation(operation_sequence& seq, int id)
143 {
144 seq.remove_operation(id);
145 }
146
147 //--------------Implementation of operation_sequence--------------------------//
148
new_operation(int id,int error_code)149 inline operation operation_sequence::new_operation(int id, int error_code)
150 {
151 using namespace std;
152 if ( error_code < 0 ||
153 (error_code > BOOST_IOSTREAMS_TEST_MAX_OPERATION_ERROR &&
154 error_code != INT_MAX) )
155 {
156 throw runtime_error( string("The error code ") +
157 lexical_cast<string>(error_code) +
158 " is out of range" );
159 }
160 if (last_executed_ != INT_MIN)
161 throw runtime_error( "Operations in progress; call reset() "
162 "before creating more operations" );
163 map_type::const_iterator it = operations_.find(id);
164 if (it != operations_.end())
165 throw runtime_error( string("The operation ") +
166 lexical_cast<string>(id) +
167 " already exists" );
168 operation op(*this, id, error_code);
169 operations_.insert(make_pair(id, ptr_type(op.pimpl_)));
170 return op;
171 }
172
message() const173 inline std::string operation_sequence::message() const
174 {
175 using namespace std;
176 if (success_)
177 return "success";
178 std::string msg = failed_ ?
179 "operations occurred out of order: " :
180 "operation sequence is incomplete: ";
181 typedef vector<int>::size_type size_type;
182 for (size_type z = 0, n = log_.size(); z < n; ++z) {
183 msg += lexical_cast<string>(log_[z]);
184 if (z < n - 1)
185 msg += ',';
186 }
187 return msg;
188 }
189
reset()190 inline void operation_sequence::reset()
191 {
192 log_.clear();
193 total_executed_ = 0;
194 last_executed_ = INT_MIN;
195 success_ = false;
196 failed_ = false;
197 }
198
execute(int id)199 inline void operation_sequence::execute(int id)
200 {
201 log_.push_back(id);
202 if (!failed_ && last_executed_ < id) {
203 if (++total_executed_ == operations_.size())
204 success_ = true;
205 last_executed_ = id;
206 } else {
207 success_ = false;
208 failed_ = true;
209 }
210 }
211
remove_operation(int id)212 inline void operation_sequence::remove_operation(int id)
213 {
214 using namespace std;
215 map_type::iterator it = operations_.find(id);
216 if (it == operations_.end())
217 throw runtime_error( string("No such operation: ") +
218 lexical_cast<string>(id) );
219 operations_.erase(it);
220 }
221
222 } } } // End namespace boost::iostreams::test.
223
224 #endif // #ifndef BOOST_IOSTREAMS_TEST_OPERATION_SEQUENCE_HPP_INCLUDED
225