1 // Copyright 2012 Google Inc. 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 mock_parser(config::tree& tree) : 73 config::parser(tree) 74 { 75 } 76 }; 77 78 79 } // anonymous namespace 80 81 82 ATF_TEST_CASE_WITHOUT_HEAD(no_keys__ok); 83 ATF_TEST_CASE_BODY(no_keys__ok) 84 { 85 atf::utils::create_file( 86 "output.lua", 87 "syntax(2)\n" 88 "local foo = 'value'\n"); 89 90 config::tree tree; 91 mock_parser(tree).parse(fs::path("output.lua")); 92 ATF_REQUIRE_THROW(config::unknown_key_error, 93 tree.lookup< config::string_node >("foo")); 94 } 95 96 97 ATF_TEST_CASE_WITHOUT_HEAD(no_keys__unknown_key); 98 ATF_TEST_CASE_BODY(no_keys__unknown_key) 99 { 100 atf::utils::create_file( 101 "output.lua", 102 "syntax(2)\n" 103 "foo = 'value'\n"); 104 105 config::tree tree; 106 ATF_REQUIRE_THROW_RE(config::syntax_error, "foo", 107 mock_parser(tree).parse(fs::path("output.lua"))); 108 } 109 110 111 ATF_TEST_CASE_WITHOUT_HEAD(some_keys__ok); 112 ATF_TEST_CASE_BODY(some_keys__ok) 113 { 114 atf::utils::create_file( 115 "output.lua", 116 "syntax(2)\n" 117 "top_string = 'foo'\n" 118 "inner.int = 12345\n" 119 "inner.dynamic.foo = 78\n" 120 "inner.dynamic.bar = 'some text'\n"); 121 122 config::tree tree; 123 mock_parser(tree).parse(fs::path("output.lua")); 124 ATF_REQUIRE_EQ("foo", tree.lookup< config::string_node >("top_string")); 125 ATF_REQUIRE_EQ(12345, tree.lookup< config::int_node >("inner.int")); 126 ATF_REQUIRE_EQ("78", 127 tree.lookup< config::string_node >("inner.dynamic.foo")); 128 ATF_REQUIRE_EQ("some text", 129 tree.lookup< config::string_node >("inner.dynamic.bar")); 130 } 131 132 133 ATF_TEST_CASE_WITHOUT_HEAD(some_keys__unknown_key); 134 ATF_TEST_CASE_BODY(some_keys__unknown_key) 135 { 136 atf::utils::create_file( 137 "output.lua", 138 "syntax(2)\n" 139 "top_string2 = 'foo'\n"); 140 config::tree tree1; 141 ATF_REQUIRE_THROW_RE(config::syntax_error, 142 "Unknown configuration property 'top_string2'", 143 mock_parser(tree1).parse(fs::path("output.lua"))); 144 145 atf::utils::create_file( 146 "output.lua", 147 "syntax(2)\n" 148 "inner.int2 = 12345\n"); 149 config::tree tree2; 150 ATF_REQUIRE_THROW_RE(config::syntax_error, 151 "Unknown configuration property 'inner.int2'", 152 mock_parser(tree2).parse(fs::path("output.lua"))); 153 } 154 155 156 ATF_TEST_CASE_WITHOUT_HEAD(invalid_syntax); 157 ATF_TEST_CASE_BODY(invalid_syntax) 158 { 159 config::tree tree; 160 161 atf::utils::create_file("output.lua", "syntax(56)\n"); 162 ATF_REQUIRE_THROW_RE(config::syntax_error, 163 "Unknown syntax version 56", 164 mock_parser(tree).parse(fs::path("output.lua"))); 165 } 166 167 168 ATF_TEST_CASE_WITHOUT_HEAD(syntax_deprecated_format); 169 ATF_TEST_CASE_BODY(syntax_deprecated_format) 170 { 171 config::tree tree; 172 173 atf::utils::create_file("output.lua", "syntax('config', 1)\n"); 174 (void)mock_parser(tree).parse(fs::path("output.lua")); 175 176 atf::utils::create_file("output.lua", "syntax('foo', 1)\n"); 177 ATF_REQUIRE_THROW_RE(config::syntax_error, "must be 'config'", 178 mock_parser(tree).parse(fs::path("output.lua"))); 179 180 atf::utils::create_file("output.lua", "syntax('config', 2)\n"); 181 ATF_REQUIRE_THROW_RE(config::syntax_error, "only takes one argument", 182 mock_parser(tree).parse(fs::path("output.lua"))); 183 } 184 185 186 ATF_TEST_CASE_WITHOUT_HEAD(syntax_not_called); 187 ATF_TEST_CASE_BODY(syntax_not_called) 188 { 189 config::tree tree; 190 tree.define< config::int_node >("var"); 191 192 atf::utils::create_file("output.lua", "var = 3\n"); 193 ATF_REQUIRE_THROW_RE(config::syntax_error, "No syntax defined", 194 mock_parser(tree).parse(fs::path("output.lua"))); 195 196 ATF_REQUIRE(!tree.is_set("var")); 197 } 198 199 200 ATF_TEST_CASE_WITHOUT_HEAD(syntax_called_more_than_once); 201 ATF_TEST_CASE_BODY(syntax_called_more_than_once) 202 { 203 config::tree tree; 204 tree.define< config::int_node >("var"); 205 206 atf::utils::create_file( 207 "output.lua", 208 "syntax(2)\n" 209 "var = 3\n" 210 "syntax(2)\n" 211 "var = 5\n"); 212 ATF_REQUIRE_THROW_RE(config::syntax_error, 213 "syntax\\(\\) can only be called once", 214 mock_parser(tree).parse(fs::path("output.lua"))); 215 216 ATF_REQUIRE_EQ(3, tree.lookup< config::int_node >("var")); 217 } 218 219 220 ATF_INIT_TEST_CASES(tcs) 221 { 222 ATF_ADD_TEST_CASE(tcs, no_keys__ok); 223 ATF_ADD_TEST_CASE(tcs, no_keys__unknown_key); 224 225 ATF_ADD_TEST_CASE(tcs, some_keys__ok); 226 ATF_ADD_TEST_CASE(tcs, some_keys__unknown_key); 227 228 ATF_ADD_TEST_CASE(tcs, invalid_syntax); 229 ATF_ADD_TEST_CASE(tcs, syntax_deprecated_format); 230 ATF_ADD_TEST_CASE(tcs, syntax_not_called); 231 ATF_ADD_TEST_CASE(tcs, syntax_called_more_than_once); 232 } 233