mirror of
https://github.com/openharmony/ark_js_runtime.git
synced 2026-07-01 06:41:42 -04:00
0b4981525d
1. using runtime container instead of stl container 2. change debugger line amd column type to int32_t issue: https://gitee.com/openharmony/ark_js_runtime/issues/I50NHW Signed-off-by: wengchangcheng <wengchangcheng@huawei.com> Change-Id: I0eb4651f17c6f6894f11de1ba904bcbe83a57db7
298 lines
12 KiB
C++
298 lines
12 KiB
C++
/*
|
|
* Copyright (c) 2021 Huawei Device Co., Ltd.
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H
|
|
#define ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H
|
|
|
|
#include "ecmascript/jspandafile/js_pandafile_manager.h"
|
|
#include "ecmascript/tooling/interface/js_debugger.h"
|
|
#include "ecmascript/tooling/test/utils/test_events.h"
|
|
#include "ecmascript/tooling/test/utils/test_extractor.h"
|
|
#include "os/mutex.h"
|
|
|
|
namespace panda::tooling::ecmascript::test {
|
|
template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
|
|
using CUnorderedMap = panda::ecmascript::CUnorderedMap<Key, T, Hash, KeyEqual>;
|
|
using TestMap = CUnorderedMap<panda_file::SourceLang, CUnorderedMap<const char *, std::unique_ptr<TestEvents>>>;
|
|
|
|
class TestUtil {
|
|
public:
|
|
static void RegisterTest(panda_file::SourceLang language, const char *testName, std::unique_ptr<TestEvents> test)
|
|
{
|
|
auto it = testMap_.find(language);
|
|
if (it == testMap_.end()) {
|
|
CUnorderedMap<const char *, std::unique_ptr<TestEvents>> entry;
|
|
auto res = testMap_.emplace(language, std::move(entry));
|
|
it = res.first;
|
|
}
|
|
it->second.insert({testName, std::move(test)});
|
|
}
|
|
|
|
static TestEvents *GetTest(const char *name)
|
|
{
|
|
for (auto it = testMap_.begin(); it != testMap_.end(); ++it) {
|
|
auto &internalMap = it->second;
|
|
auto internalIt = std::find_if(internalMap.begin(), internalMap.end(),
|
|
[name](auto &it) { return !::strcmp(it.first, name); });
|
|
if (internalIt != internalMap.end()) {
|
|
return internalIt->second.get();
|
|
}
|
|
}
|
|
LOG(FATAL, DEBUGGER) << "Test " << name << " not found";
|
|
return nullptr;
|
|
}
|
|
|
|
static PtThread WaitForBreakpoint(PtLocation location)
|
|
{
|
|
PtThread stoppedThread(PtThread::NONE);
|
|
auto predicate = [&location]() REQUIRES(eventMutex_) { return lastEventLocation_ == location; };
|
|
auto onSuccess = [&stoppedThread]() REQUIRES(eventMutex_) {
|
|
stoppedThread = lastEventThread_;
|
|
|
|
// Need to reset location, because we might want to stop at the same point
|
|
lastEventLocation_ = PtLocation("", EntityId(0), 0);
|
|
};
|
|
|
|
WaitForEvent(DebugEvent::BREAKPOINT, predicate, onSuccess);
|
|
return stoppedThread;
|
|
}
|
|
|
|
static bool WaitForExit()
|
|
{
|
|
return WaitForEvent(DebugEvent::VM_DEATH,
|
|
[]() REQUIRES(eventMutex_) {
|
|
return lastEvent_ == DebugEvent::VM_DEATH;
|
|
}, [] {});
|
|
}
|
|
|
|
static bool WaitForException()
|
|
{
|
|
auto predicate = []() REQUIRES(eventMutex_) { return lastEvent_ == DebugEvent::EXCEPTION; };
|
|
return WaitForEvent(DebugEvent::EXCEPTION, predicate, [] {});
|
|
}
|
|
|
|
static bool WaitForInit()
|
|
{
|
|
return WaitForEvent(DebugEvent::VM_INITIALIZATION,
|
|
[]() REQUIRES(eventMutex_) {
|
|
return initialized_;
|
|
}, [] {});
|
|
}
|
|
|
|
static void Event(DebugEvent event, PtThread thread = PtThread::NONE,
|
|
PtLocation location = PtLocation("", EntityId(0), 0))
|
|
{
|
|
LOG(DEBUG, DEBUGGER) << "Occurred event " << event << " in thread with id " << thread.GetId();
|
|
os::memory::LockHolder holder(eventMutex_);
|
|
lastEvent_ = event;
|
|
lastEventThread_ = thread;
|
|
lastEventLocation_ = location;
|
|
if (event == DebugEvent::VM_INITIALIZATION) {
|
|
initialized_ = true;
|
|
}
|
|
eventCv_.Signal();
|
|
}
|
|
|
|
static void Reset()
|
|
{
|
|
os::memory::LockHolder lock(eventMutex_);
|
|
initialized_ = false;
|
|
lastEvent_ = DebugEvent::VM_START;
|
|
}
|
|
|
|
static TestMap &GetTests()
|
|
{
|
|
return testMap_;
|
|
}
|
|
|
|
static bool IsTestFinished()
|
|
{
|
|
os::memory::LockHolder lock(eventMutex_);
|
|
return lastEvent_ == DebugEvent::VM_DEATH;
|
|
}
|
|
|
|
static PtLocation GetLocation(const char *sourceFile, int32_t line, int32_t column, const char *pandaFile)
|
|
{
|
|
auto jsPandaFile = ::panda::ecmascript::JSPandaFileManager::GetInstance()->OpenJSPandaFile(pandaFile);
|
|
if (jsPandaFile == nullptr) {
|
|
return PtLocation("", EntityId(0), 0);
|
|
}
|
|
TestExtractor extractor(jsPandaFile);
|
|
auto [id, offset] = extractor.GetBreakpointAddress({sourceFile, line, column});
|
|
return PtLocation(pandaFile, id, offset);
|
|
}
|
|
|
|
static SourceLocation GetSourceLocation(const PtLocation &location, const char *pandaFile)
|
|
{
|
|
auto jsPandaFile = ::panda::ecmascript::JSPandaFileManager::GetInstance()->OpenJSPandaFile(pandaFile);
|
|
if (jsPandaFile == nullptr) {
|
|
return SourceLocation();
|
|
}
|
|
TestExtractor extractor(jsPandaFile);
|
|
return extractor.GetSourceLocation(location.GetMethodId(), location.GetBytecodeOffset());
|
|
}
|
|
|
|
static bool SuspendUntilContinue(DebugEvent reason, PtThread thread, PtLocation location)
|
|
{
|
|
{
|
|
os::memory::LockHolder lock(suspendMutex_);
|
|
suspended_ = true;
|
|
}
|
|
|
|
// Notify the debugger thread about the suspend event
|
|
Event(reason, thread, location);
|
|
|
|
// Wait for continue
|
|
{
|
|
os::memory::LockHolder lock(suspendMutex_);
|
|
while (suspended_) {
|
|
suspendCv_.Wait(&suspendMutex_);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool Continue()
|
|
{
|
|
os::memory::LockHolder lock(suspendMutex_);
|
|
suspended_ = false;
|
|
suspendCv_.Signal();
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
template<class Predicate, class OnSuccessAction>
|
|
static bool WaitForEvent(DebugEvent event, Predicate predicate, OnSuccessAction action)
|
|
{
|
|
os::memory::LockHolder holder(eventMutex_);
|
|
while (!predicate()) {
|
|
if (lastEvent_ == DebugEvent::VM_DEATH) {
|
|
return false;
|
|
}
|
|
constexpr uint64_t TIMEOUT_MSEC = 100000U;
|
|
bool timeExceeded = eventCv_.TimedWait(&eventMutex_, TIMEOUT_MSEC);
|
|
if (timeExceeded) {
|
|
LOG(FATAL, DEBUGGER) << "Time limit exceeded while waiting " << event;
|
|
return false;
|
|
}
|
|
}
|
|
action();
|
|
return true;
|
|
}
|
|
|
|
static TestMap testMap_;
|
|
static os::memory::Mutex eventMutex_;
|
|
static os::memory::ConditionVariable eventCv_ GUARDED_BY(eventMutex_);
|
|
static DebugEvent lastEvent_ GUARDED_BY(eventMutex_);
|
|
static PtThread lastEventThread_ GUARDED_BY(eventMutex_);
|
|
static PtLocation lastEventLocation_ GUARDED_BY(eventMutex_);
|
|
static os::memory::Mutex suspendMutex_;
|
|
static os::memory::ConditionVariable suspendCv_ GUARDED_BY(suspendMutex_);
|
|
static bool suspended_ GUARDED_BY(suspendMutex_);
|
|
static bool initialized_ GUARDED_BY(eventMutex_);
|
|
};
|
|
|
|
std::ostream &operator<<(std::ostream &out, std::nullptr_t);
|
|
|
|
#define ASSERT_FAIL_(val1, val2, strval1, strval2, msg) \
|
|
do { \
|
|
std::cerr << "Assertion failed at " << __FILE__ << ':' << __LINE__ << std::endl; \
|
|
std::cerr << "Expected that " strval1 " is " << (msg) << " " strval2 << std::endl; \
|
|
std::cerr << "\t" strval1 ": " << (val1) << std::endl; \
|
|
std::cerr << "\t" strval2 ": " << (val2) << std::endl; \
|
|
std::abort(); \
|
|
} while (0)
|
|
|
|
#define ASSERT_TRUE(cond) \
|
|
do { \
|
|
auto res = (cond); \
|
|
if (!res) { \
|
|
ASSERT_FAIL_(res, true, #cond, "true", "equal to"); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ASSERT_FALSE(cond) \
|
|
do { \
|
|
auto res = (cond); \
|
|
if (res) { \
|
|
ASSERT_FAIL_(res, false, #cond, "false", "equal to"); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ASSERT_EQ(lhs, rhs) \
|
|
do { \
|
|
auto res1 = (lhs); \
|
|
decltype(res1) res2 = (rhs); \
|
|
if (res1 != res2) { \
|
|
ASSERT_FAIL_(res1, res2, #lhs, #rhs, "equal to"); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ASSERT_NE(lhs, rhs) \
|
|
do { \
|
|
auto res1 = (lhs); \
|
|
decltype(res1) res2 = (rhs); \
|
|
if (res1 == res2) { \
|
|
ASSERT_FAIL_(res1, res2, #lhs, #rhs, "not equal to"); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ASSERT_STREQ(lhs, rhs) \
|
|
do { \
|
|
auto res1 = (lhs); \
|
|
decltype(res1) res2 = (rhs); \
|
|
if (::strcmp(res1, res2) != 0) { \
|
|
ASSERT_FAIL_(res1, res2, #lhs, #rhs, "equal to"); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ASSERT_SUCCESS(api_call) \
|
|
do { \
|
|
auto error = api_call; \
|
|
if (error) { \
|
|
ASSERT_FAIL_(error.value().GetMessage(), "Success", "API call result", "Expected", ""); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ASSERT_EXITED() \
|
|
do { \
|
|
bool res = TestUtil::WaitForExit(); \
|
|
if (!res) { \
|
|
ASSERT_FAIL_(TestUtil::IsTestFinished(), true, "TestUtil::IsTestFinished()", "true", ""); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ASSERT_LOCATION_EQ(lhs, rhs) \
|
|
do { \
|
|
ASSERT_STREQ((lhs).GetPandaFile(), (rhs).GetPandaFile()); \
|
|
ASSERT_EQ((lhs).GetMethodId().GetOffset(), (rhs).GetMethodId().GetOffset()); \
|
|
ASSERT_EQ((lhs).GetBytecodeOffset(), (rhs).GetBytecodeOffset()); \
|
|
} while (0)
|
|
|
|
#define ASSERT_THREAD_VALID(ecmaVm) \
|
|
do { \
|
|
ASSERT_NE((ecmaVm).GetId(), PtThread::NONE.GetId()); \
|
|
} while (0)
|
|
|
|
#define ASSERT_BREAKPOINT_SUCCESS(location) \
|
|
do { \
|
|
PtThread suspended = TestUtil::WaitForBreakpoint(location); \
|
|
ASSERT_THREAD_VALID(suspended); \
|
|
} while (0)
|
|
} // namespace panda::tooling::ecmascript::test
|
|
|
|
#endif // ECMASCRIPT_TOOLING_TEST_UTILS_TEST_UTIL_H
|