Mercurial > code
view C++/tests/Js/main.cpp @ 415:ac4573b9bb4b
Js: add js::Pointer for non-managed pointers
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 08 Oct 2015 08:29:48 +0200 |
parents | 2a0b8613498f |
children | 865224c2191a |
line wrap: on
line source
/* * TestJsUnicode.cpp -- test irccd JS functions * * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <gtest/gtest.h> #include <Js.h> /* * TODO: * * - document stack modification in all functions, * - check that the stack is correct, * - add more C Duktape wrappers. */ using namespace js; /* -------------------------------------------------------- * Push & get * -------------------------------------------------------- */ TEST(PushAndGet, boolean) { Context context; ASSERT_EQ(0, context.top()); context.push(true); ASSERT_TRUE(context.get<bool>(-1)); ASSERT_EQ(1, context.top()); context.push(false); ASSERT_FALSE(context.get<bool>(-1)); ASSERT_EQ(2, context.top()); } TEST(PushAndGet, integer) { Context context; ASSERT_EQ(0, context.top()); context.push(123); ASSERT_EQ(123, context.get<int>(-1)); ASSERT_EQ(1, context.top()); context.push(456); ASSERT_EQ(456, context.get<int>(-1)); ASSERT_EQ(2, context.top()); } TEST(PushAndGet, number) { Context context; ASSERT_EQ(0, context.top()); context.push(10.5); ASSERT_EQ(10.5, context.get<double>(-1)); ASSERT_EQ(1, context.top()); context.push(50.1); ASSERT_EQ(50.1, context.get<double>(-1)); ASSERT_EQ(2, context.top()); } TEST(PushAndGet, string) { Context context; ASSERT_EQ(0, context.top()); context.push("hello world!"); ASSERT_EQ("hello world!", context.get<std::string>(-1)); ASSERT_EQ(1, context.top()); } TEST(PushAndGet, undefined) { Context context; ASSERT_EQ(0, context.top()); context.push(Undefined{}); ASSERT_EQ(DUK_TYPE_UNDEFINED, context.type(-1)); ASSERT_EQ(1, context.top()); } TEST(PushAndGet, null) { Context context; ASSERT_EQ(0, context.top()); context.push(Null{}); ASSERT_EQ(DUK_TYPE_NULL, context.type(-1)); ASSERT_EQ(1, context.top()); } TEST(PushAndGet, map) { Context context; Map<int> map{ { "one", 1 }, { "two", 2 } }; ASSERT_EQ(0, context.top()); context.push(js::Object{}); context.push(map); ASSERT_EQ(DUK_TYPE_OBJECT, context.type(-1)); ASSERT_EQ(1, context.getProperty<int>(-1, "one")); ASSERT_EQ(2, context.getProperty<int>(-1, "two")); ASSERT_EQ(1, context.top()); } TEST(PushAndGet, vector) { Context context; ASSERT_EQ(0, context.top()); context.push(std::vector<int>{1, 2, 3}); ASSERT_EQ(DUK_TYPE_OBJECT, context.type(-1)); ASSERT_EQ(1, context.top()); auto array = context.get<std::vector<int>>(-1); ASSERT_EQ(3U, array.size()); ASSERT_EQ(1, array[0]); ASSERT_EQ(2, array[1]); ASSERT_EQ(3, array[2]); } TEST(PushAndGet, pointer) { Context context; ASSERT_EQ(0, context.top()); context.push(Pointer<int>{new int(1)}); ASSERT_EQ(1, *context.get<Pointer<int>>(-1)); ASSERT_EQ(1, context.top()); } /* -------------------------------------------------------- * Require * -------------------------------------------------------- */ TEST(Require, boolean) { Context context; ASSERT_EQ(0, context.top()); try { context.push(Function{[] (Context &ctx) -> int { ctx.require<bool>(0); return 0; }}); context.peval(); FAIL() << "exception expected"; } catch (...) { } ASSERT_EQ(0, context.top()); } TEST(Require, integer) { Context context; ASSERT_EQ(0, context.top()); try { context.push(Function{[] (Context &ctx) -> int { ctx.require<int>(0); return 0; }}); context.peval(); FAIL() << "exception expected"; } catch (...) { } ASSERT_EQ(0, context.top()); } TEST(Require, number) { Context context; ASSERT_EQ(0, context.top()); try { context.push(Function{[] (Context &ctx) -> int { ctx.require<double>(0); return 0; }}); context.peval(); FAIL() << "exception expected"; } catch (...) { } ASSERT_EQ(0, context.top()); } TEST(Require, string) { Context context; ASSERT_EQ(0, context.top()); try { context.push(Function{[] (Context &ctx) -> int { ctx.require<std::string>(0); return 0; }}); context.peval(); FAIL() << "exception expected"; } catch (...) { } ASSERT_EQ(0, context.top()); } TEST(Require, cstring) { Context context; ASSERT_EQ(0, context.top()); try { context.push(Function{[] (Context &ctx) -> int { ctx.require<const char *>(0); return 0; }}); context.peval(); FAIL() << "exception expected"; } catch (...) { } ASSERT_EQ(0, context.top()); } TEST(Require, pointer) { Context context; ASSERT_EQ(0, context.top()); try { context.push(Function{[] (Context &ctx) -> int { ctx.require<Pointer<int>>(0); return 0; }}); context.peval(); FAIL() << "exception expected"; } catch (...) { } ASSERT_EQ(0, context.top()); } /* -------------------------------------------------------- * Is * -------------------------------------------------------- */ TEST(Is, boolean) { Context context; ASSERT_EQ(0, context.top()); context.push(true); ASSERT_TRUE(context.is<bool>(-1)); ASSERT_EQ(1, context.top()); } TEST(Is, integer) { Context context; ASSERT_EQ(0, context.top()); context.push(123); ASSERT_TRUE(context.is<int>(-1)); ASSERT_EQ(1, context.top()); } TEST(Is, number) { Context context; ASSERT_EQ(0, context.top()); context.push(50.5); ASSERT_TRUE(context.is<double>(-1)); ASSERT_EQ(1, context.top()); } TEST(Is, string) { Context context; ASSERT_EQ(0, context.top()); context.push(std::string{"hello"}); ASSERT_TRUE(context.is<std::string>(-1)); ASSERT_EQ(1, context.top()); } TEST(Is, cstring) { Context context; ASSERT_EQ(0, context.top()); context.push("hello"); ASSERT_TRUE(context.is<const char *>(-1)); ASSERT_EQ(1, context.top()); } TEST(Is, undefined) { Context context; ASSERT_EQ(0, context.top()); context.push(Undefined{}); ASSERT_TRUE(context.is<Undefined>(-1)); ASSERT_EQ(1, context.top()); } TEST(Is, null) { Context context; ASSERT_EQ(0, context.top()); context.push(Null{}); ASSERT_TRUE(context.is<Null>(-1)); ASSERT_EQ(1, context.top()); } TEST(Is, object) { Context context; ASSERT_EQ(0, context.top()); context.push(Object{}); ASSERT_TRUE(context.is<Object>(-1)); ASSERT_EQ(1, context.top()); } TEST(Is, array) { Context context; ASSERT_EQ(0, context.top()); context.push(Array{}); ASSERT_TRUE(context.is<Array>(-1)); ASSERT_EQ(1, context.top()); } TEST(Is, pointer) { Context context; ASSERT_EQ(0, context.top()); context.push(Pointer<int>{new int(1)}); ASSERT_TRUE(context.is<Pointer<int>>(-1)); ASSERT_EQ(1, context.top()); } /* -------------------------------------------------------- * Optional * -------------------------------------------------------- */ TEST(Optional, boolean) { Context context; ASSERT_EQ(0, context.top()); ASSERT_TRUE(context.optional<bool>(0, true)); ASSERT_FALSE(context.optional<bool>(0, false)); ASSERT_EQ(0, context.top()); } TEST(Optional, integer) { Context context; ASSERT_EQ(0, context.top()); ASSERT_EQ(123, context.optional<int>(0, 123)); ASSERT_EQ(456, context.optional<int>(0, 456)); ASSERT_EQ(0, context.top()); } TEST(Optional, number) { Context context; ASSERT_EQ(0, context.top()); ASSERT_EQ(10.0, context.optional<double>(0, 10.0)); ASSERT_EQ(20.0, context.optional<double>(0, 20.0)); ASSERT_EQ(0, context.top()); } TEST(Optional, string) { Context context; ASSERT_EQ(0, context.top()); ASSERT_EQ("no", context.optional<std::string>(0, "no")); ASSERT_EQ("yes", context.optional<std::string>(0, "yes")); ASSERT_EQ(0, context.top()); } TEST(Optional, cstring) { Context context; ASSERT_EQ(0, context.top()); ASSERT_STREQ("no", context.optional<const char *>(0, "no")); ASSERT_STREQ("yes", context.optional<const char *>(0, "yes")); ASSERT_EQ(0, context.top()); } TEST(Optional, pointer) { Context context; ASSERT_EQ(0, context.top()); ASSERT_EQ(nullptr, context.optional<js::Pointer<int>>(0, js::Pointer<int>{nullptr})); ASSERT_EQ(0, context.top()); } /* -------------------------------------------------------- * Basics * -------------------------------------------------------- */ TEST(Basics, global) { Context context; // boolean ASSERT_EQ(0, context.top()); context.putGlobal("valueBoolean", true); ASSERT_TRUE(context.getGlobal<bool>("valueBoolean")); ASSERT_EQ(0, context.top()); // integer ASSERT_EQ(0, context.top()); context.putGlobal("valueInteger", 123); ASSERT_EQ(123, context.getGlobal<int>("valueInteger")); ASSERT_EQ(0, context.top()); } TEST(Basics, top) { Context context; ASSERT_EQ(0, context.top()); context.push(true); ASSERT_EQ(1, context.top()); } TEST(Basics, pop1) { Context context; ASSERT_EQ(0, context.top()); context.push(true); context.pop(); ASSERT_EQ(0, context.top()); } TEST(Basics, pop2) { Context context; ASSERT_EQ(0, context.top()); context.push(true); context.push(true); context.pop(2); ASSERT_EQ(0, context.top()); } TEST(Basics, putProperty) { Context context; ASSERT_EQ(0, context.top()); context.push(Object{}); context.putProperty(-1, "x", 123); ASSERT_EQ(123, context.getProperty<int>(-1, "x")); ASSERT_EQ(1, context.top()); } TEST(Basics, enumerate) { Context context; ASSERT_EQ(0, context.top()); context.push(Object{}); context.putProperty(-1, "x", 123); context.putProperty(-1, "y", 456); ASSERT_EQ(1, context.top()); context.enumerate(-1, 0, true, [] (Context &ctx) { ASSERT_EQ(DUK_TYPE_STRING, ctx.type(-2)); ASSERT_EQ(DUK_TYPE_NUMBER, ctx.type(-1)); if (ctx.get<std::string>(-2) == "x") ASSERT_EQ(123, ctx.get<int>(-1)); if (ctx.get<std::string>(-2) == "y") ASSERT_EQ(456, ctx.get<int>(-1)); }); ASSERT_EQ(1, context.top()); } TEST(Basics, call) { Context context; ASSERT_EQ(0, context.top()); context.push(Function{[] (Context &ctx) -> int { ctx.putGlobal("x", 123); return 0; }}); context.call(); ASSERT_EQ(1, context.top()); ASSERT_EQ(123, context.getGlobal<int>("x")); } /* ------------------------------------------------------------------ * Eval * ------------------------------------------------------------------ */ TEST(Eval, simple) { Context context; ASSERT_EQ(0, context.top()); context.eval(Script{"x = 123;"}); ASSERT_EQ(123, context.getGlobal<int>("x")); ASSERT_EQ(1, context.top()); } TEST(Eval, function) { Context context; ASSERT_EQ(0, context.top()); context.eval(Script{"function f() { x = 123; }; f();"}); ASSERT_EQ(123, context.getGlobal<int>("x")); ASSERT_EQ(1, context.top()); } TEST(Eval, cfunction) { Context context; ASSERT_EQ(0, context.top()); context.putGlobal("f", Function{[] (Context &ctx) -> int { ctx.putGlobal("x", 123); return 0; }}); context.eval(Script{"f()"}); ASSERT_EQ(123, context.getGlobal<int>("x")); ASSERT_EQ(1, context.top()); } /* ------------------------------------------------------------------ * Protected eval * ------------------------------------------------------------------ */ TEST(Peval, success) { Context context; ASSERT_EQ(0, context.top()); try { context.peval(Script{"x = 1"}); } catch (const ErrorInfo &info) { FAIL() << "error unexpected: " << info.what(); } ASSERT_EQ(1, context.top()); } TEST(Peval, failure) { Context context; ASSERT_EQ(0, context.top()); try { context.peval(Script{"doesnotexists()"}); FAIL() << "expected exception"; } catch (const std::exception &) { } ASSERT_EQ(0, context.top()); } /* ------------------------------------------------------------------ * Exception handling * ------------------------------------------------------------------ */ TEST(Exception, error) { Context context; context.putGlobal("f", Function{[] (Context &ctx) -> duk_idx_t { ctx.raise(Error{"error thrown"}); return 0; }}); context.eval(Script{ "try {" " f();" "} catch (ex) {" " name = ex.name;" " message = ex.message;" " received = true;" "}" }); ASSERT_TRUE(context.getGlobal<bool>("received")); ASSERT_EQ("Error", context.getGlobal<std::string>("name")); ASSERT_EQ("error thrown", context.getGlobal<std::string>("message")); } /* -------------------------------------------------------- * TypeInfo with shared and pointer * -------------------------------------------------------- */ class Player { public: bool m_updated{false}; }; namespace js { template <> class TypeInfo<std::shared_ptr<Player>> : public TypeInfoShared<Player> { public: static void prototype(Context &ctx) { /* Create a temporary for the test */ ctx.push(Object{}); /* "update" method */ ctx.putProperty(-1, "update", Function{[] (Context &ctx) -> int { ctx.self<std::shared_ptr<Player>>()->m_updated = true; return 0; }}); } }; template <> class TypeInfo<Player *> : public TypeInfoPointer<Player> { public: static void prototype(Context &ctx) { /* Create a temporary for the test */ ctx.push(Object{}); /* "update" method */ ctx.putProperty(-1, "update", Function{[&] (Context &ctx) -> int { ctx.self<Player *>()->m_updated = true; return 0; }}); } }; } // !js TEST(Shared, simple) { Context ctx; std::shared_ptr<Player> p{new Player}; ctx.push(p); std::shared_ptr<Player> p2 = ctx.get<std::shared_ptr<Player>>(-1); p2->m_updated = true; ASSERT_TRUE(p->m_updated); } TEST(Shared, self) { Context ctx; std::shared_ptr<Player> p{new Player}; try { ctx.putGlobal("player", p); ctx.peval(Script{"player.update();"}); ASSERT_TRUE(p->m_updated); } catch (const std::exception &ex) { FAIL() << ex.what(); } } TEST(Pointer, simple) { Context context; Player *p{new Player}; ASSERT_EQ(0, context.top()); context.push(p); Player *p2 = context.get<Player *>(-1); ASSERT_EQ(1, context.top()); p2->m_updated = true; ASSERT_TRUE(p->m_updated); } TEST(Pointer, self) { Context ctx; Player *p{new Player}; try { ctx.putGlobal("player", p); ctx.peval(Script{"player.update();"}); ASSERT_TRUE(p->m_updated); } catch (const std::exception &ex) { FAIL() << ex.what(); } } /* -------------------------------------------------------- * FunctionMap * -------------------------------------------------------- */ TEST(PushFunctionMap, basic) { bool f1called{false}, f2called{false}; auto f1 = [&] (Context &) -> int { f1called = true; return 0; }; auto f2 = [&] (Context &) -> int { f2called = true; return 0; }; FunctionMap map{ { "f1", { f1, 0 } }, { "f2", { f2, 0 } } }; Context context; ASSERT_EQ(0, context.top()); context.push(Global{}); ASSERT_EQ(1, context.top()); context.push(map); ASSERT_EQ(1, context.top()); context.pop(); context.peval(Script{"f1();"}); context.peval(Script{"f2();"}); ASSERT_TRUE(f1called); ASSERT_TRUE(f2called); } int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }