view C++/tests/Js/main.cpp @ 398:94bfe7ba9a13

Js: - Rename some functions - Cleanup a lot - Add more documentation - Add specialization of getProperty and getGlobal to void to push on the stack
author David Demelier <markand@malikania.fr>
date Wed, 30 Sep 2015 19:59:12 +0200
parents 6b2db5425836
children 73517fd307dd
line wrap: on
line source

/*
 * TestJsUnicode.cpp -- test irccd JS functions
 *
 * Copyright (c) 2013, 2014, 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>

using namespace js;

/* --------------------------------------------------------
 * Push & get
 * -------------------------------------------------------- */

TEST(PushAndGet, boolean)
{
	Context context;

	context.push(true);
	ASSERT_TRUE(context.get<bool>(-1));
	context.push(false);
	ASSERT_FALSE(context.get<bool>(-1));
}

TEST(PushAndGet, integer)
{
	Context context;

	context.push(123);
	ASSERT_EQ(123, context.get<int>(-1));
	context.push(456);
	ASSERT_EQ(456, context.get<int>(-1));
}

TEST(PushAndGet, number)
{
	Context context;

	context.push(10.5);
	ASSERT_EQ(10.5, context.get<double>(-1));
	context.push(50.1);
	ASSERT_EQ(50.1, context.get<double>(-1));
}

TEST(PushAndGet, string)
{
	Context context;

	context.push("hello world!");
	ASSERT_EQ("hello world!", context.get<std::string>(-1));
}

/* --------------------------------------------------------
 * Basics
 * -------------------------------------------------------- */

TEST(Basics, global)
{
	Context context;

	// boolean
	context.setGlobal("valueBoolean", true);
	ASSERT_TRUE(context.getGlobal<bool>("valueBoolean"));

	// integer
	context.setGlobal("valueInteger", 123);
	ASSERT_EQ(123, context.getGlobal<int>("valueInteger"));
}

TEST(Basics, top)
{
	Context context;

	int current = context.top();
	context.push(true);
	ASSERT_EQ(current + 1, context.top());
}

TEST(Basics, pop1)
{
	Context context;

	int current = context.top();
	context.push(true);
	context.pop();
	ASSERT_EQ(current, context.top());
}

TEST(Basics, pop2)
{
	Context context;

	int current = context.top();
	context.push(true);
	context.push(true);
	context.pop(2);
	ASSERT_EQ(current, context.top());
}

TEST(Basics, putProperty)
{
	Context context;

	context.push(Object{});
	context.putProperty(-1, "x", 123);
	ASSERT_EQ(123, context.getProperty<int>(-1, "x"));
}

TEST(Basics, enumerate)
{
	Context context;

	context.push(Object{});
	context.putProperty(-1, "x", 123);
	context.putProperty(-1, "y", 456);
	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));
	});
}

TEST(Basics, call)
{
	Context context;

	context.push(Function{[] (Context &ctx) -> int {
		ctx.setGlobal("x", 123);

		return 0;
	}});
	context.call();

	ASSERT_EQ(123, context.getGlobal<int>("x"));
}

/* ------------------------------------------------------------------
 * Eval
 * ------------------------------------------------------------------ */

TEST(Eval, simple)
{
	Context context;

	context.eval(Script{"x = 123;"});
	ASSERT_EQ(123, context.getGlobal<int>("x"));
}

TEST(Eval, function)
{
	Context context;

	context.eval(Script{"function f() { x = 123; }; f();"});
	ASSERT_EQ(123, context.getGlobal<int>("x"));
}

TEST(Eval, cfunction)
{
	Context context;

	context.setGlobal("f", Function{[] (Context &ctx) -> int {
		ctx.setGlobal("x", 123);

		return 0;
	}});
	context.eval(Script{"f()"});

	ASSERT_EQ(123, context.getGlobal<int>("x"));
}

/* ------------------------------------------------------------------
 * Protected eval
 * ------------------------------------------------------------------ */

TEST(Peval, success)
{
	Context context;

	try {
		context.peval(Script{"x = 1"});
	} catch (const ErrorInfo &info) {
		FAIL() << "error unexpected: " << info.what();
	}
}

TEST(Peval, failure)
{
	Context context;

	try {
		context.peval(Script{"doesnotexists()"});

		FAIL() << "expected exception";
	} catch (const std::exception &) {
	}
}

/* ------------------------------------------------------------------
 * Exception handling
 * ------------------------------------------------------------------ */

TEST(Exception, error)
{
	Context context;

	context.setGlobal("f", Function{[] (ContextPtr pctx) -> duk_idx_t {
		Context ctx{pctx};

		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
 * -------------------------------------------------------- */

class Player {
public:
	bool m_updated{false};
};

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{[] (ContextPtr pctx) -> duk_ret_t {
			Context{pctx}.self<std::shared_ptr<Player>>()->m_updated = true;

			return 0;
		}});
	}
};

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.setGlobal("player", p);
		ctx.peval(Script{"player.update();"});

		ASSERT_TRUE(p->m_updated);
	} catch (const std::exception &ex) {
		FAIL() << ex.what();
	}
}

int main(int argc, char **argv)
{
	testing::InitGoogleTest(&argc, argv);

	return RUN_ALL_TESTS();
}