1 /*
2 * Copyright (c) 2021-2022 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 "gtest/gtest.h"
17 #include "common_utilities_hpp.h"
18 #include "ipc_transactors.h"
19
20 using namespace OHOS::uitest;
21 using namespace std;
22
23 static constexpr uint64_t TIME_DIFF_TOLERANCE_MS = 2;
24
25 class DummyTransceiver : public MessageTransceiver {
26 public:
Initialize()27 bool Initialize() override
28 {
29 return true;
30 }
31
DoEmitMessage(const TransactionMessage & message)32 void DoEmitMessage(const TransactionMessage &message) override
33 {
34 emittedMessage_ = message;
35 }
36
37 public:
GetLastEmittedMessage(TransactionMessage & out)38 void GetLastEmittedMessage(TransactionMessage &out)
39 {
40 out = emittedMessage_;
41 }
42
43 private:
44 TransactionMessage emittedMessage_;
45 };
46
47 class MessageTransceiverTest : public testing::Test {
48 public:
49 protected:
TearDown()50 void TearDown() override
51 {
52 transceiver_.Finalize();
53 if (asyncWork_.valid()) {
54 asyncWork_.get();
55 }
56 }
57
58 future<void> asyncWork_;
59 DummyTransceiver transceiver_;
60 static constexpr uint64_t pollTimeoutMs_ = 20;
61 };
62
TEST_F(MessageTransceiverTest,checkMessageContent)63 TEST_F(MessageTransceiverTest, checkMessageContent)
64 {
65 auto message = TransactionMessage {};
66 transceiver_.EmitCall("a", "b", "c");
67 transceiver_.GetLastEmittedMessage(message);
68 ASSERT_EQ(TransactionType::CALL, message.type_);
69 ASSERT_EQ("a", message.apiId_);
70 ASSERT_EQ("b", message.callerParcel_);
71 ASSERT_EQ("c", message.paramsParcel_);
72
73 message.id_ = 1234;
74 transceiver_.EmitReply(message, "d");
75 transceiver_.GetLastEmittedMessage(message);
76 ASSERT_EQ(TransactionType::REPLY, message.type_);
77 ASSERT_EQ(1234, message.id_); // calling message_id should be kept in the reply
78 ASSERT_EQ("d", message.resultParcel_);
79
80 transceiver_.EmitHandshake();
81 transceiver_.GetLastEmittedMessage(message);
82 ASSERT_EQ(TransactionType::HANDSHAKE, message.type_);
83
84 message.id_ = 5678;
85 transceiver_.EmitAck(message);
86 transceiver_.GetLastEmittedMessage(message);
87 ASSERT_EQ(TransactionType::ACK, message.type_);
88 ASSERT_EQ(5678, message.id_); // handshake message_id should be kept in the ack
89 }
90
TEST_F(MessageTransceiverTest,enqueueDequeueMessage)91 TEST_F(MessageTransceiverTest, enqueueDequeueMessage)
92 {
93 auto message = TransactionMessage {};
94 // case1: no message in queue, polling timeout, check status and delayed time
95 uint64_t startMs = GetCurrentMillisecond();
96 auto status = transceiver_.PollCallReply(message, pollTimeoutMs_);
97 uint64_t endMs = GetCurrentMillisecond();
98 ASSERT_EQ(MessageTransceiver::PollStatus::ABORT_WAIT_TIMEOUT, status);
99 ASSERT_NEAR(pollTimeoutMs_, endMs - startMs, TIME_DIFF_TOLERANCE_MS) << "Incorrect polling time";
100 // case2: message in queue, should return immediately
101 auto tempMessage = TransactionMessage {.id_ = 1234, .type_=CALL};
102 transceiver_.OnReceiveMessage(tempMessage);
103 startMs = GetCurrentMillisecond();
104 status = transceiver_.PollCallReply(message, pollTimeoutMs_);
105 endMs = GetCurrentMillisecond();
106 ASSERT_EQ(MessageTransceiver::PollStatus::SUCCESS, status);
107 ASSERT_NEAR(endMs, startMs, TIME_DIFF_TOLERANCE_MS) << "Should return immediately";
108 ASSERT_EQ(1234, message.id_) << "Incorrect message content";
109 // case3. message comes at sometime before timeout, should end polling and return it
110 constexpr uint64_t delayMs = 10;
111 asyncWork_ = async(launch::async, [this, delayMs, tempMessage]() -> void {
112 this_thread::sleep_for(chrono::milliseconds(delayMs));
113 this->transceiver_.OnReceiveMessage(tempMessage);
114 });
115 startMs = GetCurrentMillisecond();
116 status = transceiver_.PollCallReply(message, pollTimeoutMs_);
117 endMs = GetCurrentMillisecond();
118 ASSERT_EQ(MessageTransceiver::PollStatus::SUCCESS, status);
119 ASSERT_NEAR(endMs - startMs, delayMs, TIME_DIFF_TOLERANCE_MS) << "Should return soon after message enqueue";
120 }
121
TEST_F(MessageTransceiverTest,checkMessageFilter)122 TEST_F(MessageTransceiverTest, checkMessageFilter)
123 {
124 auto message = TransactionMessage {.type_=CALL};
125 // without filter, message should be accepted
126 transceiver_.OnReceiveMessage(message);
127 auto status = transceiver_.PollCallReply(message, pollTimeoutMs_);
128 ASSERT_EQ(MessageTransceiver::PollStatus::SUCCESS, status);
129 auto filter = [](TransactionType type) -> bool {
130 return type != TransactionType::CALL;
131 };
132 // message should be filtered out, poll will be timeout
133 transceiver_.SetMessageFilter(filter);
134 transceiver_.OnReceiveMessage(message);
135 status = transceiver_.PollCallReply(message, pollTimeoutMs_);
136 ASSERT_EQ(MessageTransceiver::PollStatus::ABORT_WAIT_TIMEOUT, status);
137 }
138
TEST_F(MessageTransceiverTest,checkAnwserHandshakeAtomatically)139 TEST_F(MessageTransceiverTest, checkAnwserHandshakeAtomatically)
140 {
141 auto handshake = TransactionMessage {.id_=1234, .type_=HANDSHAKE};
142 transceiver_.OnReceiveMessage(handshake);
143 auto emitted = TransactionMessage {};
144 transceiver_.GetLastEmittedMessage(emitted);
145 // should emit ack automatically on receiving handshake
146 ASSERT_EQ(TransactionType::ACK, emitted.type_);
147 ASSERT_EQ(handshake.id_, emitted.id_);
148 }
149
TEST_F(MessageTransceiverTest,immediateExitHandling)150 TEST_F(MessageTransceiverTest, immediateExitHandling)
151 {
152 auto message = TransactionMessage {.type_=TransactionType::EXIT};
153 // EXIT-message comes at sometime before timeout, should end polling and return it
154 constexpr uint64_t delayMs = 10;
155 asyncWork_ = async(launch::async, [this, delayMs, message]() {
156 this_thread::sleep_for(chrono::milliseconds(delayMs));
157 this->transceiver_.OnReceiveMessage(message);
158 });
159 const uint64_t startMs = GetCurrentMillisecond();
160 const auto status = transceiver_.PollCallReply(message, pollTimeoutMs_);
161 const uint64_t endMs = GetCurrentMillisecond();
162 ASSERT_EQ(MessageTransceiver::PollStatus::ABORT_REQUEST_EXIT, status);
163 ASSERT_NEAR(endMs - startMs, delayMs, TIME_DIFF_TOLERANCE_MS) << "Should return soon after exit-request";
164 }
165
TEST_F(MessageTransceiverTest,immediateConnectionDiedHandling)166 TEST_F(MessageTransceiverTest, immediateConnectionDiedHandling)
167 {
168 transceiver_.ScheduleCheckConnection(false);
169 // connection died before timeout, should end polling and return it
170 auto message = TransactionMessage {};
171 const uint64_t startMs = GetCurrentMillisecond();
172 auto status = transceiver_.PollCallReply(message, WATCH_DOG_TIMEOUT_MS * 2);
173 const uint64_t endMs = GetCurrentMillisecond();
174 constexpr uint64_t toleranceMs = WATCH_DOG_TIMEOUT_MS / 100;
175 ASSERT_EQ(MessageTransceiver::PollStatus::ABORT_CONNECTION_DIED, status);
176 ASSERT_NEAR(endMs - startMs, WATCH_DOG_TIMEOUT_MS, toleranceMs) << "Should return soon after connection died";
177 }
178
TEST_F(MessageTransceiverTest,checkScheduleHandshake)179 TEST_F(MessageTransceiverTest, checkScheduleHandshake)
180 {
181 transceiver_.ScheduleCheckConnection(false);
182 // connection died before timeout, should end polling and return it
183 auto message = TransactionMessage {};
184 constexpr uint64_t handshakeDelayMs = 1000;
185 asyncWork_ = async(launch::async, [this, handshakeDelayMs, message]() {
186 this_thread::sleep_for(chrono::milliseconds(handshakeDelayMs));
187 auto handshake = TransactionMessage {.type_=TransactionType::HANDSHAKE};
188 this->transceiver_.OnReceiveMessage(handshake);
189 });
190 const uint64_t startMs = GetCurrentMillisecond();
191 const auto status = transceiver_.PollCallReply(message, WATCH_DOG_TIMEOUT_MS * 2);
192 const uint64_t endMs = GetCurrentMillisecond();
193 // since handshake comes at 1000th ms, connection should die at (1000+WATCH_DOG_TIMEOUT_MS)th ms
194 constexpr uint64_t expectedConnDieMs = handshakeDelayMs + WATCH_DOG_TIMEOUT_MS;
195 constexpr uint64_t toleranceMs = WATCH_DOG_TIMEOUT_MS / 100;
196 ASSERT_EQ(MessageTransceiver::PollStatus::ABORT_CONNECTION_DIED, status);
197 ASSERT_NEAR(endMs - startMs, expectedConnDieMs, toleranceMs) << "Incorrect time elapse";
198 }
199
TEST_F(MessageTransceiverTest,ensureConnected)200 TEST_F(MessageTransceiverTest, ensureConnected)
201 {
202 constexpr uint64_t timeoutMs = 100;
203 // give no incoming message, should be timed-out
204 ASSERT_FALSE(transceiver_.EnsureConnectionAlive(timeoutMs));
205 // inject incoming message before timeout, should return true immediately
206 static constexpr uint64_t incomingDelayMs = 60;
207 asyncWork_ = async(launch::async, [this]() {
208 this_thread::sleep_for(chrono::milliseconds(incomingDelayMs));
209 auto message = TransactionMessage {.type_=TransactionType::ACK};
210 this->transceiver_.OnReceiveMessage(message);
211 });
212 const uint64_t startMs = GetCurrentMillisecond();
213 ASSERT_TRUE(transceiver_.EnsureConnectionAlive(timeoutMs));
214 const uint64_t endMs = GetCurrentMillisecond();
215 ASSERT_NEAR(startMs, endMs, incomingDelayMs + TIME_DIFF_TOLERANCE_MS); // check return immediately
216 }
217
218 class DummyServer : public TransactionServer {
219 public:
Initialize()220 bool Initialize() override
221 {
222 auto func = [](string_view api, string_view caller, string_view params) { return string(api) + "_ok"; };
223 SetCallFunction(func); // set custom api-call processing method
224 return Transactor::Initialize();
225 }
226
GetTransceiver()227 MessageTransceiver *GetTransceiver()
228 {
229 return transceiver_.get();
230 }
231
232 protected:
CreateTransceiver()233 unique_ptr<MessageTransceiver> CreateTransceiver() override
234 {
235 return make_unique<DummyTransceiver>();
236 }
237 };
238
239 class TransactionServerTest : public testing::Test {
240 protected:
SetUp()241 void SetUp() override
242 {
243 server_.Initialize();
244 }
245
TearDown()246 void TearDown() override
247 {
248 server_.Finalize();
249 if (asyncWork_.valid()) {
250 asyncWork_.get();
251 }
252 }
253
254 future<uint32_t> asyncWork_;
255 DummyServer server_;
256 };
257
TEST_F(TransactionServerTest,checkRunLoop)258 TEST_F(TransactionServerTest, checkRunLoop)
259 {
260 asyncWork_ = async(launch::async, [this]() {
261 return this->server_.RunLoop();
262 });
263
264 static constexpr size_t testSetSize = 3;
265 const array<uint32_t, testSetSize> ids = {1, 2, 3};
266 const array<string, testSetSize> apis = {"yz", "zl", "lj"};
267 const array<string, testSetSize> expectedReply = {"yz_ok", "zl_ok", "lj_ok"};
268 // check call-message handling loop
269 auto transceiver = reinterpret_cast<DummyTransceiver *>(server_.GetTransceiver());
270 for (size_t idx = 0; idx < testSetSize; idx++) {
271 // inject call message
272 auto call = TransactionMessage {.id_=ids.at(idx), .type_=TransactionType::CALL, .apiId_=apis.at(idx)};
273 transceiver->OnReceiveMessage(call);
274 // check the emitted reply message corresponding to the inject call, after a short interval
275 this_thread::sleep_for(chrono::milliseconds(TIME_DIFF_TOLERANCE_MS));
276 auto reply = TransactionMessage {};
277 transceiver->GetLastEmittedMessage(reply);
278 ASSERT_EQ(TransactionType::REPLY, reply.type_);
279 ASSERT_EQ(ids.at(idx), reply.id_);
280 ASSERT_EQ(expectedReply.at(idx), reply.resultParcel_); // check the replied result
281 }
282 // request exit, should end loop immediately with success code
283 auto terminate = TransactionMessage {.type_=TransactionType::EXIT};
284 const uint64_t startMs = GetCurrentMillisecond();
285 transceiver->OnReceiveMessage(terminate);
286 auto exitCode = asyncWork_.get();
287 const uint64_t endMs = GetCurrentMillisecond();
288 ASSERT_EQ(0, exitCode);
289 ASSERT_NEAR(startMs, endMs, TIME_DIFF_TOLERANCE_MS); // check exit immediately
290 }
291
TEST_F(TransactionServerTest,checkExitLoopWhenConnectionDied)292 TEST_F(TransactionServerTest, checkExitLoopWhenConnectionDied)
293 {
294 // enable connection check and enter loop
295 server_.GetTransceiver()->ScheduleCheckConnection(false);
296 asyncWork_ = async(launch::async, [this]() {
297 return this->server_.RunLoop();
298 });
299 // given no handshake, should end loop immediately with failure code after timeout
300 const uint64_t startMs = GetCurrentMillisecond();
301 auto exitCode = asyncWork_.get();
302 const uint64_t endMs = GetCurrentMillisecond();
303 ASSERT_NE(0, exitCode);
304 // check exit immediately after timeout
305 ASSERT_NEAR(startMs, endMs, WATCH_DOG_TIMEOUT_MS * 1.02f);
306 }
307
308 class DummyClient : public TransactionClient {
309 public:
310
GetTransceiver()311 MessageTransceiver *GetTransceiver()
312 {
313 return transceiver_.get();
314 }
315
316 protected:
CreateTransceiver()317 unique_ptr<MessageTransceiver> CreateTransceiver() override
318 {
319 return make_unique<DummyTransceiver>();
320 }
321 };
322
323 class TransactionClientTest : public testing::Test {
324 protected:
SetUp()325 void SetUp() override
326 {
327 client_.Initialize();
328 }
329
TearDown()330 void TearDown() override
331 {
332 if (asyncWork_.valid()) {
333 // do this to ensure asyncWork_ terminates normally
334 auto terminate = TransactionMessage {.type_=TransactionType::EXIT};
335 client_.GetTransceiver()->OnReceiveMessage(terminate);
336 asyncWork_.get();
337 }
338 client_.Finalize();
339 }
340
341 future<string> asyncWork_;
342 DummyClient client_;
343 };
344
TEST_F(TransactionClientTest,checkInvokeApi)345 TEST_F(TransactionClientTest, checkInvokeApi)
346 {
347 static constexpr size_t testSetSize = 3;
348 const array<string, testSetSize> apis = {"yz", "zl", "lj"};
349 const array<string, testSetSize> mockReplies = {"yz_ok", "zl_ok", "lj_ok"};
350 auto transceiver = reinterpret_cast<DummyTransceiver *>(client_.GetTransceiver());
351 auto message = TransactionMessage();
352 for (size_t idx = 0; idx < testSetSize; idx++) {
353 asyncWork_ = async(launch::async, [this, &apis, idx]() -> string {
354 return this->client_.InvokeApi(apis.at(idx), "", "");
355 });
356 // wait and check the emitted call-message
357 this_thread::sleep_for(chrono::milliseconds(TIME_DIFF_TOLERANCE_MS));
358 transceiver->GetLastEmittedMessage(message);
359 ASSERT_EQ(TransactionType::CALL, message.type_);
360 ASSERT_EQ(apis.at(idx), message.apiId_);
361 // inject reply and check invocation result
362 auto mockResult = mockReplies.at(idx);
363 auto reply = TransactionMessage {.id_=message.id_, .type_=TransactionType::REPLY, .resultParcel_= mockResult};
364 transceiver->OnReceiveMessage(reply);
365 this_thread::sleep_for(chrono::milliseconds(TIME_DIFF_TOLERANCE_MS));
366 const uint64_t startMs = GetCurrentMillisecond();
367 auto actualResult = asyncWork_.get();
368 const uint64_t endMs = GetCurrentMillisecond();
369 ASSERT_EQ(mockResult, actualResult);
370 // check return result immediately
371 ASSERT_NEAR(startMs, endMs, TIME_DIFF_TOLERANCE_MS);
372 }
373 }
374
TEST_F(TransactionClientTest,checkResultWhenConnectionDied)375 TEST_F(TransactionClientTest, checkResultWhenConnectionDied)
376 {
377 // enable connection check
378 client_.GetTransceiver()->ScheduleCheckConnection(false);
379 asyncWork_ = async(launch::async, [this]() -> string {
380 return this->client_.InvokeApi("yz", "", "");
381 });
382 // trigger connection timeout by giving no incoming message, should return error result
383 uint64_t startMs = GetCurrentMillisecond();
384 auto result = asyncWork_.get();
385 uint64_t endMs = GetCurrentMillisecond();
386 ASSERT_NE(string::npos, result.find("INTERNAL_ERROR"));
387 ASSERT_NE(string::npos, result.find("connection with uitest_daemon is dead"));
388 // check return immediately after timeout
389 ASSERT_NEAR(startMs, endMs, WATCH_DOG_TIMEOUT_MS * 1.02f);
390 // connection is already dead, should return immediately in later invocations
391 asyncWork_ = async(launch::async, [this]() -> string {
392 return this->client_.InvokeApi("yz", "", "");
393 });
394 startMs = GetCurrentMillisecond();
395 result = asyncWork_.get();
396 endMs = GetCurrentMillisecond();
397 ASSERT_NE(string::npos, result.find("INTERNAL_ERROR"));
398 ASSERT_NE(string::npos, result.find("connection with uitest_daemon is dead"));
399 // check return immediately due-to dead connection
400 ASSERT_NEAR(startMs, endMs, TIME_DIFF_TOLERANCE_MS);
401 }
402
TEST_F(TransactionClientTest,checkRejectConcurrentInvoke)403 TEST_F(TransactionClientTest, checkRejectConcurrentInvoke)
404 {
405 asyncWork_ = async(launch::async, [this]() -> string {
406 return this->client_.InvokeApi("yz", "", "");
407 });
408 // give a short delay to ensure concurrence
409 this_thread::sleep_for(chrono::milliseconds(TIME_DIFF_TOLERANCE_MS));
410 uint64_t startMs = GetCurrentMillisecond();
411 auto reply = client_.InvokeApi("yz", "", "");
412 uint64_t endMs = GetCurrentMillisecond();
413 // the second call should return immediately and reject the concurrent invoke
414 ASSERT_NE(string::npos, reply.find("USAGE_ERROR"));
415 ASSERT_NEAR(startMs, endMs, TIME_DIFF_TOLERANCE_MS);
416 }
417
TEST_F(TransactionClientTest,checkResultAfterFinalized)418 TEST_F(TransactionClientTest, checkResultAfterFinalized)
419 {
420 client_.Finalize();
421 uint64_t startMs = GetCurrentMillisecond();
422 auto reply = client_.InvokeApi("yz", "", "");
423 uint64_t endMs = GetCurrentMillisecond();
424 // the second call should return immediately and reject the concurrent invoke
425 ASSERT_NE(string::npos, reply.find("INTERNAL_ERROR"));
426 ASSERT_NE(string::npos, reply.find("connection with uitest_daemon is dead"));
427 ASSERT_NEAR(startMs, endMs, TIME_DIFF_TOLERANCE_MS);
428 }
429