text: /**
   * Copyright (c) 2011-2017 libbitcoin developers (see AUTHORS)
   *
   * This file is part of libbitcoin.
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Affero General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Affero General Public License for more details.
   *
   * You should have received a copy of the GNU Affero General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  #include <iostream>
  #include <utility>
  #include <vector>
  #include <boost/filesystem.hpp>
  #include <boost/program_options.hpp>
  #include <boost/test/test_tools.hpp>
  #include <boost/test/unit_test_suite.hpp>
  #include <bitcoin/bitcoin.hpp>
  
  namespace po = boost::program_options;
  using namespace bc::config;
  
  enum opt
  {
      short_long = 0,
      shorty,
      longy,
      simple,
      defaulty,
      required,
      toggled,
      vector,
      multitoken
  };
  
  static void load_test_options(po::options_description& options)
  {
      using namespace boost::filesystem;
      using namespace boost::program_options;
      options.add_options()
          ("short_long,s", "Long and short name.")
          (",m", "Short name only.")
          ("longy", value<std::string>()->required(), "Long name only.")
          ("simple", value<std::string>(), "Simple string.")
          ("defaulty", value<bool>()->default_value(true), "Defaulted bool.")
          ("required", value<path>()->required(), "Required path.")
          ("toggled", value<bool>()->zero_tokens(), "Toggle only bool.")
          /* The enumerability of the data types does not control multiple,
             instance behavior but it is necessary to capture multiples. */
          ("VECTOR", value<std::vector<std::string>>(), "String vector.")
          ("multitoken", value<int>()->multitoken(), "Multi-token int.");
  }
  
  static void load_test_arguments(argument_list& arguments)
  {
      arguments.push_back(argument_pair("longy", 1));
      arguments.push_back(argument_pair("simple", 2));
      arguments.push_back(argument_pair("defaulty", 3));
      arguments.push_back(argument_pair("required", 4));
      arguments.push_back(argument_pair("VECTOR", 5));
      arguments.push_back(argument_pair("multitoken", -1));
  }
  
  #define CONFIG_TEST_PARAMETER_SETUP(index) \
      po::options_description options; \
      load_test_options(options); \
      argument_list names; \
      load_test_arguments(names); \
      auto option = *(options.options()[index]); \
      bc::config::parameter parameter
  
  #define CONFIG_TEST_PARAMETER_OPTIONS_SETUP(index) \
      po::options_description options; \
      load_test_options(options); \
      auto option = *(options.options()[index]); \
      bc::config::parameter parameter
  
  BOOST_AUTO_TEST_SUITE(parameter_tests)
  
  // ------------------------------------------------------------------------- //
  BOOST_AUTO_TEST_SUITE(parameter__position)
  
  BOOST_AUTO_TEST_CASE(parameter__position__short_and_long__returns_not_positional)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::short_long);
      BOOST_REQUIRE_EQUAL(parameter.position(option, names), parameter::not_positional);
  }
  
  BOOST_AUTO_TEST_CASE(parameter__position__short_only__returns_not_positional)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::shorty);
      BOOST_REQUIRE_EQUAL(parameter.position(option, names), parameter::not_positional);
  }
  
  BOOST_AUTO_TEST_CASE(parameter__position__long_only__returns_expected_position)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::longy);
      BOOST_REQUIRE_EQUAL(parameter.position(option, names), 0);
  }
  
  BOOST_AUTO_TEST_CASE(parameter__position__simple__returns_expected_position)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::simple);
      BOOST_REQUIRE_EQUAL(parameter.position(option, names), 1);
  }
  
  BOOST_AUTO_TEST_CASE(parameter__position__defaulty__returns_expected_position)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::defaulty);
      BOOST_REQUIRE_EQUAL(parameter.position(option, names), 2);
  }
  
  BOOST_AUTO_TEST_CASE(parameter__position__required__returns_expected_position)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::required);
      BOOST_REQUIRE_EQUAL(parameter.position(option, names), 3);
  }
  
  BOOST_AUTO_TEST_CASE(parameter__position__toggled__returns_not_positional)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::toggled);
      BOOST_REQUIRE_EQUAL(parameter.position(option, names), parameter::not_positional);
  }
  
  BOOST_AUTO_TEST_CASE(parameter__position__vector__returns_expected_position)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::vector);
      BOOST_REQUIRE_EQUAL(parameter.position(option, names), 4);
  }
  
  BOOST_AUTO_TEST_CASE(parameter__position__multitoken__returns_expected_position)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::multitoken);
      BOOST_REQUIRE_EQUAL(parameter.position(option, names), 5);
  }
  
  BOOST_AUTO_TEST_SUITE_END()
  
  // ------------------------------------------------------------------------- //
  BOOST_AUTO_TEST_SUITE(parameter__initialize)
  
  BOOST_AUTO_TEST_CASE(parameter__initialize__short_long__sets_limit_0)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::short_long);
      parameter.initialize(option, names);
      BOOST_REQUIRE_EQUAL(parameter.get_position(), parameter::not_positional);
      BOOST_REQUIRE_EQUAL(parameter.get_args_limit(), 0u);
      BOOST_REQUIRE_EQUAL(parameter.get_required(), false);
      BOOST_REQUIRE_EQUAL(parameter.get_short_name(), 's');
      BOOST_REQUIRE_EQUAL(parameter.get_long_name(), "short_long");
      BOOST_REQUIRE_EQUAL(parameter.get_format_name(), "-s [ --short_long ]");
      BOOST_REQUIRE_EQUAL(parameter.get_format_parameter(), "");
      BOOST_REQUIRE_EQUAL(parameter.get_description(), "Long and short name.");
  }
  
  BOOST_AUTO_TEST_CASE(parameter__initialize__short_only__sets_limit_0)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::shorty);
      parameter.initialize(option, names);
      BOOST_REQUIRE_EQUAL(parameter.get_position(), parameter::not_positional);
      BOOST_REQUIRE_EQUAL(parameter.get_args_limit(), 0u);
      BOOST_REQUIRE_EQUAL(parameter.get_required(), false);
      BOOST_REQUIRE_EQUAL(parameter.get_short_name(), 'm');
      BOOST_REQUIRE_EQUAL(parameter.get_long_name(), "");
      // A boost 1.49 bug causes this test failure:
      // parameter.get_format_name() == "-m" failed [-m [ -- ] != -m]
      // But this behavior is not critical to operations, so we overlook it.
      // BOOST_REQUIRE_EQUAL(parameter.get_format_name(), "-m");
      BOOST_REQUIRE_EQUAL(parameter.get_format_parameter(), "");
      BOOST_REQUIRE_EQUAL(parameter.get_description(), "Short name only.");
  }
  
  BOOST_AUTO_TEST_CASE(parameter__initialize__long_only__sets_limit_0)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::longy);
      parameter.initialize(option, names);
      BOOST_REQUIRE_EQUAL(parameter.get_position(), 0);
      BOOST_REQUIRE_EQUAL(parameter.get_args_limit(), 1u);
      BOOST_REQUIRE_EQUAL(parameter.get_required(), true);
      BOOST_REQUIRE_EQUAL(parameter.get_short_name(), '\0');
      BOOST_REQUIRE_EQUAL(parameter.get_long_name(), "longy");
      BOOST_REQUIRE_EQUAL(parameter.get_format_name(), "--longy");
      BOOST_REQUIRE_EQUAL(parameter.get_format_parameter(), "arg");
      BOOST_REQUIRE_EQUAL(parameter.get_description(), "Long name only.");
  }
  
  BOOST_AUTO_TEST_CASE(parameter__initialize__simple__sets_limit_2)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::simple);
      parameter.initialize(option, names);
      BOOST_REQUIRE_EQUAL(parameter.get_position(), 1);
      BOOST_REQUIRE_EQUAL(parameter.get_args_limit(), 2u);
      BOOST_REQUIRE_EQUAL(parameter.get_required(), false);
      BOOST_REQUIRE_EQUAL(parameter.get_short_name(), '\0');
      BOOST_REQUIRE_EQUAL(parameter.get_long_name(), "simple");
      BOOST_REQUIRE_EQUAL(parameter.get_format_name(), "--simple");
      BOOST_REQUIRE_EQUAL(parameter.get_format_parameter(), "arg");
      BOOST_REQUIRE_EQUAL(parameter.get_description(), "Simple string.");
  }
  
  BOOST_AUTO_TEST_CASE(parameter__initialize__defaulted__sets_limit_3)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::defaulty);
      parameter.initialize(option, names);
      BOOST_REQUIRE_EQUAL(parameter.get_position(), 2);
      BOOST_REQUIRE_EQUAL(parameter.get_args_limit(), 3u);
      BOOST_REQUIRE_EQUAL(parameter.get_required(), false);
      BOOST_REQUIRE_EQUAL(parameter.get_short_name(), '\0');
      BOOST_REQUIRE_EQUAL(parameter.get_long_name(), "defaulty");
      BOOST_REQUIRE_EQUAL(parameter.get_format_name(), "--defaulty");
      BOOST_REQUIRE_EQUAL(parameter.get_description(), "Defaulted bool.");
  
      // The (=1) appears to be the default value (as int), i.e. (=true)
      // This makes the bool type indistinguishable at the metadata level from
      // an integer. So we must use zero_tokens() instead (see 'toggled' case).
      BOOST_REQUIRE_EQUAL(parameter.get_format_parameter(), "arg (=1)");
  }
  
  BOOST_AUTO_TEST_CASE(parameter__initialize__required__sets_limit_4)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::required);
      parameter.initialize(option, names);
      BOOST_REQUIRE_EQUAL(parameter.get_position(), 3);
      BOOST_REQUIRE_EQUAL(parameter.get_args_limit(), 4u);
      BOOST_REQUIRE_EQUAL(parameter.get_required(), true);
      BOOST_REQUIRE_EQUAL(parameter.get_short_name(), '\0');
      BOOST_REQUIRE_EQUAL(parameter.get_long_name(), "required");
      BOOST_REQUIRE_EQUAL(parameter.get_format_name(), "--required");
      BOOST_REQUIRE_EQUAL(parameter.get_format_parameter(), "arg");
      BOOST_REQUIRE_EQUAL(parameter.get_description(), "Required path.");
  }
  
  //
  BOOST_AUTO_TEST_CASE(parameter__initialize__toggled__sets_limit_0)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::toggled);
      parameter.initialize(option, names);
      BOOST_REQUIRE_EQUAL(parameter.get_required(), false);
      BOOST_REQUIRE_EQUAL(parameter.get_short_name(), '\0');
      BOOST_REQUIRE_EQUAL(parameter.get_long_name(), "toggled");
      BOOST_REQUIRE_EQUAL(parameter.get_format_name(), "--toggled");
      BOOST_REQUIRE_EQUAL(parameter.get_format_parameter(), "");
      BOOST_REQUIRE_EQUAL(parameter.get_description(), "Toggle only bool.");
  
      // This combination uniquely implies that the option is a toggle.
      BOOST_REQUIRE_EQUAL(parameter.get_position(), parameter::not_positional);
      BOOST_REQUIRE_EQUAL(parameter.get_args_limit(), 0u);
  }
  
  BOOST_AUTO_TEST_CASE(parameter__initialize__vector__sets_limit_1)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::vector);
      parameter.initialize(option, names);
      BOOST_REQUIRE_EQUAL(parameter.get_position(), 4);
      BOOST_REQUIRE_EQUAL(parameter.get_args_limit(), 5u);
      BOOST_REQUIRE_EQUAL(parameter.get_required(), false);
      BOOST_REQUIRE_EQUAL(parameter.get_short_name(), '\0');
      BOOST_REQUIRE_EQUAL(parameter.get_long_name(), "VECTOR");
      BOOST_REQUIRE_EQUAL(parameter.get_format_name(), "--VECTOR");
      BOOST_REQUIRE_EQUAL(parameter.get_format_parameter(), "arg");
      BOOST_REQUIRE_EQUAL(parameter.get_description(), "String vector.");
  }
  
  BOOST_AUTO_TEST_CASE(parameter__initialize__multitoken__sets_unlimited)
  {
      CONFIG_TEST_PARAMETER_SETUP(opt::multitoken);
      parameter.initialize(option, names);
      BOOST_REQUIRE_EQUAL(parameter.get_position(), 5);
      /* The positional "unlimited" limit is different than the named limit. */
      BOOST_REQUIRE_EQUAL(parameter.get_args_limit(), 0xFFFFFFFFu);
      BOOST_REQUIRE_EQUAL(parameter.get_required(), false);
      BOOST_REQUIRE_EQUAL(parameter.get_short_name(), '\0');
      BOOST_REQUIRE_EQUAL(parameter.get_long_name(), "multitoken");
      BOOST_REQUIRE_EQUAL(parameter.get_format_name(), "--multitoken");
      BOOST_REQUIRE_EQUAL(parameter.get_format_parameter(), "arg");
      BOOST_REQUIRE_EQUAL(parameter.get_description(), "Multi-token int.");
  }
  
  BOOST_AUTO_TEST_SUITE_END()
  
  // ------------------------------------------------------------------------- //
  BOOST_AUTO_TEST_SUITE(parameter__short_name)
  
  BOOST_AUTO_TEST_CASE(parameter__short_name__short_and_long__returns_short)
  {
      CONFIG_TEST_PARAMETER_OPTIONS_SETUP(opt::short_long);
      BOOST_REQUIRE_EQUAL(parameter.short_name(option), 's');
  }
  
  BOOST_AUTO_TEST_CASE(parameter__short_name__short_only__returns_short)
  {
      CONFIG_TEST_PARAMETER_OPTIONS_SETUP(opt::shorty);
      BOOST_REQUIRE_EQUAL(parameter.short_name(option), 'm');
  }
  
  BOOST_AUTO_TEST_CASE(parameter__short_name__long_only__returns_null_char)
  {
      CONFIG_TEST_PARAMETER_OPTIONS_SETUP(opt::longy);
      BOOST_REQUIRE_EQUAL(parameter.short_name(option), parameter::no_short_name);
  }
  
  BOOST_AUTO_TEST_SUITE_END()
  
  BOOST_AUTO_TEST_SUITE_END()
  