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