Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/scriptinterface/tests/test_ScriptInterface.h
/* Copyright (C) 2019 Wildfire Games. | /* Copyright (C) 2020 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | void test_loadscript_strict_error() | ||||
TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "JavaScript error: test.js line 1\nSyntaxError: strict mode code may not contain \'with\' statements"); | TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "JavaScript error: test.js line 1\nSyntaxError: strict mode code may not contain \'with\' statements"); | ||||
} | } | ||||
void test_clone_basic() | void test_clone_basic() | ||||
{ | { | ||||
ScriptInterface script1("Test", "Test", g_ScriptRuntime); | ScriptInterface script1("Test", "Test", g_ScriptRuntime); | ||||
ScriptInterface script2("Test", "Test", g_ScriptRuntime); | ScriptInterface script2("Test", "Test", g_ScriptRuntime); | ||||
JSContext* cx1 = script1.GetContext(); | ScriptInterface::Request rq1(script1); | ||||
JSAutoRequest rq1(cx1); | JS::RootedValue obj1(rq1.cx); | ||||
JS::RootedValue obj1(cx1); | |||||
TS_ASSERT(script1.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", &obj1)); | TS_ASSERT(script1.Eval("({'x': 123, 'y': [1, 1.5, '2', 'test', undefined, null, true, false]})", &obj1)); | ||||
{ | { | ||||
JSContext* cx2 = script2.GetContext(); | ScriptInterface::Request rq2(script2); | ||||
JSAutoRequest rq2(cx2); | |||||
JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1)); | JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherContext(script1, obj1)); | ||||
std::string source; | std::string source; | ||||
TS_ASSERT(script2.CallFunction(obj2, "toSource", source)); | TS_ASSERT(script2.CallFunction(obj2, "toSource", source)); | ||||
TS_ASSERT_STR_EQUALS(source, "({x:123, y:[1, 1.5, \"2\", \"test\", (void 0), null, true, false]})"); | TS_ASSERT_STR_EQUALS(source, "({x:123, y:[1, 1.5, \"2\", \"test\", (void 0), null, true, false]})"); | ||||
} | } | ||||
} | } | ||||
void test_clone_getters() | void test_clone_getters() | ||||
{ | { | ||||
// The tests should be run with JS_SetGCZeal so this can try to find GC bugs | // The tests should be run with JS_SetGCZeal so this can try to find GC bugs | ||||
ScriptInterface script1("Test", "Test", g_ScriptRuntime); | ScriptInterface script1("Test", "Test", g_ScriptRuntime); | ||||
ScriptInterface script2("Test", "Test", g_ScriptRuntime); | ScriptInterface script2("Test", "Test", g_ScriptRuntime); | ||||
JSContext* cx1 = script1.GetContext(); | ScriptInterface::Request rq1(script1); | ||||
JSAutoRequest rq1(cx1); | |||||
JS::RootedValue obj1(cx1); | JS::RootedValue obj1(rq1.cx); | ||||
TS_ASSERT(script1.Eval("var s = '?'; var v = ({get x() { return 123 }, 'y': {'w':{get z() { delete v.y; delete v.n; v = null; s += s; return 4 }}}, 'n': 100}); v", &obj1)); | TS_ASSERT(script1.Eval("var s = '?'; var v = ({get x() { return 123 }, 'y': {'w':{get z() { delete v.y; delete v.n; v = null; s += s; return 4 }}}, 'n': 100}); v", &obj1)); | ||||
{ | { | ||||
JSContext* cx2 = script2.GetContext(); | ScriptInterface::Request rq2(script2); | ||||
JSAutoRequest rq2(cx2); | |||||
JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1)); | JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherContext(script1, obj1)); | ||||
std::string source; | std::string source; | ||||
TS_ASSERT(script2.CallFunction(obj2, "toSource", source)); | TS_ASSERT(script2.CallFunction(obj2, "toSource", source)); | ||||
TS_ASSERT_STR_EQUALS(source, "({x:123, y:{w:{z:4}}})"); | TS_ASSERT_STR_EQUALS(source, "({x:123, y:{w:{z:4}}})"); | ||||
} | } | ||||
} | } | ||||
void test_clone_cyclic() | void test_clone_cyclic() | ||||
{ | { | ||||
ScriptInterface script1("Test", "Test", g_ScriptRuntime); | ScriptInterface script1("Test", "Test", g_ScriptRuntime); | ||||
ScriptInterface script2("Test", "Test", g_ScriptRuntime); | ScriptInterface script2("Test", "Test", g_ScriptRuntime); | ||||
JSContext* cx1 = script1.GetContext(); | ScriptInterface::Request rq1(script1); | ||||
JSAutoRequest rq1(cx1); | |||||
JS::RootedValue obj1(cx1); | JS::RootedValue obj1(rq1.cx); | ||||
TS_ASSERT(script1.Eval("var x = []; x[0] = x; ({'a': x, 'b': x})", &obj1)); | TS_ASSERT(script1.Eval("var x = []; x[0] = x; ({'a': x, 'b': x})", &obj1)); | ||||
{ | { | ||||
JSContext* cx2 = script2.GetContext(); | ScriptInterface::Request rq2(script2); | ||||
JSAutoRequest rq(cx2); | JS::RootedValue obj2(rq2.cx, script2.CloneValueFromOtherContext(script1, obj1)); | ||||
JS::RootedValue obj2(cx2, script2.CloneValueFromOtherContext(script1, obj1)); | |||||
// Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0] | // Use JSAPI function to check if the values of the properties "a", "b" are equals a.x[0] | ||||
JS::RootedValue prop_a(cx2); | JS::RootedValue prop_a(rq2.cx); | ||||
JS::RootedValue prop_b(cx2); | JS::RootedValue prop_b(rq2.cx); | ||||
JS::RootedValue prop_x1(cx2); | JS::RootedValue prop_x1(rq2.cx); | ||||
TS_ASSERT(script2.GetProperty(obj2, "a", &prop_a)); | TS_ASSERT(script2.GetProperty(obj2, "a", &prop_a)); | ||||
TS_ASSERT(script2.GetProperty(obj2, "b", &prop_b)); | TS_ASSERT(script2.GetProperty(obj2, "b", &prop_b)); | ||||
TS_ASSERT(prop_a.isObject()); | TS_ASSERT(prop_a.isObject()); | ||||
TS_ASSERT(prop_b.isObject()); | TS_ASSERT(prop_b.isObject()); | ||||
TS_ASSERT(script2.GetProperty(prop_a, "0", &prop_x1)); | TS_ASSERT(script2.GetProperty(prop_a, "0", &prop_x1)); | ||||
TS_ASSERT_EQUALS(prop_x1.get(), prop_a.get()); | TS_ASSERT_EQUALS(prop_x1.get(), prop_a.get()); | ||||
TS_ASSERT_EQUALS(prop_x1.get(), prop_b.get()); | TS_ASSERT_EQUALS(prop_x1.get(), prop_b.get()); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* This test is mainly to make sure that all required template overloads get instantiated at least once so that compiler errors | * This test is mainly to make sure that all required template overloads get instantiated at least once so that compiler errors | ||||
* in these functions are revealed instantly (but it also tests the basic functionality of these functions). | * in these functions are revealed instantly (but it also tests the basic functionality of these functions). | ||||
*/ | */ | ||||
void test_rooted_templates() | void test_rooted_templates() | ||||
{ | { | ||||
ScriptInterface script("Test", "Test", g_ScriptRuntime); | ScriptInterface script("Test", "Test", g_ScriptRuntime); | ||||
JSContext* cx = script.GetContext(); | ScriptInterface::Request rq(script); | ||||
JSAutoRequest rq(cx); | |||||
JS::RootedValue val(cx); | JS::RootedValue val(rq.cx); | ||||
JS::RootedValue out(cx); | JS::RootedValue out(rq.cx); | ||||
TS_ASSERT(script.Eval("({ " | TS_ASSERT(script.Eval("({ " | ||||
"'0':0," | "'0':0," | ||||
"inc:function() { this[0]++; return this[0]; }, " | "inc:function() { this[0]++; return this[0]; }, " | ||||
"setTo:function(nbr) { this[0] = nbr; }, " | "setTo:function(nbr) { this[0] = nbr; }, " | ||||
"add:function(nbr) { this[0] += nbr; return this[0]; } " | "add:function(nbr) { this[0] += nbr; return this[0]; } " | ||||
"})" | "})" | ||||
, &val)); | , &val)); | ||||
JS::RootedValue nbrVal(cx, JS::NumberValue(3)); | JS::RootedValue nbrVal(rq.cx, JS::NumberValue(3)); | ||||
int nbr = 0; | int nbr = 0; | ||||
// CallFunctionVoid JS::RootedValue& parameter overload | // CallFunctionVoid JS::RootedValue& parameter overload | ||||
script.CallFunctionVoid(val, "setTo", nbrVal); | script.CallFunctionVoid(val, "setTo", nbrVal); | ||||
// CallFunction JS::RootedValue* out parameter overload | // CallFunction JS::RootedValue* out parameter overload | ||||
script.CallFunction(val, "inc", &out); | script.CallFunction(val, "inc", &out); | ||||
ScriptInterface::FromJSVal(cx, out, nbr); | ScriptInterface::FromJSVal(rq, out, nbr); | ||||
TS_ASSERT_EQUALS(4, nbr); | TS_ASSERT_EQUALS(4, nbr); | ||||
// CallFunction const JS::RootedValue& parameter overload | // CallFunction const JS::RootedValue& parameter overload | ||||
script.CallFunction(val, "add", nbr, nbrVal); | script.CallFunction(val, "add", nbr, nbrVal); | ||||
TS_ASSERT_EQUALS(7, nbr); | TS_ASSERT_EQUALS(7, nbr); | ||||
// GetProperty JS::RootedValue* overload | // GetProperty JS::RootedValue* overload | ||||
nbr = 0; | nbr = 0; | ||||
script.GetProperty(val, "0", &out); | script.GetProperty(val, "0", &out); | ||||
ScriptInterface::FromJSVal(cx, out, nbr); | ScriptInterface::FromJSVal(rq, out, nbr); | ||||
TS_ASSERT_EQUALS(nbr, 7); | TS_ASSERT_EQUALS(nbr, 7); | ||||
// GetPropertyInt JS::RootedValue* overload | // GetPropertyInt JS::RootedValue* overload | ||||
nbr = 0; | nbr = 0; | ||||
script.GetPropertyInt(val, 0, &out); | script.GetPropertyInt(val, 0, &out); | ||||
ScriptInterface::FromJSVal(cx, out, nbr); | ScriptInterface::FromJSVal(rq, out, nbr); | ||||
TS_ASSERT_EQUALS(nbr, 7); | TS_ASSERT_EQUALS(nbr, 7); | ||||
handle_templates_test(script, val, &out, nbrVal); | handle_templates_test(script, val, &out, nbrVal); | ||||
} | } | ||||
void handle_templates_test(const ScriptInterface& script, JS::HandleValue val, JS::MutableHandleValue out, JS::HandleValue nbrVal) | void handle_templates_test(const ScriptInterface& script, JS::HandleValue val, JS::MutableHandleValue out, JS::HandleValue nbrVal) | ||||
{ | { | ||||
ScriptInterface::Request rq(script); | |||||
int nbr = 0; | int nbr = 0; | ||||
// CallFunctionVoid JS::HandleValue parameter overload | // CallFunctionVoid JS::HandleValue parameter overload | ||||
script.CallFunctionVoid(val, "setTo", nbrVal); | script.CallFunctionVoid(val, "setTo", nbrVal); | ||||
// CallFunction JS::MutableHandleValue out parameter overload | // CallFunction JS::MutableHandleValue out parameter overload | ||||
script.CallFunction(val, "inc", out); | script.CallFunction(val, "inc", out); | ||||
ScriptInterface::FromJSVal(script.GetContext(), out, nbr); | ScriptInterface::FromJSVal(rq, out, nbr); | ||||
TS_ASSERT_EQUALS(4, nbr); | TS_ASSERT_EQUALS(4, nbr); | ||||
// CallFunction const JS::HandleValue& parameter overload | // CallFunction const JS::HandleValue& parameter overload | ||||
script.CallFunction(val, "add", nbr, nbrVal); | script.CallFunction(val, "add", nbr, nbrVal); | ||||
TS_ASSERT_EQUALS(7, nbr); | TS_ASSERT_EQUALS(7, nbr); | ||||
// GetProperty JS::MutableHandleValue overload | // GetProperty JS::MutableHandleValue overload | ||||
nbr = 0; | nbr = 0; | ||||
script.GetProperty(val, "0", out); | script.GetProperty(val, "0", out); | ||||
ScriptInterface::FromJSVal(script.GetContext(), out, nbr); | ScriptInterface::FromJSVal(rq, out, nbr); | ||||
TS_ASSERT_EQUALS(nbr, 7); | TS_ASSERT_EQUALS(nbr, 7); | ||||
// GetPropertyInt JS::MutableHandleValue overload | // GetPropertyInt JS::MutableHandleValue overload | ||||
nbr = 0; | nbr = 0; | ||||
script.GetPropertyInt(val, 0, out); | script.GetPropertyInt(val, 0, out); | ||||
ScriptInterface::FromJSVal(script.GetContext(), out, nbr); | ScriptInterface::FromJSVal(rq, out, nbr); | ||||
TS_ASSERT_EQUALS(nbr, 7); | TS_ASSERT_EQUALS(nbr, 7); | ||||
} | } | ||||
void test_random() | void test_random() | ||||
{ | { | ||||
ScriptInterface script("Test", "Test", g_ScriptRuntime); | ScriptInterface script("Test", "Test", g_ScriptRuntime); | ||||
double d1, d2; | double d1, d2; | ||||
Show All 10 Lines | void test_random() | ||||
rng.seed((u64)0); | rng.seed((u64)0); | ||||
TS_ASSERT(script.Eval("Math.random()", d2)); | TS_ASSERT(script.Eval("Math.random()", d2)); | ||||
TS_ASSERT_EQUALS(d1, d2); | TS_ASSERT_EQUALS(d1, d2); | ||||
} | } | ||||
void test_json() | void test_json() | ||||
{ | { | ||||
ScriptInterface script("Test", "Test", g_ScriptRuntime); | ScriptInterface script("Test", "Test", g_ScriptRuntime); | ||||
JSContext* cx = script.GetContext(); | ScriptInterface::Request rq(script); | ||||
JSAutoRequest rq(cx); | |||||
std::string input = "({'x':1,'z':[2,'3\\u263A\\ud800'],\"y\":true})"; | std::string input = "({'x':1,'z':[2,'3\\u263A\\ud800'],\"y\":true})"; | ||||
JS::RootedValue val(cx); | JS::RootedValue val(rq.cx); | ||||
TS_ASSERT(script.Eval(input.c_str(), &val)); | TS_ASSERT(script.Eval(input.c_str(), &val)); | ||||
std::string stringified = script.StringifyJSON(&val); | std::string stringified = script.StringifyJSON(&val); | ||||
TS_ASSERT_STR_EQUALS(stringified, "{\n \"x\": 1,\n \"z\": [\n 2,\n \"3\xE2\x98\xBA\xEF\xBF\xBD\"\n ],\n \"y\": true\n}"); | TS_ASSERT_STR_EQUALS(stringified, "{\n \"x\": 1,\n \"z\": [\n 2,\n \"3\xE2\x98\xBA\xEF\xBF\xBD\"\n ],\n \"y\": true\n}"); | ||||
TS_ASSERT(script.ParseJSON(stringified, &val)); | TS_ASSERT(script.ParseJSON(stringified, &val)); | ||||
TS_ASSERT_STR_EQUALS(script.ToString(&val), "({x:1, z:[2, \"3\\u263A\\uFFFD\"], y:true})"); | TS_ASSERT_STR_EQUALS(script.ToString(&val), "({x:1, z:[2, \"3\\u263A\\uFFFD\"], y:true})"); | ||||
} | } | ||||
// This function tests a common way to mod functions, by crating a wrapper that | // This function tests a common way to mod functions, by crating a wrapper that | ||||
// extends the functionality and is then assigned to the name of the function. | // extends the functionality and is then assigned to the name of the function. | ||||
void test_function_override() | void test_function_override() | ||||
{ | { | ||||
ScriptInterface script("Test", "Test", g_ScriptRuntime); | ScriptInterface script("Test", "Test", g_ScriptRuntime); | ||||
JSContext* cx = script.GetContext(); | ScriptInterface::Request rq(script); | ||||
JSAutoRequest rq(cx); | |||||
TS_ASSERT(script.Eval( | TS_ASSERT(script.Eval( | ||||
"function f() { return 1; }" | "function f() { return 1; }" | ||||
"f = (function (originalFunction) {" | "f = (function (originalFunction) {" | ||||
"return function () { return originalFunction() + 1; }" | "return function () { return originalFunction() + 1; }" | ||||
"})(f);" | "})(f);" | ||||
)); | )); | ||||
JS::RootedValue out(cx); | JS::RootedValue out(rq.cx); | ||||
TS_ASSERT(script.Eval("f()", &out)); | TS_ASSERT(script.Eval("f()", &out)); | ||||
int outNbr = 0; | int outNbr = 0; | ||||
ScriptInterface::FromJSVal(cx, out, outNbr); | ScriptInterface::FromJSVal(rq, out, outNbr); | ||||
TS_ASSERT_EQUALS(2, outNbr); | TS_ASSERT_EQUALS(2, outNbr); | ||||
} | } | ||||
}; | }; |
Wildfire Games · Phabricator