diff --git a/test/resource/tooling/ohos_test.xml b/test/resource/tooling/ohos_test.xml
index 5b9dc623bc688c595e4b79096efbd3052be9a5a5..7ef9ebae93eb726e1eb744bd0fd66a78c9c184cc 100755
--- a/test/resource/tooling/ohos_test.xml
+++ b/test/resource/tooling/ohos_test.xml
@@ -91,6 +91,7 @@
+
@@ -117,6 +118,7 @@
+
diff --git a/tooling/test/BUILD.gn b/tooling/test/BUILD.gn
index 34bfcbac6817aeb62528071d05b2615d24a45e20..08368539977b68065f2524139cc656b3a2b0d90a 100644
--- a/tooling/test/BUILD.gn
+++ b/tooling/test/BUILD.gn
@@ -55,6 +55,7 @@ test_js_files = [
"local_variable_scope",
"container",
"closure_scope",
+ "closure_variable",
"branch",
"common_func",
"watch_variable",
diff --git a/tooling/test/client_utils/test_list.cpp b/tooling/test/client_utils/test_list.cpp
index b1cccbeb530a8bca1706544820b2da0e0455e87d..0a5a9ed2ae6652ddc84465528d57675556f543f9 100644
--- a/tooling/test/client_utils/test_list.cpp
+++ b/tooling/test/client_utils/test_list.cpp
@@ -45,6 +45,7 @@
#include "tooling/test/testcases/js_breakpoint_cannot_hit_test.h"
#include "tooling/test/testcases/js_breakpoint_in_different_branch.h"
#include "tooling/test/testcases/js_watch_variable_test.h"
+#include "tooling/test/testcases/js_watch_closure_variable_test.h"
#include "tooling/test/testcases/js_stepinto_arrow_test.h"
#include "tooling/test/testcases/js_stepinto_async_test.h"
#include "tooling/test/testcases/js_stepout_arrow_test.h"
@@ -115,6 +116,7 @@ static void RegisterTests()
TestUtil::RegisterTest("JsJsWatchBasicTypeTest", GetJsWatchBasicTypeTest());
TestUtil::RegisterTest("JsJsWatchSetTypeTest", GetJsWatchSetTypeTest());
TestUtil::RegisterTest("JsJsWatchOtherTypeTest", GetJsWatchOtherTypeTest());
+ TestUtil::RegisterTest("JsWatchClosureVariableTest", GetJsWatchClosureVariableTest());
TestUtil::RegisterTest("JsStepintoLoopTest", GetJsStepintoLoopTest());
TestUtil::RegisterTest("JsStepintoRecursionTest", GetJsStepintoRecursionTest());
TestUtil::RegisterTest("JsStepintoSwitchTest", GetJsStepintoSwitchTest());
diff --git a/tooling/test/testcases/js/closure_variable.js b/tooling/test/testcases/js/closure_variable.js
new file mode 100644
index 0000000000000000000000000000000000000000..215fa9418daf0c79729030d6b92116b30665b37f
--- /dev/null
+++ b/tooling/test/testcases/js/closure_variable.js
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2024 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.
+ */
+function funA() {
+ let a=1;
+ {
+ let a=2;
+ }
+ a=3;
+}
+
+funA()
\ No newline at end of file
diff --git a/tooling/test/testcases/js_watch_closure_variable_test.h b/tooling/test/testcases/js_watch_closure_variable_test.h
new file mode 100644
index 0000000000000000000000000000000000000000..323f07425107698b5569ee15e6fdd758ec6e6f51
--- /dev/null
+++ b/tooling/test/testcases/js_watch_closure_variable_test.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2024 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_TESTCASES_JS_WATCH_CLOSURE_VARIABLE_TEST_H
+#define ECMASCRIPT_TOOLING_TEST_TESTCASES_JS_WATCH_CLOSURE_VARIABLE_TEST_H
+
+#include "tooling/test/client_utils/test_util.h"
+
+namespace panda::ecmascript::tooling::test {
+class JsWatchClosureVariableTest : public TestActions {
+public:
+ JsWatchClosureVariableTest()
+ {
+ testAction = {
+ {SocketAction::SEND, "enable"},
+ {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess},
+ {SocketAction::SEND, "runtime-enable"},
+ {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess},
+ {SocketAction::SEND, "run"},
+ {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess},
+ // load closure_variable.js
+ {SocketAction::RECV, "Debugger.scriptParsed", ActionRule::STRING_CONTAIN},
+ // break on start
+ {SocketAction::RECV, "Debugger.paused", ActionRule::CUSTOM_RULE,
+ [](auto recv, auto, auto) -> bool {
+ std::unique_ptr json = PtJson::Parse(recv);
+ DebuggerClient debuggerClient(0);
+ debuggerClient.RecvReply(std::move(json));
+ return true;
+ }},
+
+ // set breakpoint
+ {SocketAction::SEND, "b " DEBUGGER_JS_DIR "closure_variable.js 20"},
+ {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess},
+
+ // hit breakpoint after resume first time
+ {SocketAction::SEND, "resume"},
+ {SocketAction::RECV, "Debugger.resumed", ActionRule::STRING_CONTAIN},
+ {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess},
+ {SocketAction::RECV, "Debugger.paused", ActionRule::CUSTOM_RULE,
+ [this](auto recv, auto, auto) -> bool { return RecvHitBreakInfo(recv, 19); }},
+
+ {SocketAction::SEND, "watch a"},
+ {SocketAction::RECV, "", ActionRule::CUSTOM_RULE,
+ [this](auto recv, auto, auto) -> bool { return RecvWatchInfo(recv); }},
+
+ // reply success and run
+ {SocketAction::SEND, "success"},
+ {SocketAction::SEND, "resume"},
+ {SocketAction::RECV, "Debugger.resumed", ActionRule::STRING_CONTAIN},
+ {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess},
+ };
+ }
+
+ bool RecvHitBreakInfo(std::string recv, int line)
+ {
+ std::unique_ptr json = PtJson::Parse(recv);
+ Result ret;
+ std::string method = "";
+ ret = json->GetString("method", &method);
+ if (ret != Result::SUCCESS || method != "Debugger.paused") {
+ return false;
+ }
+
+ std::unique_ptr params = nullptr;
+ ret = json->GetObject("params", ¶ms);
+ if (ret != Result::SUCCESS) {
+ return false;
+ }
+
+ std::unique_ptr hitBreakpoints = nullptr;
+ ret = params->GetArray("hitBreakpoints", &hitBreakpoints);
+ if (ret != Result::SUCCESS) {
+ return false;
+ }
+
+ std::string breakpoint = "";
+ breakpoint = hitBreakpoints->Get(0)->GetString();
+ if (ret != Result::SUCCESS || breakpoint.find(sourceFile_) == std::string::npos ||
+ breakpoint.find(std::to_string(line)) == std::string::npos) {
+ return false;
+ }
+ return true;
+ }
+ bool RecvWatchInfo(std::string recv)
+ {
+ std::unique_ptr json = PtJson::Parse(recv);
+ Result ret;
+ int id;
+ ret = json->GetInt("id", &id);
+ if (ret != Result::SUCCESS) {
+ return false;
+ }
+
+ std::unique_ptr result = nullptr;
+ ret = json->GetObject("result", &result);
+ if (ret != Result::SUCCESS) {
+ return false;
+ }
+
+ std::unique_ptr watchResult = nullptr;
+ ret = result->GetObject("result", &watchResult);
+ if (ret != Result::SUCCESS) {
+ return false;
+ }
+
+ std::string type = "";
+ ret = watchResult->GetString("type", &type);
+ if (ret != Result::SUCCESS || type != "number") {
+ return false;
+ }
+
+ std::string value = "";
+ ret = watchResult->GetString("unserializableValue", &value);
+ if (ret != Result::SUCCESS || value != "1") {
+ return false;
+ }
+
+ std::string description = "";
+ ret = watchResult->GetString("description", &description);
+ if (ret != Result::SUCCESS || description != "1") {
+ return false;
+ }
+ return true;
+ }
+
+ std::pair GetEntryPoint() override
+ {
+ return {pandaFile_, entryPoint_};
+ }
+ ~JsWatchClosureVariableTest() = default;
+
+private:
+ std::string pandaFile_ = DEBUGGER_ABC_DIR "closure_variable.abc";
+ std::string sourceFile_ = DEBUGGER_JS_DIR "closure_variable.js";
+ std::string entryPoint_ = "_GLOBAL::func_main_0";
+};
+
+std::unique_ptr GetJsWatchClosureVariableTest()
+{
+ return std::make_unique();
+}
+} // namespace panda::ecmascript::tooling::test
+
+#endif // ECMASCRIPT_TOOLING_TEST_TESTCASES_JS_WATCH_CLOSURE_VARIABLE_TEST_H
\ No newline at end of file