• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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