1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "tooling/test/client_utils/test_util.h"
17
18#include "tooling/client/domain/debugger_client.h"
19#include "tooling/client/domain/runtime_client.h"
20#include "tooling/client/utils/cli_command.h"
21#include "tooling/client/session/session.h"
22#include "websocket/client/websocket_client.h"
23
24#include <cstring>
25
26namespace panda::ecmascript::tooling::test {
27TestMap TestUtil::testMap_;
28
29MatchFunc MatchRule::replySuccess = [] (auto recv, auto, auto) -> bool {
30    std::unique_ptr<PtJson> json = PtJson::Parse(recv);
31    Result ret;
32    int32_t id = 0;
33    ret = json->GetInt("id", &id);
34    if (ret != Result::SUCCESS) {
35        return false;
36    }
37
38    std::unique_ptr<PtJson> result = nullptr;
39    ret = json->GetObject("result", &result);
40    if (ret != Result::SUCCESS) {
41        return false;
42    }
43
44    int32_t code = 0;
45    ret = result->GetInt("code", &code);
46    if (ret != Result::NOT_EXIST) {
47        return false;
48    }
49    return true;
50};
51
52std::ostream &operator<<(std::ostream &out, ActionRule value)
53{
54    const char *s = nullptr;
55
56#define ADD_CASE(entry) \
57    case (entry):       \
58        s = #entry;     \
59        break
60
61    switch (value) {
62        ADD_CASE(ActionRule::STRING_EQUAL);
63        ADD_CASE(ActionRule::STRING_CONTAIN);
64        ADD_CASE(ActionRule::CUSTOM_RULE);
65        default: {
66            ASSERT(false && "Unknown ActionRule");
67        }
68    }
69#undef ADD_CASE
70
71    return out << s;
72}
73
74void TestUtil::NotifySuccess()
75{
76    std::vector<std::string> cliCmdStr = { "success" };
77    CliCommand cmd(cliCmdStr, 0);
78    if (cmd.ExecCommand() == ErrCode::ERR_FAIL) {
79        LOG_DEBUGGER(ERROR) << "ExecCommand Test.success fail";
80    }
81}
82
83void TestUtil::NotifyFail()
84{
85    std::vector<std::string> cliCmdStr = { "fail" };
86    CliCommand cmd(cliCmdStr, 0);
87    if (cmd.ExecCommand() == ErrCode::ERR_FAIL) {
88        LOG_DEBUGGER(ERROR) << "ExecCommand Test.fail fail";
89    }
90}
91
92void TestUtil::ForkSocketClient([[maybe_unused]] int port, const std::string &name)
93{
94#ifdef OHOS_PLATFORM
95    auto correntPid = getpid();
96#endif
97    pid_t pid = fork();
98    if (pid < 0) {
99        LOG_DEBUGGER(FATAL) << "fork error";
100        UNREACHABLE();
101    } else if (pid == 0) {
102        LOG_DEBUGGER(INFO) << "fork son pid: " << getpid();
103        std::this_thread::sleep_for(std::chrono::microseconds(500000));  // 500000: 500ms for wait debugger
104#ifdef OHOS_PLATFORM
105        std::string pidStr = std::to_string(correntPid);
106        std::string sockInfo = pidStr + "PandaDebugger";
107#else
108        std::string sockInfo = std::to_string(port);
109#endif
110        int ret = SessionManager::getInstance().CreateTestSession(sockInfo);
111        LOG_ECMA_IF(ret, FATAL) << "CreateTestSession fail";
112
113        WebSocketClient &client = SessionManager::getInstance().GetCurrentSession()->GetWebSocketClient();
114        auto &testAction = TestUtil::GetTest(name)->testAction;
115        for (const auto &action: testAction) {
116            LOG_DEBUGGER(INFO) << "message: " << action.message;
117            bool success = true;
118            if (action.action == SocketAction::SEND) {
119                std::vector<std::string> cliCmdStr = Utils::SplitString(action.message, " ");
120                CliCommand cmd(cliCmdStr, 0);
121                success = (cmd.ExecCommand() == ErrCode::ERR_OK);
122            } else {
123                ASSERT(action.action == SocketAction::RECV);
124                std::string recv = client.Decode();
125                HandleAcceptanceMessages(action, client, recv, success);
126                SendMessage(action, recv);
127                LOG_DEBUGGER(INFO) << "recv: " << recv;
128                LOG_DEBUGGER(INFO) << "rule: " << action.rule;
129            }
130            if (!success) {
131                LOG_DEBUGGER(ERROR) << "Notify fail";
132                NotifyFail();
133                SessionManager::getInstance().DelSessionById(0);
134                exit(-1);
135            }
136        }
137
138        SessionManager::getInstance().DelSessionById(0);
139        exit(0);
140    }
141    LOG_DEBUGGER(INFO) << "ForkSocketClient end";
142}
143
144void TestUtil::HandleAcceptanceMessages(ActionInfo action, WebSocketClient &client, std::string &recv, bool &success)
145{
146    if (recv.empty()) {
147        LOG_DEBUGGER(ERROR) << "Notify fail";
148        NotifyFail();
149        SessionManager::getInstance().DelSessionById(0);
150        exit(-1);
151    }
152    int times = 0;
153    while ((!strcmp(recv.c_str(), "try again")) && (times <= 5)) { // 5: five times
154        std::this_thread::sleep_for(std::chrono::microseconds(500000)); // 500000: 500ms
155        recv = client.Decode();
156        times++;
157    }
158    switch (action.rule) {
159        case ActionRule::STRING_EQUAL: {
160            success = (recv == action.message);
161            break;
162        }
163        case ActionRule::STRING_CONTAIN: {
164            success = (recv.find(action.message) != std::string::npos);
165            break;
166        }
167        case ActionRule::CUSTOM_RULE: {
168            bool needMoreMsg = false;
169            success = action.matchFunc(recv, action.message, needMoreMsg);
170            while (needMoreMsg) {
171                recv = client.Decode();
172                success = action.matchFunc(recv, action.message, needMoreMsg);
173            }
174            break;
175        }
176    }
177    return;
178}
179
180void TestUtil::SendMessage(ActionInfo action, std::string recv)
181{
182    switch (action.event) {
183        case TestCase::SOURCE: {
184            std::unique_ptr<PtJson> json = PtJson::Parse(recv);
185            std::unique_ptr<PtJson> params = nullptr;
186            Result ret = json->GetObject("params", &params);
187            std::string scriptId;
188            ret = params->GetString("scriptId", &scriptId);
189            if (ret != Result::SUCCESS) {
190                return;
191            }
192            SessionManager::getInstance().GetCurrentSession()->
193                GetSourceManager().SendRequeSource(std::atoi(scriptId.c_str()));
194            break;
195        }
196        case TestCase::WATCH: {
197            if (!SessionManager::getInstance().GetCurrentSession()->GetWatchManager().GetWatchInfoSize()) {
198                SessionManager::getInstance().GetCurrentSession()->GetWatchManager().AddWatchInfo("a");
199                SessionManager::getInstance().GetCurrentSession()->GetWatchManager().AddWatchInfo("this");
200            }
201            const uint watchInfoSize = 2;
202            for (uint i = 0; i < watchInfoSize; i++) {
203                SessionManager::getInstance().GetCurrentSession()->GetWatchManager().SendRequestWatch(i, "0");
204            }
205            break;
206        }
207        case TestCase::WATCH_OBJECT: {
208            std::unique_ptr<PtJson> json = PtJson::Parse(recv);
209            std::unique_ptr<PtJson> result = nullptr;
210            Result ret = json->GetObject("result", &result);
211            std::unique_ptr<PtJson> watchResult = nullptr;
212            ret = result->GetObject("result", &watchResult);
213            if (ret != Result::SUCCESS) {
214                return;
215            }
216            std::string objectId;
217            ret = watchResult->GetString("objectId", &objectId);
218            if (ret != Result::SUCCESS) {
219                return;
220            }
221            SessionManager::getInstance().GetCurrentSession()->GetWatchManager().GetPropertiesCommand(0, objectId);
222            break;
223        }
224        default: {
225            return;
226        }
227    }
228    return;
229}
230}  // namespace panda::ecmascript::tooling::test
231