/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com) * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "check.hpp" #include "config.h" namespace net = boost::asio; namespace resp3 = aedis::resp3; using test_stream = boost::beast::test::stream; using aedis::adapter::adapt; using node_type = aedis::resp3::node; //------------------------------------------------------------------- template struct expect { std::string in; Result expected; std::string name; boost::system::error_condition ec; }; template void test_sync(net::any_io_executor ex, expect e) { std::string rbuffer; test_stream ts {ex}; ts.append(e.in); Result result; boost::system::error_code ec; resp3::read(ts, net::dynamic_buffer(rbuffer), adapt(result), ec); expect_error(ec, e.ec); if (e.ec) return; check_empty(rbuffer); expect_eq(result, e.expected, e.name); } template class async_test: public std::enable_shared_from_this> { private: std::string rbuffer_; test_stream ts_; expect data_; Result result_; public: async_test(net::any_io_executor ex, expect e) : ts_{ex} , data_{e} { ts_.append(e.in); } void run() { auto self = this->shared_from_this(); auto f = [self](auto ec, auto n) { expect_error(ec, self->data_.ec); if (self->data_.ec) return; check_empty(self->rbuffer_); expect_eq(self->result_, self->data_.expected, self->data_.name); }; resp3::async_read( ts_, net::dynamic_buffer(rbuffer_), adapt(result_), f); } }; template void test_async(net::any_io_executor ex, expect e) { std::make_shared>(ex, e)->run(); } void test_number(net::io_context& ioc) { boost::optional ok; ok = 11; // Success auto const in01 = expect{":3\r\n", node_type{resp3::type::number, 1UL, 0UL, {"3"}}, "number.node (positive)"}; auto const in02 = expect{":-3\r\n", node_type{resp3::type::number, 1UL, 0UL, {"-3"}}, "number.node (negative)"}; auto const in03 = expect{":11\r\n", int{11}, "number.int"}; auto const in04 = expect>{":11\r\n", ok, "number.optional.int"}; auto const in05 = expect>{"*1\r\n:11\r\n", std::tuple{11}, "number.tuple.int"}; auto const in06 = expect>{"%11\r\n", boost::optional{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_simple_type)}; auto const in07 = expect>{":11\r\n", std::set{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_set_aggregate)}; auto const in08 = expect>{":11\r\n", std::unordered_set{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_set_aggregate)}; auto const in09 = expect>{":11\r\n", std::map{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_map_like_aggregate)}; auto const in10 = expect>{":11\r\n", std::unordered_map{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_map_like_aggregate)}; auto const in11 = expect>{":11\r\n", std::list{}, "number.optional.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_aggregate)}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_sync(ex, in03); test_sync(ex, in04); test_sync(ex, in05); test_sync(ex, in06); test_sync(ex, in07); test_sync(ex, in08); test_sync(ex, in09); test_sync(ex, in10); test_sync(ex, in11); test_async(ex, in01); test_async(ex, in02); test_async(ex, in03); test_async(ex, in04); test_async(ex, in05); test_async(ex, in06); test_async(ex, in07); test_async(ex, in08); test_async(ex, in09); test_async(ex, in10); test_async(ex, in11); } void test_bool(net::io_context& ioc) { boost::optional ok; ok = true; // Success. auto const in08 = expect{"#f\r\n", node_type{resp3::type::boolean, 1UL, 0UL, {"f"}}, "bool.node (false)"}; auto const in09 = expect{"#t\r\n", node_type{resp3::type::boolean, 1UL, 0UL, {"t"}}, "bool.node (true)"}; auto const in10 = expect{"#t\r\n", bool{true}, "bool.bool (true)"}; auto const in11 = expect{"#f\r\n", bool{false}, "bool.bool (true)"}; auto const in13 = expect>{"#t\r\n", ok, "optional.int"}; // Error auto const in01 = expect>{"#11\r\n", boost::optional{}, "bool.error", aedis::resp3::make_error_condition(aedis::resp3::error::unexpected_bool_value)}; auto const in03 = expect>{"#t\r\n", std::set{}, "bool.error", aedis::adapter::make_error_condition(aedis::adapter::error::expects_set_aggregate)}; auto const in04 = expect>{"#t\r\n", std::unordered_set{}, "bool.error", aedis::adapter::make_error_condition(aedis::adapter::error::expects_set_aggregate)}; auto const in05 = expect>{"#t\r\n", std::map{}, "bool.error", aedis::adapter::make_error_condition(aedis::adapter::error::expects_map_like_aggregate)}; auto const in06 = expect>{"#t\r\n", std::unordered_map{}, "bool.error", aedis::adapter::make_error_condition(aedis::adapter::error::expects_map_like_aggregate)}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in03); test_sync(ex, in04); test_sync(ex, in05); test_sync(ex, in06); test_sync(ex, in08); test_sync(ex, in09); test_sync(ex, in10); test_sync(ex, in11); test_async(ex, in01); test_async(ex, in03); test_async(ex, in04); test_async(ex, in05); test_async(ex, in06); test_async(ex, in08); test_async(ex, in09); test_async(ex, in10); test_async(ex, in11); } void test_streamed_string(net::io_context& ioc) { std::string const wire = "$?\r\n;4\r\nHell\r\n;5\r\no wor\r\n;1\r\nd\r\n;0\r\n"; std::vector e1a { {aedis::resp3::type::streamed_string_part, 1, 1, "Hell"} , {aedis::resp3::type::streamed_string_part, 1, 1, "o wor"} , {aedis::resp3::type::streamed_string_part, 1, 1, "d"} , {aedis::resp3::type::streamed_string_part, 1, 1, ""} }; std::vector e1b { {resp3::type::streamed_string_part, 1UL, 1UL, {}} }; auto const in01 = expect>{wire, e1a, "streamed_string.node"}; auto const in03 = expect{wire, std::string{"Hello word"}, "streamed_string.string"}; auto const in02 = expect>{"$?\r\n;0\r\n", e1b, "streamed_string.node.empty"}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_sync(ex, in03); test_async(ex, in01); test_async(ex, in02); test_async(ex, in03); } void test_push(net::io_context& ioc) { std::string const wire = ">4\r\n+pubsub\r\n+message\r\n+some-channel\r\n+some message\r\n"; std::vector e1a { {resp3::type::push, 4UL, 0UL, {}} , {resp3::type::simple_string, 1UL, 1UL, "pubsub"} , {resp3::type::simple_string, 1UL, 1UL, "message"} , {resp3::type::simple_string, 1UL, 1UL, "some-channel"} , {resp3::type::simple_string, 1UL, 1UL, "some message"} }; std::vector e1b { {resp3::type::push, 0UL, 0UL, {}} }; auto const in01 = expect>{wire, e1a, "push.node"}; auto const in02 = expect>{">0\r\n", e1b, "push.node.empty"}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_async(ex, in01); test_async(ex, in02); } void test_map(net::io_context& ioc) { using map_type = std::map; using mmap_type = std::multimap; using umap_type = std::unordered_map; using mumap_type = std::unordered_multimap; using vec_type = std::vector; using op_map_type = boost::optional>; using op_vec_type = boost::optional>; using tuple_type = std::tuple; std::string const wire = "%4\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n$4\r\nkey3\r\n$6\r\nvalue3\r\n$4\r\nkey3\r\n$6\r\nvalue3\r\n"; std::vector expected_1a { {resp3::type::map, 4UL, 0UL, {}} , {resp3::type::blob_string, 1UL, 1UL, {"key1"}} , {resp3::type::blob_string, 1UL, 1UL, {"value1"}} , {resp3::type::blob_string, 1UL, 1UL, {"key2"}} , {resp3::type::blob_string, 1UL, 1UL, {"value2"}} , {resp3::type::blob_string, 1UL, 1UL, {"key3"}} , {resp3::type::blob_string, 1UL, 1UL, {"value3"}} , {resp3::type::blob_string, 1UL, 1UL, {"key3"}} , {resp3::type::blob_string, 1UL, 1UL, {"value3"}} }; map_type expected_1b { {"key1", "value1"} , {"key2", "value2"} , {"key3", "value3"} }; umap_type e1g { {"key1", "value1"} , {"key2", "value2"} , {"key3", "value3"} }; mmap_type e1k { {"key1", "value1"} , {"key2", "value2"} , {"key3", "value3"} , {"key3", "value3"} }; mumap_type e1l { {"key1", "value1"} , {"key2", "value2"} , {"key3", "value3"} , {"key3", "value3"} }; std::vector expected_1c { "key1", "value1" , "key2", "value2" , "key3", "value3" , "key3", "value3" }; op_map_type expected_1d; expected_1d = expected_1b; op_vec_type expected_1e; expected_1e = expected_1c; tuple_type e1f { std::string{"key1"}, std::string{"value1"} , std::string{"key2"}, std::string{"value2"} , std::string{"key3"}, std::string{"value3"} , std::string{"key3"}, std::string{"value3"} }; auto const in00 = expect>{wire, expected_1a, "map.node"}; auto const in01 = expect{"%0\r\n", map_type{}, "map.map.empty"}; auto const in02 = expect{wire, expected_1b, "map.map"}; auto const in03 = expect{wire, e1k, "map.multimap"}; auto const in04 = expect{wire, e1g, "map.unordered_map"}; auto const in05 = expect{wire, e1l, "map.unordered_multimap"}; auto const in06 = expect{wire, expected_1c, "map.vector"}; auto const in07 = expect{wire, expected_1d, "map.optional.map"}; auto const in08 = expect{wire, expected_1e, "map.optional.vector"}; auto const in09 = expect>{"*1\r\n" + wire, std::tuple{expected_1d}, "map.transaction.optional.map"}; auto const in10 = expect{"%11\r\n", int{}, "map.invalid.int", aedis::adapter::make_error_condition(aedis::adapter::error::expects_simple_type)}; auto const in11 = expect{wire, e1f, "map.tuple"}; auto ex = ioc.get_executor(); test_sync(ex, in00); test_sync(ex, in01); test_sync(ex, in02); test_sync(ex, in03); test_sync(ex, in04); test_sync(ex, in05); test_sync(ex, in07); test_sync(ex, in08); test_sync(ex, in09); test_sync(ex, in00); test_sync(ex, in11); test_async(ex, in00); test_async(ex, in01); test_async(ex, in02); test_async(ex, in03); test_async(ex, in04); test_async(ex, in05); test_async(ex, in07); test_async(ex, in08); test_async(ex, in09); test_async(ex, in00); test_async(ex, in11); } void test_attribute(net::io_context& ioc) { char const* wire = "|1\r\n+key-popularity\r\n%2\r\n$1\r\na\r\n,0.1923\r\n$1\r\nb\r\n,0.0012\r\n"; std::vector e1a { {resp3::type::attribute, 1UL, 0UL, {}} , {resp3::type::simple_string, 1UL, 1UL, "key-popularity"} , {resp3::type::map, 2UL, 1UL, {}} , {resp3::type::blob_string, 1UL, 2UL, "a"} , {resp3::type::doublean, 1UL, 2UL, "0.1923"} , {resp3::type::blob_string, 1UL, 2UL, "b"} , {resp3::type::doublean, 1UL, 2UL, "0.0012"} }; std::vector e1b; auto const in01 = expect>{wire, e1a, "attribute.node"}; auto const in02 = expect>{"|0\r\n", e1b, "attribute.node.empty"}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_async(ex, in01); test_async(ex, in02); } void test_array(net::io_context& ioc) { char const* wire = "*3\r\n$2\r\n11\r\n$2\r\n22\r\n$1\r\n3\r\n"; std::vector e1a { {resp3::type::array, 3UL, 0UL, {}} , {resp3::type::blob_string, 1UL, 1UL, {"11"}} , {resp3::type::blob_string, 1UL, 1UL, {"22"}} , {resp3::type::blob_string, 1UL, 1UL, {"3"}} }; std::vector const e1b{11, 22, 3}; std::vector const e1c{"11", "22", "3"}; std::vector const e1d{}; std::vector const e1e{{resp3::type::array, 0UL, 0UL, {}}}; std::array const e1f{11, 22, 3}; std::list const e1g{11, 22, 3}; std::deque const e1h{11, 22, 3}; auto const in01 = expect>{wire, e1a, "array.node"}; auto const in02 = expect>{wire, e1b, "array.int"}; auto const in03 = expect>{"*0\r\n", e1e, "array.node.empty"}; auto const in04 = expect>{"*0\r\n", e1d, "array.string.empty"}; auto const in05 = expect>{wire, e1c, "array.string"}; auto const in06 = expect>{wire, e1f, "array.array"}; auto const in07 = expect>{wire, e1g, "array.list"}; auto const in08 = expect>{wire, e1h, "array.deque"}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_sync(ex, in03); test_sync(ex, in04); test_sync(ex, in05); test_sync(ex, in06); test_sync(ex, in07); test_sync(ex, in08); test_async(ex, in01); test_async(ex, in02); test_async(ex, in03); test_async(ex, in04); test_async(ex, in05); test_async(ex, in06); test_async(ex, in07); test_async(ex, in08); } void test_set(net::io_context& ioc) { using set_type = std::set; using mset_type = std::multiset; using uset_type = std::unordered_set; using muset_type = std::unordered_multiset; using vec_type = std::vector; using op_vec_type = boost::optional>; std::string const wire = "~6\r\n+orange\r\n+apple\r\n+one\r\n+two\r\n+three\r\n+orange\r\n"; std::vector const expected1a { {resp3::type::set, 6UL, 0UL, {}} , {resp3::type::simple_string, 1UL, 1UL, {"orange"}} , {resp3::type::simple_string, 1UL, 1UL, {"apple"}} , {resp3::type::simple_string, 1UL, 1UL, {"one"}} , {resp3::type::simple_string, 1UL, 1UL, {"two"}} , {resp3::type::simple_string, 1UL, 1UL, {"three"}} , {resp3::type::simple_string, 1UL, 1UL, {"orange"}} }; mset_type const e1f{"apple", "one", "orange", "orange", "three", "two"}; uset_type const e1c{"apple", "one", "orange", "three", "two"}; muset_type const e1g{"apple", "one", "orange", "orange", "three", "two"}; vec_type const e1d = {"orange", "apple", "one", "two", "three", "orange"}; op_vec_type expected_1e; expected_1e = e1d; auto const in00 = expect>{wire, expected1a, "set.node"}; auto const in01 = expect>{"~0\r\n", std::vector{ {resp3::type::set, 0UL, 0UL, {}} }, "set.node (empty)"}; auto const in02 = expect{wire, set_type{"apple", "one", "orange", "three", "two"}, "set.set"}; auto const in03 = expect{wire, e1f, "set.multiset"}; auto const in04 = expect{wire, e1d, "set.vector "}; auto const in05 = expect{wire, expected_1e, "set.vector "}; auto const in06 = expect{wire, e1c, "set.unordered_set"}; auto const in07 = expect{wire, e1g, "set.unordered_multiset"}; auto const in08 = expect>{"*1\r\n" + wire, std::tuple{e1c}, "set.tuple"}; auto ex = ioc.get_executor(); test_sync(ex, in00); test_sync(ex, in01); test_sync(ex, in02); test_sync(ex, in03); test_sync(ex, in04); test_sync(ex, in05); test_sync(ex, in06); test_sync(ex, in07); test_sync(ex, in08); test_async(ex, in00); test_async(ex, in01); test_async(ex, in02); test_async(ex, in03); test_async(ex, in04); test_async(ex, in05); test_async(ex, in06); test_async(ex, in07); test_async(ex, in08); } void test_simple_error(net::io_context& ioc) { auto const in01 = expect{"-Error\r\n", node_type{resp3::type::simple_error, 1UL, 0UL, {"Error"}}, "simple_error.node"}; auto const in02 = expect{"-\r\n", node_type{resp3::type::simple_error, 1UL, 0UL, {""}}, "simple_error.node.empty"}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_async(ex, in01); test_async(ex, in02); } void test_blob_string(net::io_context& ioc) { std::string str(100000, 'a'); str[1000] = '\r'; str[1001] = '\n'; std::string wire; wire += '$'; wire += std::to_string(str.size()); wire += "\r\n"; wire += str; wire += "\r\n"; auto const in01 = expect{"$2\r\nhh\r\n", node_type{resp3::type::blob_string, 1UL, 0UL, {"hh"}}, "blob_string.node"}; auto const in02 = expect{"$26\r\nhhaa\aaaa\raaaaa\r\naaaaaaaaaa\r\n", node_type{resp3::type::blob_string, 1UL, 0UL, {"hhaa\aaaa\raaaaa\r\naaaaaaaaaa"}}, "blob_string.node (with separator)"}; auto const in03 = expect{"$0\r\n\r\n", node_type{resp3::type::blob_string, 1UL, 0UL, {}}, "blob_string.node.empty"}; auto const in04 = expect{wire, node_type{resp3::type::blob_string, 1UL, 0UL, {str}}, "blob_string.node (long string)"}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_sync(ex, in03); test_sync(ex, in04); test_async(ex, in01); test_async(ex, in02); test_async(ex, in03); test_async(ex, in04); } void test_double(net::io_context& ioc) { auto const in01 = expect{",1.23\r\n", node_type{resp3::type::doublean, 1UL, 0UL, {"1.23"}}, "double.node"}; auto const in02 = expect{",inf\r\n", node_type{resp3::type::doublean, 1UL, 0UL, {"inf"}}, "double.node (inf)"}; auto const in03 = expect{",-inf\r\n", node_type{resp3::type::doublean, 1UL, 0UL, {"-inf"}}, "double.node (-inf)"}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_sync(ex, in03); test_async(ex, in01); test_async(ex, in02); test_async(ex, in03); } void test_blob_error(net::io_context& ioc) { auto const in01 = expect{"!21\r\nSYNTAX invalid syntax\r\n", node_type{resp3::type::blob_error, 1UL, 0UL, {"SYNTAX invalid syntax"}}, "blob_error"}; auto const in02 = expect{"!0\r\n\r\n", node_type{resp3::type::blob_error, 1UL, 0UL, {}}, "blob_error.empty"}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_async(ex, in01); test_async(ex, in02); } void test_verbatim_string(net::io_context& ioc) { auto const in01 = expect{"=15\r\ntxt:Some string\r\n", node_type{resp3::type::verbatim_string, 1UL, 0UL, {"txt:Some string"}}, "verbatim_string"}; auto const in02 = expect{"=0\r\n\r\n", node_type{resp3::type::verbatim_string, 1UL, 0UL, {}}, "verbatim_string.empty"}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_async(ex, in01); test_async(ex, in02); } void test_big_number(net::io_context& ioc) { auto const in01 = expect{"(3492890328409238509324850943850943825024385\r\n", node_type{resp3::type::big_number, 1UL, 0UL, {"3492890328409238509324850943850943825024385"}}, "big_number.node"}; auto const in02 = expect{"(\r\n", int{}, "big_number.error (empty field)", aedis::resp3::make_error_condition(aedis::resp3::error::empty_field)}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_async(ex, in01); test_async(ex, in02); } void test_simple_string(net::io_context& ioc) { boost::optional ok1, ok2; ok1 = "OK"; ok2 = ""; auto in00 = expect{"+OK\r\n", std::string{"OK"}, "simple_string.string"}; auto in01 = expect{"+\r\n", std::string{""}, "simple_string.string.empty"}; auto in02 = expect>{"+OK\r\n", boost::optional{"OK"}, "simple_string.optional"}; auto in03 = expect>{"+\r\n", boost::optional{""}, "simple_string.optional.empty"}; auto ex = ioc.get_executor(); test_sync(ex, in00); test_sync(ex, in01); test_sync(ex, in02); test_sync(ex, in03); test_async(ex, in00); test_async(ex, in01); test_async(ex, in02); test_async(ex, in03); } void test_resp3(net::io_context& ioc) { auto const in01 = expect{"s11\r\n", int{}, "number.error", aedis::resp3::make_error_condition(aedis::resp3::error::invalid_type)}; auto const in02 = expect{":adf\r\n", int{11}, "number.int", aedis::resp3::make_error_condition(aedis::resp3::error::not_a_number)}; auto const in03 = expect{":\r\n", int{}, "number.error (empty field)", aedis::resp3::make_error_condition(aedis::resp3::error::empty_field)}; auto const in04 = expect>{"#\r\n", boost::optional{}, "bool.error", aedis::resp3::make_error_condition(aedis::resp3::error::empty_field)}; auto const in05 = expect{",\r\n", std::string{}, "double.error (empty field)", aedis::resp3::make_error_condition(aedis::resp3::error::empty_field)}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_sync(ex, in03); test_sync(ex, in04); test_sync(ex, in05); test_async(ex, in01); test_async(ex, in02); test_async(ex, in03); test_async(ex, in04); test_async(ex, in05); } void test_null(net::io_context& ioc) { using op_type_01 = boost::optional; using op_type_02 = boost::optional; using op_type_03 = boost::optional; using op_type_04 = boost::optional>; using op_type_05 = boost::optional>; using op_type_06 = boost::optional>; using op_type_07 = boost::optional>; using op_type_08 = boost::optional>; using op_type_09 = boost::optional>; auto const in01 = expect{"_\r\n", op_type_01{}, "null.optional.bool"}; auto const in02 = expect{"_\r\n", op_type_02{}, "null.optional.int"}; auto const in03 = expect{"_\r\n", op_type_03{}, "null.optional.string"}; auto const in04 = expect{"_\r\n", op_type_04{}, "null.optional.vector"}; auto const in05 = expect{"_\r\n", op_type_05{}, "null.optional.list"}; auto const in06 = expect{"_\r\n", op_type_06{}, "null.optional.map"}; auto const in07 = expect{"_\r\n", op_type_07{}, "null.optional.unordered_map"}; auto const in08 = expect{"_\r\n", op_type_08{}, "null.optional.set"}; auto const in09 = expect{"_\r\n", op_type_09{}, "null.optional.unordered_set"}; auto ex = ioc.get_executor(); test_sync(ex, in01); test_sync(ex, in02); test_sync(ex, in03); test_sync(ex, in04); test_sync(ex, in05); test_sync(ex, in06); test_sync(ex, in07); test_sync(ex, in08); test_sync(ex, in09); test_async(ex, in01); test_async(ex, in02); test_async(ex, in03); test_async(ex, in04); test_async(ex, in05); test_async(ex, in06); test_async(ex, in07); test_async(ex, in08); test_async(ex, in09); } int main() { net::io_context ioc {1}; // Simple types. test_simple_string(ioc); test_simple_error(ioc); test_blob_string(ioc); test_blob_error(ioc); test_number(ioc); test_double(ioc); test_bool(ioc); test_null(ioc); test_big_number(ioc); test_verbatim_string(ioc); // Aggregates. test_array(ioc); test_set(ioc); test_map(ioc); test_push(ioc); test_streamed_string(ioc); // RESP3 test_resp3(ioc); ioc.run(); }