test: introduce parent test suite format
[dpdk.git] / app / test / test.c
index a33b2b5..ac0a663 100644 (file)
@@ -1,34 +1,5 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2014 Intel Corporation
  */
 
 #include <string.h>
@@ -41,7 +12,7 @@
 #include <ctype.h>
 #include <sys/queue.h>
 
-#ifdef RTE_LIBRTE_CMDLINE
+#ifdef RTE_LIB_CMDLINE
 #include <cmdline_rdline.h>
 #include <cmdline_parse.h>
 #include <cmdline_socket.h>
@@ -50,24 +21,35 @@ extern cmdline_parse_ctx_t main_ctx[];
 #endif
 
 #include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_tailq.h>
 #include <rte_eal.h>
 #include <rte_cycles.h>
 #include <rte_log.h>
 #include <rte_string_fns.h>
-#ifdef RTE_LIBRTE_TIMER
+#ifdef RTE_LIB_TIMER
 #include <rte_timer.h>
 #endif
 
 #include "test.h"
+#ifdef RTE_LIB_PDUMP
+#include "test_pdump.h"
+#endif
 
 #define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1
 
+#define FOR_EACH_SUITE_TESTCASE(iter, suite, case)                     \
+       for (iter = 0, case = suite->unit_test_cases[0];                \
+               suite->unit_test_cases[iter].testcase;                  \
+               iter++, case = suite->unit_test_cases[iter])
+
+#define FOR_EACH_SUITE_TESTSUITE(iter, suite, sub_ts)                  \
+       for (iter = 0, sub_ts = suite->unit_test_suites ?               \
+               suite->unit_test_suites[0]:NULL; sub_ts &&              \
+               suite->unit_test_suites[iter]->suite_name != NULL;      \
+               iter++, sub_ts = suite->unit_test_suites[iter])
+
 const char *prgname; /* to be set to argv[0] */
 
-#ifndef RTE_EXEC_ENV_BAREMETAL
-static const char *recursive_call; /* used in linuxapp for MP and other tests */
+static const char *recursive_call; /* used in linux for MP and other tests */
 
 static int
 no_action(void){ return 0; }
@@ -81,63 +63,115 @@ do_recursive_call(void)
                int (*action_fn)(void);
        } actions[] =  {
                        { "run_secondary_instances", test_mp_secondary },
+#ifdef RTE_LIB_PDUMP
+#ifdef RTE_NET_RING
+                       { "run_pdump_server_tests", test_pdump },
+#endif
+#endif
                        { "test_missing_c_flag", no_action },
-                       { "test_missing_n_flag", no_action },
+                       { "test_main_lcore_flag", no_action },
+                       { "test_invalid_n_flag", no_action },
                        { "test_no_hpet_flag", no_action },
-                       { "test_whitelist_flag", no_action },
+                       { "test_allow_flag", no_action },
                        { "test_invalid_b_flag", no_action },
                        { "test_invalid_vdev_flag", no_action },
                        { "test_invalid_r_flag", no_action },
-#ifdef RTE_LIBRTE_XEN_DOM0
-                       { "test_dom0_misc_flags", no_action },
-#else
                        { "test_misc_flags", no_action },
-#endif
                        { "test_memory_flags", no_action },
                        { "test_file_prefix", no_action },
                        { "test_no_huge_flag", no_action },
-                       { "test_ivshmem", test_ivshmem },
+#ifdef RTE_LIB_TIMER
+                       { "timer_secondary_spawn_wait", test_timer_secondary },
+#endif
        };
 
        if (recursive_call == NULL)
                return -1;
-       for (i = 0; i < sizeof(actions)/sizeof(actions[0]); i++) {
+       for (i = 0; i < RTE_DIM(actions); i++) {
                if (strcmp(actions[i].env_var, recursive_call) == 0)
                        return (actions[i].action_fn)();
        }
        printf("ERROR - missing action to take for %s\n", recursive_call);
        return -1;
 }
