/* * Tencent is pleased to support the open source community by making ScriptX available. * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "test.h" namespace script::test { DEFINE_ENGINE_TEST(ExceptionTest); TEST_F(ExceptionTest, Normal) { { // ok EngineScope engineScope(engine); try { throw Exception("inside"); } catch (const Exception&) { } } } TEST_F(ExceptionTest, ExceptionOutOfEngineScope) { try { EngineScope engineScope(engine); throw Exception("inside"); // exception go out of engine scope, still ok } catch (const Exception&) { } } TEST_F(ExceptionTest, Function) { EngineScope engineScope(engine); try { EXPECT_THROW( { engine->eval(TS().js("throw Error('hello error')").lua("error('hello error')").select()); }, Exception); auto func = Function::newFunction([](const script::Arguments& args) { if (args.size() == 0) { throw Exception("invalid argument"); } return Number::newNumber(0); }); func.call({}, {Object::newObject()}); EXPECT_THROW({ func.call({}); }, Exception); engine->set("func", func); Local ret; ret = engine->eval(TS().js(R"( try { func(); false; } catch (e) { true; } )") .lua(R"( return not pcall(func) )") .select()); EXPECT_TRUE(ret.isBoolean()); EXPECT_TRUE(ret.asBoolean().value()); engine->set("func", {}); try { func.call({}); FAIL(); } catch (Exception& e) { EXPECT_NE(e.message().find("invalid argument"), std::string::npos); } } catch (Exception& exception) { FAIL() << exception.message() << exception.stacktrace(); } } TEST_F(ExceptionTest, StackTrace) { EngineScope engineScope(engine); Local func; func = engine->eval(TS().js(R"( function exceptionStackTraceTestThrow() { throw new Error("recursive too deep"); } function exceptionStackTraceTest(depth) { if (depth >= 10) exceptionStackTraceTestThrow(); exceptionStackTraceTest(depth + 1); } exceptionStackTraceTest; )") .lua(R"( function exceptionStackTraceTestThrow() error("recursive too deep") end function exceptionStackTraceTest(depth) if depth >= 10 then exceptionStackTraceTestThrow() end exceptionStackTraceTest(depth + 1) end return exceptionStackTraceTest )") .select()); try { #ifdef SCRIPTX_BACKEND_QUICKJS // update max stack size for QuickJs // otherwise a "stack overflow" maybe thrown // even we have limited to 10 layers of recursion. JS_SetMaxStackSize(qjs_interop::currentRuntime(), 1024 * 1024); #endif func.asFunction().call({}, Number::newNumber(0)); FAIL() << "should thrown"; } catch (const Exception& e) { EXPECT_NE(e.message().find("recursive too deep"), std::string::npos) << e; EXPECT_NE(e.stacktrace().find("exceptionStackTraceTestThrow"), std::string::npos) << e; EXPECT_NE(e.stacktrace().find("exceptionStackTraceTest"), std::string::npos) << e; } Exception().stacktrace(); std::ostringstream() << Exception(); } TEST_F(ExceptionTest, Cross) { EngineScope engineScope(engine); Exception e("test"); EXPECT_NE(e.message().find("test"), std::string::npos); EXPECT_NE(std::string(e.what()).find("test"), std::string::npos); auto exception = e.exception(); try { EXPECT_FALSE(exception.isNull()); auto throwIt = engine->eval(TS().js("function throwIt(e) { throw e; }; throwIt") .lua("return function (e) error(e) end;") .select()); throwIt.asFunction().call({}, exception); } catch (Exception& ex) { EXPECT_NE(ex.message().find("test"), std::string::npos); #ifdef SCRIPTX_LANG_JAVASCRIPT EXPECT_TRUE(exception == ex.exception()); #endif #ifdef SCRIPTX_LANG_LUA auto exException = ex.exception(); EXPECT_TRUE(exException.isObject()); EXPECT_NE(exException.asObject().get("message").asString().toString().find( exception.asString().toString()), std::string::npos); #endif } { Exception ex(String::newString("test")); EXPECT_NE(ex.message().find("test"), std::string::npos); } } #ifndef SCRIPTX_BACKEND_WEBASSEMBLY TEST_F(ExceptionTest, EngineScopeOnDestroy) { bool executed = false; { EngineScope engineScope(engine); utils::Message m([](auto msg) {}, [](auto msg) { *static_cast(msg.ptr0) = true; EXPECT_THROW({ EngineScope scope(static_cast(msg.ptr1)); }, std::logic_error); }); m.tag = engine; m.ptr0 = &executed; m.ptr1 = engine; engine->messageQueue()->postMessage(m); } destroyEngine(); EXPECT_TRUE(executed); } #endif TEST_F(ExceptionTest, ThrowPerformance) { std::string chunk; chunk.resize(1024, '.'); EngineScope engineScope(engine); for (int i = 0; i < 1000; ++i) { try { throw Exception(chunk); } catch (Exception&) { } } } } // namespace script::test