nuttx-apps/testing/ostest/getopt.c
Gregory Nutt dd7c3bfa53 apps/testing/ostest: Add test for required argument format
The Linux man page requires that the getopt_long() and getopt_long_only() functions accept arguments to options in a form like:

    --option=argument

This PR adds a test that missing functionality that was recently added to NuttX.

This change also fixes an error in string comparison that was working before only because of the way that strings are stored by in linker ELF.  The address of the strings were being compared, not the value of the string.

This change effects only the getopt() tests of the OS test.

Tested on a simulator NSH configuration and used to verify the NuttX change.
2021-04-04 14:14:21 -05:00

668 lines
16 KiB
C

/****************************************************************************
* apps/testing/ostest/getopt.c
*
* 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 <getopt.h>
#include "ostest.h"
/****************************************************************************
* Pre-processor DEFINTIONS
****************************************************************************/
#define SHORT_RESULT_A(n) \
{ \
results[n].ret = 'a'; \
results[n].flag = 0; \
results[n].arg = NULL; \
}
#define SHORT_RESULT_B(n) \
{ \
results[n].ret = 'b'; \
results[n].flag = 0; \
results[n].arg = NULL; \
}
#define SHORT_RESULT_C1(n) \
{ \
results[n].ret = 'c'; \
results[n].flag = 0; \
results[n].arg = "Arg1"; \
}
#define SHORT_RESULT_C2(n) \
{ \
results[n].ret = 'c'; \
results[n].flag = 0; \
results[n].arg = NULL; \
}
#define SHORT_RESULT_D(n) \
{ \
results[n].ret = 'd'; \
results[n].flag = 0; \
results[n].arg = "Arg2"; \
}
#define SHORT_RESULT_X(n) \
{ \
results[n].ret = '?'; \
results[n].flag = 0; \
results[n].arg = NULL; \
}
#define LONG_OPTION_A(n) \
{ \
long_options[n].name = "OptionA"; \
long_options[n].has_arg = no_argument; \
long_options[n].flag = NULL; \
long_options[n].val = 'a'; \
}
#define LONG_OPTION_B(n) \
{ \
long_options[n].name = "OptionB"; \
long_options[n].has_arg = no_argument; \
long_options[n].flag = &g_flag; \
long_options[n].val = 'b'; \
}
#define LONG_OPTION_C(n) \
{ \
long_options[n].name = "OptionC"; \
long_options[n].has_arg = required_argument; \
long_options[n].flag = NULL; \
long_options[n].val = 'c'; \
}
#define LONG_OPTION_D(n) \
{ \
long_options[n].name = "OptionD"; \
long_options[n].has_arg = optional_argument; \
long_options[n].flag = &g_flag; \
long_options[n].val = 'd'; \
}
#define LONG_OPTION_END(n) \
{ \
long_options[n].name = NULL; \
long_options[n].has_arg = 0; \
long_options[n].flag = NULL; \
long_options[n].val = 0; \
}
#define LONG_RESULT_A(n) \
{ \
results[n].ret = 'a'; \
results[n].flag = 0; \
results[n].arg = NULL; \
}
#define LONG_RESULT_B(n) \
{ \
results[n].ret = OK; \
results[n].flag = 'b'; \
results[n].arg = NULL; \
}
#define LONG_RESULT_C(n) \
{ \
results[n].ret = 'c'; \
results[n].flag = 0; \
results[n].arg = "Arg1"; \
}
#define LONG_RESULT_D1(n) \
{ \
results[n].ret = OK; \
results[n].flag = 'd'; \
results[n].arg = "Arg2"; \
}
#define LONG_RESULT_D2(n) \
{ \
results[n].ret = OK; \
results[n].flag = 'd'; \
results[n].arg = NULL; \
}
#define LONG_RESULT_X(n) \
{ \
results[n].ret = '?'; \
results[n].flag = 0; \
results[n].arg = NULL; \
}
/****************************************************************************
* Private Types
****************************************************************************/
struct result_s
{
int ret;
int flag;
FAR char *arg;
};
/****************************************************************************
* Private Data
****************************************************************************/
static int g_flag;
static const char *g_optstring = "abc::d:";
/****************************************************************************
* Private Functions
****************************************************************************/
static int getopt_short_test(int noptions, int argc, FAR char **argv,
FAR const char *optstring,
FAR const struct result_s *expected)
{
int ndx;
int ret;
optarg = NULL;
for (ndx = 0;
(ret = getopt(argc, argv, optstring)) != ERROR && ndx < noptions;
ndx++)
{
/* optind may index through argc (to the NULL argv entry) since it is
* required to always point to the next command line argument.
*/
if (optind < 1 || optind > argc)
{
printf("ERROR: optind=%d\n", optind);
}
/* Parse until getopt(), but do not process anything if ndx exceeds
* noptions.
*/
if (ndx < noptions)
{
if (expected[ndx].ret != ret)
{
printf("ERROR: arg %d: ret=%d (expected %d)\n",
ndx + 1, ret, expected[ndx].ret);
}
if ((expected[ndx].arg == NULL &&
optarg != NULL) ||
(expected[ndx].arg != NULL &&
strcmp(expected[ndx].arg, optarg) != 0))
{
printf("ERROR: arg %d: optarg=%s (expected %s)\n",
ndx + 1, optarg == NULL ? "null" : optarg,
expected[ndx].arg == NULL ? "null" :
expected[ndx].arg);
}
}
optarg = NULL;
}
/* Verify the number of options. Some tests have and extra value at the
* end of the command line after the options.
*/
if (ndx != noptions && ndx != noptions + 1)
{
printf("ERROR: ndx=%d (expected %d)\n", ndx, noptions);
}
return OK;
}
static int getopt_long_test(int noptions, int argc, FAR char **argv,
FAR const char *optstring,
FAR const struct option *longopts,
FAR int *longindex,
FAR const struct result_s *expected)
{
int ndx;
int ret;
optarg = NULL;
g_flag = 0;
for (ndx = 0;
(ret = getopt_long(argc, argv, optstring, longopts,
longindex)) != ERROR;
ndx++)
{
/* optind may index through argc (to the NULL argv entry) since it is
* required to always point to the next command line argument.
*/
if (optind < 1 || optind > argc)
{
printf("ERROR: optind=%d\n", optind);
}
/* Parse until getop_long(), but do not process anything if ndx exceeds
* noptions.
*/
if (ndx < noptions)
{
if (expected[ndx].ret != ret)
{
printf("ERROR: arg %d: ret=%d (expected %d)\n",
ndx + 1, ret, expected[ndx].ret);
}
if (expected[ndx].flag != g_flag)
{
printf("ERROR: arg %d; flag=%d (expected %d)\n",
ndx + 1, expected[ndx].flag, g_flag);
}
if ((expected[ndx].arg == NULL &&
optarg != NULL) ||
(expected[ndx].arg != NULL &&
strcmp(expected[ndx].arg, optarg) != 0))
{
printf("ERROR: arg %d: optarg=%s (expected %s)\n",
ndx + 1, optarg == NULL ? "null" : optarg,
expected[ndx].arg == NULL ? "null" :
expected[ndx].arg);
}
}
optarg = NULL;
g_flag = 0;
}
/* Verify the number of options. Some tests have and extra value at the
* end of the command line after the options.
*/
if (ndx != noptions && ndx != noptions + 1)
{
printf("ERROR: ndx=%d (expected %d)\n", ndx, noptions);
}
return OK;
}
static int getopt_longonly_test(int noptions, int argc, FAR char **argv,
FAR const char *optstring,
FAR const struct option *longopts,
FAR int *longindex,
FAR const struct result_s *expected)
{
int ndx;
int ret;
optarg = NULL;
g_flag = 0;
for (ndx = 0;
(ret = getopt_long_only(argc, argv, optstring, longopts,
longindex)) != ERROR;
ndx++)
{
/* optind may index through argc (to the NULL argv entry) since it is
* required to always point to the next command line argument.
*/
if (optind < 1 || optind > argc)
{
printf("ERROR: optind=%d\n", optind);
}
/* Parse until getop_long(), but do not process anything if ndx exceeds
* noptions.
*/
if (ndx < noptions)
{
if (expected[ndx].ret != ret)
{
printf("ERROR: arg %d: ret=%d (expected %d)\n",
ndx + 1, ret, expected[ndx].ret);
}
if (expected[ndx].flag != g_flag)
{
printf("ERROR: arg %d; flag=%d (expected %d)\n",
ndx + 1, expected[ndx].flag, g_flag);
}
if ((expected[ndx].arg == NULL &&
optarg != NULL) ||
(expected[ndx].arg != NULL &&
strcmp(expected[ndx].arg, optarg) != 0))
{
printf("ERROR: arg %d: optarg=%s (expected %s)\n",
ndx + 1, optarg == NULL ? "null" : optarg,
expected[ndx].arg == NULL ? "null" :
expected[ndx].arg);
}
}
optarg = NULL;
g_flag = 0;
}
/* Verify the number of options. Some tests have and extra value at the
* end of the command line after the options.
*/
if (ndx != noptions && ndx != noptions + 1)
{
printf("ERROR: ndx=%d (expected %d)\n", ndx, noptions);
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int getopt_test(void)
{
struct option long_options[5];
struct result_s results[5];
FAR char *argv[10];
printf("getopt(): Simple test\n");
argv[0] = NULL;
argv[1] = "-a";
argv[2] = "-b";
argv[3] = "-c";
argv[4] = "Arg1";
argv[5] = "-d";
argv[6] = "Arg2";
argv[7] = "NoOption";
argv[8] = NULL;
SHORT_RESULT_A(0);
SHORT_RESULT_B(1);
SHORT_RESULT_C1(2);
SHORT_RESULT_D(3);
getopt_short_test(4, 8, argv, g_optstring, results);
printf("getopt(): Invalid argument\n");
argv[0] = NULL;
argv[1] = "-a";
argv[2] = "-b";
argv[3] = "-c";
argv[4] = "Arg1";
argv[5] = "-d";
argv[6] = "Arg2";
argv[7] = "-x";
argv[8] = "NoOption";
argv[9] = NULL;
SHORT_RESULT_A(0);
SHORT_RESULT_B(1);
SHORT_RESULT_C1(2);
SHORT_RESULT_D(3);
SHORT_RESULT_X(4);
getopt_short_test(5, 9, argv, g_optstring, results);
argv[8] = NULL;
getopt_short_test(5, 8, argv, g_optstring, results);
printf("getopt(): Missing optional argument\n");
argv[0] = NULL;
argv[1] = "-a";
argv[2] = "-b";
argv[3] = "-c";
argv[4] = "-d";
argv[5] = "Arg2";
argv[6] = "NoOption";
argv[7] = NULL;
SHORT_RESULT_A(0);
SHORT_RESULT_B(1);
SHORT_RESULT_C2(2);
SHORT_RESULT_D(3);
getopt_short_test(4, 7, argv, g_optstring, results);
printf("getopt_long(): Simple test\n");
argv[0] = NULL;
argv[1] = "--OptionA";
argv[2] = "--OptionB";
argv[3] = "--OptionC";
argv[4] = "Arg1";
argv[5] = "--OptionD";
argv[6] = "Arg2";
argv[7] = "NoOption";
argv[8] = NULL;
LONG_OPTION_A(0);
LONG_OPTION_B(1);
LONG_OPTION_C(2);
LONG_OPTION_D(3);
LONG_OPTION_END(4)
LONG_RESULT_A(0);
LONG_RESULT_B(1);
LONG_RESULT_C(2);
LONG_RESULT_D1(3);
getopt_long_test(4, 8, argv, g_optstring, long_options, NULL,
results);
printf("getopt_long(): No short options\n");
getopt_long_test(4, 8, argv, NULL, long_options, NULL,
results);
printf("getopt_long(): Argument for --option=argument\n");
argv[0] = NULL;
argv[1] = "--OptionA";
argv[2] = "--OptionB";
argv[3] = "--OptionC=Arg1";
argv[4] = "--OptionD=Arg2";
argv[5] = "NoOption";
argv[6] = NULL;
LONG_OPTION_A(0);
LONG_OPTION_B(1);
LONG_OPTION_C(2);
LONG_OPTION_D(3);
LONG_OPTION_END(4)
LONG_RESULT_A(0);
LONG_RESULT_B(1);
LONG_RESULT_C(2);
LONG_RESULT_D1(3);
getopt_long_test(4, 6, argv, g_optstring, long_options, NULL,
results);
printf("getopt_long(): Invalid long option\n");
argv[0] = NULL;
argv[1] = "--OptionA";
argv[2] = "--OptionB";
argv[3] = "--OptionC";
argv[4] = "Arg1";
argv[5] = "--OptionD";
argv[6] = "Arg2";
argv[7] = "--OptionX";
argv[8] = "NoOption";
argv[9] = NULL;
LONG_OPTION_A(0);
LONG_OPTION_B(1);
LONG_OPTION_C(2);
LONG_OPTION_D(3);
LONG_OPTION_END(4)
LONG_RESULT_A(0);
LONG_RESULT_B(1);
LONG_RESULT_C(2);
LONG_RESULT_D1(3);
LONG_RESULT_X(4);
getopt_long_test(5, 9, argv, g_optstring, long_options, NULL,
results);
argv[8] = NULL;
getopt_long_test(5, 8, argv, g_optstring, long_options, NULL,
results);
printf("getopt_long(): Mixed long and short options\n");
argv[0] = NULL;
argv[1] = "--OptionA";
argv[2] = "-b";
argv[3] = "--OptionC";
argv[4] = "Arg1";
argv[5] = "-d";
argv[6] = "Arg2";
argv[7] = "NoOption";
argv[8] = NULL;
LONG_OPTION_A(0);
LONG_OPTION_C(1);
LONG_OPTION_END(2)
LONG_RESULT_A(0);
SHORT_RESULT_B(1);
LONG_RESULT_C(2);
SHORT_RESULT_D(3);
getopt_long_test(4, 8, argv, g_optstring, long_options, NULL,
results);
printf("getopt_long(): Invalid short option\n");
argv[0] = NULL;
argv[1] = "--OptionA";
argv[2] = "--OptionB";
argv[3] = "--OptionC";
argv[4] = "Arg1";
argv[5] = "--OptionD";
argv[6] = "Arg2";
argv[7] = "-x";
argv[8] = "NoOption";
argv[9] = NULL;
LONG_OPTION_A(0);
LONG_OPTION_B(1);
LONG_OPTION_C(2);
LONG_OPTION_D(3);
LONG_OPTION_END(4)
LONG_RESULT_A(0);
LONG_RESULT_B(1);
LONG_RESULT_C(2);
LONG_RESULT_D1(3);
SHORT_RESULT_X(4);
getopt_long_test(5, 9, argv, g_optstring, long_options, NULL,
results);
printf("getopt_long(): Missing optional arguments\n");
argv[0] = NULL;
argv[1] = "--OptionA";
argv[2] = "--OptionB";
argv[3] = "-c";
argv[4] = "--OptionD";
argv[5] = NULL;
LONG_OPTION_A(0);
LONG_OPTION_B(1);
LONG_OPTION_D(2);
LONG_OPTION_END(3)
LONG_RESULT_A(0);
LONG_RESULT_B(1);
SHORT_RESULT_C2(2);
LONG_RESULT_D2(3);
getopt_long_test(4, 6, argv, g_optstring, long_options, NULL,
results);
printf("getopt_long_only(): Mixed long and short options\n");
argv[0] = NULL;
argv[1] = "-a";
argv[2] = "--OptionB";
argv[3] = "-c";
argv[4] = "Arg1";
argv[5] = "--OptionD";
argv[6] = "Arg2";
argv[7] = "NoOption";
argv[8] = NULL;
LONG_OPTION_B(0);
LONG_OPTION_D(1);
LONG_OPTION_END(2)
SHORT_RESULT_A(0);
LONG_RESULT_B(1);
SHORT_RESULT_C1(2);
LONG_RESULT_D1(3);
getopt_longonly_test(4, 8, argv, g_optstring, long_options, NULL,
results);
printf("getopt_long_only(): Single hyphen long options\n");
argv[0] = NULL;
argv[1] = "-OptionA";
argv[2] = "-b";
argv[3] = "--OptionC";
argv[4] = "Arg1";
argv[5] = "-d";
argv[6] = "Arg2";
argv[7] = "NoOption";
argv[8] = NULL;
LONG_OPTION_A(0);
LONG_OPTION_C(1);
LONG_OPTION_END(2)
LONG_RESULT_A(0);
SHORT_RESULT_B(1);
LONG_RESULT_C(2);
SHORT_RESULT_D(3);
getopt_longonly_test(4, 8, argv, g_optstring, long_options, NULL,
results);
return OK;
}