-#endif
+
+int last_test_result;
+
+#define MAX_EXTRA_ARGS 32
 
 int
 main(int argc, char **argv)
 {
-#ifdef RTE_LIBRTE_CMDLINE
+#ifdef RTE_LIB_CMDLINE
        struct cmdline *cl;
+       char *tests[argc]; /* store an array of tests to run */
+       int test_count = 0;
+       int i;
 #endif
+       char *extra_args;
        int ret;
 
-       ret = rte_eal_init(argc, argv);
-       if (ret < 0)
-               return -1;
+       extra_args = getenv("DPDK_TEST_PARAMS");
+       if (extra_args != NULL && strlen(extra_args) > 0) {
+               char **all_argv;
+               char *eargv[MAX_EXTRA_ARGS];
+               int all_argc;
+               int eargc;
+               int i;
 
-#ifdef RTE_LIBRTE_TIMER
-       rte_timer_subsystem_init();
-#endif
+               RTE_LOG(INFO, APP, "Using additional DPDK_TEST_PARAMS: '%s'\n",
+                               extra_args);
+               eargc = rte_strsplit(extra_args, strlen(extra_args),
+                               eargv, MAX_EXTRA_ARGS, ' ');
 
-       if (commands_init() < 0)
-               return -1;
+               /* merge argc/argv and the environment args */
+               all_argc = argc + eargc;
+               all_argv = malloc(sizeof(*all_argv) * (all_argc + 1));
+               if (all_argv == NULL) {
+                       ret = -1;
+                       goto out;
+               }
+
+               for (i = 0; i < argc; i++)
+                       all_argv[i] = argv[i];
+               for (i = 0; i < eargc; i++)
+                       all_argv[argc + i] = eargv[i];
+               all_argv[all_argc] = NULL;
+
+               /* call eal_init with combined args */
+               ret = rte_eal_init(all_argc, all_argv);
+               free(all_argv);
+       } else
+               ret = rte_eal_init(argc, argv);
+       if (ret < 0) {
+               ret = -1;
+               goto out;
+       }
 
        argv += ret;
+       argc -= ret;
 
        prgname = argv[0];
 
-#ifndef RTE_EXEC_ENV_BAREMETAL
-       if ((recursive_call = getenv(RECURSIVE_ENV_VAR)) != NULL)
-               return do_recursive_call();
+#ifdef RTE_LIB_TIMER
+       ret = rte_timer_subsystem_init();
+       if (ret < 0 && ret != -EALREADY) {
+               ret = -1;
+               goto out;
+       }
 #endif
 
+       if (commands_init() < 0) {
+               ret = -1;
+               goto out;
+       }
+
+       recursive_call = getenv(RECURSIVE_ENV_VAR);
+       if (recursive_call != NULL) {
+               ret = do_recursive_call();
+               goto out;
+       }
+
 #ifdef RTE_LIBEAL_USE_HPET
        if (rte_eal_hpet_init(1) < 0)
 #endif
@@ -145,67 +179,256 @@ main(int argc, char **argv)
                                "HPET is not enabled, using TSC as default timer\n");
 
 
-#ifdef RTE_LIBRTE_CMDLINE
-       cl = cmdline_stdin_new(main_ctx, "RTE>>");
-       if (cl == NULL) {
-               return -1;
+#ifdef RTE_LIB_CMDLINE
+       char *dpdk_test = getenv("DPDK_TEST");
+
+       if (dpdk_test && strlen(dpdk_test) == 0)
+               dpdk_test = NULL;
+
+       if (dpdk_test && !command_valid(dpdk_test)) {
+               RTE_LOG(WARNING, APP, "Invalid DPDK_TEST value '%s'\n", dpdk_test);
+               dpdk_test = NULL;
+       }
+
+       if (dpdk_test)
+               tests[test_count++] = dpdk_test;
+       for (i = 1; i < argc; i++) {
+               if (!command_valid(argv[i]))
+                       RTE_LOG(WARNING, APP, "Invalid test requested: '%s'\n", argv[i]);
+               else
+                       tests[test_count++] = argv[i];
        }
-       cmdline_interact(cl);
-       cmdline_stdin_exit(cl);
+
+       if (test_count > 0) {
+               char buf[1024];
+
+               cl = cmdline_new(main_ctx, "RTE>>", 0, 1);
+               if (cl == NULL) {
+                       ret = -1;
+                       goto out;
+               }
+
+               for (i = 0; i < test_count; i++) {
+                       snprintf(buf, sizeof(buf), "%s\n", tests[i]);
+                       if (cmdline_in(cl, buf, strlen(buf)) < 0) {
+                               printf("error on cmdline input\n");
+
+                               ret = -1;
+                       } else
+                               ret = last_test_result;
+
+                       if (ret != 0)
+                               break;
+               }
+               cmdline_free(cl);
+               goto out;
+       } else {
+               /* if no DPDK_TEST env variable, go interactive */
+               cl = cmdline_stdin_new(main_ctx, "RTE>>");
+               if (cl == NULL) {
+                       ret = -1;
+                       goto out;
+               }
+
+               cmdline_interact(cl);
+               cmdline_stdin_exit(cl);
+               cmdline_free(cl);
+       }
+#endif
+       ret = 0;
+
+out:
+#ifdef RTE_LIB_TIMER
+       rte_timer_subsystem_finalize();
 #endif
+       rte_eal_cleanup();
+       return ret;
+}
 
