• 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 <fcntl.h>
17 #include <sys/mman.h>
18 #include "gtest/gtest.h"
19 #include "ipc_transactor.h"
20 
21 using namespace OHOS::uitest;
22 using namespace std;
23 
24 using PipeFds = int32_t[2];
25 
PrintEnd()26 static void PrintEnd()
27 {
28     cout << "...End...";
29 }
30 
RedirectStdoutToPipe(string_view procName,PipeFds fds)31 static void RedirectStdoutToPipe(string_view procName, PipeFds fds)
32 {
33     close(fds[0]);
34     close(STDOUT_FILENO);
35     close(STDERR_FILENO);
36     dup2(fds[1], STDOUT_FILENO);
37     cout << "...Start..." << procName;
38     (void)atexit(PrintEnd);
39 }
40 
WaitPidAndReadPipe(pid_t pid,PipeFds fds)41 static string WaitPidAndReadPipe(pid_t pid, PipeFds fds)
42 {
43     string separatorLine = "============================================\n";
44     string output = separatorLine;
45     close(fds[1]);
46     int32_t status = 0;
47     waitpid(pid, &status, 0);
48     char buf[1024] = {0};
49     auto size = read(fds[0], buf, sizeof(buf));
50     output.append(string(buf, size)).append("\n").append(separatorLine);
51     return output;
52 }
53 
54 struct InitConnTestParams {
55     // preset conditions
56     bool tokenLegal;
57     uint32_t clientDelay;
58     uint32_t serverDelay;
59     // expected stats
60     bool expectConnSuccess;
61     // case description
62     string_view description;
63 };
64 
65 // the enviroment of situations to test
66 static constexpr InitConnTestParams ParamSuite[] = {
67     // connection timeout: 5000ms
68     {true, 0000, 0000, true, "NormalConnection"},
69     {true, 4000, 0000, true, "NormalConnection-ClientDelayInTolerance"},
70     {true, 0000, 4000, true, "NormalConnection-ServerDelayInTolerance"},
71     {false, 000, 0000, false, "BadConnection-IllegalToken"},
72     {true, 6000, 0000, false, "BadConnection-ClientDelayOutofTolerance"},
73     {true, 0000, 6000, false, "BadConnection-ServerDelayOutofTolerance"},
74 };
75 static constexpr uint32_t suiteSize = 6U;
76 
77 class ApiTransactorConnectionTest : public ::testing::TestWithParam<uint32_t> {};
78 
TEST_P(ApiTransactorConnectionTest,testInitConnection)79 TEST_P(ApiTransactorConnectionTest, testInitConnection)
80 {
81     size_t idx = GetParam();
82     const auto params = ParamSuite[idx];
83     auto token = string("testInitConnection_#") + string(params.description);
84     // fork and run server
85     auto pid = fork();
86     ASSERT_NE(pid, -1);
87     if (pid == 0) {
88         this_thread::sleep_for(chrono::milliseconds(params.serverDelay));
89         ApiTransactor server(true);
90         server.InitAndConnectPeer(token, nullptr);
91         exit(0); // call exit explicity, to avoid hangs for unkown reason
92     }
93     // fork and run client
94     int32_t fds[2];
95     ASSERT_EQ(pipe(fds), 0);
96     pid = fork();
97     ASSERT_NE(pid, -1);
98     if (pid == 0) {
99         RedirectStdoutToPipe(token + "-client", fds);
100         // client runs in parent process
101         this_thread::sleep_for(chrono::milliseconds(params.clientDelay));
102         if (!params.tokenLegal) {
103             token.append("_bad");
104         }
105         ApiTransactor client(false);
106         auto success = client.InitAndConnectPeer(token, nullptr);
107         ASSERT_EQ(success, params.expectConnSuccess);
108         exit(0); // call exit explicity, to avoid hangs for unkown reason
109     }
110     // receive output of client in main process and do assertions
111     auto clientOutput = WaitPidAndReadPipe(pid, fds);
112     cout << clientOutput << endl;
113     ASSERT_TRUE(clientOutput.find(" FAILED ") == string::npos);
114 }
115 
116 INSTANTIATE_TEST_SUITE_P(_, ApiTransactorConnectionTest, testing::Range(0U, suiteSize, 1U));
117 
TEST(ApiTransactorTest,testApiTransaction)118 TEST(ApiTransactorTest, testApiTransaction)
119 {
120     constexpr string_view token = "testAapiTransaction";
121     constexpr uint32_t serverLifeMs = 100;
122     // fork and run server
123     auto pid = fork();
124     ASSERT_NE(pid, -1);
125     if (pid == 0) {
126         auto executor = [](const ApiCallInfo &call, ApiReplyInfo &result) {
127             string ret = call.apiId_;
128             ret.append("/").append(call.callerObjRef_).append("/");
129             ret.append(call.paramList_.at(0).get<string>());
130             result.resultValue_ = ret;
131             result.exception_ = ApiCallErr(ERR_API_USAGE, "MockedError");
132         };
133         ApiTransactor server(true);
134         server.InitAndConnectPeer(token, executor);
135         this_thread::sleep_for(chrono::milliseconds(serverLifeMs));
136         exit(0);
137     }
138     // fork and run client
139     int32_t fds[2];
140     ASSERT_EQ(pipe(fds), 0);
141     pid = fork();
142     ASSERT_NE(pid, -1);
143     if (pid == 0) {
144         RedirectStdoutToPipe("testApiTransaction-client", fds);
145         ApiTransactor client(false);
146         auto connSuccess = client.InitAndConnectPeer(token, nullptr);
147         ASSERT_EQ(connSuccess, true);
148         auto call = ApiCallInfo {.apiId_ = "testApi", .callerObjRef_ = "testObject"};
149         call.paramList_.emplace_back("testParam");
150         ApiReplyInfo result;
151         client.Transact(call, result);
152         // check1. call_data and reply_data are correctly deleviderd
153         ASSERT_EQ(result.resultValue_.type(), nlohmann::detail::value_t::string);
154         ASSERT_EQ(result.resultValue_.get<string>(), "testApi/testObject/testParam");
155         ASSERT_EQ(result.exception_.code_, ERR_API_USAGE);
156         ASSERT_EQ(result.exception_.message_, "MockedError");
157         // check2. connection state change after server died
158         this_thread::sleep_for(chrono::milliseconds(serverLifeMs << 1)); // wait server expired
159         ASSERT_EQ(client.GetConnectionStat(), DISCONNECTED);
160         // check3. transaction after connection dead gives connection died error
161         client.Transact(call, result);
162         ASSERT_EQ(result.exception_.code_, ERR_INTERNAL);
163         ASSERT_TRUE(result.exception_.message_.find("ipc connection is dead") != string::npos);
164         exit(0);
165     }
166     // receive output of client in main process and do assertions
167     auto clientOutput = WaitPidAndReadPipe(pid, fds);
168     cout << clientOutput << endl;
169     ASSERT_TRUE(clientOutput.find(" FAILED ") == string::npos);
170 }
171 
172 #ifdef MFD_ALLOW_SEALING
TEST(ApiTransactorTest,testHandleFdParam)173 TEST(ApiTransactorTest, testHandleFdParam)
174 {
175     constexpr string_view token = "testHandleFdParam";
176     constexpr uint32_t serverLifeMs = 100;
177     // fork and run server
178     auto pid = fork();
179     ASSERT_NE(pid, -1);
180     if (pid == 0) {
181         auto executor = [token](const ApiCallInfo &call, ApiReplyInfo &result) {
182             // read the file size and return it
183             auto fd = call.paramList_.at(0).get<int32_t>();
184             if (fcntl(fd, F_GETFD) == -1 && errno == EBADF) {
185                 result.exception_ = ApiCallErr(ERR_INVALID_INPUT, "Bad fd");
186             } else {
187                 write(fd, token.data(), token.length());
188             }
189         };
190         ApiTransactor server(true);
191         server.InitAndConnectPeer(token, executor);
192         this_thread::sleep_for(chrono::milliseconds(serverLifeMs));
193         exit(0);
194     }
195     // fork and run client
196     int32_t fds[2];
197     ASSERT_EQ(pipe(fds), 0);
198     pid = fork();
199     ASSERT_NE(pid, -1);
200     if (pid == 0) {
201         RedirectStdoutToPipe("testHandleFdParam-client", fds);
202         ApiTransactor client(false);
203         auto connSuccess = client.InitAndConnectPeer(token, nullptr);
204         ASSERT_EQ(connSuccess, true);
205         auto call = ApiCallInfo {.apiId_ = "testApi"};
206         auto fd = memfd_create("dummy_file", MFD_ALLOW_SEALING);
207         ASSERT_TRUE(fd > 0) << "Failed to create memfd";
208         ftruncate(fd, 0);
209         call.paramList_.push_back(fd);
210         call.fdParamIndex_ = 0;
211         ApiReplyInfo result;
212         client.Transact(call, result);
213         auto fileSize = lseek(fd, 0, SEEK_END);
214         // check get correct file size from reply
215         ASSERT_EQ(result.exception_.code_, NO_ERROR);
216         ASSERT_EQ(fileSize, token.length());
217         exit(0);
218     }
219     // receive output of client in main process and do assertions
220     auto clientOutput = WaitPidAndReadPipe(pid, fds);
221     cout << clientOutput << endl;
222     ASSERT_TRUE(clientOutput.find(" FAILED ") == string::npos);
223 }
224 #endif
225 
TEST(ApiTransactorTest,testDetectConcurrentTransaction)226 TEST(ApiTransactorTest, testDetectConcurrentTransaction)
227 {
228     constexpr string_view token = "testDetectConcurrentTransaction";
229     static constexpr uint32_t apiCostMs = 100; // mock the timecost of api invocation
230     // fork and run server
231     auto pid = fork();
232     ASSERT_NE(pid, -1);
233     if (pid == 0) {
234         auto executor = [](const ApiCallInfo &call, ApiReplyInfo &result) {
235             this_thread::sleep_for(chrono::milliseconds(apiCostMs));
236         };
237         ApiTransactor server(true);
238         if (server.InitAndConnectPeer(token, executor)) {
239             this_thread::sleep_for(chrono::milliseconds(apiCostMs << 1));
240         }
241         exit(0);
242     }
243     // fork and run client
244     int32_t fds[2];
245     ASSERT_EQ(pipe(fds), 0);
246     pid = fork();
247     ASSERT_NE(pid, -1);
248     if (pid == 0) {
249         RedirectStdoutToPipe("testDetectConcurrentTransaction-client", fds);
250         ApiTransactor client(false);
251         auto connSuccess = client.InitAndConnectPeer(token, nullptr);
252         ASSERT_EQ(connSuccess, true);
253         auto call0 = ApiCallInfo {.apiId_ = "testApi0"};
254         ApiReplyInfo result0;
255         auto call1 = ApiCallInfo {.apiId_ = "testApi1"};
256         ApiReplyInfo result1;
257         auto ft = async(launch::async, [&]() { client.Transact(call0, result0); });
258         // delay to ensure invocation(call0,reply0) on going
259         this_thread::sleep_for(chrono::milliseconds(apiCostMs >> 1));
260         client.Transact(call1, result1);
261         ft.get();
262         ASSERT_EQ(result0.exception_.code_, NO_ERROR);
263         ASSERT_EQ(result1.exception_.code_, ERR_API_USAGE);
264         exit(0);
265     }
266     // receive output of client in main process and do assertions
267     auto clientOutput = WaitPidAndReadPipe(pid, fds);
268     cout << clientOutput << endl;
269     ASSERT_TRUE(clientOutput.find(" FAILED ") == string::npos);
270 }
271 
272 // clientDecteServerDie is tested in above case.
273 // in this test, we need to server in parent process and client in child process
TEST(ApiTransactorTest,testServerDetectClientDeath)274 TEST(ApiTransactorTest, testServerDetectClientDeath)
275 {
276     constexpr string_view token = "testServerDetectClientDeath";
277     // fork and run client
278     auto pid = fork();
279     ASSERT_NE(pid, -1);
280     if (pid == 0) {
281         ApiTransactor client(false);
282         client.InitAndConnectPeer(token, nullptr);
283         client.Finalize();
284         exit(0);
285     }
286     // fork and run server
287     int32_t fds[2];
288     ASSERT_EQ(pipe(fds), 0);
289     pid = fork();
290     ASSERT_NE(pid, -1);
291     if (pid == 0) {
292         RedirectStdoutToPipe("testServerDetectClientDeath-server", fds);
293         ApiTransactor server(true);
294         auto success = server.InitAndConnectPeer(token, nullptr);
295         ASSERT_EQ(true, success);                          // connected on init
296         this_thread::sleep_for(chrono::milliseconds(100)); // wait for client expired
297         ASSERT_EQ(server.GetConnectionStat(), DISCONNECTED);
298         exit(0);
299     }
300     // receive output of client in main process and do assertions
301     auto clientOutput = WaitPidAndReadPipe(pid, fds);
302     cout << clientOutput << endl;
303     ASSERT_TRUE(clientOutput.find(" FAILED ") == string::npos);
304 }