text: /*
   * 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.
   *
   * Tests the function templates boost::iostreams::detail::execute_all and
   * boost::iostreams::detail::execute_foreach
   *
   * File:        libs/iostreams/test/execute_test.cpp
   * Date:        Thu Dec 06 13:21:54 MST 2007
   * Copyright:   2007-2008 CodeRage, LLC
   * Author:      Jonathan Turkanis
   * Contact:     turkanis at coderage dot com
   */
  
  #include <boost/iostreams/detail/execute.hpp>
  #include <boost/test/test_tools.hpp>
  #include <boost/test/unit_test.hpp>  
  
  using namespace std;
  using namespace boost;
  using namespace boost::iostreams;
  using namespace boost::iostreams::detail;
  using boost::unit_test::test_suite;
  
  // Function object that sets a boolean flag and returns a value
  // specified at construction
  template<typename Result>
  class operation {
  public:
      typedef Result result_type;
      explicit operation(Result r, bool& executed) 
          : r_(r), executed_(executed) 
          { }
      Result operator()() const 
      { 
          executed_ = true;
          return r_; 
      }
  private:
      operation& operator=(const operation&);
      Result  r_;
      bool&   executed_;  
  };
  
  // Specialization for void return
  template<>
  class operation<void> {
  public:
      typedef void result_type;
      explicit operation(bool& executed) : executed_(executed) { }
      void operator()() const { executed_ = true; }
  private:
      operation& operator=(const operation&);
      bool& executed_; 
  };
  
  // Simple exception class with error code built in to type
  template<int Code>
  struct error { };
  
  // Function object that sets a boolean flag and throws an exception
  template<int Code>
  class thrower {
  public:
      typedef void result_type;
      explicit thrower(bool& executed) : executed_(executed) { }
      void operator()() const 
      { 
          executed_ = true;
          throw error<Code>(); 
      }
  private:
      thrower& operator=(const thrower&);
      bool& executed_; 
  };
  
  // Function object for use by foreach_test
  class foreach_func {
  public:
      typedef void result_type;
      explicit foreach_func(int& count) : count_(count) { }
      void operator()(int x) const
      {
          ++count_;
          switch (x) {
          case 0: throw error<0>();
          case 1: throw error<1>();
          case 2: throw error<2>();
          case 3: throw error<3>();
          case 4: throw error<4>();
          case 5: throw error<5>();
          case 6: throw error<6>();
          case 7: throw error<7>();
          case 8: throw error<8>();
          case 9: throw error<9>();
          default:
              break;
          }
      }
  private:
      foreach_func& operator=(const foreach_func&);
      int&  count_; // Number of times operator() has been called
  };
  
  void success_test()
  {
      // Test returning an int
      {
          bool executed = false;
          BOOST_CHECK(execute_all(operation<int>(9, executed)) == 9);
          BOOST_CHECK(executed);
      }
  
      // Test returning void
      {
          bool executed = false;
          execute_all(operation<void>(executed));
          BOOST_CHECK(executed);
      }
  
      // Test returning an int with one cleanup operation
      {
          bool executed = false, cleaned_up = false;
          BOOST_CHECK(
              execute_all(
                  operation<int>(9, executed),
                  operation<void>(cleaned_up)
              ) == 9
          );
          BOOST_CHECK(executed && cleaned_up);
      }
  
      // Test returning void with one cleanup operation
      {
          bool executed = false, cleaned_up = false;
          execute_all(
              operation<void>(executed),
              operation<void>(cleaned_up)
          );
          BOOST_CHECK(executed && cleaned_up);
      }
  
      // Test returning an int with two cleanup operations
      {
          bool executed = false, cleaned_up1 = false, cleaned_up2 = false;
          BOOST_CHECK(
              execute_all(
                  operation<int>(9, executed),
                  operation<void>(cleaned_up1),
                  operation<void>(cleaned_up2)
              ) == 9
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2);
      }
  
      // Test returning void with two cleanup operations
      {
          bool executed = false, cleaned_up1 = false, cleaned_up2 = false;
          execute_all(
              operation<void>(executed),
              operation<void>(cleaned_up1),
              operation<void>(cleaned_up2)
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2);
      }
  
      // Test returning an int with three cleanup operations
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK(
              execute_all(
                  operation<int>(9, executed),
                  operation<void>(cleaned_up1),
                  operation<void>(cleaned_up2),
                  operation<void>(cleaned_up3)
              ) == 9
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  
      // Test returning void with three cleanup operations
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          execute_all(
              operation<void>(executed),
              operation<void>(cleaned_up1),
              operation<void>(cleaned_up2),
              operation<void>(cleaned_up3)
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  }
  
  void operation_throws_test()
  {
      // Test primary operation throwing with no cleanup operations
      {
          bool executed = false;
          BOOST_CHECK_THROW(
              execute_all(thrower<0>(executed)),
              error<0>
          );
          BOOST_CHECK(executed);
      }
  
      // Test primary operation throwing with one cleanup operation
      {
          bool executed = false, cleaned_up = false;
          BOOST_CHECK_THROW(
              execute_all(
                  thrower<0>(executed),
                  operation<void>(cleaned_up)
              ),
              error<0>
          );
          BOOST_CHECK(executed && cleaned_up);
      }
  
      // Test primary operation throwing with two cleanup operations
      {
          bool executed = false, cleaned_up1 = false, cleaned_up2 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  thrower<0>(executed),
                  operation<void>(cleaned_up1),
                  operation<void>(cleaned_up2)
              ), 
              error<0>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2);
      }
  
      // Test primary operation throwing with three cleanup operations
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  thrower<0>(executed),
                  operation<void>(cleaned_up1),
                  operation<void>(cleaned_up2),
                  operation<void>(cleaned_up3)
              ), 
              error<0>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  }
  
  void cleanup_throws_test()
  {
      // Test single cleanup operation that throws
      {
          bool executed = false, cleaned_up = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  thrower<1>(cleaned_up)
              ),
              error<1>
          );
          BOOST_CHECK(executed && cleaned_up);
      }
  
      // Test fist of two cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, cleaned_up2 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  thrower<1>(cleaned_up1),
                  operation<void>(cleaned_up2)
              ),
              error<1>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2);
      }
  
      // Test second of two cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, cleaned_up2 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  operation<void>(cleaned_up1),
                  thrower<2>(cleaned_up2)
              ),
              error<2>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2);
      }
  
      // Test first of three cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  thrower<1>(cleaned_up1),
                  operation<void>(cleaned_up2),
                  operation<void>(cleaned_up3)
              ), 
              error<1>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  
      // Test second of three cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  operation<void>(cleaned_up1),
                  thrower<2>(cleaned_up2),
                  operation<void>(cleaned_up3)
              ), 
              error<2>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  
      // Test third of three cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  operation<void>(cleaned_up1),
                  operation<void>(cleaned_up2),
                  thrower<3>(cleaned_up3)
              ), 
              error<3>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  }
  
  void multiple_exceptions_test()
  {
      // Test primary operation and cleanup operation throwing
      {
          bool executed = false, cleaned_up = false;
          BOOST_CHECK_THROW(
              execute_all(
                  thrower<0>(executed),
                  thrower<1>(cleaned_up)
              ),
              error<0>
          );
          BOOST_CHECK(executed && cleaned_up);
      }
  
      // Test primary operation and first of two cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, cleaned_up2 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  thrower<0>(executed),
                  thrower<1>(cleaned_up1),
                  operation<void>(cleaned_up2)
              ),
              error<0>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2);
      }
  
      // Test primary operation and second of two cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, cleaned_up2 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  thrower<0>(executed),
                  operation<void>(cleaned_up1),
                  thrower<2>(cleaned_up2)
              ),
              error<0>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2);
      }
  
      // Test two cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, cleaned_up2 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  thrower<1>(cleaned_up1),
                  thrower<2>(cleaned_up2)
              ),
              error<1>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2);
      }
  
      // Test primary operation and first of three cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  thrower<0>(executed),
                  thrower<1>(cleaned_up1),
                  operation<void>(cleaned_up2),
                  operation<void>(cleaned_up3)
              ),
              error<0>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  
      // Test primary operation and second of three cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  thrower<0>(executed),
                  operation<void>(cleaned_up1),
                  thrower<2>(cleaned_up2),
                  operation<void>(cleaned_up3)
              ),
              error<0>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  
      // Test primary operation and third of three cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  thrower<0>(executed),
                  operation<void>(cleaned_up1),
                  operation<void>(cleaned_up2),
                  thrower<3>(cleaned_up3)
              ),
              error<0>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  
      // Test first and second of three cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  thrower<1>(cleaned_up1),
                  thrower<2>(cleaned_up2),
                  operation<void>(cleaned_up3)
              ),
              error<1>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  
      // Test first and third of three cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  thrower<1>(cleaned_up1),
                  operation<void>(cleaned_up2),
                  thrower<3>(cleaned_up3)
              ),
              error<1>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  
      // Test second and third of three cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  operation<void>(cleaned_up1),
                  thrower<2>(cleaned_up2),
                  thrower<3>(cleaned_up3)
              ),
              error<2>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  
      // Test three cleanup operations throwing
      {
          bool executed = false, cleaned_up1 = false, 
               cleaned_up2 = false, cleaned_up3 = false;
          BOOST_CHECK_THROW(
              execute_all(
                  operation<void>(executed),
                  thrower<1>(cleaned_up1),
                  thrower<2>(cleaned_up2),
                  thrower<3>(cleaned_up3)
              ),
              error<1>
          );
          BOOST_CHECK(executed && cleaned_up1 && cleaned_up2 && cleaned_up3);
      }
  }
  
  #define ARRAY_SIZE(ar) (sizeof(ar) / sizeof(ar[0]))
  
  void foreach_test()
  {
      // Test case where neither of two operations throws
      {
          int count = 0;
          int seq[] = {-1, -1};
          BOOST_CHECK_NO_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count))
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where first of two operations throws
      {
          int count = 0;
          int seq[] = {0, -1};
          BOOST_CHECK_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count)),
              error<0>
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where second of two operations throws
      {
          int count = 0;
          int seq[] = {-1, 1};
          BOOST_CHECK_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count)),
              error<1>
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where both of two operations throw
      {
          int count = 0;
          int seq[] = {0, 1};
          BOOST_CHECK_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count)),
              error<0>
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where none of three operations throws
      {
          int count = 0;
          int seq[] = {-1, -1, -1};
          BOOST_CHECK_NO_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count))
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where first of three operations throw
      {
          int count = 0;
          int seq[] = {0, -1, -1};
          BOOST_CHECK_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count)),
              error<0>
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where second of three operations throw
      {
          int count = 0;
          int seq[] = {-1, 1, -1};
          BOOST_CHECK_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count)),
              error<1>
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where third of three operations throw
      {
          int count = 0;
          int seq[] = {-1, -1, 2};
          BOOST_CHECK_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count)),
              error<2>
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where first and second of three operations throw
      {
          int count = 0;
          int seq[] = {0, 1, -1};
          BOOST_CHECK_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count)),
              error<0>
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where first and third of three operations throw
      {
          int count = 0;
          int seq[] = {0, -1, 2};
          BOOST_CHECK_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count)),
              error<0>
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where second and third of three operations throw
      {
          int count = 0;
          int seq[] = {-1, 1, 2};
          BOOST_CHECK_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count)),
              error<1>
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  
      // Test case where three of three operations throw
      {
          int count = 0;
          int seq[] = {0, 1, 2};
          BOOST_CHECK_THROW(
              execute_foreach(seq, seq + ARRAY_SIZE(seq), foreach_func(count)),
              error<0>
          );
          BOOST_CHECK(count == ARRAY_SIZE(seq));
      }
  }
  
  test_suite* init_unit_test_suite(int, char* []) 
  {
      test_suite* test = BOOST_TEST_SUITE("execute test");
      test->add(BOOST_TEST_CASE(&success_test));
      test->add(BOOST_TEST_CASE(&operation_throws_test));
      test->add(BOOST_TEST_CASE(&cleanup_throws_test));
      test->add(BOOST_TEST_CASE(&multiple_exceptions_test));
      test->add(BOOST_TEST_CASE(&foreach_test));
      return test;
  }
  