-       return 0;
+static void
+unit_test_suite_count_tcs_on_setup_fail(struct unit_test_suite *suite,
+               int test_success, unsigned int *sub_ts_failed,
+               unsigned int *sub_ts_skipped, unsigned int *sub_ts_total)
+{
+       struct unit_test_case tc;
+       struct unit_test_suite *ts;
+       int i;
+
+       FOR_EACH_SUITE_TESTSUITE(i, suite, ts) {
+               unit_test_suite_count_tcs_on_setup_fail(
+                       ts, test_success, sub_ts_failed,
+                       sub_ts_skipped, sub_ts_total);
+               suite->total += ts->total;
+               suite->failed += ts->failed;
+               suite->skipped += ts->skipped;
+               if (ts->failed)
+                       (*sub_ts_failed)++;
+               else
+                       (*sub_ts_skipped)++;
+               (*sub_ts_total)++;
+       }
+       FOR_EACH_SUITE_TESTCASE(i, suite, tc) {
+               suite->total++;
+               if (!tc.enabled || test_success == TEST_SKIPPED)
+                       suite->skipped++;
+               else
+                       suite->failed++;
+       }
 }
 
+static void
+unit_test_suite_reset_counts(struct unit_test_suite *suite)
+{
+       struct unit_test_suite *ts;
+       int i;
+
+       FOR_EACH_SUITE_TESTSUITE(i, suite, ts)
+               unit_test_suite_reset_counts(ts);
+       suite->total = 0;
+       suite->executed = 0;
+       suite->succeeded = 0;
+       suite->skipped = 0;
+       suite->failed = 0;
+       suite->unsupported = 0;
+}
 
 int
 unit_test_suite_runner(struct unit_test_suite *suite)
 {
-       int retval, i = 0;
+       int test_success, i, ret;
+       const char *status;
+       struct unit_test_case tc;
+       struct unit_test_suite *ts;
+       unsigned int sub_ts_succeeded = 0, sub_ts_failed = 0;
+       unsigned int sub_ts_skipped = 0, sub_ts_total = 0;
 
-       if (suite->suite_name)
-               printf("Test Suite : %s\n", suite->suite_name);
+       unit_test_suite_reset_counts(suite);
 
-       if (suite->setup)
-               if (suite->setup() != 0)
-                       return -1;
+       if (suite->suite_name) {
+               printf(" + ------------------------------------------------------- +\n");
+               printf(" + Test Suite : %s\n", suite->suite_name);
+       }
 
-       while (suite->unit_test_cases[i].testcase) {
-               /* Run test case setup */
-               if (suite->unit_test_cases[i].setup) {
-                       retval = suite->unit_test_cases[i].setup();
-                       if (retval != 0)
-                               return retval;
+       if (suite->setup) {
+               test_success = suite->setup();
+               if (test_success != 0) {
+                       /*
+                        * setup did not pass, so count all enabled tests and
+                        * mark them as failed/skipped
+                        */
+                       unit_test_suite_count_tcs_on_setup_fail(suite,
+                                       test_success, &sub_ts_failed,
+                                       &sub_ts_skipped, &sub_ts_total);
+                       goto suite_summary;
                }
+       }
 
-               /* Run test case */
-               if (suite->unit_test_cases[i].testcase() == 0) {
-                       printf("TestCase %2d: %s\n", i,
-                                       suite->unit_test_cases[i].success_msg ?
-                                       suite->unit_test_cases[i].success_msg :
-                                       "passed");
-               }
-               else {
-                       printf("TestCase %2d: %s\n", i, suite->unit_test_cases[i].fail_msg ?
-                                       suite->unit_test_cases[i].fail_msg :
-                                       "failed");
-                       return -1;
+       printf(" + ------------------------------------------------------- +\n");
+
+       FOR_EACH_SUITE_TESTCASE(suite->total, suite, tc) {
+               if (!tc.enabled) {
+                       suite->skipped++;
+                       continue;
+               } else {
+                       suite->executed++;
                }
 
-               /* Run test case teardown */
-               if (suite->unit_test_cases[i].teardown) {
-                       retval = suite->unit_test_cases[i].teardown();
-                       if (retval != 0)
-                               return retval;
+               /* run test case setup */
+               if (tc.setup)
+                       test_success = tc.setup();
+               else
+                       test_success = TEST_SUCCESS;
+
+               if (test_success == TEST_SUCCESS) {
+                       /* run the test case */
+                       test_success = tc.testcase();
+                       if (test_success == TEST_SUCCESS)
+                               suite->succeeded++;
+                       else if (test_success == TEST_SKIPPED)
+                               suite->skipped++;
+                       else if (test_success == -ENOTSUP)
+                               suite->unsupported++;
+                       else
+                               suite->failed++;
+               } else if (test_success == -ENOTSUP) {
+                       suite->unsupported++;
+               } else {
+                       suite->failed++;
                }
 
-               i++;
+               /* run the test case teardown */
+               if (tc.teardown)
+                       tc.teardown();
+
+               if (test_success == TEST_SUCCESS)
+                       status = "succeeded";
+               else if (test_success == TEST_SKIPPED)
+                       status = "skipped";
+               else if (test_success == -ENOTSUP)
+                       status = "unsupported";
+               else
+                       status = "failed";
+
+               printf(" + TestCase [%2d] : %s %s\n", suite->total,
+                               tc.name, status);
+       }
+       FOR_EACH_SUITE_TESTSUITE(i, suite, ts) {
+               ret = unit_test_suite_runner(ts);
+               if (ret == TEST_SUCCESS)
+                       sub_ts_succeeded++;
+               else if (ret == TEST_SKIPPED)
+                       sub_ts_skipped++;
+               else
+                       sub_ts_failed++;
+               sub_ts_total++;
+
+               suite->total += ts->total;
+               suite->succeeded += ts->succeeded;
+               suite->failed += ts->failed;
+               suite->skipped += ts->skipped;
+               suite->unsupported += ts->unsupported;
+               suite->executed += ts->executed;
        }
 
        /* Run test suite teardown */
        if (suite->teardown)
-               if (suite->teardown() != 0)
-                       return -1;
+               suite->teardown();
+
+       goto suite_summary;
+
+suite_summary:
+       printf(" + ------------------------------------------------------- +\n");
+       printf(" + Test Suite Summary : %s\n", suite->suite_name);
+       printf(" + ------------------------------------------------------- +\n");
+
+       FOR_EACH_SUITE_TESTSUITE(i, suite, ts)
+               printf(" + %s : %d/%d passed, %d/%d skipped, "
+                       "%d/%d failed, %d/%d unsupported\n", ts->suite_name,
+                       ts->succeeded, ts->total, ts->skipped, ts->total,
+                       ts->failed, ts->total, ts->unsupported, ts->total);
+
+       if (suite->unit_test_suites) {
+               printf(" + ------------------------------------------------------- +\n");
+               printf(" + Sub Testsuites Total :     %2d\n", sub_ts_total);
+               printf(" + Sub Testsuites Skipped :   %2d\n", sub_ts_skipped);
+               printf(" + Sub Testsuites Passed :    %2d\n", sub_ts_succeeded);
+               printf(" + Sub Testsuites Failed :    %2d\n", sub_ts_failed);
+               printf(" + ------------------------------------------------------- +\n");
+       }
+
+       printf(" + Tests Total :       %2d\n", suite->total);
+       printf(" + Tests Skipped :     %2d\n", suite->skipped);
+       printf(" + Tests Executed :    %2d\n", suite->executed);
+       printf(" + Tests Unsupported:  %2d\n", suite->unsupported);
+       printf(" + Tests Passed :      %2d\n", suite->succeeded);
+       printf(" + Tests Failed :      %2d\n", suite->failed);
+       printf(" + ------------------------------------------------------- +\n");
+
+       last_test_result = suite->failed;
 
-       return 0;
+       if (suite->failed)
+               return TEST_FAILED;
+       if (suite->total == suite->skipped)
+               return TEST_SKIPPED;
+       return TEST_SUCCESS;
 }