diff options
author | Marcin Siodelski <marcin@isc.org> | 2014-01-17 12:44:08 +0100 |
---|---|---|
committer | Marcin Siodelski <marcin@isc.org> | 2014-01-17 12:44:08 +0100 |
commit | 16b5a194f9e8767ab77b1c344f3fa37659f28c22 (patch) | |
tree | 8c2fffe7bbccfefa293efbb39b6709582ac48eec /src/lib/dhcp | |
parent | Merge branch 'trac1033' (diff) | |
download | kea-16b5a194f9e8767ab77b1c344f3fa37659f28c22.tar.xz kea-16b5a194f9e8767ab77b1c344f3fa37659f28c22.zip |
[3292] Fixed option definition class to handle booleans carried as strings.
Diffstat (limited to 'src/lib/dhcp')
-rw-r--r-- | src/lib/dhcp/option_definition.cc | 42 | ||||
-rw-r--r-- | src/lib/dhcp/tests/option_definition_unittest.cc | 119 |
2 files changed, 151 insertions, 10 deletions
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc index be1a5e7890..adbcdb162c 100644 --- a/src/lib/dhcp/option_definition.cc +++ b/src/lib/dhcp/option_definition.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -411,33 +411,59 @@ OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str) "unable to do lexical cast to non-integer and" << " non-boolean data type"); } + + // The lexical cast will not handle conversion of the string holding + // "true" or "false". Therefore we will do the conversion on our own. + // If the string value doesn't match any of "true" or "false", it + // is possible that "0" or "1" has been specified, which we are + // ok with. For conversion of "0" or "1" we will try lexical cast + // below. + if (OptionDataTypeTraits<T>::type == OPT_BOOLEAN_TYPE) { + if (value_str == "true") { + return (true); + + } else if (value_str == "false") { + return (false); + + } + } + // We use the 64-bit value here because it has wider range than // any other type we use here and it allows to detect out of // bounds conditions e.g. negative value specified for uintX_t // data type. Obviously if the value exceeds the limits of int64 // this function will not handle that properly. + // Note that with the conversion below we also handle boolean + // values specified as "0" or "1". int64_t result = 0; try { result = boost::lexical_cast<int64_t>(value_str); - } catch (const boost::bad_lexical_cast& ex) { + } catch (const boost::bad_lexical_cast&) { // Prepare error message here. std::string data_type_str = "boolean"; if (OptionDataTypeTraits<T>::integer_type) { data_type_str = "integer"; } - isc_throw(BadDataTypeCast, "unable to do lexical cast to " - << data_type_str << " data type for value " - << value_str << ": " << ex.what()); + isc_throw(BadDataTypeCast, "unable to convert the value '" + << value_str << "' to " << data_type_str + << " data type"); } // Perform range checks for integer values only (exclude bool values). if (OptionDataTypeTraits<T>::integer_type) { if (result > numeric_limits<T>::max() || result < numeric_limits<T>::min()) { - isc_throw(BadDataTypeCast, "unable to do lexical cast for value " - << value_str << ". This value is expected to be" - << " in the range of " << numeric_limits<T>::min() + isc_throw(BadDataTypeCast, "unable to convert '" + << value_str << "' to numeric type. This value is " + " expected to be in the range of " + << numeric_limits<T>::min() << ".." << numeric_limits<T>::max()); } + // The specified value is of the boolean type and we are checking + // that it has successfuly converted to 0 or 1. Any other numeric + // value is not accepted to represent boolean value. + } else if (result != 1 && result != 0) { + isc_throw(BadDataTypeCast, "unable to convert '" << value_str + << "' to boolean data type"); } return (static_cast<T>(result)); } diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc index 357ed562c8..3a0819a141 100644 --- a/src/lib/dhcp/tests/option_definition_unittest.cc +++ b/src/lib/dhcp/tests/option_definition_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -663,6 +663,120 @@ TEST_F(OptionDefinitionTest, recordIAAddr6Tokenized) { EXPECT_EQ(5678, option_cast_v6->getValid()); } +// The purpose of this test is to verify that the definition for option +// that comprises a boolean value can be created and that this definition +// can be used to create and option with a single boolean value. +TEST_F(OptionDefinitionTest, boolValue) { + // The IP Forwarding option comprises one boolean value. + OptionDefinition opt_def("ip-forwarding", DHO_IP_FORWARDING, + "boolean"); + + OptionPtr option_v4; + // Use an option buffer which holds one value of 1 (true). + ASSERT_NO_THROW( + option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, + OptionBuffer(1, 1)); + ); + ASSERT_TRUE(typeid(*option_v4) == typeid(OptionCustom)); + // Validate parsed value in the received option. + boost::shared_ptr<OptionCustom> option_cast_v4 = + boost::static_pointer_cast<OptionCustom>(option_v4); + EXPECT_TRUE(option_cast_v4->readBoolean()); + + // Repeat the test above, but set the value to 0 (false). + ASSERT_NO_THROW( + option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, + OptionBuffer(1, 0)); + ); + option_cast_v4 = boost::static_pointer_cast<OptionCustom>(option_v4); + EXPECT_FALSE(option_cast_v4->readBoolean()); + + // Try to provide zero-length buffer. Expect exception. + EXPECT_THROW( + option_v4 = opt_def.optionFactory(Option::V6, DHO_IP_FORWARDING, + OptionBuffer()), + InvalidOptionValue + ); + +} + +// The purpose of this test is to verify that definition for option that +// comprises single boolean value can be created and that this definition +// can be used to create an option holding a single boolean value. The +// boolean value is converted from a string which is expected to hold +// the following values: "true", "false", "1" or "0". For all other +// values exception should be thrown. +TEST_F(OptionDefinitionTest, boolTokenized) { + OptionDefinition opt_def("ip-forwarding", DHO_IP_FORWARDING, "boolean"); + + OptionPtr option_v4; + std::vector<std::string> values; + // Provide two boolean values. It is expected that only the first + // one will be used. + values.push_back("true"); + values.push_back("false"); + ASSERT_NO_THROW( + option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, + values); + ); + ASSERT_TRUE(typeid(*option_v4) == typeid(OptionCustom)); + // Validate the value. + OptionCustomPtr option_cast_v4 = + boost::static_pointer_cast<OptionCustom>(option_v4); + EXPECT_TRUE(option_cast_v4->readBoolean()); + + // Repeat the test but for "false" value this time. + values[0] = "false"; + values[1] = "true"; + ASSERT_NO_THROW( + option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, + values); + ); + ASSERT_TRUE(typeid(*option_v4) == typeid(OptionCustom)); + // Validate the value. + option_cast_v4 = boost::static_pointer_cast<OptionCustom>(option_v4); + EXPECT_FALSE(option_cast_v4->readBoolean()); + + // Check if that will work for numeric values. + values[0] = "0"; + values[1] = "1"; + ASSERT_NO_THROW( + option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, + values); + ); + ASSERT_TRUE(typeid(*option_v4) == typeid(OptionCustom)); + // Validate the value. + option_cast_v4 = boost::static_pointer_cast<OptionCustom>(option_v4); + EXPECT_FALSE(option_cast_v4->readBoolean()); + + // Swap numeric values and test if it works for "true" case. + values[0] = "1"; + values[1] = "0"; + ASSERT_NO_THROW( + option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, + values); + ); + ASSERT_TRUE(typeid(*option_v4) == typeid(OptionCustom)); + // Validate the value. + option_cast_v4 = boost::static_pointer_cast<OptionCustom>(option_v4); + EXPECT_TRUE(option_cast_v4->readBoolean()); + + // A conversion of non-numeric value to boolean should fail if + // this value is neither "true" nor "false". + values[0] = "garbage"; + values[1] = "false"; + EXPECT_THROW(opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, values), + isc::dhcp::BadDataTypeCast); + + // A conversion of numeric value to boolean should fail if this value + // is neither "0" nor "1". + values[0] = "2"; + values[1] = "0"; + EXPECT_THROW(opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, values), + isc::dhcp::BadDataTypeCast); + +} + // The purpose of this test is to verify that definition for option that // comprises single uint8 value can be created and that this definition // can be used to create an option with single uint8 value. @@ -672,7 +786,8 @@ TEST_F(OptionDefinitionTest, uint8) { OptionPtr option_v6; // Try to use correct buffer length = 1 byte. ASSERT_NO_THROW( - option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, OptionBuffer(1, 1)); + option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, + OptionBuffer(1, 1)); ); ASSERT_TRUE(typeid(*option_v6) == typeid(OptionInt<uint8_t>)); // Validate the value. |