increase test coverage of json.h

This commit is contained in:
ipknHama 2014-04-22 03:27:53 +09:00
parent 1173eba332
commit dd74354a7b
5 changed files with 63 additions and 107 deletions

View File

@ -4,8 +4,3 @@ Flask++
Simple C++ HTTP Server (inspired by Python Flask)
Other JSON library
==================
https://github.com/d-led/picojson_serializer

View File

@ -7,7 +7,7 @@
#include <type_traits>
#include <thread>
#define FLASK_ENABLE_LOGGING
//#define FLASK_ENABLE_LOGGING
#include "http_server.h"
#include "utility.h"
@ -39,12 +39,6 @@ namespace flask
return router_.new_rule_tagged<Tag>(std::move(rule));
}
auto route(std::string&& rule)
-> typename std::result_of<decltype(&Router::new_rule)(Router, std::string&&)>::type
{
return router_.new_rule(std::move(rule));
}
Flask& port(std::uint16_t port)
{
port_ = port;

22
json.h
View File

@ -267,15 +267,6 @@ namespace flask
return boost::lexical_cast<double>(start_, end_-start_);
}
detail::r_string s_raw() const
{
#ifndef FLASKPP_JSON_NO_ERROR_CHECK
if (t() != type::String)
throw std::runtime_error("value is not string");
#endif
return detail::r_string{start_, (uint32_t)(end_-start_), has_escaping()};
}
detail::r_string s() const
{
#ifndef FLASKPP_JSON_NO_ERROR_CHECK
@ -481,7 +472,7 @@ namespace flask
case type::False: os << "false"; break;
case type::True: os << "true"; break;
case type::Number: os << r.d(); break;
case type::String: os << '"' << r.s_raw() << '"'; break;
case type::String: os << '"' << r.s() << '"'; break;
case type::List:
{
os << '[';
@ -591,7 +582,7 @@ namespace flask
uint8_t has_escaping = 0;
while(1)
{
if (flask_json_likely(*data != '"' && *data != '\\'))
if (flask_json_likely(*data != '"' && *data != '\\' && *data != '\0'))
{
data ++;
}
@ -625,6 +616,8 @@ namespace flask
return {};
}
}
else
return {};
}
return {};
}
@ -906,9 +899,9 @@ namespace flask
rvalue parse()
{
ws_skip();
auto ret = decode_object(); // or decode value?
auto ret = decode_value(); // or decode object?
ws_skip();
if (*data != '\0')
if (ret && *data != '\0')
ret.set_error();
return ret;
}
@ -920,7 +913,8 @@ namespace flask
inline rvalue load(const char* data, size_t size)
{
char* s = new char[size+1];
memcpy(s, data, size+1);
memcpy(s, data, size);
s[size] = 0;
auto ret = load_nocopy_internal(s, size);
if (ret)
ret.key_.force(s, size);

View File

@ -46,56 +46,6 @@ namespace flask
friend class Router;
};
class Rule : public BaseRule
{
public:
Rule(std::string rule) noexcept
: BaseRule(std::move(rule))
{
}
Rule& name(std::string name) noexcept
{
name_ = std::move(name);
return *this;
}
template <typename Func>
void operator()(Func&& f)
{
static_assert(black_magic::CallHelper<Func, black_magic::S<>>::value,
"Handler type is mismatched with URL paramters");
static_assert(!std::is_same<void, decltype(f())>::value,
"Handler function cannot have void return type; valid return types: string, int, flask::resposne");
handler_ = [f = std::move(f)]{
return response(f());
};
}
template <typename Func>
void operator()(std::string name, Func&& f)
{
name_ = std::move(name);
this->operator()<Func>(f);
}
void validate()
{
if (!handler_)
{
throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
}
}
response handle(const request&, const routing_params&)
{
return handler_();
}
protected:
std::function<response()> handler_;
};
template <typename ... Args>
class TaggedRule : public BaseRule
{
@ -192,6 +142,10 @@ namespace flask
void validate()
{
if (!handler_ && !handler_with_req_)
{
throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
}
}
template <typename Func>
@ -208,6 +162,7 @@ namespace flask
handler_ = [f = std::move(f)](Args ... args){
return response(f(args...));
};
handler_with_req_ = nullptr;
}
template <typename Func>
@ -224,6 +179,7 @@ namespace flask
handler_with_req_ = [f = std::move(f)](const flask::request& request, Args ... args){
return response(f(request, args...));
};
handler_ = nullptr;
}
template <typename Func>
@ -481,8 +437,6 @@ public:
char c = url[i];
if (c == '<')
{
bool found = false;
static struct ParamTraits
{
ParamType type;
@ -498,27 +452,21 @@ public:
{ ParamType::PATH, "<path>" },
};
for(auto it = std::begin(paramTraits); it != std::end(paramTraits); ++it)
for(auto& x:paramTraits)
{
if (url.compare(i, it->name.size(), it->name) == 0)
if (url.compare(i, x.name.size(), x.name) == 0)
{
if (!nodes_[idx].param_childrens[(int)it->type])
if (!nodes_[idx].param_childrens[(int)x.type])
{
auto new_node_idx = new_node();
nodes_[idx].param_childrens[(int)it->type] = new_node_idx;
nodes_[idx].param_childrens[(int)x.type] = new_node_idx;
}
idx = nodes_[idx].param_childrens[(int)it->type];
i += it->name.size();
found = true;
idx = nodes_[idx].param_childrens[(int)x.type];
i += x.name.size();
break;
}
}
if (!found)
{
throw std::runtime_error("Invalid parameter type: " + url +
" (" + boost::lexical_cast<std::string>(i) + ")");
}
i --;
}
else
@ -616,14 +564,6 @@ public:
return *ruleObject;
}
Rule& new_rule(const std::string& rule)
{
Rule* r(new Rule(rule));
rules_.emplace_back(r);
trie_.add(rule, rules_.size() - 1);
return *r;
}
void validate()
{
trie_.validate();

View File

@ -29,7 +29,7 @@ void fail(Args...args) { error_print(args...);failed__ = true; }
#define ASSERT_TRUE(x) if (!(x)) fail("Assert fail: expected ", #x, " is true, at " __FILE__ ":",__LINE__)
#define ASSERT_EQUAL(a, b) if ((a) != (b)) fail("Assert fail: expected ", (a), " actual " , (b), ", " #a " == " #b ", at " __FILE__ ":",__LINE__)
#define ASSERT_NOTEQUAL(a, b) if ((a) != (b)) fail("Assert fail: not expected ", (a), ", " #a " != " #b ", at " __FILE__ ":",__LINE__)
#define ASSERT_NOTEQUAL(a, b) if ((a) == (b)) fail("Assert fail: not expected ", (a), ", " #a " != " #b ", at " __FILE__ ":",__LINE__)
#define ASSERT_THROW(x) \
try \
{ \
@ -49,14 +49,14 @@ void fail(Args...args) { error_print(args...);failed__ = true; }
TEST(Rule)
{
Rule r("/http/");
TaggedRule<> r("/http/");
r.name("abc");
// empty handler - fail to validate
try
{
r.validate();
fail();
fail("empty handler should fail to validate");
}
catch(runtime_error& e)
{
@ -73,6 +73,16 @@ TEST(Rule)
ASSERT_EQUAL(0, x);
r.handle(request(), routing_params());
ASSERT_EQUAL(1, x);
// registering handler with request argument
r([&x](const flask::request&){x = 2;return "";});
r.validate();
// executing handler
ASSERT_EQUAL(1, x);
r.handle(request(), routing_params());
ASSERT_EQUAL(2, x);
}
TEST(ParameterTagging)
@ -211,8 +221,8 @@ TEST(multi_server)
{
static char buf[2048];
Flask app1, app2;
app1.route("/")([]{return "A";});
app2.route("/")([]{return "B";});
FLASK_ROUTE(app1, "/")([]{return "A";});
FLASK_ROUTE(app2, "/")([]{return "B";});
Server<Flask> server1(&app1, 45451);
Server<Flask> server2(&app2, 45452);
@ -252,9 +262,29 @@ TEST(multi_server)
TEST(json_read)
{
{
auto x = json::load("{} 3");
if (x)
fail("should fail to parse");
const char* json_error_tests[] =
{
"{} 3", "{{}", "{3}",
"3.4.5", "+3", "3-2", "00", "03", "1e3e3", "1e+.3",
"nll", "f", "t",
"{\"x\":3,}",
"{\"x\"}",
"{\"x\":3 q}",
"{\"x\":[3 4]}",
"{\"x\":[\"",
"{\"x\":[[], 4],\"y\",}",
"{\"x\":[3",
"{\"x\":[ null, false, true}",
};
for(auto s:json_error_tests)
{
auto x = json::load(s);
if (x)
{
fail("should fail to parse ", s);
return;
}
}
}
auto x = json::load(R"({"message":"hello, world"})");
@ -270,6 +300,8 @@ TEST(json_read)
std::string s = R"({"int":3, "ints" :[1,2,3,4,5] })";
auto y = json::load(s);
ASSERT_EQUAL(3, y["int"]);
ASSERT_EQUAL(3.0, y["int"]);
ASSERT_NOTEQUAL(3.01, y["int"]);
ASSERT_EQUAL(5, y["ints"].size());
ASSERT_EQUAL(1, y["ints"][0]);
ASSERT_EQUAL(2, y["ints"][1]);
@ -308,7 +340,8 @@ TEST(json_write)
y["scores"][2][0] = "real";
y["scores"][2][1] = false;
ASSERT_EQUAL(R"({"scores":[1,"king",["real",false]]})", json::dump(y));
y["scores"][2][2] = true;
ASSERT_EQUAL(R"({"scores":[1,"king",["real",false,true]]})", json::dump(y));
y["scores"]["a"]["b"]["c"] = nullptr;
ASSERT_EQUAL(R"({"scores":{"a":{"b":{"c":null}}}})", json::dump(y));