// // json_binder_polymorphic.t.h // json // // Created by Sam Jaffe on 11/14/18. // Copyright © 2018 Sam Jaffe. All rights reserved. // #include "json/json_binder.hpp" #include struct Base { virtual ~Base() = default; virtual void run() const = 0; }; struct Left : public Base { int a; void run() const override {} }; struct Right : public Base { std::string a; void run() const override {} }; struct Middle : public Base { std::tuple a; void run() const override {} }; bool operator==(Left const & lhs, Left const & rhs) { return lhs.a == rhs.a; } bool operator==(Right const & lhs, Right const & rhs) { return lhs.a == rhs.a; } using namespace json::binder; using namespace json::parser; class JsonBinderPolymorphicTest : public ::testing::Test { protected: using pBase = std::unique_ptr; polymorphic_binder & GetBinder() { static polymorphic_binder val = polymorphic_binder()( "Left", object_binder()("a", &Left::a))( "Right", object_binder()("a", &Right::a)); return val; } }; template bool instanceof (Base * ptr) { return dynamic_cast(ptr) != nullptr; } using namespace ::testing; TEST_F(JsonBinderPolymorphicTest, CanHitLeftPolymorph) { char data[] = "{\"@id\":\"Left\", \"@value\":{\"a\":1}}"; pBase out; Left expected; expected.a = 1; EXPECT_NO_THROW(parse(json::binder::bind(out, GetBinder()), data)); EXPECT_THAT(out, Not(IsNull())); EXPECT_PRED1(instanceof , out.get()); EXPECT_THAT(dynamic_cast(*out), expected); } TEST_F(JsonBinderPolymorphicTest, CanHitRightPolymorph) { char data[] = "{\"@id\":\"Right\", \"@value\":{\"a\":\"hello\"}}"; pBase out; Right expected; expected.a = "hello"; EXPECT_NO_THROW(parse(json::binder::bind(out, GetBinder()), data)); EXPECT_THAT(out, Not(IsNull())); EXPECT_PRED1(instanceof , out.get()); EXPECT_THAT(dynamic_cast(*out), expected); } TEST_F(JsonBinderPolymorphicTest, ThrowsOnUnknownSubclass) { char data[] = "{\"@id\":\"Middle\", \"@value\":{\"a\":[]]}}"; pBase out; EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data), json::malformed_json_exception); EXPECT_THAT(out, IsNull()); } TEST_F(JsonBinderPolymorphicTest, ThrowsOnIdNotString) { char data[] = "{\"@id\":0, \"@value\":{\"a\":[]]}}"; pBase out; EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data), json::malformed_json_exception); EXPECT_THAT(out, IsNull()); } TEST_F(JsonBinderPolymorphicTest, ThrowsOnUnexpectedKey1) { char data[] = "{\"@class\":\"Left\", \"@value\":{\"a\":[]]}}"; pBase out; EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data), json::malformed_json_exception); EXPECT_THAT(out, IsNull()); } TEST_F(JsonBinderPolymorphicTest, ThrowsOnUnexpectedKey2) { char data[] = "{\"@id\":\"Left\", \"@impl\":null, \"@value\":{\"a\":[]]}}"; pBase out; EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data), json::malformed_json_exception); EXPECT_THAT(out, IsNull()); } TEST_F(JsonBinderPolymorphicTest, ThrowsOnUnexpectedKey3) { char data[] = "{\"@id\":\"Left\", \"@impl\":{\"a\":[]]}}"; pBase out; EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data), json::malformed_json_exception); EXPECT_THAT(out, IsNull()); } TEST_F(JsonBinderPolymorphicTest, ThrowsOnUnterminatedJson) { char data[] = "{\"@id\":\"Right\", \"@value\":{\"a\":\"hello\"}"; pBase out; Right expected; expected.a = "hello"; EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data), json::malformed_json_exception); EXPECT_THAT(out, Not(IsNull())); EXPECT_PRED1(instanceof , out.get()); EXPECT_THAT(dynamic_cast(*out), expected); } TEST_F(JsonBinderPolymorphicTest, ThrowsOnMoreData) { char data[] = "{\"@id\":\"Right\", \"@value\":{\"a\":\"hello\"}," " \"key\":null }"; pBase out; Right expected; expected.a = "hello"; EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data), json::malformed_json_exception); EXPECT_THAT(out, Not(IsNull())); EXPECT_PRED1(instanceof , out.get()); EXPECT_THAT(dynamic_cast(*out), expected); } TEST_F(JsonBinderPolymorphicTest, ThrowsOnNonObject) { char data[] = "[\"Right\", \"@value\":{\"a\":\"hello\"}]"; pBase out; Right expected; expected.a = "hello"; EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data), json::malformed_json_exception); EXPECT_THAT(out, IsNull()); } TEST_F(JsonBinderPolymorphicTest, ThrowsMalformedObjectKey) { char data[] = "{@id\":\"Right\", \"@value\":{\"a\":\"hello\"}}"; pBase out; Right expected; expected.a = "hello"; EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data), json::malformed_json_exception); EXPECT_THAT(out, IsNull()); } TEST_F(JsonBinderPolymorphicTest, ThrowsMalformedObjectAssoc) { char data[] = "{\"@id\"=\"Right\", \"@value\":{\"a\":\"hello\"}}"; pBase out; Right expected; expected.a = "hello"; EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data), json::malformed_json_exception); EXPECT_THAT(out, IsNull()); } TEST_F(JsonBinderPolymorphicTest, WritesRightImplWithoutWhitespace) { std::string const expected = R"({"@id":"Right","@value":{"a":"hello"}})"; Right in; in.a = "hello"; pBase ptr(new Right(in)); std::stringstream ss; EXPECT_NO_THROW(write(json::binder::bind(ptr, GetBinder()), ss)); EXPECT_THAT(ss.str(), expected); } TEST_F(JsonBinderPolymorphicTest, WritesLeftImplWithoutWhitespace) { std::string const expected = R"({"@id":"Left","@value":{"a":1}})"; Left in; in.a = 1; pBase ptr(new Left(in)); std::stringstream ss; EXPECT_NO_THROW(write(json::binder::bind(ptr, GetBinder()), ss)); EXPECT_THAT(ss.str(), expected); } TEST_F(JsonBinderPolymorphicTest, ThrowsWhenWritingUnboundImpl) { Middle in; in.a = {1, "hello"}; pBase ptr(new Middle(in)); std::stringstream ss; EXPECT_THROW(write(json::binder::bind(ptr, GetBinder()), ss), std::domain_error); } TEST_F(JsonBinderPolymorphicTest, ParsesNullSuccessfully) { char const data[] = "null"; pBase out; EXPECT_NO_THROW(parse(json::binder::bind(out, GetBinder()), data)); EXPECT_THAT(out, IsNull()); } TEST_F(JsonBinderPolymorphicTest, WritesNullSuccessfully) { pBase ptr = nullptr; std::stringstream ss; EXPECT_NO_THROW(write(json::binder::bind(ptr, GetBinder()), ss)); EXPECT_THAT(ss.str(), "null"); }