1 // Copyright 2012 The Kyua Authors. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // * Neither the name of Google Inc. nor the names of its contributors 14 // may be used to endorse or promote products derived from this software 15 // without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #include "utils/config/parser.hpp" 30 31 #include <stdexcept> 32 33 #include <atf-c++.hpp> 34 35 #include "utils/config/exceptions.hpp" 36 #include "utils/config/parser.hpp" 37 #include "utils/config/tree.ipp" 38 #include "utils/format/macros.hpp" 39 #include "utils/fs/path.hpp" 40 41 namespace config = utils::config; 42 namespace fs = utils::fs; 43 44 45 namespace { 46 47 48 /// Implementation of a parser for testing purposes. 49 class mock_parser : public config::parser { 50 /// Initializes the tree keys before reading the file. 51 /// 52 /// \param [in,out] tree The tree in which to define the key structure. 53 /// \param syntax_version The version of the file format as specified in the 54 /// configuration file. 55 void 56 setup(config::tree& tree, const int syntax_version) 57 { 58 if (syntax_version == 1) { 59 // Do nothing on config_tree. 60 } else if (syntax_version == 2) { 61 tree.define< config::string_node >("top_string"); 62 tree.define< config::int_node >("inner.int"); 63 tree.define_dynamic("inner.dynamic"); 64 } else { 65 throw std::runtime_error(F("Unknown syntax version %s") % 66 syntax_version); 67 } 68 } 69 70 public: 71 /// Initializes a parser. 72 /// 73 /// \param tree The mock config tree to parse. 74 mock_parser(config::tree& tree) : 75 config::parser(tree) 76 { 77 } 78 }; 79 80 81 } // anonymous namespace 82 83 84 ATF_TEST_CASE_WITHOUT_HEAD(no_keys__ok); 85 ATF_TEST_CASE_BODY(no_keys__ok) 86 { 87 atf::utils::create_file( 88 "output.lua", 89 "syntax(2)\n" 90 "local foo = 'value'\n"); 91 92 config::tree tree; 93 mock_parser(tree).parse(fs::path("output.lua")); 94 ATF_REQUIRE_THROW(config::unknown_key_error, 95 tree.lookup< config::string_node >("foo")); 96 } 97 98 99 ATF_TEST_CASE_WITHOUT_HEAD(no_keys__unknown_key); 100 ATF_TEST_CASE_BODY(no_keys__unknown_key) 101 { 102 atf::utils::create_file( 103 "output.lua", 104 "syntax(2)\n" 105 "foo = 'value'\n"); 106 107 config::tree tree; 108 ATF_REQUIRE_THROW_RE(config::syntax_error, "foo", 109 mock_parser(tree).parse(fs::path("output.lua"))); 110 } 111 112 113 ATF_TEST_CASE_WITHOUT_HEAD(some_keys__ok); 114 ATF_TEST_CASE_BODY(some_keys__ok) 115 { 116 atf::utils::create_file( 117 "output.lua", 118 "syntax(2)\n" 119 "top_string = 'foo'\n" 120 "inner.int = 12345\n" 121 "inner.dynamic.foo = 78\n" 122 "inner.dynamic.bar = 'some text'\n"); 123 124 config::tree tree; 125 mock_parser(tree).parse(fs::path("output.lua")); 126 ATF_REQUIRE_EQ("foo", tree.lookup< config::string_node >("top_string")); 127 ATF_REQUIRE_EQ(12345, tree.lookup< config::int_node >("inner.int")); 128 ATF_REQUIRE_EQ("78", 129 tree.lookup< config::string_node >("inner.dynamic.foo")); 130 ATF_REQUIRE_EQ("some text", 131 tree.lookup< config::string_node >("inner.dynamic.bar")); 132 } 133 134 135 ATF_TEST_CASE_WITHOUT_HEAD(some_keys__not_strict); 136 ATF_TEST_CASE_BODY(some_keys__not_strict) 137 { 138 atf::utils::create_file( 139 "output.lua", 140 "syntax(2)\n" 141 "top_string = 'foo'\n" 142 "unknown_string = 'bar'\n" 143 "top_string = 'baz'\n"); 144 145 config::tree tree(false); 146 mock_parser(tree).parse(fs::path("output.lua")); 147 ATF_REQUIRE_EQ("baz", tree.lookup< config::string_node >("top_string")); 148 ATF_REQUIRE(!tree.is_set("unknown_string")); 149 } 150 151 152 ATF_TEST_CASE_WITHOUT_HEAD(some_keys__unknown_key); 153 ATF_TEST_CASE_BODY(some_keys__unknown_key) 154 { 155 atf::utils::create_file( 156 "output.lua", 157 "syntax(2)\n" 158 "top_string2 = 'foo'\n"); 159 config::tree tree1; 160 ATF_REQUIRE_THROW_RE(config::syntax_error, 161 "Unknown configuration property 'top_string2'", 162 mock_parser(tree1).parse(fs::path("output.lua"))); 163 164 atf::utils::create_file( 165 "output.lua", 166 "syntax(2)\n" 167 "inner.int2 = 12345\n"); 168 config::tree tree2; 169 ATF_REQUIRE_THROW_RE(config::syntax_error, 170 "Unknown configuration property 'inner.int2'", 171 mock_parser(tree2).parse(fs::path("output.lua"))); 172 } 173 174 175 ATF_TEST_CASE_WITHOUT_HEAD(invalid_syntax); 176 ATF_TEST_CASE_BODY(invalid_syntax) 177 { 178 config::tree tree; 179 180 atf::utils::create_file("output.lua", "syntax(56)\n"); 181 ATF_REQUIRE_THROW_RE(config::syntax_error, 182 "Unknown syntax version 56", 183 mock_parser(tree).parse(fs::path("output.lua"))); 184 } 185 186 187 ATF_TEST_CASE_WITHOUT_HEAD(syntax_deprecated_format); 188 ATF_TEST_CASE_BODY(syntax_deprecated_format) 189 { 190 config::tree tree; 191 192 atf::utils::create_file("output.lua", "syntax('config', 1)\n"); 193 (void)mock_parser(tree).parse(fs::path("output.lua")); 194 195 atf::utils::create_file("output.lua", "syntax('foo', 1)\n"); 196 ATF_REQUIRE_THROW_RE(config::syntax_error, "must be 'config'", 197 mock_parser(tree).parse(fs::path("output.lua"))); 198 199 atf::utils::create_file("output.lua", "syntax('config', 2)\n"); 200 ATF_REQUIRE_THROW_RE(config::syntax_error, "only takes one argument", 201 mock_parser(tree).parse(fs::path("output.lua"))); 202 } 203 204 205 ATF_TEST_CASE_WITHOUT_HEAD(syntax_not_called); 206 ATF_TEST_CASE_BODY(syntax_not_called) 207 { 208 config::tree tree; 209 tree.define< config::int_node >("var"); 210 211 atf::utils::create_file("output.lua", "var = 3\n"); 212 ATF_REQUIRE_THROW_RE(config::syntax_error, "No syntax defined", 213 mock_parser(tree).parse(fs::path("output.lua"))); 214 215 ATF_REQUIRE(!tree.is_set("var")); 216 } 217 218 219 ATF_TEST_CASE_WITHOUT_HEAD(syntax_called_more_than_once); 220 ATF_TEST_CASE_BODY(syntax_called_more_than_once) 221 { 222 config::tree tree; 223 tree.define< config::int_node >("var"); 224 225 atf::utils::create_file( 226 "output.lua", 227 "syntax(2)\n" 228 "var = 3\n" 229 "syntax(2)\n" 230 "var = 5\n"); 231 ATF_REQUIRE_THROW_RE(config::syntax_error, 232 "syntax\\(\\) can only be called once", 233 mock_parser(tree).parse(fs::path("output.lua"))); 234 235 ATF_REQUIRE_EQ(3, tree.lookup< config::int_node >("var")); 236 } 237 238 239 ATF_INIT_TEST_CASES(tcs) 240 { 241 ATF_ADD_TEST_CASE(tcs, no_keys__ok); 242 ATF_ADD_TEST_CASE(tcs, no_keys__unknown_key); 243 244 ATF_ADD_TEST_CASE(tcs, some_keys__ok); 245 ATF_ADD_TEST_CASE(tcs, some_keys__not_strict); 246 ATF_ADD_TEST_CASE(tcs, some_keys__unknown_key); 247 248 ATF_ADD_TEST_CASE(tcs, invalid_syntax); 249 ATF_ADD_TEST_CASE(tcs, syntax_deprecated_format); 250 ATF_ADD_TEST_CASE(tcs, syntax_not_called); 251 ATF_ADD_TEST_CASE(tcs, syntax_called_more_than_once); 252 } 253