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 41namespace config = utils::config; 42namespace fs = utils::fs; 43 44 45namespace { 46 47 48/// Implementation of a parser for testing purposes. 49class 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 70public: 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 84ATF_TEST_CASE_WITHOUT_HEAD(no_keys__ok); 85ATF_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 99ATF_TEST_CASE_WITHOUT_HEAD(no_keys__unknown_key); 100ATF_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 113ATF_TEST_CASE_WITHOUT_HEAD(some_keys__ok); 114ATF_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 135ATF_TEST_CASE_WITHOUT_HEAD(some_keys__not_strict); 136ATF_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 152ATF_TEST_CASE_WITHOUT_HEAD(some_keys__unknown_key); 153ATF_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 175ATF_TEST_CASE_WITHOUT_HEAD(invalid_syntax); 176ATF_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 187ATF_TEST_CASE_WITHOUT_HEAD(syntax_deprecated_format); 188ATF_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 205ATF_TEST_CASE_WITHOUT_HEAD(syntax_not_called); 206ATF_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 219ATF_TEST_CASE_WITHOUT_HEAD(syntax_called_more_than_once); 220ATF_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 239ATF_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