1/* 2 * Copyright (c) 2023-2023 Huawei Device Co., Ltd. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without modification, 5 * are permitted provided that the following conditions are met: 6 * 7 * 1. Redistributions of source code must retain the above copyright notice, this list of 8 * conditions and the following disclaimer. 9 * 10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 * of conditions and the following disclaimer in the documentation and/or other materials 12 * provided with the distribution. 13 * 14 * 3. Neither the name of the copyright holder nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without specific prior written 16 * permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 20 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include <iostream> 32#include <vector> 33#include <string> 34#include <algorithm> 35#include <cstdlib> 36#include <cstdio> 37#include <dirent.h> 38#include <unistd.h> 39#include <sys/wait.h> 40#include <securec.h> 41 42using namespace std; 43const static int TEST_PATH_MAX = 255; 44const static int TEST_PARAM_NUM = 5; 45const static int TEST_CASE_MODE_FULL = 0; 46const static int TEST_CASE_MODE_SMOKE = 1; 47const static int TEST_DEFAULT_CASE_MAX = 5; 48const static int TEST_T_PARAM_LEN = 2; 49const static unsigned int TEST_CASE_FLAGS_ALL = 1; 50const static unsigned int TEST_CASE_FLAGS_SPECIFY = 2; 51const static unsigned int TEST_CASE_FAILED_COUNT_MAX = 500; 52static unsigned int g_testsuitesCount = 0; 53static unsigned int g_testsuitesFailedCount = 0; 54 55struct TestCase { 56 char bin[TEST_PATH_MAX]; 57 char *param[TEST_PARAM_NUM]; 58 int caseLen; 59}; 60 61struct TestsuitesParam { 62 char testsuitesBin[TEST_PATH_MAX]; 63 char testsuitesDir[TEST_PATH_MAX]; 64 int testsuitesDirLen; 65 struct TestCase *testCase[TEST_DEFAULT_CASE_MAX]; 66 int testCaseNum; 67 int testMode; 68 int repeat; 69}; 70 71static struct TestsuitesParam g_param; 72static char *g_testsuitesFailedCase[TEST_CASE_FAILED_COUNT_MAX]; 73 74static vector<string> GetAllTestsuites(string testsuitesDir) 75{ 76 vector<string> vFiles; 77 int suffixLen = strlen(".bin"); 78 DIR *dir = opendir(testsuitesDir.c_str()); 79 if (dir == nullptr) { 80 cout << "opendir " << testsuitesDir << " failed" << endl; 81 return vFiles; 82 } 83 84 do { 85 struct dirent *pDir = readdir(dir); 86 if (pDir == nullptr) { 87 break; 88 } 89 90 if ((strcmp(pDir->d_name, ".") == 0) || (strcmp(pDir->d_name, "..") == 0)) { 91 continue; 92 } 93 94 if (pDir->d_type == DT_REG) { 95 int pathLen = strlen(pDir->d_name); 96 if (pathLen <= suffixLen) { 97 continue; 98 } 99 if (strcmp(".bin", (char *)(pDir->d_name + (pathLen - suffixLen))) != 0) { 100 continue; 101 } 102 103 vFiles.push_back(testsuitesDir + "/" + pDir->d_name); 104 } 105 } while (1); 106 closedir(dir); 107 return vFiles; 108} 109 110static bool TestsuitesIsSmoke(string testsuite, int fileLen, string smokeTest, int smokeTestLen) 111{ 112 if (fileLen <= smokeTestLen) { 113 return false; 114 } 115 116 if (strcmp(smokeTest.c_str(), (char *)(testsuite.c_str() + (fileLen - smokeTestLen))) == 0) { 117 return true; 118 } 119 return false; 120} 121 122static void RunCase(const char *testCase, char *param[]) 123{ 124 int ret; 125 int size; 126 int status = 0; 127 pid_t pid = fork(); 128 if (pid < 0) { 129 cout << "fork failed, " << strerror(errno) << endl; 130 return; 131 } 132 133 g_testsuitesCount++; 134 if (pid == 0) { 135 cout << testCase << ":" << endl; 136 ret = execve(param[0], param, nullptr); 137 if (ret != 0) { 138 cout << "execl: " << testCase << " failed, " << strerror(errno) << endl; 139 exit(0); 140 } 141 } 142 143 ret = waitpid(pid, &status, 0); 144 if (ret != pid) { 145 cout << "waitpid failed, " << strerror(errno) << endl; 146 return; 147 } 148 149 if (WEXITSTATUS(status) == 0) { 150 return; 151 } 152 153 if (g_testsuitesFailedCount >= TEST_CASE_FAILED_COUNT_MAX) { 154 cout << "[UNITTEST_RUN] Failure cases more than upper limit!\n"; 155 return; 156 } 157 158 size = strlen(testCase) + 1; 159 g_testsuitesFailedCase[g_testsuitesFailedCount] = static_cast<char *>(malloc(size)); 160 if (g_testsuitesFailedCase[g_testsuitesFailedCount] == nullptr) { 161 cout << "[UNITTEST_RUN] malloc failed!\n"; 162 return; 163 } 164 165 if (memcpy_s(g_testsuitesFailedCase[g_testsuitesFailedCount], size, testCase, size) != EOK) { 166 cout << "[UNITTEST_RUN] memcpy failed!\n"; 167 return; 168 } 169 g_testsuitesFailedCount++; 170} 171 172static void RunAllTestCase(vector<string> files, struct TestsuitesParam *param) 173{ 174 const char *testMode = nullptr; 175 const char *smokeTest = "door.bin"; 176 const char *unittestRun = g_param.testsuitesBin; 177 int unittestRunLen = strlen(unittestRun); 178 int smokeTestLen = strlen(smokeTest); 179 char *execParam[TEST_PARAM_NUM]; 180 181 if (param->testMode == TEST_CASE_MODE_SMOKE) { 182 testMode = "door.bin"; 183 } else { 184 testMode = ".bin"; 185 } 186 187 for (size_t i = 0; i < files.size(); i++) { 188 int fileLen = strlen(files[i].c_str()); 189 if (fileLen >= unittestRunLen) { 190 if (strcmp((char *)(files[i].c_str() + (fileLen - unittestRunLen)), unittestRun) == 0) { 191 continue; 192 } 193 } 194 195 if (strcmp(testMode, smokeTest) == 0) { 196 if (!TestsuitesIsSmoke(files[i], fileLen, smokeTest, smokeTestLen)) { 197 continue; 198 } 199 } else { 200 if (TestsuitesIsSmoke(files[i], fileLen, smokeTest, smokeTestLen)) { 201 continue; 202 } 203 } 204 205 (void)memset_s(execParam, sizeof(char *) * TEST_PARAM_NUM, 0, sizeof(char *) * TEST_PARAM_NUM); 206 execParam[0] = (char *)files[i].c_str(); 207 RunCase(files[i].c_str(), execParam); 208 } 209} 210 211static void TestsuitesToolsUsage(void) 212{ 213 cout << "Usage: " << endl; 214 cout << "liteos_unittest_run.bin [testsuites_dir] [options]" << endl; 215 cout << "options:" << endl; 216 cout << " -r [1-1000] --- The number of repeated runs of the test program." << endl; 217 cout << " -m [smoke/full] --- Run the smoke or full test case in this directory." << endl; 218 cout << " -t [case] [args] -t ... --- Runs the specified executable program name." << endl; 219} 220 221static int TestsuitesDirFormat(char *testDir, int len) 222{ 223 if (memcpy_s(g_param.testsuitesDir, TEST_PATH_MAX, testDir, len + 1) != EOK) { 224 cout << "testsuites dir: " << strerror(ENAMETOOLONG) << endl; 225 return -1; 226 } 227 228 char *end = g_param.testsuitesDir + len; 229 while ((testDir != end) && (*end == '/')) { 230 *end = '\0'; 231 end--; 232 len--; 233 } 234 235 g_param.testsuitesDirLen = len; 236 if (len <= 0) { 237 return -1; 238 } 239 return 0; 240} 241 242static int GetTestsuitesToolsName(const char *argv[]) 243{ 244 const char *testBin = static_cast<const char *>(argv[0]); 245 const char *end = testBin + strlen(testBin); 246 while (*end != '/') { 247 end--; 248 } 249 end++; 250 251 if (memcpy_s(g_param.testsuitesBin, TEST_PATH_MAX, end, strlen(end)) != EOK) { 252 cout << "testsuites dir: " << strerror(ENAMETOOLONG) << endl; 253 return -1; 254 } 255 return 0; 256} 257 258static int ParseTestCaseAndParam(int argc, const char *argv[], int *index) 259{ 260 int j; 261 262 (*index)++; 263 if (*index >= argc) { 264 return -1; 265 } 266 267 if (g_param.testCaseNum >= TEST_DEFAULT_CASE_MAX) { 268 return -1; 269 } 270 271 g_param.testCase[g_param.testCaseNum] = (struct TestCase *)malloc(sizeof(struct TestCase)); 272 if (g_param.testCase[g_param.testCaseNum] == nullptr) { 273 cout << "test case " << strerror(ENOMEM) << endl; 274 return -1; 275 } 276 (void)memset_s(g_param.testCase[g_param.testCaseNum], sizeof(struct TestCase), 0, sizeof(struct TestCase)); 277 struct TestCase *testCase = g_param.testCase[g_param.testCaseNum]; 278 testCase->caseLen = strlen(argv[*index]); 279 if (memcpy_s(testCase->bin, TEST_PATH_MAX, argv[*index], testCase->caseLen + 1) != EOK) { 280 testCase->caseLen = 0; 281 cout << "test case " << strerror(ENAMETOOLONG) << endl; 282 return -1; 283 } 284 testCase->param[0] = testCase->bin; 285 (*index)++; 286 for (j = 1; (j < TEST_PARAM_NUM) && (*index < argc) && (strncmp("-t", argv[*index], TEST_T_PARAM_LEN) != 0); j++) { 287 testCase->param[j] = (char *)argv[*index]; 288 (*index)++; 289 } 290 g_param.testCaseNum++; 291 if (((*index) < argc) && (strncmp("-t", argv[*index], TEST_T_PARAM_LEN) == 0)) { 292 (*index)--; 293 } 294 return 0; 295} 296 297static int TestsuitesParamCheck(int argc, const char *argv[]) 298{ 299 int ret; 300 unsigned int mask = 0; 301 g_param.testMode = TEST_CASE_MODE_FULL; 302 g_param.repeat = 1; 303 ret = TestsuitesDirFormat((char *)argv[1], strlen(argv[1])); 304 if (ret < 0) { 305 return -1; 306 } 307 308 for (int i = 2; i < argc; i++) { /* 2: param index */ 309 if (strcmp("-m", argv[i]) == 0) { 310 i++; 311 if (i >= argc) { 312 return -1; 313 } 314 mask |= TEST_CASE_FLAGS_ALL; 315 if (strcmp("smoke", argv[i]) == 0) { 316 g_param.testMode = TEST_CASE_MODE_SMOKE; 317 } else if (strcmp("full", argv[i]) == 0) { 318 g_param.testMode = TEST_CASE_MODE_FULL; 319 } else { 320 return -1; 321 } 322 } else if (strcmp("-t", argv[i]) == 0) { 323 mask |= TEST_CASE_FLAGS_SPECIFY; 324 ret = ParseTestCaseAndParam(argc, argv, &i); 325 if (ret < 0) { 326 return ret; 327 } 328 } else if (strcmp("-r", argv[i]) == 0) { 329 i++; 330 if (i >= argc) { 331 return -1; 332 } 333 g_param.repeat = atoi(argv[i]); 334 if ((g_param.repeat <= 0) || (g_param.repeat > 1000)) { /* 1000: repeat limit */ 335 return -1; 336 } 337 } 338 } 339 340 if (((mask & TEST_CASE_FLAGS_ALL) != 0) && ((mask & TEST_CASE_FLAGS_SPECIFY) != 0)) { 341 cout << "Invalid parameter combination" << endl; 342 return -1; 343 } 344 return 0; 345} 346 347static void IsCase(vector<string> files, struct TestCase *testCase) 348{ 349 for (size_t i = 0; i < files.size(); i++) { 350 string file = files[i]; 351 int fileLen = strlen(file.c_str()); 352 if (fileLen <= testCase->caseLen) { 353 continue; 354 } 355 356 const string &suffix = file.c_str() + (fileLen - testCase->caseLen); 357 if (strcmp(suffix.c_str(), testCase->bin) != 0) { 358 continue; 359 } 360 361 if (memcpy_s(testCase->bin, TEST_PATH_MAX, file.c_str(), fileLen + 1) != EOK) { 362 testCase->caseLen = 0; 363 return; 364 } 365 testCase->caseLen = fileLen; 366 g_param.testCaseNum++; 367 return; 368 } 369 cout << "liteos_unittest_run.bin: not find test case: " << testCase->bin << endl; 370 return; 371} 372 373static int FindTestCase(vector<string> files) 374{ 375 int count = g_param.testCaseNum; 376 g_param.testCaseNum = 0; 377 378 for (int i = 0; i < count; i++) { 379 IsCase(files, g_param.testCase[i]); 380 } 381 382 if (g_param.testCaseNum == 0) { 383 cout << "Not find test case !" << endl; 384 return -1; 385 } 386 return 0; 387} 388 389static void FreeTestCaseMem(void) 390{ 391 for (int index = 0; index < g_param.testCaseNum; index++) { 392 free(g_param.testCase[index]); 393 g_param.testCase[index] = nullptr; 394 } 395} 396 397static void ShowTestLog(int count) 398{ 399 cout << "[UNITTEST_RUN] Repeats: " << count << " Succeed count: " 400 << g_testsuitesCount - g_testsuitesFailedCount 401 << " Failed count: " << g_testsuitesFailedCount << endl; 402 403 if (g_testsuitesFailedCount == 0) { 404 return; 405 } 406 cout << "[UNITTEST_RUN] Failed testcase: " << endl; 407 for (int i = 0; i < g_testsuitesFailedCount; i++) { 408 cout << "[" << i << "] -> " << g_testsuitesFailedCase[i] << endl; 409 free(g_testsuitesFailedCase[i]); 410 g_testsuitesFailedCase[i] = nullptr; 411 } 412} 413 414int main(int argc, const char *argv[]) 415{ 416 int ret; 417 int count = 0; 418 419 if ((argc < 2) || (argv == nullptr)) { /* 2: param index */ 420 cout << argv[0] << ": " << strerror(EINVAL) << endl; 421 return -1; 422 } 423 424 if ((strcmp("--h", argv[1]) == 0) || (strcmp("--help", argv[1]) == 0)) { 425 TestsuitesToolsUsage(); 426 return 0; 427 } 428 429 (void)memset_s(&g_param, sizeof(struct TestsuitesParam), 0, sizeof(struct TestsuitesParam)); 430 431 ret = GetTestsuitesToolsName(argv); 432 if (ret < 0) { 433 return -1; 434 } 435 436 ret = TestsuitesParamCheck(argc, argv); 437 if (ret < 0) { 438 cout << strerror(EINVAL) << endl; 439 FreeTestCaseMem(); 440 return -1; 441 } 442 443 vector<string> files = GetAllTestsuites(g_param.testsuitesDir); 444 445 if (g_param.testCaseNum != 0) { 446 ret = FindTestCase(files); 447 if (ret < 0) { 448 files.clear(); 449 FreeTestCaseMem(); 450 return -1; 451 } 452 } 453 454 while (count < g_param.repeat) { 455 if (g_param.testCaseNum == 0) { 456 RunAllTestCase(files, &g_param); 457 } else { 458 for (int index = 0; index < g_param.testCaseNum; index++) { 459 RunCase(g_param.testCase[index]->bin, g_param.testCase[index]->param); 460 } 461 } 462 count++; 463 } 464 files.clear(); 465 FreeTestCaseMem(); 466 ShowTestLog(count); 467 return 0; 468} 469