nuttx-apps/testing/libc/popen/popen_test.c
cuiziwei 97802bd9e0 testing/libc/popen: add popen/dpopen test for shell and no-shell modes
Add tests for popen()/pclose() and dpopen()/dpclose() that work in
both shell (NSH) and no-shell (direct posix_spawnp) modes.  The test
binary doubles as its own target via the --echo sub-command.

Tests:
 1-4: popen  - basic read, multi-arg, pclose, invalid mode
 5-7: dpopen - basic read, multi-arg, dpclose

Signed-off-by: cuiziwei <cuiziwei@xiaomi.com>
2026-06-02 23:35:28 +08:00

250 lines
5.5 KiB
C

/****************************************************************************
* apps/testing/libc/popen/popen_test.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
/****************************************************************************
* Private Functions
****************************************************************************/
static int test_popen_read(void)
{
FILE *fp;
char buf[256];
int found = 0;
printf("[TEST 1] popen read...\n");
fp = popen("popen_test --echo hello_popen", "r");
if (fp == NULL)
{
printf("[FAIL] popen returned NULL\n");
return 1;
}
while (fgets(buf, sizeof(buf), fp) != NULL)
{
printf(" > %s", buf);
if (strstr(buf, "hello_popen"))
{
found = 1;
}
}
pclose(fp);
printf("%s\n", found ? "[PASS]" : "[FAIL] missing output");
return !found;
}
static int test_popen_multiarg(void)
{
FILE *fp;
char buf[256];
int found = 0;
printf("[TEST 2] popen multi-arg...\n");
fp = popen("popen_test --echo aaa bbb ccc", "r");
if (fp == NULL)
{
printf("[FAIL] popen returned NULL\n");
return 1;
}
while (fgets(buf, sizeof(buf), fp) != NULL)
{
printf(" > %s", buf);
if (strstr(buf, "aaa bbb ccc"))
{
found = 1;
}
}
pclose(fp);
printf("%s\n", found ? "[PASS]" : "[FAIL] wrong output");
return !found;
}
static int test_pclose_complete(void)
{
FILE *fp;
char buf[256];
printf("[TEST 3] pclose completes...\n");
fp = popen("popen_test --echo ok", "r");
if (fp == NULL)
{
printf("[FAIL] popen returned NULL\n");
return 1;
}
while (fgets(buf, sizeof(buf), fp) != NULL);
pclose(fp);
printf("[PASS]\n");
return 0;
}
static int test_popen_invalid_mode(void)
{
FILE *fp;
printf("[TEST 4] popen invalid mode...\n");
fp = popen("popen_test", "x");
if (fp == NULL)
{
printf("[PASS]\n");
return 0;
}
pclose(fp);
printf("[FAIL] should return NULL\n");
return 1;
}
static int test_dpopen_read(void)
{
char buf[256];
ssize_t n;
pid_t pid;
int found = 0;
int fd;
printf("[TEST 5] dpopen read...\n");
fd = dpopen("popen_test --echo hello_dpopen", O_RDONLY, &pid);
if (fd < 0)
{
printf("[FAIL] dpopen returned %d\n", fd);
return 1;
}
while ((n = read(fd, buf, sizeof(buf) - 1)) > 0)
{
buf[n] = '\0';
printf(" > %s", buf);
if (strstr(buf, "hello_dpopen"))
{
found = 1;
}
}
dpclose(fd, pid);
printf("%s\n", found ? "[PASS]" : "[FAIL] missing output");
return !found;
}
static int test_dpopen_multiarg(void)
{
char buf[256];
ssize_t n;
pid_t pid;
int found = 0;
int fd;
printf("[TEST 6] dpopen multi-arg...\n");
fd = dpopen("popen_test --echo xxx yyy zzz", O_RDONLY, &pid);
if (fd < 0)
{
printf("[FAIL] dpopen returned %d\n", fd);
return 1;
}
while ((n = read(fd, buf, sizeof(buf) - 1)) > 0)
{
buf[n] = '\0';
printf(" > %s", buf);
if (strstr(buf, "xxx yyy zzz"))
{
found = 1;
}
}
dpclose(fd, pid);
printf("%s\n", found ? "[PASS]" : "[FAIL] wrong output");
return !found;
}
static int test_dpclose_complete(void)
{
char buf[256];
pid_t pid;
int fd;
printf("[TEST 7] dpclose completes...\n");
fd = dpopen("popen_test --echo ok", O_RDONLY, &pid);
if (fd < 0)
{
printf("[FAIL] dpopen returned %d\n", fd);
return 1;
}
while (read(fd, buf, sizeof(buf)) > 0);
dpclose(fd, pid);
printf("[PASS]\n");
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int main(int argc, FAR char *argv[])
{
int fail = 0;
/* Sub-command: echo args back, used as popen/dpopen target */
if (argc > 1 && strcmp(argv[1], "--echo") == 0)
{
int i;
for (i = 2; i < argc; i++)
{
printf("%s%s", i > 2 ? " " : "", argv[i]);
}
printf("\n");
return 0;
}
printf("=== popen/dpopen test ===\n\n");
/* popen tests */
fail += test_popen_read();
fail += test_popen_multiarg();
fail += test_pclose_complete();
fail += test_popen_invalid_mode();
/* dpopen tests */
fail += test_dpopen_read();
fail += test_dpopen_multiarg();
fail += test_dpclose_complete();
printf("\n=== Results: %d failed ===\n", fail);
return fail;
}