cmdline: tests
authorIntel <intel.com>
Wed, 19 Dec 2012 23:00:00 +0000 (00:00 +0100)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Thu, 25 Jul 2013 13:01:58 +0000 (15:01 +0200)
Signed-off-by: Intel
19 files changed:
app/Makefile
app/cmdline_test/Makefile [new file with mode: 0644]
app/cmdline_test/cmdline_test.c [new file with mode: 0644]
app/cmdline_test/cmdline_test.h [new file with mode: 0644]
app/cmdline_test/cmdline_test.py [new file with mode: 0755]
app/cmdline_test/cmdline_test_data.py [new file with mode: 0644]
app/cmdline_test/commands.c [new file with mode: 0644]
app/test/Makefile
app/test/commands.c
app/test/test.h
app/test/test_cmdline.c [new file with mode: 0644]
app/test/test_cmdline.h [new file with mode: 0644]
app/test/test_cmdline_cirbuf.c [new file with mode: 0644]
app/test/test_cmdline_etheraddr.c [new file with mode: 0644]
app/test/test_cmdline_ipaddr.c [new file with mode: 0644]
app/test/test_cmdline_lib.c [new file with mode: 0644]
app/test/test_cmdline_num.c [new file with mode: 0644]
app/test/test_cmdline_portlist.c [new file with mode: 0644]
app/test/test_cmdline_string.c [new file with mode: 0644]

index d245f9f..3cf7e87 100644 (file)
@@ -36,6 +36,7 @@ DIRS-$(CONFIG_RTE_APP_TEST) += test
 DIRS-$(CONFIG_RTE_TEST_PMD) += test-pmd
 DIRS-$(CONFIG_RTE_APP_CHKINCS) += chkincs
 
+DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += cmdline_test
 DIRS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += dump_cfg
 
 include $(RTE_SDK)/mk/rte.subdir.mk
diff --git a/app/cmdline_test/Makefile b/app/cmdline_test/Makefile
new file mode 100644 (file)
index 0000000..87aa812
--- /dev/null
@@ -0,0 +1,49 @@
+#   BSD LICENSE
+# 
+#   Copyright(c) 2010-2012 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.
+# 
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+#
+# library name
+#
+APP = cmdline_test
+
+#
+# all sources are stored in SRCS-y
+#
+SRCS-$(CONFIG_RTE_APP_TEST) += cmdline_test.c
+SRCS-$(CONFIG_RTE_APP_TEST) += commands.c
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.app.mk
diff --git a/app/cmdline_test/cmdline_test.c b/app/cmdline_test/cmdline_test.c
new file mode 100644 (file)
index 0000000..21c4351
--- /dev/null
@@ -0,0 +1,65 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <termios.h>
+#include <ctype.h>
+#include <sys/queue.h>
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+#include "cmdline_test.h"
+
+int
+main(int __attribute__((unused)) argc, char __attribute__((unused)) ** argv)
+{
+       struct cmdline *cl;
+
+       cl = cmdline_stdin_new(main_ctx, "CMDLINE_TEST>>");
+       if (cl == NULL) {
+               return -1;
+       }
+       cmdline_interact(cl);
+       cmdline_stdin_exit(cl);
+
+       return 0;
+}
diff --git a/app/cmdline_test/cmdline_test.h b/app/cmdline_test/cmdline_test.h
new file mode 100644 (file)
index 0000000..b579687
--- /dev/null
@@ -0,0 +1,47 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#ifndef _CMDLINE_TEST_H_
+#define _CMDLINE_TEST_H_
+
+/* icc on baremetal gives us troubles with function named 'main' */
+#ifdef RTE_EXEC_ENV_BAREMETAL
+#define main _main
+#endif
+
+extern cmdline_parse_ctx_t main_ctx[];
+
+int main(int argc, char **argv);
+
+#endif
diff --git a/app/cmdline_test/cmdline_test.py b/app/cmdline_test/cmdline_test.py
new file mode 100755 (executable)
index 0000000..ae3a60e
--- /dev/null
@@ -0,0 +1,115 @@
+#!/usr/bin/python
+
+#   BSD LICENSE
+# 
+#   Copyright(c) 2010-2012 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.
+# 
+
+# Script that runs cmdline_test app and feeds keystrokes into it.
+
+import sys, pexpect, string, os, cmdline_test_data
+
+#
+# function to run test
+#
+def runTest(child,test):
+       child.send(test["Sequence"])
+       if test["Result"] == None:
+               return 0
+       child.expect(test["Result"],1)
+
+#
+# history test is a special case
+#
+# This test does the following:
+# 1) fills the history with garbage up to its full capacity
+#    (just enough to remove last entry)
+# 2) scrolls back history to the very beginning
+# 3) checks if the output is as expected, that is, the first
+#    number in the sequence (not the last entry before it)
+#
+# This is a self-contained test, it needs only a pexpect child
+#
+def runHistoryTest(child):
+       # find out history size
+       child.sendline(cmdline_test_data.CMD_GET_BUFSIZE)
+       child.expect("History buffer size: \\d+", timeout=1)
+       history_size = int(child.after[len(cmdline_test_data.BUFSIZE_TEMPLATE):])
+       i = 0
+
+       # fill the history with numbers
+       while i < history_size / 10:
+               # add 1 to prevent from parsing as octals
+               child.send("1" + str(i).zfill(8) + cmdline_test_data.ENTER)
+               # the app will simply print out the number                      
+               child.expect(str(i + 100000000), timeout=1)
+               i += 1
+       # scroll back history
+       child.send(cmdline_test_data.UP * (i + 2) + cmdline_test_data.ENTER)
+       child.expect("100000000", timeout=1)
+
+# the path to cmdline_test executable is supplied via command-line.
+if len(sys.argv) < 2:
+       print "Error: please supply cmdline_test app path"
+       sys.exit(1)
+
+test_app_path = sys.argv[1]
+
+if not os.path.exists(test_app_path):
+       print "Error: please supply cmdline_test app path"
+       sys.exit(1)
+
+child = pexpect.spawn(test_app_path)
+
+print "Running command-line tests..."
+for test in cmdline_test_data.tests:
+       print (test["Name"] + ":").ljust(30),
+       try:
+               runTest(child,test)
+               print "PASS"
+       except:
+               print "FAIL"
+               print child
+               sys.exit(1)
+
+# since last test quits the app, run new instance
+child = pexpect.spawn(test_app_path)
+
+print ("History fill test:").ljust(30),
+try:
+       runHistoryTest(child)
+       print "PASS"
+except:
+       print "FAIL"
+       print child
+       sys.exit(1)
+child.close()
+sys.exit(0)
+
diff --git a/app/cmdline_test/cmdline_test_data.py b/app/cmdline_test/cmdline_test_data.py
new file mode 100644 (file)
index 0000000..cfe34eb
--- /dev/null
@@ -0,0 +1,313 @@
+#!/usr/bin/python
+
+#   BSD LICENSE
+# 
+#   Copyright(c) 2010-2012 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.
+# 
+
+# collection of static data
+
+import sys
+
+# keycode constants
+CTRL_A = chr(1)
+CTRL_B = chr(2)
+CTRL_C = chr(3)
+CTRL_D = chr(4)
+CTRL_E = chr(5)
+CTRL_F = chr(6)
+CTRL_K = chr(11)
+CTRL_L = chr(12)
+CTRL_N = chr(14)
+CTRL_P = chr(16)
+CTRL_W = chr(23)
+CTRL_Y = chr(25)
+ALT_B = chr(27) + chr(98)
+ALT_D = chr(27) + chr(100)
+ALT_F = chr(27) + chr(102)
+ALT_BKSPACE = chr(27) + chr(127)
+DEL = chr(27) + chr(91) + chr(51) + chr(126)
+TAB = chr(9)
+HELP = chr(63)
+BKSPACE = chr(127)
+RIGHT = chr(27) + chr(91) + chr(67)
+DOWN = chr(27) + chr(91) + chr(66)
+LEFT = chr(27) + chr(91) + chr(68)
+UP = chr(27) + chr(91) + chr(65)
+ENTER2 = '\r'
+ENTER = '\n'
+
+# expected result constants
+NOT_FOUND = "Command not found"
+BAD_ARG = "Bad arguments"
+AMBIG = "Ambiguous command"
+CMD1 = "Command 1 parsed!"
+CMD2 = "Command 2 parsed!"
+SINGLE = "Single word command parsed!"
+SINGLE_LONG = "Single long word command parsed!"
+AUTO1 = "Autocomplete command 1 parsed!"
+AUTO2 = "Autocomplete command 2 parsed!"
+
+# misc defines
+CMD_QUIT = "quit"
+CMD_GET_BUFSIZE = "get_history_bufsize"
+BUFSIZE_TEMPLATE = "History buffer size: "
+PROMPT = "CMDLINE_TEST>>"
+
+# test defines
+# each test tests progressively diverse set of keys. this way for example
+# if we want to use some key sequence in the test, we first need to test
+# that it itself does what it is expected to do. Most of the tests are
+# designed that way.
+#
+# example: "arrows & delete test 1". we enter a partially valid command,
+# then move 3 chars left and use delete three times. this way we get to
+# know that "delete", "left" and "ctrl+B" all work (because if any of
+# them fails, the whole test will fail and next tests won't be run).
+#
+# each test consists of name, character sequence to send to child,
+# and expected output (if any).
+
+tests = [
+# test basic commands
+       {"Name" : "command test 1",
+        "Sequence" : "ambiguous first" + ENTER,
+        "Result" : CMD1},
+       {"Name" : "command test 2",
+        "Sequence" : "ambiguous second" + ENTER,
+        "Result" : CMD2},
+       {"Name" : "command test 3",
+        "Sequence" : "ambiguous ambiguous" + ENTER,
+        "Result" : AMBIG},
+       {"Name" : "command test 4",
+        "Sequence" : "ambiguous ambiguous2" + ENTER,
+        "Result" : AMBIG},
+
+       {"Name" : "invalid command test 1",
+        "Sequence" : "ambiguous invalid" + ENTER,
+        "Result" : BAD_ARG},
+# test invalid commands
+       {"Name" : "invalid command test 2",
+        "Sequence" : "invalid" + ENTER,
+        "Result" : NOT_FOUND},
+       {"Name" : "invalid command test 3",
+        "Sequence" : "ambiguousinvalid" + ENTER2,
+        "Result" : NOT_FOUND},
+
+# test arrows and deletes
+       {"Name" : "arrows & delete test 1",
+        "Sequence" : "singlebad" + LEFT*2 + CTRL_B + DEL*3 + ENTER,
+        "Result" : SINGLE},
+       {"Name" : "arrows & delete test 2",
+        "Sequence" : "singlebad" + LEFT*5 + RIGHT + CTRL_F + DEL*3 + ENTER,
+        "Result" : SINGLE},
+
+# test backspace
+       {"Name" : "backspace test",
+        "Sequence" : "singlebad" + BKSPACE*3 + ENTER,
+        "Result" : SINGLE},
+
+# test goto left and goto right
+       {"Name" : "goto left test",
+        "Sequence" : "biguous first" + CTRL_A + "am" + ENTER,
+        "Result" : CMD1},
+       {"Name" : "goto right test",
+        "Sequence" : "biguous fir" + CTRL_A + "am" + CTRL_E + "st" + ENTER,
+        "Result" : CMD1},
+
+# test goto words
+       {"Name" : "goto left word test",
+        "Sequence" : "ambiguous st" + ALT_B + "fir" + ENTER,
+        "Result" : CMD1},
+       {"Name" : "goto right word test",
+        "Sequence" : "ambig first" + CTRL_A + ALT_F + "uous" + ENTER,
+        "Result" : CMD1},
+
+# test removing words
+       {"Name" : "remove left word 1",
+        "Sequence" : "single invalid" + CTRL_W + ENTER,
+        "Result" : SINGLE},
+       {"Name" : "remove left word 2",
+        "Sequence" : "single invalid" + ALT_BKSPACE + ENTER,
+        "Result" : SINGLE},
+       {"Name" : "remove right word",
+        "Sequence" : "single invalid" + ALT_B + ALT_D + ENTER,
+        "Result" : SINGLE},
+
+# test kill buffer (copy and paste)
+       {"Name" : "killbuffer test 1",
+        "Sequence" : "ambiguous" + CTRL_A + CTRL_K + " first" + CTRL_A + CTRL_Y + ENTER,
+        "Result" : CMD1},
+       {"Name" : "killbuffer test 2",
+        "Sequence" : "ambiguous" + CTRL_A + CTRL_K + CTRL_Y*26 + ENTER,
+        "Result" : NOT_FOUND},
+
+# test newline
+       {"Name" : "newline test",
+        "Sequence" : "invalid" + CTRL_C + "single" + ENTER,
+        "Result" : SINGLE},
+
+# test redisplay (nothing should really happen)
+       {"Name" : "redisplay test",
+        "Sequence" : "single" + CTRL_L + ENTER,
+        "Result" : SINGLE},
+
+# test autocomplete
+       {"Name" : "autocomplete test 1",
+        "Sequence" : "si" + TAB + ENTER,
+        "Result" : SINGLE},
+       {"Name" : "autocomplete test 2",
+        "Sequence" : "si" + TAB + "_" + TAB + ENTER,
+        "Result" : SINGLE_LONG},
+       {"Name" : "autocomplete test 3",
+        "Sequence" : "in" + TAB + ENTER,
+        "Result" : NOT_FOUND},
+       {"Name" : "autocomplete test 4",
+        "Sequence" : "am" + TAB + ENTER,
+        "Result" : BAD_ARG},
+       {"Name" : "autocomplete test 5",
+        "Sequence" : "am" + TAB + "fir" + TAB + ENTER,
+        "Result" : CMD1},
+       {"Name" : "autocomplete test 6",
+        "Sequence" : "am" + TAB + "fir" + TAB + TAB + ENTER,
+        "Result" : CMD1},
+       {"Name" : "autocomplete test 7",
+        "Sequence" : "am" + TAB + "fir" + TAB + " " + TAB + ENTER,
+        "Result" : CMD1},
+       {"Name" : "autocomplete test 8",
+        "Sequence" : "am" + TAB + "     am" + TAB + "   " + ENTER,
+        "Result" : AMBIG},
+       {"Name" : "autocomplete test 9",
+        "Sequence" : "am" + TAB + "inv" + TAB + ENTER,
+        "Result" : BAD_ARG},
+       {"Name" : "autocomplete test 10",
+        "Sequence" : "au" + TAB + ENTER,
+        "Result" : NOT_FOUND},
+       {"Name" : "autocomplete test 11",
+        "Sequence" : "au" + TAB + "1" + ENTER,
+        "Result" : AUTO1},
+       {"Name" : "autocomplete test 12",
+        "Sequence" : "au" + TAB + "2" + ENTER,
+        "Result" : AUTO2},
+       {"Name" : "autocomplete test 13",
+        "Sequence" : "au" + TAB + "2" + TAB + ENTER,
+        "Result" : AUTO2},
+       {"Name" : "autocomplete test 14",
+        "Sequence" : "au" + TAB + "2   " + TAB + ENTER,
+        "Result" : AUTO2},
+       {"Name" : "autocomplete test 15",
+        "Sequence" : "24" + TAB + ENTER,
+        "Result" : "24"},
+
+# test history
+       {"Name" : "history test 1",
+        "Sequence" : "invalid" + ENTER + "single" + ENTER + "invalid" + ENTER + UP + CTRL_P + ENTER,
+        "Result" : SINGLE},
+       {"Name" : "history test 2",
+        "Sequence" : "invalid" + ENTER + "ambiguous first" + ENTER + "invalid" + ENTER + "single" + ENTER + UP * 3 + CTRL_N + DOWN + ENTER,
+        "Result" : SINGLE},
+
+#
+# tests that improve coverage
+#
+
+# empty space tests
+       {"Name" : "empty space test 1",
+        "Sequence" : RIGHT + LEFT + CTRL_B + CTRL_F + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "empty space test 2",
+        "Sequence" : BKSPACE + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "empty space test 3",
+        "Sequence" : CTRL_E*2 + CTRL_A*2 + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "empty space test 4",
+        "Sequence" : ALT_F*2 + ALT_B*2 + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "empty space test 5",
+        "Sequence" : " " + CTRL_E*2 + CTRL_A*2 + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "empty space test 6",
+        "Sequence" : " " + CTRL_A + ALT_F*2 + ALT_B*2 + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "empty space test 7",
+        "Sequence" : "  " + CTRL_A + CTRL_D + CTRL_E + CTRL_D + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "empty space test 8",
+        "Sequence" : " space" + CTRL_W*2 + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "empty space test 9",
+        "Sequence" : " space" + ALT_BKSPACE*2 + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "empty space test 10",
+        "Sequence" : " space " + CTRL_A + ALT_D*3 + ENTER,
+        "Result" : PROMPT},
+
+# non-printable char tests
+       {"Name" : "non-printable test 1",
+        "Sequence" : chr(27) + chr(47) + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "non-printable test 2",
+        "Sequence" : chr(27) + chr(128) + ENTER*7,
+        "Result" : PROMPT},
+       {"Name" : "non-printable test 3",
+        "Sequence" : chr(27) + chr(91) + chr(127) + ENTER*6,
+        "Result" : PROMPT},
+
+# miscellaneous tests
+       {"Name" : "misc test 1",
+        "Sequence" : ENTER,
+        "Result" : PROMPT},
+       {"Name" : "misc test 2",
+        "Sequence" : "single #comment" + ENTER,
+        "Result" : SINGLE},
+       {"Name" : "misc test 3",
+        "Sequence" : "#empty line" + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "misc test 4",
+        "Sequence" : "   single  " + ENTER,
+        "Result" : SINGLE},
+       {"Name" : "misc test 5",
+        "Sequence" : "single#" + ENTER,
+        "Result" : SINGLE},
+       {"Name" : "misc test 6",
+        "Sequence" : 'a' * 257 + ENTER,
+        "Result" : NOT_FOUND},
+       {"Name" : "misc test 7",
+        "Sequence" : "clear_history" + UP*5 + DOWN*5 + ENTER,
+        "Result" : PROMPT},
+       {"Name" : "misc test 8",
+        "Sequence" : "a" + HELP + CTRL_C,
+        "Result" : PROMPT},
+       {"Name" : "misc test 9",
+        "Sequence" : CTRL_D*3,
+        "Result" : None},
+]
+
diff --git a/app/cmdline_test/commands.c b/app/cmdline_test/commands.c
new file mode 100644 (file)
index 0000000..5c2142d
--- /dev/null
@@ -0,0 +1,390 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <inttypes.h>
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_num.h>
+#include <cmdline.h>
+
+#include "cmdline_test.h"
+
+/*** quit ***/
+/* exit application */
+
+struct cmd_quit_result {
+       cmdline_fixed_string_t quit;
+};
+
+static void
+cmd_quit_parsed(__attribute__((unused)) void *parsed_result,
+               struct cmdline *cl,
+               __attribute__((unused)) void *data)
+{
+       cmdline_quit(cl);
+}
+
+cmdline_parse_token_string_t cmd_quit_tok =
+       TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit,
+                                "quit");
+
+cmdline_parse_inst_t cmd_quit = {
+       .f = cmd_quit_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "exit application",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_quit_tok,
+               NULL,
+       },
+};
+
+
+
+/*** single ***/
+/* a simple single-word command */
+
+struct cmd_single_result {
+       cmdline_fixed_string_t single;
+};
+
+static void
+cmd_single_parsed(__attribute__((unused)) void *parsed_result,
+               struct cmdline *cl,
+               __attribute__((unused)) void *data)
+{
+       cmdline_printf(cl, "Single word command parsed!\n");
+}
+
+cmdline_parse_token_string_t cmd_single_tok =
+       TOKEN_STRING_INITIALIZER(struct cmd_single_result, single,
+                                "single");
+
+cmdline_parse_inst_t cmd_single = {
+       .f = cmd_single_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "a simple single-word command",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_single_tok,
+               NULL,
+       },
+};
+
+
+
+/*** single_long ***/
+/* a variant of "single" command. useful to test autocomplete */
+
+struct cmd_single_long_result {
+       cmdline_fixed_string_t single_long;
+};
+
+static void
+cmd_single_long_parsed(__attribute__((unused)) void *parsed_result,
+               struct cmdline *cl,
+               __attribute__((unused)) void *data)
+{
+       cmdline_printf(cl, "Single long word command parsed!\n");
+}
+
+cmdline_parse_token_string_t cmd_single_long_tok =
+       TOKEN_STRING_INITIALIZER(struct cmd_single_long_result, single_long,
+                                "single_long");
+
+cmdline_parse_inst_t cmd_single_long = {
+       .f = cmd_single_long_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "a variant of \"single\" command, useful to test autocomplete",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_single_long_tok,
+               NULL,
+       },
+};
+
+
+
+/*** autocomplete_1 ***/
+/* first command to test autocomplete when multiple commands have chars
+ * in common but none should complete due to ambiguity
+ */
+
+struct cmd_autocomplete_1_result {
+       cmdline_fixed_string_t token;
+};
+
+static void
+cmd_autocomplete_1_parsed(__attribute__((unused)) void *parsed_result,
+               struct cmdline *cl,
+               __attribute__((unused)) void *data)
+{
+       cmdline_printf(cl, "Autocomplete command 1 parsed!\n");
+}
+
+cmdline_parse_token_string_t cmd_autocomplete_1_tok =
+       TOKEN_STRING_INITIALIZER(struct cmd_autocomplete_1_result, token,
+                                "autocomplete_1");
+
+cmdline_parse_inst_t cmd_autocomplete_1 = {
+       .f = cmd_autocomplete_1_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "first ambiguous autocomplete command",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_autocomplete_1_tok,
+               NULL,
+       },
+};
+
+
+
+/*** autocomplete_2 ***/
+/* second command to test autocomplete when multiple commands have chars
+ * in common but none should complete due to ambiguity
+ */
+
+struct cmd_autocomplete_2_result {
+       cmdline_fixed_string_t token;
+};
+
+static void
+cmd_autocomplete_2_parsed(__attribute__((unused)) void *parsed_result,
+               struct cmdline *cl,
+               __attribute__((unused)) void *data)
+{
+       cmdline_printf(cl, "Autocomplete command 2 parsed!\n");
+}
+
+cmdline_parse_token_string_t cmd_autocomplete_2_tok =
+       TOKEN_STRING_INITIALIZER(struct cmd_autocomplete_2_result, token,
+                                "autocomplete_2");
+
+cmdline_parse_inst_t cmd_autocomplete_2 = {
+       .f = cmd_autocomplete_2_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "second ambiguous autocomplete command",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_autocomplete_2_tok,
+               NULL,
+       },
+};
+
+
+
+/*** number command ***/
+/* a command that simply returns whatever (uint32) number is supplied to it */
+
+struct cmd_num_result {
+       unsigned num;
+};
+
+static void
+cmd_num_parsed(void *parsed_result,
+               struct cmdline *cl,
+               __attribute__((unused)) void *data)
+{
+       unsigned result = ((struct cmd_num_result*)parsed_result)->num;
+       cmdline_printf(cl, "%u\n", result);
+}
+
+cmdline_parse_token_num_t cmd_num_tok =
+       TOKEN_NUM_INITIALIZER(struct cmd_num_result, num, UINT32);
+
+cmdline_parse_inst_t cmd_num = {
+       .f = cmd_num_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "a command that simply returns whatever number is entered",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_num_tok,
+               NULL,
+       },
+};
+
+
+
+/*** ambiguous first|ambiguous ***/
+/* first command used to test command ambiguity */
+
+struct cmd_ambig_result_1 {
+       cmdline_fixed_string_t common_part;
+       cmdline_fixed_string_t ambig_part;
+};
+
+static void
+cmd_ambig_1_parsed(__attribute__((unused)) void *parsed_result,
+               struct cmdline *cl,
+               __attribute__((unused)) void *data)
+{
+       cmdline_printf(cl, "Command 1 parsed!\n");
+}
+
+cmdline_parse_token_string_t cmd_ambig_common_1 =
+       TOKEN_STRING_INITIALIZER(struct cmd_ambig_result_1, common_part,
+                                "ambiguous");
+cmdline_parse_token_string_t cmd_ambig_ambig_1 =
+       TOKEN_STRING_INITIALIZER(struct cmd_ambig_result_1, ambig_part,
+                                "first#ambiguous#ambiguous2");
+
+cmdline_parse_inst_t cmd_ambig_1 = {
+       .f = cmd_ambig_1_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "first command used to test command ambiguity",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_ambig_common_1,
+               (void*)&cmd_ambig_ambig_1,
+               NULL,
+       },
+};
+
+
+
+/*** ambiguous second|ambiguous ***/
+/* second command used to test command ambiguity */
+
+struct cmd_ambig_result_2 {
+       cmdline_fixed_string_t common_part;
+       cmdline_fixed_string_t ambig_part;
+};
+
+static void
+cmd_ambig_2_parsed(__attribute__((unused)) void *parsed_result,
+               struct cmdline *cl,
+               __attribute__((unused)) void *data)
+{
+       cmdline_printf(cl, "Command 2 parsed!\n");
+}
+
+cmdline_parse_token_string_t cmd_ambig_common_2 =
+       TOKEN_STRING_INITIALIZER(struct cmd_ambig_result_2, common_part,
+                                "ambiguous");
+cmdline_parse_token_string_t cmd_ambig_ambig_2 =
+       TOKEN_STRING_INITIALIZER(struct cmd_ambig_result_2, ambig_part,
+                                "second#ambiguous#ambiguous2");
+
+cmdline_parse_inst_t cmd_ambig_2 = {
+       .f = cmd_ambig_2_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "second command used to test command ambiguity",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_ambig_common_2,
+               (void*)&cmd_ambig_ambig_2,
+               NULL,
+       },
+};
+
+
+
+/*** get_history_bufsize ***/
+/* command that displays total space in history buffer
+ * this will be useful for testing history (to fill it up just enough to
+ * remove the last entry, we need to know how big it is).
+ */
+
+struct cmd_get_history_bufsize_result {
+       cmdline_fixed_string_t str;
+};
+
+static void
+cmd_get_history_bufsize_parsed(__attribute__((unused)) void *parsed_result,
+               struct cmdline *cl,
+               __attribute__((unused)) void *data)
+{
+       cmdline_printf(cl, "History buffer size: %u\n",
+                       sizeof(cl->rdl.history_buf));
+}
+
+cmdline_parse_token_string_t cmd_get_history_bufsize_tok =
+       TOKEN_STRING_INITIALIZER(struct cmd_get_history_bufsize_result, str,
+                                "get_history_bufsize");
+
+cmdline_parse_inst_t cmd_get_history_bufsize = {
+       .f = cmd_get_history_bufsize_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "command that displays total space in history buffer",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_get_history_bufsize_tok,
+               NULL,
+       },
+};
+
+
+
+/*** clear_history ***/
+/* clears history buffer */
+
+struct cmd_clear_history_result {
+       cmdline_fixed_string_t str;
+};
+
+static void
+cmd_clear_history_parsed(__attribute__((unused)) void *parsed_result,
+               struct cmdline *cl,
+               __attribute__((unused)) void *data)
+{
+       rdline_clear_history(&cl->rdl);
+}
+
+cmdline_parse_token_string_t cmd_clear_history_tok =
+       TOKEN_STRING_INITIALIZER(struct cmd_clear_history_result, str,
+                                "clear_history");
+
+cmdline_parse_inst_t cmd_clear_history = {
+       .f = cmd_clear_history_parsed,  /* function to call */
+       .data = NULL,      /* 2nd arg of func */
+       .help_str = "clear command history",
+       .tokens = {        /* token list, NULL terminated */
+               (void *)&cmd_clear_history_tok,
+               NULL,
+       },
+};
+
+
+
+/****************/
+
+cmdline_parse_ctx_t main_ctx[] = {
+               (cmdline_parse_inst_t *)&cmd_quit,
+               (cmdline_parse_inst_t *)&cmd_ambig_1,
+               (cmdline_parse_inst_t *)&cmd_ambig_2,
+               (cmdline_parse_inst_t *)&cmd_single,
+               (cmdline_parse_inst_t *)&cmd_single_long,
+               (cmdline_parse_inst_t *)&cmd_num,
+               (cmdline_parse_inst_t *)&cmd_get_history_bufsize,
+               (cmdline_parse_inst_t *)&cmd_clear_history,
+               (cmdline_parse_inst_t *)&cmd_autocomplete_1,
+               (cmdline_parse_inst_t *)&cmd_autocomplete_2,
+       NULL,
+};
index d296dbb..32d6a48 100644 (file)
@@ -72,6 +72,14 @@ SRCS-$(CONFIG_RTE_APP_TEST) += test_alarm.c
 SRCS-$(CONFIG_RTE_APP_TEST) += test_interrupts.c
 SRCS-$(CONFIG_RTE_APP_TEST) += test_version.c
 SRCS-$(CONFIG_RTE_APP_TEST) += test_eal_fs.c
+SRCS-$(CONFIG_RTE_APP_TEST) += test_cmdline.c
+SRCS-$(CONFIG_RTE_APP_TEST) += test_cmdline_num.c
+SRCS-$(CONFIG_RTE_APP_TEST) += test_cmdline_etheraddr.c
+SRCS-$(CONFIG_RTE_APP_TEST) += test_cmdline_portlist.c
+SRCS-$(CONFIG_RTE_APP_TEST) += test_cmdline_ipaddr.c
+SRCS-$(CONFIG_RTE_APP_TEST) += test_cmdline_cirbuf.c
+SRCS-$(CONFIG_RTE_APP_TEST) += test_cmdline_string.c
+SRCS-$(CONFIG_RTE_APP_TEST) += test_cmdline_lib.c
 
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS)
index 71dbf96..fd488d5 100644 (file)
@@ -129,6 +129,8 @@ static void cmd_autotest_parsed(void *parsed_result,
                ret |= test_lpm();
        if (all || !strcmp(res->autotest, "cpuflags_autotest"))
                ret |= test_cpuflags();
+       if (all || !strcmp(res->autotest, "cmdline_autotest"))
+               ret |= test_cmdline();
        /* tailq autotest must go after all lpm and hashs tests or any other
         * tests which need to create tailq objects (ring and mempool are implicitly
         * created in earlier tests so can go later)
@@ -180,6 +182,7 @@ cmdline_parse_token_string_t cmd_autotest_autotest =
                        "cpuflags_autotest#eal_flags_autotest#"
                        "alarm_autotest#interrupt_autotest#"
                        "version_autotest#eal_fs_autotest#"
+                       "cmdline_autotest#"
                        "all_autotests");
 
 cmdline_parse_inst_t cmd_autotest = {
index d208992..08513a4 100644 (file)
@@ -80,6 +80,7 @@ int test_alarm(void);
 int test_interrupt(void);
 int test_version(void);
 int test_eal_fs(void);
+int test_cmdline(void);
 
 int test_pci_run;
 
diff --git a/app/test/test_cmdline.c b/app/test/test_cmdline.c
new file mode 100644 (file)
index 0000000..0dc9a15
--- /dev/null
@@ -0,0 +1,94 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#include <stdio.h>
+
+#include <cmdline_parse.h>
+
+#include "test.h"
+#include "test_cmdline.h"
+
+int
+test_cmdline(void)
+{
+       printf("Testind parsing ethernet addresses...\n");
+       if (test_parse_etheraddr_valid() < 0)
+               return -1;
+       if (test_parse_etheraddr_invalid_data() < 0)
+               return -1;
+       if (test_parse_etheraddr_invalid_param() < 0)
+               return -1;
+       printf("Testind parsing port lists...\n");
+       if (test_parse_portlist_valid() < 0)
+               return -1;
+       if (test_parse_portlist_invalid_data() < 0)
+               return -1;
+       if (test_parse_portlist_invalid_param() < 0)
+               return -1;
+       printf("Testind parsing numbers...\n");
+       if (test_parse_num_valid() < 0)
+               return -1;
+       if (test_parse_num_invalid_data() < 0)
+               return -1;
+       if (test_parse_num_invalid_param() < 0)
+               return -1;
+       printf("Testing parsing IP addresses...\n");
+       if (test_parse_ipaddr_valid() < 0)
+               return -1;
+       if (test_parse_ipaddr_invalid_data() < 0)
+               return -1;
+       if (test_parse_ipaddr_invalid_param() < 0)
+               return -1;
+       printf("Testing parsing strings...\n");
+       if (test_parse_string_valid() < 0)
+               return -1;
+       if (test_parse_string_invalid_data() < 0)
+               return -1;
+       if (test_parse_string_invalid_param() < 0)
+               return -1;
+       printf("Testing circular buffer...\n");
+       if (test_cirbuf_char() < 0)
+               return -1;
+       if (test_cirbuf_string() < 0)
+               return -1;
+       if (test_cirbuf_align() < 0)
+               return -1;
+       if (test_cirbuf_invalid_param() < 0)
+               return -1;
+       printf("Testing library functions...\n");
+       if (test_cmdline_lib() < 0)
+               return -1;
+       return 0;
+}
+
diff --git a/app/test/test_cmdline.h b/app/test/test_cmdline.h
new file mode 100644 (file)
index 0000000..5a7dbe8
--- /dev/null
@@ -0,0 +1,74 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#ifndef TEST_CMDLINE_H_
+#define TEST_CMDLINE_H_
+
+#define CMDLINE_TEST_BUFSIZE 64
+
+/* cmdline_parse_num tests */
+int test_parse_num_valid(void);
+int test_parse_num_invalid_data(void);
+int test_parse_num_invalid_param(void);
+
+/* cmdline_parse_etheraddr tests */
+int test_parse_etheraddr_valid(void);
+int test_parse_etheraddr_invalid_data(void);
+int test_parse_etheraddr_invalid_param(void);
+
+/* cmdline_parse_portlist tests */
+int test_parse_portlist_valid(void);
+int test_parse_portlist_invalid_data(void);
+int test_parse_portlist_invalid_param(void);
+
+/* cmdline_parse_ipaddr tests */
+int test_parse_ipaddr_valid(void);
+int test_parse_ipaddr_invalid_data(void);
+int test_parse_ipaddr_invalid_param(void);
+
+/* cmdline_parse_string tests */
+int test_parse_string_valid(void);
+int test_parse_string_invalid_data(void);
+int test_parse_string_invalid_param(void);
+
+/* cmdline_cirbuf tests */
+int test_cirbuf_invalid_param(void);
+int test_cirbuf_char(void);
+int test_cirbuf_string(void);
+int test_cirbuf_align(void);
+
+/* test the rest of the library */
+int test_cmdline_lib(void);
+
+#endif /* TEST_CMDLINE_H_ */
diff --git a/app/test/test_cmdline_cirbuf.c b/app/test/test_cmdline_cirbuf.c
new file mode 100644 (file)
index 0000000..d14823e
--- /dev/null
@@ -0,0 +1,1331 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_string_fns.h>
+
+#include <cmdline_cirbuf.h>
+
+#include "test_cmdline.h"
+
+/* different length strings */
+#define CIRBUF_STR_HEAD " HEAD"
+#define CIRBUF_STR_TAIL "TAIL"
+
+/* miscelaneous tests - they make bullseye happy */
+static int
+test_cirbuf_string_misc(void)
+{
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       char tmp[CMDLINE_TEST_BUFSIZE];
+
+       /* initialize buffers */
+       memset(buf, 0, sizeof(buf));
+       memset(tmp, 0, sizeof(tmp));
+
+       /*
+        * initialize circular buffer
+        */
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to initialize circular buffer!\n");
+               return -1;
+       }
+
+       /*
+        * add strings to head and tail, but read only tail
+        * this results in read operation that does not transcend
+        * from buffer end to buffer beginning (in other words,
+        * strlen <= cb->maxlen - cb->end)
+        */
+
+       /* add string to head */
+       if (cirbuf_add_buf_head(&cb, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD))
+                       != (sizeof(CIRBUF_STR_HEAD))) {
+               printf("Error: failed to add string to head!\n");
+               return -1;
+       }
+       /* add string to tail */
+       if (cirbuf_add_buf_tail(&cb, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL))
+                       != (sizeof(CIRBUF_STR_TAIL))) {
+               printf("Error: failed to add string to head!\n");
+               return -1;
+       }
+       /* read string from tail */
+       if (cirbuf_get_buf_tail(&cb, tmp, sizeof(CIRBUF_STR_TAIL))
+                       != (sizeof(CIRBUF_STR_TAIL))) {
+               printf("Error: failed to get string from tail!\n");
+               return -1;
+       }
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL)) != 0) {
+               printf("Error: tail strings do not match!\n");
+               return -1;
+       }
+       /* clear buffers */
+       memset(tmp, 0, sizeof(tmp));
+       memset(buf, 0, sizeof(buf));
+
+
+
+       /*
+        * add a string to buffer when start/end is at end of buffer
+        */
+
+       /*
+        * reinitialize circular buffer with start at the end of cirbuf
+        */
+       if (cirbuf_init(&cb, buf, CMDLINE_TEST_BUFSIZE - 2, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+
+       /* add string to tail */
+       if (cirbuf_add_buf_tail(&cb, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL))
+                       != (sizeof(CIRBUF_STR_TAIL))) {
+               printf("Error: failed to add string to tail!\n");
+               return -1;
+       }
+       /* read string from tail */
+       if (cirbuf_get_buf_tail(&cb, tmp, sizeof(CIRBUF_STR_TAIL))
+                       != (sizeof(CIRBUF_STR_TAIL))) {
+               printf("Error: failed to get string from tail!\n");
+               return -1;
+       }
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL)) != 0) {
+               printf("Error: tail strings do not match!\n");
+               return -1;
+       }
+       /* clear tmp buffer */
+       memset(tmp, 0, sizeof(tmp));
+
+
+       /* add string to head */
+       if (cirbuf_add_buf_head(&cb, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD))
+                       != (sizeof(CIRBUF_STR_HEAD))) {
+               printf("Error: failed to add string to head!\n");
+               return -1;
+       }
+       /* read string from tail */
+       if (cirbuf_get_buf_head(&cb, tmp, sizeof(CIRBUF_STR_HEAD))
+                       != (sizeof(CIRBUF_STR_HEAD))) {
+               printf("Error: failed to get string from head!\n");
+               return -1;
+       }
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD)) != 0) {
+               printf("Error: headstrings do not match!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* test adding and deleting strings */
+static int
+test_cirbuf_string_add_del(void)
+{
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       char tmp[CMDLINE_TEST_BUFSIZE];
+
+       /* initialize buffers */
+       memset(buf, 0, sizeof(buf));
+       memset(tmp, 0, sizeof(tmp));
+
+       /*
+        * initialize circular buffer
+        */
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to initialize circular buffer!\n");
+               return -1;
+       }
+
+       /* add string to head */
+       if (cirbuf_add_buf_head(&cb, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD))
+                       != (sizeof(CIRBUF_STR_HEAD))) {
+               printf("Error: failed to add string to head!\n");
+               return -1;
+       }
+       /* read string from head */
+       if (cirbuf_get_buf_head(&cb, tmp, sizeof(CIRBUF_STR_HEAD))
+                       != (sizeof(CIRBUF_STR_HEAD))) {
+               printf("Error: failed to get string from head!\n");
+               return -1;
+       }
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD)) != 0) {
+               printf("Error: head strings do not match!\n");
+               return -1;
+       }
+       /* clear tmp buffer */
+       memset(tmp, 0, sizeof(tmp));
+       /* read string from tail */
+       if (cirbuf_get_buf_tail(&cb, tmp, sizeof(CIRBUF_STR_HEAD))
+                       != (sizeof(CIRBUF_STR_HEAD))) {
+               printf("Error: failed to get string from head!\n");
+               return -1;
+       }
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD)) != 0) {
+               printf("Error: head strings do not match!\n");
+               return -1;
+       }
+       /* delete string from head*/
+       if (cirbuf_del_buf_head(&cb, sizeof(CIRBUF_STR_HEAD)) < 0) {
+               printf("Error: failed to delete string from head!\n");
+               return -1;
+       }
+       /* verify string was deleted */
+       if (cirbuf_del_head_safe(&cb) == 0) {
+               printf("Error: buffer should have been empty!\n");
+               return -1;
+       }
+       /* clear tmp buffer */
+       memset(tmp, 0, sizeof(tmp));
+
+
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* add string to tail */
+       if (cirbuf_add_buf_tail(&cb, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL))
+                       != (sizeof(CIRBUF_STR_TAIL))) {
+               printf("Error: failed to add string to tail!\n");
+               return -1;
+       }
+       /* get string from tail */
+       if (cirbuf_get_buf_tail(&cb, tmp, sizeof(CIRBUF_STR_TAIL))
+                       != (sizeof(CIRBUF_STR_TAIL))) {
+               printf("Error: failed to get string from tail!\n");
+               return -1;
+       }
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL)) != 0) {
+               printf("Error: tail strings do not match!\n");
+               return -1;
+       }
+       /* clear tmp buffer */
+       memset(tmp, 0, sizeof(tmp));
+       /* get string from head */
+       if (cirbuf_get_buf_head(&cb, tmp, sizeof(CIRBUF_STR_TAIL))
+                       != (sizeof(CIRBUF_STR_TAIL))) {
+               printf("Error: failed to get string from tail!\n");
+               return -1;
+       }
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL)) != 0) {
+               printf("Error: tail strings do not match!\n");
+               return -1;
+       }
+       /* delete string from tail */
+       if (cirbuf_del_buf_tail(&cb, sizeof(CIRBUF_STR_TAIL)) < 0) {
+               printf("Error: failed to delete string from tail!\n");
+               return -1;
+       }
+       /* verify string was deleted */
+       if (cirbuf_del_tail_safe(&cb) == 0) {
+               printf("Error: buffer should have been empty!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* test adding from head and deleting from tail, and vice versa */
+static int
+test_cirbuf_string_add_del_reverse(void)
+{
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       char tmp[CMDLINE_TEST_BUFSIZE];
+
+       /* initialize buffers */
+       memset(buf, 0, sizeof(buf));
+       memset(tmp, 0, sizeof(tmp));
+
+       /*
+        * initialize circular buffer
+        */
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to initialize circular buffer!\n");
+               return -1;
+       }
+
+       /* add string to head */
+       if (cirbuf_add_buf_head(&cb, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD))
+                       != (sizeof(CIRBUF_STR_HEAD))) {
+               printf("Error: failed to add string to head!\n");
+               return -1;
+       }
+       /* delete string from tail */
+       if (cirbuf_del_buf_tail(&cb, sizeof(CIRBUF_STR_HEAD)) < 0) {
+               printf("Error: failed to delete string from tail!\n");
+               return -1;
+       }
+       /* verify string was deleted */
+       if (cirbuf_del_tail_safe(&cb) == 0) {
+               printf("Error: buffer should have been empty!\n");
+               return -1;
+       }
+       /* clear tmp buffer */
+       memset(tmp, 0, sizeof(tmp));
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* add string to tail */
+       if (cirbuf_add_buf_tail(&cb, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL))
+                       != (sizeof(CIRBUF_STR_TAIL))) {
+               printf("Error: failed to add string to tail!\n");
+               return -1;
+       }
+       /* delete string from head */
+       if (cirbuf_del_buf_head(&cb, sizeof(CIRBUF_STR_TAIL)) < 0) {
+               printf("Error: failed to delete string from head!\n");
+               return -1;
+       }
+       /* verify string was deleted */
+       if (cirbuf_del_head_safe(&cb) == 0) {
+               printf("Error: buffer should have been empty!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* try to write more than available */
+static int
+test_cirbuf_string_add_boundaries(void)
+{
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       unsigned i;
+
+       /* initialize buffers */
+       memset(buf, 0, sizeof(buf));
+
+       /*
+        * initialize circular buffer
+        */
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to initialize circular buffer!\n");
+               return -1;
+       }
+
+       /* fill the buffer from tail */
+       for (i = 0; i < CMDLINE_TEST_BUFSIZE - sizeof(CIRBUF_STR_TAIL) + 1; i++)
+               cirbuf_add_tail_safe(&cb, 't');
+
+       /* try adding a string to tail */
+       if (cirbuf_add_buf_tail(&cb, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL))
+                       > 0) {
+               printf("Error: buffer should have been full!\n");
+               return -1;
+       }
+       /* try adding a string to head */
+       if (cirbuf_add_buf_head(&cb, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL))
+                       > 0) {
+               printf("Error: buffer should have been full!\n");
+               return -1;
+       }
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* fill the buffer from head */
+       for (i = 0; i < CMDLINE_TEST_BUFSIZE - sizeof(CIRBUF_STR_HEAD) + 1; i++)
+               cirbuf_add_head_safe(&cb, 'h');
+
+       /* try adding a string to head */
+       if (cirbuf_add_buf_head(&cb, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD))
+                       > 0) {
+               printf("Error: buffer should have been full!\n");
+               return -1;
+       }
+       /* try adding a string to tail */
+       if (cirbuf_add_buf_tail(&cb, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD))
+                       > 0) {
+               printf("Error: buffer should have been full!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* try to read/delete more than written */
+static int
+test_cirbuf_string_get_del_boundaries(void)
+{
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       char tmp[CMDLINE_TEST_BUFSIZE];
+
+       /* initialize buffers */
+       memset(buf, 0, sizeof(buf));
+       memset(tmp, 0, sizeof(tmp));
+
+       /*
+        * initialize circular buffer
+        */
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to initialize circular buffer!\n");
+               return -1;
+       }
+
+
+       /* add string to head */
+       if (cirbuf_add_buf_head(&cb, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD))
+                               != (sizeof(CIRBUF_STR_HEAD))) {
+               printf("Error: failed to add string to head!\n");
+               return -1;
+       }
+       /* read more than written (head) */
+       if (cirbuf_get_buf_head(&cb, tmp, sizeof(CIRBUF_STR_HEAD) + 1)
+                       != sizeof(CIRBUF_STR_HEAD)) {
+               printf("Error: unexpected result when reading too much data!\n");
+               return -1;
+       }
+       /* read more than written (tail) */
+       if (cirbuf_get_buf_tail(&cb, tmp, sizeof(CIRBUF_STR_HEAD) + 1)
+                       != sizeof(CIRBUF_STR_HEAD)) {
+               printf("Error: unexpected result when reading too much data!\n");
+               return -1;
+       }
+       /* delete more than written (head) */
+       if (cirbuf_del_buf_head(&cb, sizeof(CIRBUF_STR_HEAD) + 1) == 0) {
+               printf("Error: unexpected result when deleting too much data!\n");
+               return -1;
+       }
+       /* delete more than written (tail) */
+       if (cirbuf_del_buf_tail(&cb, sizeof(CIRBUF_STR_HEAD) + 1) == 0) {
+               printf("Error: unexpected result when deleting too much data!\n");
+               return -1;
+       }
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* add string to tail */
+       if (cirbuf_add_buf_tail(&cb, CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL))
+                               != (sizeof(CIRBUF_STR_TAIL))) {
+               printf("Error: failed to add string to tail!\n");
+               return -1;
+       }
+       /* read more than written (tail) */
+       if (cirbuf_get_buf_tail(&cb, tmp, sizeof(CIRBUF_STR_TAIL) + 1)
+                       != sizeof(CIRBUF_STR_TAIL)) {
+               printf("Error: unexpected result when reading too much data!\n");
+               return -1;
+       }
+       /* read more than written (head) */
+       if (cirbuf_get_buf_head(&cb, tmp, sizeof(CIRBUF_STR_TAIL) + 1)
+                       != sizeof(CIRBUF_STR_TAIL)) {
+               printf("Error: unexpected result when reading too much data!\n");
+               return -1;
+       }
+       /* delete more than written (tail) */
+       if (cirbuf_del_buf_tail(&cb, sizeof(CIRBUF_STR_TAIL) + 1) == 0) {
+               printf("Error: unexpected result when deleting too much data!\n");
+               return -1;
+       }
+       /* delete more than written (head) */
+       if (cirbuf_del_buf_tail(&cb, sizeof(CIRBUF_STR_TAIL) + 1) == 0) {
+               printf("Error: unexpected result when deleting too much data!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* try to read/delete less than written */
+static int
+test_cirbuf_string_get_del_partial(void)
+{
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       char tmp[CMDLINE_TEST_BUFSIZE];
+       char tmp2[CMDLINE_TEST_BUFSIZE];
+
+       /* initialize buffers */
+       memset(buf, 0, sizeof(buf));
+       memset(tmp, 0, sizeof(tmp));
+       memset(tmp2, 0, sizeof(tmp));
+
+       rte_snprintf(tmp2, sizeof(tmp2), "%s", CIRBUF_STR_HEAD);
+
+       /*
+        * initialize circular buffer
+        */
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to initialize circular buffer!\n");
+               return -1;
+       }
+
+       /* add string to head */
+       if (cirbuf_add_buf_head(&cb, CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD))
+                               != (sizeof(CIRBUF_STR_HEAD))) {
+               printf("Error: failed to add string to head!\n");
+               return -1;
+       }
+       /* read less than written (head) */
+       if (cirbuf_get_buf_head(&cb, tmp, sizeof(CIRBUF_STR_HEAD) - 1)
+                       != sizeof(CIRBUF_STR_HEAD) - 1) {
+               printf("Error: unexpected result when reading from head!\n");
+               return -1;
+       }
+       /* verify string */
+       if (strncmp(tmp, tmp2, sizeof(CIRBUF_STR_HEAD) - 1) != 0) {
+               printf("Error: strings mismatch!\n");
+               return -1;
+       }
+       memset(tmp, 0, sizeof(tmp));
+       /* read less than written (tail) */
+       if (cirbuf_get_buf_tail(&cb, tmp, sizeof(CIRBUF_STR_HEAD) - 1)
+                       != sizeof(CIRBUF_STR_HEAD) - 1) {
+               printf("Error: unexpected result when reading from tail!\n");
+               return -1;
+       }
+       /* verify string */
+       if (strncmp(tmp, &tmp2[1], sizeof(CIRBUF_STR_HEAD) - 1) != 0) {
+               printf("Error: strings mismatch!\n");
+               return -1;
+       }
+
+       /*
+        * verify correct deletion
+        */
+
+       /* clear buffer */
+       memset(tmp, 0, sizeof(tmp));
+
+       /* delete less than written (head) */
+       if (cirbuf_del_buf_head(&cb, 1) != 0) {
+               printf("Error: delete from head failed!\n");
+               return -1;
+       }
+       /* read from head */
+       if (cirbuf_get_buf_head(&cb, tmp, sizeof(CIRBUF_STR_HEAD) - 1)
+                       != sizeof(CIRBUF_STR_HEAD) - 1) {
+               printf("Error: unexpected result when reading from head!\n");
+               return -1;
+       }
+       /* since we deleted from head, first char should be deleted */
+       if (strncmp(tmp, &tmp2[1], sizeof(CIRBUF_STR_HEAD) - 1) != 0) {
+               printf("Error: strings mismatch!\n");
+               return -1;
+       }
+       /* clear buffer */
+       memset(tmp, 0, sizeof(tmp));
+
+       /* delete less than written (tail) */
+       if (cirbuf_del_buf_tail(&cb, 1) != 0) {
+               printf("Error: delete from tail failed!\n");
+               return -1;
+       }
+       /* read from tail */
+       if (cirbuf_get_buf_tail(&cb, tmp, sizeof(CIRBUF_STR_HEAD) - 2)
+                       != sizeof(CIRBUF_STR_HEAD) - 2) {
+               printf("Error: unexpected result when reading from head!\n");
+               return -1;
+       }
+       /* since we deleted from tail, last char should be deleted */
+       if (strncmp(tmp, &tmp2[1], sizeof(CIRBUF_STR_HEAD) - 2) != 0) {
+               printf("Error: strings mismatch!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* test cmdline_cirbuf char add/del functions */
+static int
+test_cirbuf_char_add_del(void)
+{
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       char tmp[CMDLINE_TEST_BUFSIZE];
+
+       /* clear buffer */
+       memset(buf, 0, sizeof(buf));
+       memset(tmp, 0, sizeof(tmp));
+
+       /*
+        * initialize circular buffer
+        */
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to initialize circular buffer!\n");
+               return -1;
+       }
+
+       /*
+        * try to delete something from cirbuf. since it's empty,
+        * these should fail.
+        */
+       if (cirbuf_del_head_safe(&cb) == 0) {
+               printf("Error: deleting from empty cirbuf head succeeded!\n");
+               return -1;
+       }
+       if (cirbuf_del_tail_safe(&cb) == 0) {
+               printf("Error: deleting from empty cirbuf tail succeeded!\n");
+               return -1;
+       }
+
+       /*
+        * add, verify and delete. these should pass.
+        */
+       if (cirbuf_add_head_safe(&cb,'h') < 0) {
+               printf("Error: adding to cirbuf head failed!\n");
+               return -1;
+       }
+       if (cirbuf_get_head(&cb) != 'h') {
+               printf("Error: wrong head content!\n");
+               return -1;
+       }
+       if (cirbuf_del_head_safe(&cb) < 0) {
+               printf("Error: deleting from cirbuf head failed!\n");
+               return -1;
+       }
+       if (cirbuf_add_tail_safe(&cb,'t') < 0) {
+               printf("Error: adding to cirbuf tail failed!\n");
+               return -1;
+       }
+       if (cirbuf_get_tail(&cb) != 't') {
+               printf("Error: wrong tail content!\n");
+               return -1;
+       }
+       if (cirbuf_del_tail_safe(&cb) < 0) {
+               printf("Error: deleting from cirbuf tail failed!\n");
+               return -1;
+       }
+       /* do the same for unsafe versions. those are void. */
+       cirbuf_add_head(&cb,'h');
+       if (cirbuf_get_head(&cb) != 'h') {
+               printf("Error: wrong head content!\n");
+               return -1;
+       }
+       cirbuf_del_head(&cb);
+
+       /* test if char has been deleted. we can't call cirbuf_get_head
+        * because it's unsafe, but we can call cirbuf_get_buf_head.
+        */
+       if (cirbuf_get_buf_head(&cb, tmp, 1) > 0) {
+               printf("Error: buffer should have been empty!\n");
+               return -1;
+       }
+
+       cirbuf_add_tail(&cb,'t');
+       if (cirbuf_get_tail(&cb) != 't') {
+               printf("Error: wrong tail content!\n");
+               return -1;
+       }
+       cirbuf_del_tail(&cb);
+
+       /* test if char has been deleted. we can't call cirbuf_get_tail
+        * because it's unsafe, but we can call cirbuf_get_buf_tail.
+        */
+       if (cirbuf_get_buf_tail(&cb, tmp, 1) > 0) {
+               printf("Error: buffer should have been empty!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* test filling up buffer with chars */
+static int
+test_cirbuf_char_fill(void)
+{
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       unsigned i;
+
+       /* clear buffer */
+       memset(buf, 0, sizeof(buf));
+
+       /*
+        * initialize circular buffer
+        */
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to initialize circular buffer!\n");
+               return -1;
+       }
+
+       /*
+        * fill the buffer from head or tail, verify contents, test boundaries
+        * and clear the buffer
+        */
+
+       /* fill the buffer from tail */
+       for (i = 0; i < CMDLINE_TEST_BUFSIZE; i++)
+               cirbuf_add_tail_safe(&cb, 't');
+       /* verify that contents of the buffer are what they are supposed to be */
+       for (i = 0; i < sizeof(buf); i++) {
+               if (buf[i] != 't') {
+                       printf("Error: wrong content in buffer!\n");
+                       return -1;
+               }
+       }
+       /* try to add to a full buffer from tail */
+       if (cirbuf_add_tail_safe(&cb, 't') == 0) {
+               printf("Error: buffer should have been full!\n");
+               return -1;
+       }
+       /* try to add to a full buffer from head */
+       if (cirbuf_add_head_safe(&cb, 'h') == 0) {
+               printf("Error: buffer should have been full!\n");
+               return -1;
+       }
+       /* delete buffer from tail */
+       for(i = 0; i < CMDLINE_TEST_BUFSIZE; i++)
+               cirbuf_del_tail_safe(&cb);
+       /* try to delete from an empty buffer */
+       if (cirbuf_del_tail_safe(&cb) >= 0) {
+               printf("Error: buffer should have been empty!\n");
+               return -1;
+       }
+
+       /* fill the buffer from head */
+       for (i = 0; i < CMDLINE_TEST_BUFSIZE; i++)
+               cirbuf_add_head_safe(&cb, 'h');
+       /* verify that contents of the buffer are what they are supposed to be */
+       for (i = 0; i < sizeof(buf); i++) {
+               if (buf[i] != 'h') {
+                       printf("Error: wrong content in buffer!\n");
+                       return -1;
+               }
+       }
+       /* try to add to a full buffer from head */
+       if (cirbuf_add_head_safe(&cb,'h') >= 0) {
+               printf("Error: buffer should have been full!\n");
+               return -1;
+       }
+       /* try to add to a full buffer from tail */
+       if (cirbuf_add_tail_safe(&cb, 't') == 0) {
+               printf("Error: buffer should have been full!\n");
+               return -1;
+       }
+       /* delete buffer from head */
+       for(i = 0; i < CMDLINE_TEST_BUFSIZE; i++)
+               cirbuf_del_head_safe(&cb);
+       /* try to delete from an empty buffer */
+       if (cirbuf_del_head_safe(&cb) >= 0) {
+               printf("Error: buffer should have been empty!\n");
+               return -1;
+       }
+
+       /*
+        * fill the buffer from both head and tail, with alternating characters,
+        * verify contents and clear the buffer
+        */
+
+       /* fill half of buffer from tail */
+       for (i = 0; i < CMDLINE_TEST_BUFSIZE / 2; i++)
+               cirbuf_add_tail_safe(&cb, (char) (i % 2 ? 't' : 'T'));
+       /* fill other half of the buffer from head */
+       for (i = 0; i < CMDLINE_TEST_BUFSIZE / 2; i++)
+               cirbuf_add_head_safe(&cb, (char) (i % 2 ? 'H' : 'h')); /* added in reverse */
+
+       /* verify that contents of the buffer are what they are supposed to be */
+       for (i = 0; i < sizeof(buf) / 2; i++) {
+               if (buf[i] != (char) (i % 2 ? 't' : 'T')) {
+                       printf("Error: wrong content in buffer at %u!\n", i);
+                       return -1;
+               }
+       }
+       for (i = sizeof(buf) / 2; i < sizeof(buf); i++) {
+               if (buf[i] != (char) (i % 2 ? 'h' : 'H')) {
+                       printf("Error: wrong content in buffer %u!\n", i);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/* test left alignment */
+static int
+test_cirbuf_align_left(void)
+{
+#define HALF_OFFSET CMDLINE_TEST_BUFSIZE / 2
+#define SMALL_OFFSET HALF_OFFSET / 2
+/* resulting buffer lengths for each of the test cases */
+#define LEN1 HALF_OFFSET - SMALL_OFFSET - 1
+#define LEN2 HALF_OFFSET + SMALL_OFFSET + 2
+#define LEN3 HALF_OFFSET - SMALL_OFFSET
+#define LEN4 HALF_OFFSET + SMALL_OFFSET - 1
+
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       char tmp[CMDLINE_TEST_BUFSIZE];
+       unsigned i;
+
+       /*
+        * align left when start < end and start in left half
+        */
+
+       /*
+        * initialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to initialize circular buffer!\n");
+               return -1;
+       }
+
+       /* push end into left half */
+       for (i = 0; i < HALF_OFFSET - 1; i++)
+               cirbuf_add_tail_safe(&cb, 't');
+
+       /* push start into left half < end */
+       for (i = 0; i < SMALL_OFFSET; i++)
+               cirbuf_del_head_safe(&cb);
+
+       /* align */
+       if (cirbuf_align_left(&cb) < 0) {
+               printf("Error: alignment failed!\n");
+               return -1;
+       }
+
+       /* verify result */
+       if (cb.start != 0 || cb.len != LEN1 || cb.end != cb.len - 1) {
+               printf("Error: buffer alignment is wrong!\n");
+               return -1;
+       }
+
+       /*
+        * align left when start > end and start in left half
+        */
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* push start into left half */
+       for (i = 0; i < HALF_OFFSET + 2; i++)
+               cirbuf_add_head_safe(&cb, 'h');
+
+       /* push end into left half > start */
+       for (i = 0; i < SMALL_OFFSET; i++)
+               cirbuf_add_tail_safe(&cb, 't');
+
+       /* align */
+       if (cirbuf_align_left(&cb) < 0) {
+               printf("Error: alignment failed!\n");
+               return -1;
+       }
+
+       /* verify result */
+       if (cb.start != 0 || cb.len != LEN2 || cb.end != cb.len - 1) {
+               printf("Error: buffer alignment is wrong!");
+               return -1;
+       }
+
+       /*
+        * align left when start < end and start in right half
+        */
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* push start into the right half */
+       for (i = 0; i < HALF_OFFSET; i++)
+               cirbuf_add_head_safe(&cb, 'h');
+
+       /* push end into left half > start */
+       for (i = 0; i < SMALL_OFFSET; i++)
+               cirbuf_del_tail_safe(&cb);
+
+       /* align */
+       if (cirbuf_align_left(&cb) < 0) {
+               printf("Error: alignment failed!\n");
+               return -1;
+       }
+
+       /* verify result */
+       if (cb.start != 0 || cb.len != LEN3 || cb.end != cb.len - 1) {
+               printf("Error: buffer alignment is wrong!");
+               return -1;
+       }
+
+       /*
+        * align left when start > end and start in right half
+        */
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* push start into the right half */
+       for (i = 0; i < HALF_OFFSET - 1; i++)
+               cirbuf_add_head_safe(&cb, 'h');
+
+       /* push end into left half < start */
+       for (i = 0; i < SMALL_OFFSET; i++)
+               cirbuf_add_tail_safe(&cb, 't');
+
+       /* align */
+       if (cirbuf_align_left(&cb) < 0) {
+               printf("Error: alignment failed!\n");
+               return -1;
+       }
+
+       /* verify result */
+       if (cb.start != 0 || cb.len != LEN4 ||
+                       cb.end != cb.len - 1) {
+               printf("Error: buffer alignment is wrong!");
+               return -1;
+       }
+
+       /*
+        * Verify that alignment doesn't corrupt data
+        */
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* add string to tail and head */
+       if (cirbuf_add_buf_head(&cb, CIRBUF_STR_HEAD,
+                       sizeof(CIRBUF_STR_HEAD)) < 0 || cirbuf_add_buf_tail(&cb,
+                                       CIRBUF_STR_TAIL, sizeof(CIRBUF_STR_TAIL)) < 0) {
+               printf("Error: failed to add strings!\n");
+               return -1;
+       }
+
+       /* align */
+       if (cirbuf_align_left(&cb) < 0) {
+               printf("Error: alignment failed!\n");
+               return -1;
+       }
+
+       /* get string from head */
+       if (cirbuf_get_buf_head(&cb, tmp,
+                       sizeof(CIRBUF_STR_HEAD) + sizeof(CIRBUF_STR_TAIL)) < 0) {
+               printf("Error: failed to read string from head!\n");
+               return -1;
+       }
+
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_HEAD "\0" CIRBUF_STR_TAIL,
+                       sizeof(CIRBUF_STR_HEAD) + sizeof(CIRBUF_STR_TAIL)) != 0) {
+               printf("Error: strings mismatch!\n");
+               return -1;
+       }
+
+       /* reset tmp buffer */
+       memset(tmp, 0, sizeof(tmp));
+
+       /* get string from tail */
+       if (cirbuf_get_buf_tail(&cb, tmp,
+                       sizeof(CIRBUF_STR_HEAD) + sizeof(CIRBUF_STR_TAIL)) < 0) {
+               printf("Error: failed to read string from head!\n");
+               return -1;
+       }
+
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_HEAD "\0" CIRBUF_STR_TAIL,
+                       sizeof(CIRBUF_STR_HEAD) + sizeof(CIRBUF_STR_TAIL)) != 0) {
+               printf("Error: strings mismatch!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* test right alignment */
+static int
+test_cirbuf_align_right(void)
+{
+#define END_OFFSET CMDLINE_TEST_BUFSIZE - 1
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       char tmp[CMDLINE_TEST_BUFSIZE];
+       unsigned i;
+
+
+       /*
+        * align right when start < end and start in left half
+        */
+
+       /*
+        * initialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to initialize circular buffer!\n");
+               return -1;
+       }
+
+       /* push end into left half */
+       for (i = 0; i < HALF_OFFSET - 1; i++)
+               cirbuf_add_tail_safe(&cb, 't');
+
+       /* push start into left half < end */
+       for (i = 0; i < SMALL_OFFSET; i++)
+               cirbuf_del_head_safe(&cb);
+
+       /* align */
+       cirbuf_align_right(&cb);
+
+       /* verify result */
+       if (cb.start != END_OFFSET || cb.len != LEN1 || cb.end != cb.len - 2) {
+               printf("Error: buffer alignment is wrong!\n");
+               return -1;
+       }
+
+       /*
+        * align right when start > end and start in left half
+        */
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* push start into left half */
+       for (i = 0; i < HALF_OFFSET + 2; i++)
+               cirbuf_add_head_safe(&cb, 'h');
+
+       /* push end into left half > start */
+       for (i = 0; i < SMALL_OFFSET; i++)
+               cirbuf_add_tail_safe(&cb, 't');
+
+       /* align */
+       cirbuf_align_right(&cb);
+
+       /* verify result */
+       if (cb.start != END_OFFSET || cb.len != LEN2 || cb.end != cb.len - 2) {
+               printf("Error: buffer alignment is wrong!");
+               return -1;
+       }
+
+       /*
+        * align right when start < end and start in right half
+        */
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* push start into the right half */
+       for (i = 0; i < HALF_OFFSET; i++)
+               cirbuf_add_head_safe(&cb, 'h');
+
+       /* push end into left half > start */
+       for (i = 0; i < SMALL_OFFSET; i++)
+               cirbuf_del_tail_safe(&cb);
+
+       /* align */
+       cirbuf_align_right(&cb);
+
+       /* verify result */
+       if (cb.end != END_OFFSET || cb.len != LEN3 || cb.start != cb.end - cb.len + 1) {
+               printf("Error: buffer alignment is wrong!");
+               return -1;
+       }
+
+       /*
+        * align right when start > end and start in right half
+        */
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* push start into the right half */
+       for (i = 0; i < HALF_OFFSET - 1; i++)
+               cirbuf_add_head_safe(&cb, 'h');
+
+       /* push end into left half < start */
+       for (i = 0; i < SMALL_OFFSET; i++)
+               cirbuf_add_tail_safe(&cb, 't');
+
+       /* align */
+       cirbuf_align_right(&cb);
+
+       /* verify result */
+       if (cb.end != END_OFFSET || cb.len != LEN4 || cb.start != cb.end - cb.len + 1) {
+               printf("Error: buffer alignment is wrong!");
+               return -1;
+       }
+
+       /*
+        * Verify that alignment doesn't corrupt data
+        */
+
+       /*
+        * reinitialize circular buffer
+        */
+       memset(buf, 0, sizeof(buf));
+       if (cirbuf_init(&cb, buf, 0, sizeof(buf)) < 0) {
+               printf("Error: failed to reinitialize circular buffer!\n");
+               return -1;
+       }
+
+       /* add string to tail and head */
+       if (cirbuf_add_buf_tail(&cb, CIRBUF_STR_TAIL,
+                       sizeof(CIRBUF_STR_TAIL)) < 0 || cirbuf_add_buf_head(&cb,
+                                       CIRBUF_STR_HEAD, sizeof(CIRBUF_STR_HEAD)) < 0) {
+               printf("Error: failed to add strings!\n");
+               return -1;
+       }
+
+       /* align */
+       if (cirbuf_align_right(&cb) < 0) {
+               printf("Error: alignment failed!\n");
+               return -1;
+       }
+
+       /* get string from head */
+       if (cirbuf_get_buf_head(&cb, tmp,
+                       sizeof(CIRBUF_STR_HEAD) + sizeof(CIRBUF_STR_TAIL)) < 0) {
+               printf("Error: failed to read string from head!\n");
+               return -1;
+       }
+
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_HEAD "\0" CIRBUF_STR_TAIL,
+                       sizeof(CIRBUF_STR_HEAD) + sizeof(CIRBUF_STR_TAIL)) != 0) {
+               printf("Error: strings mismatch!\n");
+               return -1;
+       }
+
+       /* reset tmp buffer */
+       memset(tmp, 0, sizeof(tmp));
+
+       /* get string from tail */
+       if (cirbuf_get_buf_tail(&cb, tmp,
+                       sizeof(CIRBUF_STR_HEAD) + sizeof(CIRBUF_STR_TAIL)) < 0) {
+               printf("Error: failed to read string from head!\n");
+               return -1;
+       }
+       /* verify string */
+       if (strncmp(tmp, CIRBUF_STR_HEAD "\0" CIRBUF_STR_TAIL,
+                       sizeof(CIRBUF_STR_HEAD) + sizeof(CIRBUF_STR_TAIL)) != 0) {
+               printf("Error: strings mismatch!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* call functions with invalid parameters */
+int
+test_cirbuf_invalid_param(void)
+{
+       struct cirbuf cb;
+       char buf[CMDLINE_TEST_BUFSIZE];
+
+       /* null cirbuf */
+       if (cirbuf_init(0, buf, 0, sizeof(buf)) == 0)
+               return -1;
+       /* null buffer */
+       if (cirbuf_init(&cb, 0, 0, sizeof(buf)) == 0)
+               return -1;
+       /* null cirbuf */
+       if (cirbuf_add_head_safe(0, 'h') == 0)
+               return -1;
+       if (cirbuf_add_tail_safe(0, 't') == 0)
+               return -1;
+       if (cirbuf_del_head_safe(0) == 0)
+               return -1;
+       if (cirbuf_del_tail_safe(0) == 0)
+               return -1;
+       /* null buffer */
+       if (cirbuf_add_buf_head(&cb, 0, 0) == 0)
+               return -1;
+       if (cirbuf_add_buf_tail(&cb, 0, 0) == 0)
+               return -1;
+       /* null cirbuf */
+       if (cirbuf_add_buf_head(0, buf, 0) == 0)
+               return -1;
+       if (cirbuf_add_buf_tail(0, buf, 0) == 0)
+               return -1;
+       /* null size */
+       if (cirbuf_add_buf_head(&cb, buf, 0) == 0)
+               return -1;
+       if (cirbuf_add_buf_tail(&cb, buf, 0) == 0)
+               return -1;
+       /* null cirbuf */
+       if (cirbuf_del_buf_head(0, 0) == 0)
+               return -1;
+       if (cirbuf_del_buf_tail(0, 0) == 0)
+               return -1;
+       /* null size */
+       if (cirbuf_del_buf_head(&cb, 0) == 0)
+               return -1;
+       if (cirbuf_del_buf_tail(&cb, 0) == 0)
+               return -1;
+       /* null cirbuf */
+       if (cirbuf_get_buf_head(0, 0, 0) == 0)
+               return -1;
+       if (cirbuf_get_buf_tail(0, 0, 0) == 0)
+               return -1;
+       /* null buffer */
+       if (cirbuf_get_buf_head(&cb, 0, 0) == 0)
+               return -1;
+       if (cirbuf_get_buf_tail(&cb, 0, 0) == 0)
+               return -1;
+       /* null size, this is valid but should return 0 */
+       if (cirbuf_get_buf_head(&cb, buf, 0) != 0)
+               return -1;
+       if (cirbuf_get_buf_tail(&cb, buf, 0) != 0)
+               return -1;
+       /* null cirbuf */
+       if (cirbuf_align_left(0) == 0)
+               return -1;
+       if (cirbuf_align_right(0) == 0)
+               return -1;
+
+       return 0;
+}
+
+/* test cmdline_cirbuf char functions */
+int
+test_cirbuf_char(void)
+{
+       int ret;
+
+       ret = test_cirbuf_char_add_del();
+       if (ret < 0)
+               return -1;
+
+       ret = test_cirbuf_char_fill();
+       if (ret < 0)
+               return -1;
+
+       return 0;
+}
+
+/* test cmdline_cirbuf string functions */
+int
+test_cirbuf_string(void)
+{
+       if (test_cirbuf_string_add_del() < 0)
+               return -1;
+
+       if (test_cirbuf_string_add_del_reverse() < 0)
+               return -1;
+
+       if (test_cirbuf_string_add_boundaries() < 0)
+               return -1;
+
+       if (test_cirbuf_string_get_del_boundaries() < 0)
+               return -1;
+
+       if (test_cirbuf_string_get_del_partial() < 0)
+               return -1;
+
+       if (test_cirbuf_string_misc() < 0)
+               return -1;
+
+       return 0;
+}
+
+/* test cmdline_cirbuf align functions */
+int
+test_cirbuf_align(void)
+{
+       if (test_cirbuf_align_left() < 0)
+               return -1;
+       if (test_cirbuf_align_right() < 0)
+               return -1;
+       return 0;
+}
diff --git a/app/test/test_cmdline_etheraddr.c b/app/test/test_cmdline_etheraddr.c
new file mode 100644 (file)
index 0000000..a436570
--- /dev/null
@@ -0,0 +1,254 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <rte_ether.h>
+#include <rte_string_fns.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_etheraddr.h>
+
+#include "test_cmdline.h"
+
+struct ether_addr_str {
+       const char * str;
+       uint64_t address;
+};
+
+/* valid strings */
+const struct ether_addr_str ether_addr_valid_strs[] = {
+               {"01:23:45:67:89:AB", 0xAB8967452301ULL},
+               {"4567:89AB:CDEF", 0xEFCDAB896745ULL},
+};
+
+/* valid strings with various garbage at the end.
+ * these strings are still valid because parser checks for
+ * end of token, which is either space chars, null char or
+ * a hash sign.
+ */
+const char * ether_addr_garbage_strs[] = {
+               "00:11:22:33:44:55\0garbage",
+               "00:11:22:33:44:55#garbage",
+               "00:11:22:33:44:55 garbage",
+               "00:11:22:33:44:55\tgarbage",
+               "00:11:22:33:44:55\ngarbage",
+               "00:11:22:33:44:55\rgarbage",
+               "00:11:22:33:44:55#",
+               "00:11:22:33:44:55 ",
+               "00:11:22:33:44:55\t",
+               "00:11:22:33:44:55\n",
+               "00:11:22:33:44:55\r",
+};
+#define GARBAGE_ETHERADDR 0x554433221100ULL /* corresponding address */
+
+
+const char * ether_addr_invalid_strs[] = {
+               /* valid chars, invalid syntax */
+               "0123:45:67:89:AB",
+               "01:23:4567:89:AB",
+               "01:23:45:67:89AB",
+               "012:345:678:9AB",
+               "01:23:45:67:89:ABC",
+               "01:23:45:67:89:A",
+               "01:23:45:67:89",
+               "01:23:45:67:89:AB:CD",
+               /* invalid chars, valid syntax */
+               "IN:VA:LI:DC:HA:RS",
+               "INVA:LIDC:HARS",
+               /* misc */
+               "01 23 45 67 89 AB",
+               "01.23.45.67.89.AB",
+               "01,23,45,67,89,AB",
+               "01:23:45\0:67:89:AB",
+               "01:23:45#:67:89:AB",
+               "random invalid text",
+               "random text",
+               "",
+               "\0",
+               " ",
+};
+
+#define ETHERADDR_VALID_STRS_SIZE \
+       (sizeof(ether_addr_valid_strs) / sizeof(ether_addr_valid_strs[0]))
+#define ETHERADDR_GARBAGE_STRS_SIZE \
+       (sizeof(ether_addr_garbage_strs) / sizeof(ether_addr_garbage_strs[0]))
+#define ETHERADDR_INVALID_STRS_SIZE \
+       (sizeof(ether_addr_invalid_strs) / sizeof(ether_addr_invalid_strs[0]))
+
+
+
+static int
+is_addr_different(const struct ether_addr addr, uint64_t num)
+{
+       int i;
+       for (i = 0; i < ETHER_ADDR_LEN; i++, num >>= 8)
+               if (addr.addr_bytes[i] != (num & 0xFF)) {
+                       return 1;
+               }
+       return 0;
+}
+
+/* test invalid parameters */
+int
+test_parse_etheraddr_invalid_param(void)
+{
+       char buf[CMDLINE_TEST_BUFSIZE];
+       struct ether_addr result;
+       int ret = 0;
+
+       /* try all null */
+       ret = cmdline_parse_etheraddr(NULL, NULL, NULL);
+       if (ret != -1) {
+               printf("Error: parser accepted null parameters!\n");
+               return -1;
+       }
+
+       /* try null buf */
+       ret = cmdline_parse_etheraddr(NULL, NULL, (void*)&result);
+       if (ret != -1) {
+               printf("Error: parser accepted null string!\n");
+               return -1;
+       }
+
+       /* try null result */
+
+       /* copy string to buffer */
+       rte_snprintf(buf, sizeof(buf), "%s",
+                       ether_addr_valid_strs[0]);
+
+       ret = cmdline_parse_etheraddr(NULL, buf, NULL);
+       if (ret == -1) {
+               printf("Error: parser rejected null result!\n");
+               return -1;
+       }
+
+       /* token is not used in ether_parse anyway so there's no point in
+        * testing it */
+
+       /* test help function */
+       memset(&buf, 0, sizeof(buf));
+
+       /* try null buf */
+       ret = cmdline_get_help_etheraddr(NULL, NULL, sizeof(buf));
+       if (ret != -1) {
+               printf("Error: help function accepted null buffer!\n");
+               return -1;
+       }
+
+       /* coverage! */
+       ret = cmdline_get_help_etheraddr(NULL, buf, sizeof(buf));
+       if (ret < 0) {
+               printf("Error: help function failed with valid parameters!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* test valid parameters but invalid data */
+int
+test_parse_etheraddr_invalid_data(void)
+{
+       int ret = 0;
+       unsigned i;
+       struct ether_addr result;
+
+       /* test full strings */
+       for (i = 0; i < ETHERADDR_INVALID_STRS_SIZE; i++) {
+
+               memset(&result, 0, sizeof(struct ether_addr));
+
+               ret = cmdline_parse_etheraddr(NULL, ether_addr_invalid_strs[i],
+                               (void*)&result);
+               if (ret != -1) {
+                       printf("Error: parsing %s succeeded!\n",
+                                       ether_addr_invalid_strs[i]);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/* test valid parameters and data */
+int
+test_parse_etheraddr_valid(void)
+{
+       int ret = 0;
+       unsigned i;
+       struct ether_addr result;
+
+       /* test full strings */
+       for (i = 0; i < ETHERADDR_VALID_STRS_SIZE; i++) {
+
+               memset(&result, 0, sizeof(struct ether_addr));
+
+               ret = cmdline_parse_etheraddr(NULL, ether_addr_valid_strs[i].str,
+                               (void*)&result);
+               if (ret < 0) {
+                       printf("Error: parsing %s failed!\n",
+                                       ether_addr_valid_strs[i].str);
+                       return -1;
+               }
+               if (is_addr_different(result, ether_addr_valid_strs[i].address)) {
+                       printf("Error: parsing %s failed: address mismatch!\n",
+                                       ether_addr_valid_strs[i].str);
+                       return -1;
+               }
+       }
+
+       /* test garbage strings */
+       for (i = 0; i < ETHERADDR_GARBAGE_STRS_SIZE; i++) {
+
+               memset(&result, 0, sizeof(struct ether_addr));
+
+               ret = cmdline_parse_etheraddr(NULL, ether_addr_garbage_strs[i],
+                               (void*)&result);
+               if (ret < 0) {
+                       printf("Error: parsing %s failed!\n",
+                                       ether_addr_garbage_strs[i]);
+                       return -1;
+               }
+               if (is_addr_different(result, GARBAGE_ETHERADDR)) {
+                       printf("Error: parsing %s failed: address mismatch!\n",
+                                       ether_addr_garbage_strs[i]);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
diff --git a/app/test/test_cmdline_ipaddr.c b/app/test/test_cmdline_ipaddr.c
new file mode 100644 (file)
index 0000000..201dae3
--- /dev/null
@@ -0,0 +1,707 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+
+#ifndef __linux__
+#include <net/socket.h>
+#endif
+
+#include <rte_string_fns.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_ipaddr.h>
+
+#include "test_cmdline.h"
+
+#define IP4(a,b,c,d) {((uint32_t)(((a) & 0xff)) | \
+                                          (((b) & 0xff) << 8) | \
+                                          (((c) & 0xff) << 16)  | \
+                                          ((d) & 0xff)  << 24)}
+
+#define U16_SWAP(x) \
+               (((x & 0xFF) << 8) | ((x & 0xFF00) >> 8))
+
+/* create IPv6 address, swapping bytes where needed */
+#define IP6(a,b,c,d,e,f,g,h) .ipv6 = \
+               {.s6_addr16 = \
+               {U16_SWAP(a),U16_SWAP(b),U16_SWAP(c),U16_SWAP(d),\
+                U16_SWAP(e),U16_SWAP(f),U16_SWAP(g),U16_SWAP(h)}}
+
+/** these are defined in netinet/in.h but not present in linux headers */
+#ifdef __linux__
+#define NIPQUAD_FMT "%u.%u.%u.%u"
+#define NIPQUAD(addr)                          \
+       (unsigned)((unsigned char *)&addr)[0],  \
+       (unsigned)((unsigned char *)&addr)[1],  \
+       (unsigned)((unsigned char *)&addr)[2],  \
+       (unsigned)((unsigned char *)&addr)[3]
+
+#define NIP6_FMT "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x"
+#define NIP6(addr)                                     \
+       (unsigned)((addr).s6_addr[0]),                  \
+       (unsigned)((addr).s6_addr[1]),                  \
+       (unsigned)((addr).s6_addr[2]),                  \
+       (unsigned)((addr).s6_addr[3]),                  \
+       (unsigned)((addr).s6_addr[4]),                  \
+       (unsigned)((addr).s6_addr[5]),                  \
+       (unsigned)((addr).s6_addr[6]),                  \
+       (unsigned)((addr).s6_addr[7]),                  \
+       (unsigned)((addr).s6_addr[8]),                  \
+       (unsigned)((addr).s6_addr[9]),                  \
+       (unsigned)((addr).s6_addr[10]),                 \
+       (unsigned)((addr).s6_addr[11]),                 \
+       (unsigned)((addr).s6_addr[12]),                 \
+       (unsigned)((addr).s6_addr[13]),                 \
+       (unsigned)((addr).s6_addr[14]),                 \
+       (unsigned)((addr).s6_addr[15])
+#endif
+
+
+
+struct ipaddr_str {
+       const char * str;
+       cmdline_ipaddr_t addr;
+       unsigned flags;
+};
+
+const struct ipaddr_str ipaddr_valid_strs[] = {
+               {"0.0.0.0", {AF_INET, {IP4(0,0,0,0)}, 0},
+                               CMDLINE_IPADDR_V4},
+               {"0.0.0.0/0", {AF_INET, {IP4(0,0,0,0)}, 0},
+                               CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK},
+               {"0.0.0.0/24", {AF_INET, {IP4(0,0,0,0)}, 24},
+                               CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK},
+               {"192.168.1.0/24", {AF_INET, {IP4(192,168,1,0)}, 24},
+                               CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK},
+               {"012.34.56.78/24", {AF_INET, {IP4(12,34,56,78)}, 24},
+                               CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK},
+               {"34.56.78.90/1", {AF_INET, {IP4(34,56,78,90)}, 1},
+                               CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK},
+               {"::", {AF_INET6, {IP6(0,0,0,0,0,0,0,0)}, 0},
+                                       CMDLINE_IPADDR_V6},
+               {"::1", {AF_INET6, {IP6(0,0,0,0,0,0,0,1)}, 0},
+                               CMDLINE_IPADDR_V6},
+               {"::1/32", {AF_INET6, {IP6(0,0,0,0,0,0,0,1)}, 32},
+                               CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK},
+               {"::/32", {AF_INET6, {IP6(0,0,0,0,0,0,0,0)}, 32},
+                                       CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK},
+               /* RFC5952 requests that only lowercase should be used */
+               {"1234:5678:90ab:cdef:4321:8765:BA09:FEDC", {AF_INET6,
+                               {IP6(0x1234,0x5678,0x90AB,0xCDEF,0x4321,0x8765,0xBA09,0xFEDC)},
+                               0},
+                               CMDLINE_IPADDR_V6},
+               {"1234::1234/64", {AF_INET6,
+                               {IP6(0x1234,0,0,0,0,0,0,0x1234)},
+                               64},
+                               CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK},
+               {"1234::/64", {AF_INET6,
+                               {IP6(0x1234,0,0,0,0,0,0,0)},
+                               64},
+                               CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK},
+               {"1:1::1/32", {AF_INET6,
+                               {IP6(1,1,0,0,0,0,0,1)},
+                               32},
+                               CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK},
+               {"1:2:3:4::/64", {AF_INET6,
+                               {IP6(1,2,3,4,0,0,0,0)},
+                               64},
+                       CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK},
+               {"::ffff:192.168.1.0/64", {AF_INET6,
+                               {IP6(0,0,0,0,0,0xFFFF,0xC0A8,0x100)},
+                               64},
+                       CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK},
+               /* RFC5952 requests not using :: to skip one block of zeros*/
+               {"1::2:3:4:5:6:7", {AF_INET6,
+                               {IP6(1,0,2,3,4,5,6,7)},
+                               0},
+                       CMDLINE_IPADDR_V6},
+};
+
+const char * ipaddr_garbage_addr4_strs[] = {
+               /* IPv4 */
+               "192.168.1.0 garbage",
+               "192.168.1.0\0garbage",
+               "192.168.1.0#garbage",
+               "192.168.1.0\tgarbage",
+               "192.168.1.0\rgarbage",
+               "192.168.1.0\ngarbage",
+};
+#define IPv4_GARBAGE_ADDR IP4(192,168,1,0)
+
+const char * ipaddr_garbage_addr6_strs[] = {
+               /* IPv6 */
+               "1:2:3:4::8 garbage",
+               "1:2:3:4::8#garbage",
+               "1:2:3:4::8\0garbage",
+               "1:2:3:4::8\rgarbage",
+               "1:2:3:4::8\ngarbage",
+               "1:2:3:4::8\tgarbage",
+};
+#define IPv6_GARBAGE_ADDR {IP6(1,2,3,4,0,0,0,8)}
+
+const char * ipaddr_garbage_network4_strs[] = {
+               /* IPv4 */
+               "192.168.1.0/24 garbage",
+               "192.168.1.0/24\0garbage",
+               "192.168.1.0/24#garbage",
+               "192.168.1.0/24\tgarbage",
+               "192.168.1.0/24\rgarbage",
+               "192.168.1.0/24\ngarbage",
+};
+#define IPv4_GARBAGE_PREFIX 24
+
+const char * ipaddr_garbage_network6_strs[] = {
+               /* IPv6 */
+               "1:2:3:4::8/64 garbage",
+               "1:2:3:4::8/64#garbage",
+               "1:2:3:4::8/64\0garbage",
+               "1:2:3:4::8/64\rgarbage",
+               "1:2:3:4::8/64\ngarbage",
+               "1:2:3:4::8/64\tgarbage",
+};
+#define IPv6_GARBAGE_PREFIX 64
+
+
+
+const char * ipaddr_invalid_strs[] = {
+               /** IPv4 **/
+
+               /* invalid numbers */
+               "0.0.0.-1",
+               "0.0.-1.0",
+               "0.-1.0.0",
+               "-1.0.0.0",
+               "0.0.0.-1/24",
+               "256.123.123.123",
+               "255.256.123.123",
+               "255.255.256.123",
+               "255.255.255.256",
+               "256.123.123.123/24",
+               "255.256.123.123/24",
+               "255.255.256.123/24",
+               "255.255.255.256/24",
+               /* invalid network mask */
+               "1.2.3.4/33",
+               "1.2.3.4/33231313",
+               "1.2.3.4/-1",
+               "1.2.3.4/24/33",
+               "1.2.3.4/24/-1",
+               "1.2.3.4/24/",
+               /* wrong format */
+               "1/24"
+               "/24"
+               "123.123.123",
+               "123.123.123.",
+               "123.123.123.123.",
+               "123.123.123..123",
+               "123.123.123.123.123",
+               ".123.123.123",
+               ".123.123.123.123",
+               "123.123.123/24",
+               "123.123.123./24",
+               "123.123.123.123./24",
+               "123.123.123..123/24",
+               "123.123.123.123.123/24",
+               ".123.123.123/24",
+               ".123.123.123.123/24",
+               /* invalid characters */
+               "123.123.123.12F",
+               "123.123.12F.123",
+               "123.12F.123.123",
+               "12F.123.123.123",
+               "12J.123.123.123",
+               "123,123,123,123",
+               "123!123!123!12F",
+               "123.123.123.123/4F",
+
+               /** IPv6 **/
+
+               /* wrong format */
+               "::fffff",
+               "ffff:",
+               "1:2:3:4:5:6:7:192.168.1.1",
+               "1234:192.168.1.1:ffff::",
+               "1:2:3:4:5:6:7:890ab",
+               "1:2:3:4:5:6:7890a:b",
+               "1:2:3:4:5:67890:a:b",
+               "1:2:3:4:56789:0:a:b",
+               "1:2:3:45678:9:0:a:b",
+               "1:2:34567:8:9:0:a:b",
+               "1:23456:7:8:9:0:a:b",
+               "12345:6:7:8:9:0:a:b",
+               "1:::2",
+               "1::::2",
+               "::fffff/64",
+               "1::2::3",
+               "1::2::3/64",
+               ":1:2",
+               ":1:2/64",
+               ":1::2",
+               ":1::2/64",
+               "1::2:3:4:5:6:7:8/64",
+
+               /* invalid network mask */
+               "1:2:3:4:5:6:7:8/129",
+               "1:2:3:4:5:6:7:8/-1",
+
+               /* invalid characters */
+               "a:b:c:d:e:f:g::",
+
+               /** misc **/
+
+               /* too long */
+               "1234:1234:1234:1234:1234:1234:1234:1234:1234:1234:1234"
+               "random invalid text",
+               "",
+               "\0",
+               " ",
+};
+
+#define IPADDR_VALID_STRS_SIZE \
+       (sizeof(ipaddr_valid_strs) / sizeof(ipaddr_valid_strs[0]))
+#define IPADDR_GARBAGE_ADDR4_STRS_SIZE \
+       (sizeof(ipaddr_garbage_addr4_strs) / sizeof(ipaddr_garbage_addr4_strs[0]))
+#define IPADDR_GARBAGE_ADDR6_STRS_SIZE \
+       (sizeof(ipaddr_garbage_addr6_strs) / sizeof(ipaddr_garbage_addr6_strs[0]))
+#define IPADDR_GARBAGE_NETWORK4_STRS_SIZE \
+       (sizeof(ipaddr_garbage_network4_strs) / sizeof(ipaddr_garbage_network4_strs[0]))
+#define IPADDR_GARBAGE_NETWORK6_STRS_SIZE \
+       (sizeof(ipaddr_garbage_network6_strs) / sizeof(ipaddr_garbage_network6_strs[0]))
+#define IPADDR_INVALID_STRS_SIZE \
+       (sizeof(ipaddr_invalid_strs) / sizeof(ipaddr_invalid_strs[0]))
+
+static void
+dump_addr(cmdline_ipaddr_t addr)
+{
+       switch (addr.family) {
+       case AF_INET:
+       {
+               printf(NIPQUAD_FMT " prefixlen=%u\n",
+                               NIPQUAD(addr.addr.ipv4.s_addr), addr.prefixlen);
+               break;
+       }
+       case AF_INET6:
+       {
+               printf(NIP6_FMT " prefixlen=%u\n",
+                               NIP6(addr.addr.ipv6), addr.prefixlen);
+               break;
+       }
+       default:
+               printf("Can't dump: unknown address family.\n");
+               return;
+       }
+}
+
+
+static int
+is_addr_different(cmdline_ipaddr_t addr1, cmdline_ipaddr_t addr2)
+{
+       if (addr1.family != addr2.family)
+               return 1;
+
+       if (addr1.prefixlen != addr2.prefixlen)
+               return 1;
+
+       switch (addr1.family) {
+       /* IPv4 */
+       case AF_INET:
+               if (memcmp(&addr1.addr.ipv4, &addr2.addr.ipv4,
+                               sizeof(struct in_addr)) != 0)
+                       return 1;
+               break;
+       /* IPv6 */
+       case AF_INET6:
+       {
+               if (memcmp(&addr1.addr.ipv6, &addr2.addr.ipv6,
+                               sizeof(struct in6_addr)) != 0)
+                       return 1;
+               break;
+       }
+       /* thing that should not be */
+       default:
+               return -1;
+       }
+       return 0;
+}
+
+static int
+can_parse_addr(unsigned addr_flags, unsigned test_flags)
+{
+       if ((test_flags & addr_flags) == addr_flags) {
+               /* if we are not trying to parse network addresses */
+               if (test_flags < CMDLINE_IPADDR_NETWORK)
+                       return 1;
+               /* if this is a network address */
+               else if (addr_flags & CMDLINE_IPADDR_NETWORK)
+                       return 1;
+       }
+       return 0;
+}
+
+int
+test_parse_ipaddr_valid(void)
+{
+       cmdline_parse_token_ipaddr_t token;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       cmdline_ipaddr_t result;
+       unsigned i;
+       uint8_t flags;
+       int ret;
+
+       /* cover all cases in help */
+       for (flags = 0x1; flags < 0x8; flags++) {
+               token.ipaddr_data.flags = flags;
+
+               memset(buf, 0, sizeof(buf));
+
+               if (cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                               buf, sizeof(buf)) == -1) {
+                       printf("Error: help rejected valid parameters!\n");
+                       return -1;
+               }
+       }
+
+       /* test valid strings */
+       for (i = 0; i < IPADDR_VALID_STRS_SIZE; i++) {
+
+               /* test each valid string against different flags */
+               for (flags = 1; flags < 0x8; flags++) {
+
+                       /* skip bad flag */
+                       if (flags == CMDLINE_IPADDR_NETWORK)
+                               continue;
+
+                       /* clear out everything */
+                       memset(buf, 0, sizeof(buf));
+                       memset(&result, 0, sizeof(result));
+                       memset(&token, 0, sizeof(token));
+
+                       token.ipaddr_data.flags = flags;
+
+                       cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                                       buf, sizeof(buf));
+
+                       ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                       ipaddr_valid_strs[i].str, (void*)&result);
+
+                       /* if should have passed, or should have failed */
+                       if ((ret < 0) ==
+                                       (can_parse_addr(ipaddr_valid_strs[i].flags, flags))) {
+                               printf("Error: unexpected behavior when parsing %s as %s!\n",
+                                               ipaddr_valid_strs[i].str, buf);
+                               printf("Parsed result: ");
+                               dump_addr(result);
+                               printf("Expected result: ");
+                               dump_addr(ipaddr_valid_strs[i].addr);
+                               return -1;
+                       }
+                       if (ret != -1 &&
+                                       is_addr_different(result, ipaddr_valid_strs[i].addr)) {
+                               printf("Error: result mismatch when parsing %s as %s!\n",
+                                               ipaddr_valid_strs[i].str, buf);
+                               printf("Parsed result: ");
+                               dump_addr(result);
+                               printf("Expected result: ");
+                               dump_addr(ipaddr_valid_strs[i].addr);
+                               return -1;
+                       }
+               }
+       }
+
+       /* test garbage ipv4 address strings */
+       for (i = 0; i < IPADDR_GARBAGE_ADDR4_STRS_SIZE; i++) {
+
+               struct in_addr tmp = IPv4_GARBAGE_ADDR;
+
+               /* test each valid string against different flags */
+               for (flags = 1; flags < 0x8; flags++) {
+
+                       /* skip bad flag */
+                       if (flags == CMDLINE_IPADDR_NETWORK)
+                               continue;
+
+                       /* clear out everything */
+                       memset(buf, 0, sizeof(buf));
+                       memset(&result, 0, sizeof(result));
+                       memset(&token, 0, sizeof(token));
+
+                       token.ipaddr_data.flags = flags;
+
+                       cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                                       buf, sizeof(buf));
+
+                       ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                       ipaddr_garbage_addr4_strs[i], (void*)&result);
+
+                       /* if should have passed, or should have failed */
+                       if ((ret < 0) ==
+                                       (can_parse_addr(CMDLINE_IPADDR_V4, flags))) {
+                               printf("Error: unexpected behavior when parsing %s as %s!\n",
+                                               ipaddr_garbage_addr4_strs[i], buf);
+                               return -1;
+                       }
+                       if (ret != -1 &&
+                                       memcmp(&result.addr.ipv4, &tmp, sizeof(tmp))) {
+                               printf("Error: result mismatch when parsing %s as %s!\n",
+                                               ipaddr_garbage_addr4_strs[i], buf);
+                               return -1;
+                       }
+               }
+       }
+
+       /* test garbage ipv6 address strings */
+       for (i = 0; i < IPADDR_GARBAGE_ADDR6_STRS_SIZE; i++) {
+
+               cmdline_ipaddr_t tmp = {.addr = IPv6_GARBAGE_ADDR};
+
+               /* test each valid string against different flags */
+               for (flags = 1; flags < 0x8; flags++) {
+
+                       /* skip bad flag */
+                       if (flags == CMDLINE_IPADDR_NETWORK)
+                               continue;
+
+                       /* clear out everything */
+                       memset(buf, 0, sizeof(buf));
+                       memset(&result, 0, sizeof(result));
+                       memset(&token, 0, sizeof(token));
+
+                       token.ipaddr_data.flags = flags;
+
+                       cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                                       buf, sizeof(buf));
+
+                       ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                       ipaddr_garbage_addr6_strs[i], (void*)&result);
+
+                       /* if should have passed, or should have failed */
+                       if ((ret < 0) ==
+                                       (can_parse_addr(CMDLINE_IPADDR_V6, flags))) {
+                               printf("Error: unexpected behavior when parsing %s as %s!\n",
+                                               ipaddr_garbage_addr6_strs[i], buf);
+                               return -1;
+                       }
+                       if (ret != -1 &&
+                                       memcmp(&result.addr.ipv6, &tmp.addr.ipv6, sizeof(struct in6_addr))) {
+                               printf("Error: result mismatch when parsing %s as %s!\n",
+                                               ipaddr_garbage_addr6_strs[i], buf);
+                               return -1;
+                       }
+               }
+       }
+
+
+       /* test garbage ipv4 network strings */
+       for (i = 0; i < IPADDR_GARBAGE_NETWORK4_STRS_SIZE; i++) {
+
+               struct in_addr tmp = IPv4_GARBAGE_ADDR;
+
+               /* test each valid string against different flags */
+               for (flags = 1; flags < 0x8; flags++) {
+
+                       /* skip bad flag */
+                       if (flags == CMDLINE_IPADDR_NETWORK)
+                               continue;
+
+                       /* clear out everything */
+                       memset(buf, 0, sizeof(buf));
+                       memset(&result, 0, sizeof(result));
+                       memset(&token, 0, sizeof(token));
+
+                       token.ipaddr_data.flags = flags;
+
+                       cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                                       buf, sizeof(buf));
+
+                       ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                       ipaddr_garbage_network4_strs[i], (void*)&result);
+
+                       /* if should have passed, or should have failed */
+                       if ((ret < 0) ==
+                                       (can_parse_addr(CMDLINE_IPADDR_V4 | CMDLINE_IPADDR_NETWORK, flags))) {
+                               printf("Error: unexpected behavior when parsing %s as %s!\n",
+                                               ipaddr_garbage_network4_strs[i], buf);
+                               return -1;
+                       }
+                       if (ret != -1 &&
+                                       memcmp(&result.addr.ipv4, &tmp, sizeof(tmp))) {
+                               printf("Error: result mismatch when parsing %s as %s!\n",
+                                               ipaddr_garbage_network4_strs[i], buf);
+                               return -1;
+                       }
+               }
+       }
+
+       /* test garbage ipv6 address strings */
+       for (i = 0; i < IPADDR_GARBAGE_NETWORK6_STRS_SIZE; i++) {
+
+               cmdline_ipaddr_t tmp = {.addr = IPv6_GARBAGE_ADDR};
+
+               /* test each valid string against different flags */
+               for (flags = 1; flags < 0x8; flags++) {
+
+                       /* skip bad flag */
+                       if (flags == CMDLINE_IPADDR_NETWORK)
+                               continue;
+
+                       /* clear out everything */
+                       memset(buf, 0, sizeof(buf));
+                       memset(&result, 0, sizeof(result));
+                       memset(&token, 0, sizeof(token));
+
+                       token.ipaddr_data.flags = flags;
+
+                       cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                                       buf, sizeof(buf));
+
+                       ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                       ipaddr_garbage_network6_strs[i], (void*)&result);
+
+                       /* if should have passed, or should have failed */
+                       if ((ret < 0) ==
+                                       (can_parse_addr(CMDLINE_IPADDR_V6 | CMDLINE_IPADDR_NETWORK, flags))) {
+                               printf("Error: unexpected behavior when parsing %s as %s!\n",
+                                               ipaddr_garbage_network6_strs[i], buf);
+                               return -1;
+                       }
+                       if (ret != -1 &&
+                                       memcmp(&result.addr.ipv6, &tmp.addr.ipv6, sizeof(struct in6_addr))) {
+                               printf("Error: result mismatch when parsing %s as %s!\n",
+                                               ipaddr_garbage_network6_strs[i], buf);
+                               return -1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int
+test_parse_ipaddr_invalid_data(void)
+{
+       cmdline_parse_token_ipaddr_t token;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       cmdline_ipaddr_t result;
+       unsigned i;
+       uint8_t flags;
+       int ret;
+
+       memset(&result, 0, sizeof(result));
+       
+       /* test invalid strings */
+       for (i = 0; i < IPADDR_INVALID_STRS_SIZE; i++) {
+
+               /* test each valid string against different flags */
+               for (flags = 1; flags < 0x8; flags++) {
+
+                       /* skip bad flag */
+                       if (flags == CMDLINE_IPADDR_NETWORK)
+                               continue;
+
+                       /* clear out everything */
+                       memset(buf, 0, sizeof(buf));
+                       memset(&token, 0, sizeof(token));
+
+                       token.ipaddr_data.flags = flags;
+
+                       cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                       buf, sizeof(buf));
+
+                       ret = cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                                       ipaddr_invalid_strs[i], (void*)&result);
+
+                       if (ret != -1) {
+                               printf("Error: parsing %s as %s succeeded!\n",
+                                               ipaddr_invalid_strs[i], buf);
+                               printf("Parsed result: ");
+                               dump_addr(result);
+                               return -1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int
+test_parse_ipaddr_invalid_param(void)
+{
+       cmdline_parse_token_ipaddr_t token;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       cmdline_ipaddr_t result;
+
+       rte_snprintf(buf, sizeof(buf), "1.2.3.4");
+       token.ipaddr_data.flags = CMDLINE_IPADDR_V4;
+
+       /* null token */
+       if (cmdline_parse_ipaddr(NULL, buf, (void*)&result) != -1) {
+               printf("Error: parser accepted invalid parameters!\n");
+               return -1;
+       }
+       /* null buffer */
+       if (cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                       NULL, (void*)&result) != -1) {
+               printf("Error: parser accepted invalid parameters!\n");
+               return -1;
+       }
+       /* empty buffer */
+       if (cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                       "", (void*)&result) != -1) {
+               printf("Error: parser accepted invalid parameters!\n");
+               return -1;
+       }
+       /* null result */
+       if (cmdline_parse_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                       buf, NULL) == -1) {
+               printf("Error: parser rejected null result!\n");
+               return -1;
+       }
+
+       /* null token */
+       if (cmdline_get_help_ipaddr(NULL, buf, 0) != -1) {
+               printf("Error: help accepted invalid parameters!\n");
+               return -1;
+       }
+       /* null buffer */
+       if (cmdline_get_help_ipaddr((cmdline_parse_token_hdr_t*)&token,
+                       NULL, 0) != -1) {
+               printf("Error: help accepted invalid parameters!\n");
+               return -1;
+       }
+       return 0;
+}
diff --git a/app/test/test_cmdline_lib.c b/app/test/test_cmdline_lib.c
new file mode 100644 (file)
index 0000000..17da9f7
--- /dev/null
@@ -0,0 +1,264 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <termios.h>
+#include <ctype.h>
+#include <sys/queue.h>
+
+#include <cmdline_vt100.h>
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+#include "test_cmdline.h"
+
+/****************************************************************/
+/* static functions required for some tests */
+static void
+valid_buffer(__attribute__((unused))struct rdline *rdl,
+                       __attribute__((unused))const char *buf,
+                       __attribute__((unused)) unsigned int size)
+{
+}
+
+static int
+complete_buffer(__attribute__((unused)) struct rdline *rdl,
+                       __attribute__((unused)) const char *buf,
+                       __attribute__((unused)) char *dstbuf,
+                       __attribute__((unused)) unsigned int dstsize,
+                       __attribute__((unused)) int *state)
+{
+       return 0;
+}
+
+/****************************************************************/
+
+static int
+test_cmdline_parse_fns(void)
+{
+       struct cmdline cl;
+       int i = 0;
+       char dst[CMDLINE_TEST_BUFSIZE];
+
+       if (cmdline_parse(NULL, "buffer") >= 0)
+               goto error;
+       if (cmdline_parse(&cl, NULL) >= 0)
+               goto error;
+
+       if (cmdline_complete(NULL, "buffer", &i, dst, sizeof(dst)) >= 0)
+               goto error;
+       if (cmdline_complete(&cl, NULL, &i, dst, sizeof(dst)) >= 0)
+               goto error;
+       if (cmdline_complete(&cl, "buffer", NULL, dst, sizeof(dst)) >= 0)
+               goto error;
+       if (cmdline_complete(&cl, "buffer", &i, NULL, sizeof(dst)) >= 0)
+               goto error;
+
+       return 0;
+
+error:
+       printf("Error: function accepted null parameter!\n");
+       return -1;
+}
+
+static int
+test_cmdline_rdline_fns(void)
+{
+       struct rdline rdl;
+       rdline_write_char_t *wc = &cmdline_write_char;
+       rdline_validate_t *v = &valid_buffer;
+       rdline_complete_t *c = &complete_buffer;
+
+       if (rdline_init(NULL, wc, v, c) >= 0)
+               goto error;
+       if (rdline_init(&rdl, NULL, v, c) >= 0)
+               goto error;
+       if (rdline_init(&rdl, wc, NULL, c) >= 0)
+               goto error;
+       if (rdline_init(&rdl, wc, v, NULL) >= 0)
+               goto error;
+       if (rdline_char_in(NULL, 0) >= 0)
+               goto error;
+       if (rdline_get_buffer(NULL) != NULL)
+               goto error;
+       if (rdline_add_history(NULL, "history") >= 0)
+               goto error;
+       if (rdline_add_history(&rdl, NULL) >= 0)
+               goto error;
+       if (rdline_get_history_item(NULL, 0) != NULL)
+               goto error;
+
+       /* void functions */
+       rdline_newline(NULL, "prompt");
+       rdline_newline(&rdl, NULL);
+       rdline_stop(NULL);
+       rdline_quit(NULL);
+       rdline_restart(NULL);
+       rdline_redisplay(NULL);
+       rdline_reset(NULL);
+       rdline_clear_history(NULL);
+
+       return 0;
+
+error:
+       printf("Error: function accepted null parameter!\n");
+       return -1;
+}
+
+static int
+test_cmdline_vt100_fns(void)
+{
+       if (vt100_parser(NULL, 0) >= 0) {
+               printf("Error: function accepted null parameter!\n");
+               return -1;
+       }
+
+       /* void functions */
+       vt100_init(NULL);
+
+       return 0;
+}
+
+static int
+test_cmdline_socket_fns(void)
+{
+       cmdline_parse_ctx_t ctx;
+
+       if (cmdline_stdin_new(NULL, "prompt") != NULL)
+               goto error;
+       if (cmdline_stdin_new(&ctx, NULL) != NULL)
+               goto error;
+       if (cmdline_file_new(NULL, "prompt", "/dev/null") != NULL)
+               goto error;
+       if (cmdline_file_new(&ctx, NULL, "/dev/null") != NULL)
+               goto error;
+       if (cmdline_file_new(&ctx, "prompt", NULL) != NULL)
+               goto error;
+       if (cmdline_file_new(&ctx, "prompt", "-/invalid/~/path") != NULL) {
+               printf("Error: succeeded in opening invalid file for reading!");
+               return -1;
+       }
+       if (cmdline_file_new(&ctx, "prompt", "/dev/null") == NULL) {
+               printf("Error: failed to open /dev/null for reading!");
+               return -1;
+       }
+
+       /* void functions */
+       cmdline_stdin_exit(NULL);
+
+       return 0;
+error:
+       printf("Error: function accepted null parameter!\n");
+       return -1;
+}
+
+static int
+test_cmdline_fns(void)
+{
+       cmdline_parse_ctx_t ctx;
+       struct cmdline cl, *tmp;
+
+       memset(&ctx, 0, sizeof(ctx));
+       tmp = cmdline_new(&ctx, "test", -1, -1);
+       if (tmp == NULL)
+               goto error;
+
+       if (cmdline_new(NULL, "prompt", 0, 0) != NULL)
+               goto error;
+       if (cmdline_new(&ctx, NULL, 0, 0) != NULL)
+               goto error;
+       if (cmdline_in(NULL, "buffer", CMDLINE_TEST_BUFSIZE) >= 0)
+               goto error;
+       if (cmdline_in(&cl, NULL, CMDLINE_TEST_BUFSIZE) >= 0)
+               goto error;
+       if (cmdline_write_char(NULL, 0) >= 0)
+               goto error;
+
+       /* void functions */
+       cmdline_set_prompt(NULL, "prompt");
+       cmdline_free(NULL);
+       cmdline_printf(NULL, "format");
+       /* this should fail as stream handles are invalid */
+       cmdline_printf(tmp, "format");
+       cmdline_interact(NULL);
+       cmdline_quit(NULL);
+
+       /* check if void calls change anything when they should fail */
+       cl = *tmp;
+
+       cmdline_printf(&cl, NULL);
+       if (memcmp(&cl, tmp, sizeof(cl))) goto mismatch;
+       cmdline_set_prompt(&cl, NULL);
+       if (memcmp(&cl, tmp, sizeof(cl))) goto mismatch;
+       cmdline_in(&cl, NULL, CMDLINE_TEST_BUFSIZE);
+       if (memcmp(&cl, tmp, sizeof(cl))) goto mismatch;
+
+       cmdline_free(tmp);
+
+       return 0;
+
+error:
+       printf("Error: function accepted null parameter!\n");
+       return -1;
+mismatch:
+       printf("Error: data changed!\n");
+       return -1;
+}
+
+/* test library functions. the point of these tests is not so much to test
+ * functions' behaviour as it is to make sure there are no segfaults if
+ * they are called with invalid parameters.
+ */
+int
+test_cmdline_lib(void)
+{
+       if (test_cmdline_parse_fns() < 0)
+               return -1;
+       if (test_cmdline_rdline_fns() < 0)
+               return -1;
+       if (test_cmdline_vt100_fns() < 0)
+               return -1;
+       if (test_cmdline_socket_fns() < 0)
+               return -1;
+       if (test_cmdline_fns() < 0)
+               return -1;
+       return 0;
+}
diff --git a/app/test/test_cmdline_num.c b/app/test/test_cmdline_num.c
new file mode 100644 (file)
index 0000000..d570f89
--- /dev/null
@@ -0,0 +1,624 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#include <string.h>
+#include <inttypes.h>
+
+#include <rte_string_fns.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+
+#include "test_cmdline.h"
+
+struct num_unsigned_str {
+       const char * str;
+       uint64_t result;
+};
+
+struct num_signed_str {
+       const char * str;
+       int64_t result;
+};
+
+const struct num_unsigned_str num_valid_positive_strs[] = {
+               /* decimal positive */
+               {"0", 0 },
+               {"127", INT8_MAX },
+               {"128", INT8_MAX + 1 },
+               {"255", UINT8_MAX },
+               {"256", UINT8_MAX + 1 },
+               {"32767", INT16_MAX },
+               {"32768", INT16_MAX + 1 },
+               {"65535", UINT16_MAX },
+               {"65536", UINT16_MAX + 1 },
+               {"2147483647", INT32_MAX },
+               {"2147483648", INT32_MAX + 1U },
+               {"4294967295", UINT32_MAX },
+               {"4294967296", UINT32_MAX + 1ULL },
+               {"9223372036854775807", INT64_MAX },
+               {"9223372036854775808", INT64_MAX + 1ULL},
+               {"18446744073709551615", UINT64_MAX },
+               /* hexadecimal (no leading zeroes) */
+               {"0x0", 0 },
+               {"0x7F", INT8_MAX },
+               {"0x80", INT8_MAX + 1 },
+               {"0xFF", UINT8_MAX },
+               {"0x100", UINT8_MAX + 1 },
+               {"0x7FFF", INT16_MAX },
+               {"0x8000", INT16_MAX + 1 },
+               {"0xFFFF", UINT16_MAX },
+               {"0x10000", UINT16_MAX + 1 },
+               {"0x7FFFFFFF", INT32_MAX },
+               {"0x80000000", INT32_MAX + 1U },
+               {"0xFFFFFFFF", UINT32_MAX },
+               {"0x100000000", UINT32_MAX + 1ULL },
+               {"0x7FFFFFFFFFFFFFFF", INT64_MAX },
+               {"0x8000000000000000", INT64_MAX + 1ULL},
+               {"0xFFFFFFFFFFFFFFFF", UINT64_MAX },
+               /* hexadecimal (with leading zeroes) */
+               {"0x00", 0 },
+               {"0x7F", INT8_MAX },
+               {"0x80", INT8_MAX + 1 },
+               {"0xFF", UINT8_MAX },
+               {"0x0100", UINT8_MAX + 1 },
+               {"0x7FFF", INT16_MAX },
+               {"0x8000", INT16_MAX + 1 },
+               {"0xFFFF", UINT16_MAX },
+               {"0x00010000", UINT16_MAX + 1 },
+               {"0x7FFFFFFF", INT32_MAX },
+               {"0x80000000", INT32_MAX + 1U },
+               {"0xFFFFFFFF", UINT32_MAX },
+               {"0x0000000100000000", UINT32_MAX + 1ULL },
+               {"0x7FFFFFFFFFFFFFFF", INT64_MAX },
+               {"0x8000000000000000", INT64_MAX + 1ULL},
+               {"0xFFFFFFFFFFFFFFFF", UINT64_MAX },
+               /* check all characters */
+               {"0x1234567890ABCDEF", 0x1234567890ABCDEFULL },
+               {"0x1234567890abcdef", 0x1234567890ABCDEFULL },
+               /* binary (no leading zeroes) */
+               {"0b0", 0 },
+               {"0b1111111", INT8_MAX },
+               {"0b10000000", INT8_MAX + 1 },
+               {"0b11111111", UINT8_MAX },
+               {"0b100000000", UINT8_MAX + 1 },
+               {"0b111111111111111", INT16_MAX },
+               {"0b1000000000000000", INT16_MAX + 1 },
+               {"0b1111111111111111", UINT16_MAX },
+               {"0b10000000000000000", UINT16_MAX + 1 },
+               {"0b1111111111111111111111111111111", INT32_MAX },
+               {"0b10000000000000000000000000000000", INT32_MAX + 1U },
+               {"0b11111111111111111111111111111111", UINT32_MAX },
+               {"0b100000000000000000000000000000000", UINT32_MAX + 1ULL },
+               {"0b111111111111111111111111111111111111111111111111111111111111111",
+                               INT64_MAX },
+               {"0b1000000000000000000000000000000000000000000000000000000000000000",
+                               INT64_MAX + 1ULL},
+               {"0b1111111111111111111111111111111111111111111111111111111111111111",
+                               UINT64_MAX },
+               /* binary (with leading zeroes) */
+               {"0b01111111", INT8_MAX },
+               {"0b0000000100000000", UINT8_MAX + 1 },
+               {"0b0111111111111111", INT16_MAX },
+               {"0b00000000000000010000000000000000", UINT16_MAX + 1 },
+               {"0b01111111111111111111111111111111", INT32_MAX },
+               {"0b0000000000000000000000000000000100000000000000000000000000000000",
+                               UINT32_MAX + 1ULL },
+               {"0b0111111111111111111111111111111111111111111111111111111111111111",
+                               INT64_MAX },
+               /* octal */
+               {"00", 0 },
+               {"0177", INT8_MAX },
+               {"0200", INT8_MAX + 1 },
+               {"0377", UINT8_MAX },
+               {"0400", UINT8_MAX + 1 },
+               {"077777", INT16_MAX },
+               {"0100000", INT16_MAX + 1 },
+               {"0177777", UINT16_MAX },
+               {"0200000", UINT16_MAX + 1 },
+               {"017777777777", INT32_MAX },
+               {"020000000000", INT32_MAX + 1U },
+               {"037777777777", UINT32_MAX },
+               {"040000000000", UINT32_MAX + 1ULL },
+               {"0777777777777777777777", INT64_MAX },
+               {"01000000000000000000000", INT64_MAX + 1ULL},
+               {"01777777777777777777777", UINT64_MAX },
+               /* check all numbers */
+               {"012345670", 012345670 },
+               {"076543210", 076543210 },
+};
+
+const struct num_signed_str num_valid_negative_strs[] = {
+               /* deciman negative */
+               {"-128", INT8_MIN },
+               {"-129", INT8_MIN - 1 },
+               {"-32768", INT16_MIN },
+               {"-32769", INT16_MIN - 1 },
+               {"-2147483648", INT32_MIN },
+               {"-2147483649", INT32_MIN - 1LL },
+               {"-9223372036854775808", INT64_MIN },
+};
+
+const struct num_unsigned_str num_garbage_positive_strs[] = {
+               /* valid strings with garbage on the end, should still be valid */
+               /* decimal */
+               {"9223372036854775807\0garbage", INT64_MAX },
+               {"9223372036854775807\tgarbage", INT64_MAX },
+               {"9223372036854775807\rgarbage", INT64_MAX },
+               {"9223372036854775807\ngarbage", INT64_MAX },
+               {"9223372036854775807#garbage", INT64_MAX },
+               {"9223372036854775807 garbage", INT64_MAX },
+               /* hex */
+               {"0x7FFFFFFFFFFFFFFF\0garbage", INT64_MAX },
+               {"0x7FFFFFFFFFFFFFFF\tgarbage", INT64_MAX },
+               {"0x7FFFFFFFFFFFFFFF\rgarbage", INT64_MAX },
+               {"0x7FFFFFFFFFFFFFFF\ngarbage", INT64_MAX },
+               {"0x7FFFFFFFFFFFFFFF#garbage", INT64_MAX },
+               {"0x7FFFFFFFFFFFFFFF garbage", INT64_MAX },
+               /* binary */
+               {"0b1111111111111111111111111111111\0garbage", INT32_MAX },
+               {"0b1111111111111111111111111111111\rgarbage", INT32_MAX },
+               {"0b1111111111111111111111111111111\tgarbage", INT32_MAX },
+               {"0b1111111111111111111111111111111\ngarbage", INT32_MAX },
+               {"0b1111111111111111111111111111111#garbage", INT32_MAX },
+               {"0b1111111111111111111111111111111 garbage", INT32_MAX },
+               /* octal */
+               {"01777777777777777777777\0garbage", UINT64_MAX },
+               {"01777777777777777777777\rgarbage", UINT64_MAX },
+               {"01777777777777777777777\tgarbage", UINT64_MAX },
+               {"01777777777777777777777\ngarbage", UINT64_MAX },
+               {"01777777777777777777777#garbage", UINT64_MAX },
+               {"01777777777777777777777 garbage", UINT64_MAX },
+};
+
+const struct num_signed_str num_garbage_negative_strs[] = {
+               /* valid strings with garbage on the end, should still be valid */
+               {"-9223372036854775808\0garbage", INT64_MIN },
+               {"-9223372036854775808\rgarbage", INT64_MIN },
+               {"-9223372036854775808\tgarbage", INT64_MIN },
+               {"-9223372036854775808\ngarbage", INT64_MIN },
+               {"-9223372036854775808#garbage", INT64_MIN },
+               {"-9223372036854775808 garbage", INT64_MIN },
+};
+
+const char * num_invalid_strs[] = {
+               "18446744073709551616", /* out of range unsigned */
+               "-9223372036854775809", /* out of range negative signed */
+               "0x10000000000000000", /* out of range hex */
+               /* out of range binary */
+               "0b10000000000000000000000000000000000000000000000000000000000000000",
+               "020000000000000000000000", /* out of range octal */
+               /* wrong chars */
+               "0123456239",
+               "0x1234580AGE",
+               "0b0111010101g001",
+               "0b01110101017001",
+               /* false negative numbers */
+               "-12345F623",
+               "-0x1234580A",
+               "-0b0111010101",
+               /* too long (128+ chars) */
+               "0b1111000011110000111100001111000011110000111100001111000011110000"
+                 "1111000011110000111100001111000011110000111100001111000011110000",
+               "1E3",
+               "0A",
+               "-B",
+               "+4",
+               "1.23G",
+               "",
+               " ",
+               "#",
+               "\r",
+               "\t",
+               "\n",
+               "\0",
+};
+
+#define NUM_POSITIVE_STRS_SIZE \
+       (sizeof(num_valid_positive_strs) / sizeof(num_valid_positive_strs[0]))
+#define NUM_NEGATIVE_STRS_SIZE \
+       (sizeof(num_valid_negative_strs) / sizeof(num_valid_negative_strs[0]))
+#define NUM_POSITIVE_GARBAGE_STRS_SIZE \
+       (sizeof(num_garbage_positive_strs) / sizeof(num_garbage_positive_strs[0]))
+#define NUM_NEGATIVE_GARBAGE_STRS_SIZE \
+       (sizeof(num_garbage_negative_strs) / sizeof(num_garbage_negative_strs[0]))
+#define NUM_INVALID_STRS_SIZE \
+       (sizeof(num_invalid_strs) / sizeof(num_invalid_strs[0]))
+
+
+
+static int
+can_parse_unsigned(uint64_t expected_result, enum cmdline_numtype type)
+{
+       switch (type) {
+       case UINT8:
+               if (expected_result > UINT8_MAX)
+                       return 0;
+               break;
+       case UINT16:
+               if (expected_result > UINT16_MAX)
+                       return 0;
+               break;
+       case UINT32:
+               if (expected_result > UINT32_MAX)
+                       return 0;
+               break;
+       case INT8:
+               if (expected_result > INT8_MAX)
+                       return 0;
+               break;
+       case INT16:
+               if (expected_result > INT16_MAX)
+                       return 0;
+               break;
+       case INT32:
+               if (expected_result > INT32_MAX)
+                       return 0;
+               break;
+       case INT64:
+               if (expected_result > INT64_MAX)
+                       return 0;
+               break;
+       default:
+               return 1;
+       }
+       return 1;
+}
+
+static int
+can_parse_signed(int64_t expected_result, enum cmdline_numtype type)
+{
+       switch (type) {
+       case UINT8:
+               if (expected_result > UINT8_MAX || expected_result < 0)
+                       return 0;
+               break;
+       case UINT16:
+               if (expected_result > UINT16_MAX || expected_result < 0)
+                       return 0;
+               break;
+       case UINT32:
+               if (expected_result > UINT32_MAX || expected_result < 0)
+                       return 0;
+               break;
+       case UINT64:
+               if (expected_result < 0)
+                       return 0;
+       case INT8:
+               if (expected_result > INT8_MAX || expected_result < INT8_MIN)
+                       return 0;
+               break;
+       case INT16:
+               if (expected_result > INT16_MAX || expected_result < INT16_MIN)
+                       return 0;
+               break;
+       case INT32:
+               if (expected_result > INT32_MAX || expected_result < INT32_MIN)
+                       return 0;
+               break;
+       default:
+               return 1;
+       }
+       return 1;
+}
+
+/* test invalid parameters */
+int
+test_parse_num_invalid_param(void)
+{
+       char buf[CMDLINE_TEST_BUFSIZE];
+       uint32_t result;
+       cmdline_parse_token_num_t token;
+       int ret = 0;
+
+       /* set up a token */
+       token.num_data.type = UINT32;
+
+       /* copy string to buffer */
+       rte_snprintf(buf, sizeof(buf), "%s",
+                       num_valid_positive_strs[0].str);
+
+       /* try all null */
+       ret = cmdline_parse_num(NULL, NULL, NULL);
+       if (ret != -1) {
+               printf("Error: parser accepted null parameters!\n");
+               return -1;
+       }
+
+       /* try null token */
+       ret = cmdline_parse_num(NULL, buf, (void*)&result);
+       if (ret != -1) {
+               printf("Error: parser accepted null token!\n");
+               return -1;
+       }
+
+       /* try null buf */
+       ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, NULL,
+                       (void*)&result);
+       if (ret != -1) {
+               printf("Error: parser accepted null string!\n");
+               return -1;
+       }
+
+       /* try null result */
+       ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token, buf, NULL);
+       if (ret == -1) {
+               printf("Error: parser rejected null result!\n");
+               return -1;
+       }
+
+       /* test help function */
+       memset(&buf, 0, sizeof(buf));
+
+       /* try all null */
+       ret = cmdline_get_help_num(NULL, NULL, 0);
+       if (ret != -1) {
+               printf("Error: help function accepted null parameters!\n");
+               return -1;
+       }
+
+       /* try null token */
+       ret = cmdline_get_help_num(NULL, buf, sizeof(buf));
+       if (ret != -1) {
+               printf("Error: help function accepted null token!\n");
+               return -1;
+       }
+
+       /* try null buf */
+       ret = cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, NULL, sizeof(buf));
+       if (ret != -1) {
+               printf("Error: help function accepted null buffer!\n");
+               return -1;
+       }
+
+       /* coverage! */
+       ret = cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token, buf, sizeof(buf));
+       if (ret < 0) {
+               printf("Error: help function failed with valid parameters!\n");
+               return -1;
+       }
+
+       return 0;
+}
+/* test valid parameters but invalid data */
+int
+test_parse_num_invalid_data(void)
+{
+       enum cmdline_numtype type;
+       int ret = 0;
+       unsigned i;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       uint64_t result; /* pick largest buffer */
+       cmdline_parse_token_num_t token;
+
+       /* cycle through all possible parsed types */
+       for (type = UINT8; type <= INT64; type++) {
+               token.num_data.type = type;
+
+               /* test full strings */
+               for (i = 0; i < NUM_INVALID_STRS_SIZE; i++) {
+
+                       memset(&result, 0, sizeof(uint64_t));
+                       memset(&buf, 0, sizeof(buf));
+
+                       ret = cmdline_parse_num((cmdline_parse_token_hdr_t*)&token,
+                                       num_invalid_strs[i], (void*)&result);
+                       if (ret != -1) {
+                               /* get some info about what we are trying to parse */
+                               cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+                                               buf, sizeof(buf));
+
+                               printf("Error: parsing %s as %s succeeded!\n",
+                                               num_invalid_strs[i], buf);
+                               return -1;
+                       }
+               }
+       }
+       return 0;
+}
+
+/* test valid parameters and data */
+int
+test_parse_num_valid(void)
+{
+       int ret = 0;
+       enum cmdline_numtype type;
+       unsigned i;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       uint64_t result;
+       cmdline_parse_token_num_t token;
+
+       /** valid strings **/
+
+       /* cycle through all possible parsed types */
+       for (type = UINT8; type <= INT64; type++) {
+               token.num_data.type = type;
+
+               /* test positive strings */
+               for (i = 0; i < NUM_POSITIVE_STRS_SIZE; i++) {
+                       result = 0;
+                       memset(&buf, 0, sizeof(buf));
+
+                       cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+                                       buf, sizeof(buf));
+
+                       ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, num_valid_positive_strs[i].str,
+                                       (void*)&result);
+
+                       /* if it should have passed but didn't, or if it should have failed but didn't */
+                       if ((ret < 0) == (can_parse_unsigned(num_valid_positive_strs[i].result, type) > 0)) {
+                               printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
+                                               num_valid_positive_strs[i].str, buf);
+                               return -1;
+                       }
+                       /* check if result matches what it should have matched
+                        * since unsigned numbers don't care about number of bits, we can just convert
+                        * everything to uint64_t without any worries. */
+                       if (ret > 0 && num_valid_positive_strs[i].result != result) {
+                               printf("Error: parsing %s as %s failed: result mismatch!\n",
+                                               num_valid_positive_strs[i].str, buf);
+                               return -1;
+                       }
+               }
+
+               /* test negative strings */
+               for (i = 0; i < NUM_NEGATIVE_STRS_SIZE; i++) {
+                       result = 0;
+                       memset(&buf, 0, sizeof(buf));
+
+                       cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+                                       buf, sizeof(buf));
+
+                       ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, num_valid_negative_strs[i].str,
+                                       (void*)&result);
+
+                       /* if it should have passed but didn't, or if it should have failed but didn't */
+                       if ((ret < 0) == (can_parse_signed(num_valid_negative_strs[i].result, type) > 0)) {
+                               printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
+                                               num_valid_negative_strs[i].str, buf);
+                               return -1;
+                       }
+                       /* check if result matches what it should have matched
+                        * the result is signed in this case, so we have to account for that */
+                       if (ret > 0) {
+                               /* detect negative */
+                               switch (type) {
+                               case INT8:
+                                       result = (int8_t) result;
+                                       break;
+                               case INT16:
+                                       result = (int16_t) result;
+                                       break;
+                               case INT32:
+                                       result = (int32_t) result;
+                                       break;
+                               default:
+                                       break;
+                               }
+                               if (num_valid_negative_strs[i].result == (int64_t) result)
+                                       continue;
+                               printf("Error: parsing %s as %s failed: result mismatch!\n",
+                                               num_valid_negative_strs[i].str, buf);
+                               return -1;
+                       }
+               }
+       }
+
+       /** garbage strings **/
+
+       /* cycle through all possible parsed types */
+       for (type = UINT8; type <= INT64; type++) {
+               token.num_data.type = type;
+
+               /* test positive garbage strings */
+               for (i = 0; i < NUM_POSITIVE_GARBAGE_STRS_SIZE; i++) {
+                       result = 0;
+                       memset(&buf, 0, sizeof(buf));
+
+                       cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+                                       buf, sizeof(buf));
+
+                       ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, num_garbage_positive_strs[i].str,
+                                       (void*)&result);
+
+                       /* if it should have passed but didn't, or if it should have failed but didn't */
+                       if ((ret < 0) == (can_parse_unsigned(num_garbage_positive_strs[i].result, type) > 0)) {
+                               printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
+                                               num_garbage_positive_strs[i].str, buf);
+                               return -1;
+                       }
+                       /* check if result matches what it should have matched
+                        * since unsigned numbers don't care about number of bits, we can just convert
+                        * everything to uint64_t without any worries. */
+                       if (ret > 0 && num_garbage_positive_strs[i].result != result) {
+                               printf("Error: parsing %s as %s failed: result mismatch!\n",
+                                               num_garbage_positive_strs[i].str, buf);
+                               return -1;
+                       }
+               }
+
+               /* test negative strings */
+               for (i = 0; i < NUM_NEGATIVE_GARBAGE_STRS_SIZE; i++) {
+                       result = 0;
+                       memset(&buf, 0, sizeof(buf));
+
+                       cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+                                       buf, sizeof(buf));
+
+                       ret = cmdline_parse_num((cmdline_parse_token_hdr_t*) &token, num_garbage_negative_strs[i].str,
+                                       (void*)&result);
+
+                       /* if it should have passed but didn't, or if it should have failed but didn't */
+                       if ((ret < 0) == (can_parse_signed(num_garbage_negative_strs[i].result, type) > 0)) {
+                               printf("Error: parser behaves unexpectedly when parsing %s as %s!\n",
+                                               num_garbage_negative_strs[i].str, buf);
+                               return -1;
+                       }
+                       /* check if result matches what it should have matched
+                        * the result is signed in this case, so we have to account for that */
+                       if (ret > 0) {
+                               /* detect negative */
+                               switch (type) {
+                               case INT8:
+                                       if (result & (INT8_MAX + 1))
+                                               result |= 0xFFFFFFFFFFFFFF00ULL;
+                                       break;
+                               case INT16:
+                                       if (result & (INT16_MAX + 1))
+                                               result |= 0xFFFFFFFFFFFF0000ULL;
+                                       break;
+                               case INT32:
+                                       if (result & (INT32_MAX + 1ULL))
+                                               result |= 0xFFFFFFFF00000000ULL;
+                                       break;
+                               default:
+                                       break;
+                               }
+                               if (num_garbage_negative_strs[i].result == (int64_t) result)
+                                       continue;
+                               printf("Error: parsing %s as %s failed: result mismatch!\n",
+                                               num_garbage_negative_strs[i].str, buf);
+                               return -1;
+                       }
+               }
+       }
+
+       memset(&buf, 0, sizeof(buf));
+
+       /* coverage! */
+       cmdline_get_help_num((cmdline_parse_token_hdr_t*)&token,
+                       buf, sizeof(buf));
+
+       return 0;
+}
diff --git a/app/test/test_cmdline_portlist.c b/app/test/test_cmdline_portlist.c
new file mode 100644 (file)
index 0000000..01bd3e6
--- /dev/null
@@ -0,0 +1,257 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_portlist.h>
+
+#include "test_cmdline.h"
+
+struct portlist_str {
+       const char * str;
+       uint32_t portmap;
+};
+
+/* valid strings */
+const struct portlist_str portlist_valid_strs[] = {
+               {"0", 0x1U },
+               {"0-10", 0x7FFU},
+               {"10-20", 0x1FFC00U},
+               {"all", UINT32_MAX},
+               {"0,1,2,3", 0xFU},
+               {"0,1-5", 0x3FU},
+               {"0,0,0", 0x1U},
+               {"31,0-10,15", 0x800087FFU},
+               {"0000", 0x1U},
+               {"00,01,02,03", 0xFU},
+               {"000,001,002,003", 0xFU},
+};
+
+/* valid strings but with garbage at the end.
+ * these strings should still be valid because parser checks
+ * for end of token, which is either a space/tab, a newline/return,
+ * or a hash sign.
+ */
+
+const char * portlist_garbage_strs[] = {
+               "0-31 garbage",
+               "0-31#garbage",
+               "0-31\0garbage",
+               "0-31\ngarbage",
+               "0-31\rgarbage",
+               "0-31\tgarbage",
+               "0,1,2,3-31 garbage",
+               "0,1,2,3-31#garbage",
+               "0,1,2,3-31\0garbage",
+               "0,1,2,3-31\ngarbage",
+               "0,1,2,3-31\rgarbage",
+               "0,1,2,3-31\tgarbage",
+               "all garbage",
+               "all#garbage",
+               "all\0garbage",
+               "all\ngarbage",
+               "all\rgarbage",
+               "all\tgarbage",
+};
+
+/* invalid strings */
+const char * portlist_invalid_strs[] = {
+               /* valid syntax, invalid chars */
+               "A-B",
+               "0-S",
+               "1,2,3,4,Q",
+               "A-4,3-15",
+               "0-31invalid",
+               /* valid chars, invalid syntax */
+               "1, 2",
+               "1- 4",
+               ",2",
+               ",2 ",
+               "-1, 4",
+               "5-1",
+               "2-",
+               /* misc */
+               "-"
+               "a",
+               "A",
+               ",",
+               "#",
+               " ",
+               "\0",
+               "",
+               /* too long */
+               "0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,"
+               "0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,2",
+};
+
+#define PORTLIST_VALID_STRS_SIZE \
+       (sizeof(portlist_valid_strs) / sizeof(portlist_valid_strs[0]))
+#define PORTLIST_GARBAGE_STRS_SIZE \
+       (sizeof(portlist_garbage_strs) / sizeof(portlist_garbage_strs[0]))
+#define PORTLIST_INVALID_STRS_SIZE \
+       (sizeof(portlist_invalid_strs) / sizeof(portlist_invalid_strs[0]))
+
+
+
+
+/* test invalid parameters */
+int
+test_parse_portlist_invalid_param(void)
+{
+       cmdline_portlist_t result;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       int ret;
+
+       memset(&buf, 0, sizeof(buf));
+       memset(&result, 0, sizeof(cmdline_portlist_t));
+
+       /* try all null */
+       ret = cmdline_parse_portlist(NULL, NULL, NULL);
+       if (ret != -1) {
+               printf("Error: parser accepted null parameters!\n");
+               return -1;
+       }
+
+       /* try null buf */
+       ret = cmdline_parse_portlist(NULL, NULL, (void*)&result);
+       if (ret != -1) {
+               printf("Error: parser accepted null string!\n");
+               return -1;
+       }
+
+       /* try null result */
+       ret = cmdline_parse_portlist(NULL, portlist_valid_strs[0].str, NULL);
+       if (ret == -1) {
+               printf("Error: parser rejected null result!\n");
+               return -1;
+       }
+
+       /* token is not used in ether_parse anyway so there's no point in
+        * testing it */
+
+       /* test help function */
+
+       /* try null buf */
+       ret = cmdline_get_help_portlist(NULL, NULL, sizeof(buf));
+       if (ret != -1) {
+               printf("Error: help function accepted null buffer!\n");
+               return -1;
+       }
+
+       /* coverage! */
+       ret = cmdline_get_help_portlist(NULL, buf, sizeof(buf));
+       if (ret < 0) {
+               printf("Error: help function failed with valid parameters!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* test valid parameters but invalid data */
+int
+test_parse_portlist_invalid_data(void)
+{
+       int ret = 0;
+       unsigned i;
+       cmdline_portlist_t result;
+
+       /* test invalid strings */
+       for (i = 0; i < PORTLIST_INVALID_STRS_SIZE; i++) {
+
+               memset(&result, 0, sizeof(cmdline_portlist_t));
+
+               ret = cmdline_parse_portlist(NULL, portlist_invalid_strs[i],
+                               (void*)&result);
+               if (ret != -1) {
+                       printf("Error: parsing %s succeeded!\n",
+                                       portlist_invalid_strs[i]);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/* test valid parameters and data */
+int
+test_parse_portlist_valid(void)
+{
+       int ret = 0;
+       unsigned i;
+       cmdline_portlist_t result;
+
+       /* test full strings */
+       for (i = 0; i < PORTLIST_VALID_STRS_SIZE; i++) {
+
+               memset(&result, 0, sizeof(cmdline_portlist_t));
+
+               ret = cmdline_parse_portlist(NULL, portlist_valid_strs[i].str,
+                               (void*)&result);
+               if (ret < 0) {
+                       printf("Error: parsing %s failed!\n",
+                                       portlist_valid_strs[i].str);
+                       return -1;
+               }
+               if (result.map != portlist_valid_strs[i].portmap) {
+                       printf("Error: parsing %s failed: map mismatch!\n",
+                                       portlist_valid_strs[i].str);
+                       return -1;
+               }
+       }
+
+       /* test garbage strings */
+       for (i = 0; i < PORTLIST_GARBAGE_STRS_SIZE; i++) {
+
+               memset(&result, 0, sizeof(cmdline_portlist_t));
+
+               ret = cmdline_parse_portlist(NULL, portlist_garbage_strs[i],
+                               (void*)&result);
+               if (ret < 0) {
+                       printf("Error: parsing %s failed!\n",
+                                       portlist_garbage_strs[i]);
+                       return -1;
+               }
+               if (result.map != UINT32_MAX) {
+                       printf("Error: parsing %s failed: map mismatch!\n",
+                                       portlist_garbage_strs[i]);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
diff --git a/app/test/test_cmdline_string.c b/app/test/test_cmdline_string.c
new file mode 100644 (file)
index 0000000..773d693
--- /dev/null
@@ -0,0 +1,402 @@
+/*-
+ *   BSD LICENSE
+ * 
+ *   Copyright(c) 2010-2012 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.
+ * 
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <rte_string_fns.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_string.h>
+
+#include "test_cmdline.h"
+
+/* structures needed to run tests */
+
+struct string_elt_str {
+       const char * str;       /* parsed string */
+       const char * result;    /* expected string */
+       int idx;        /* position at which result is expected to be */
+};
+
+struct string_elt_str string_elt_strs[] = {
+               {"one#two#three", "three", 2},
+               {"one#two with spaces#three", "three", 2},
+               {"one#two\twith\ttabs#three", "three", 2},
+               {"one#two\rwith\rreturns#three", "three", 2},
+               {"one#two\nwith\nnewlines#three", "three", 2},
+               {"one#two#three", "one", 0},
+               {"one#two#three", "two", 1},
+               {"one#two\0three", "two", 1},
+               {"one#two with spaces#three", "two with spaces", 1},
+               {"one#two\twith\ttabs#three", "two\twith\ttabs", 1},
+               {"one#two\rwith\rreturns#three", "two\rwith\rreturns", 1},
+               {"one#two\nwith\nnewlines#three", "two\nwith\nnewlines", 1},
+};
+
+
+
+struct string_nb_str {
+       const char * str;       /* parsed string */
+       int nb_strs;    /* expected number of strings in str */
+};
+
+struct string_nb_str string_nb_strs[] = {
+               {"one#two#three", 3},
+               {"one", 1},
+               {"one# \t two \r # three \n #four", 4},
+};
+
+
+
+struct string_parse_str {
+       const char * str;       /* parsed string */
+       const char * fixed_str; /* parsing mode (any, fixed or multi) */
+       const char * result;    /* expected result */
+};
+
+struct string_parse_str string_parse_strs[] = {
+               {"one", NULL, "one"},   /* any string */
+               {"two", "one#two#three", "two"},        /* multiple choice string */
+               {"three", "three", "three"},    /* fixed string */
+               {"three", "one#two with\rgarbage\tcharacters\n#three", "three"},
+               {"two with\rgarbage\tcharacters\n",
+                               "one#two with\rgarbage\tcharacters\n#three",
+                               "two with\rgarbage\tcharacters\n"},
+};
+
+
+
+struct string_invalid_str {
+       const char * str;       /* parsed string */
+       const char * fixed_str; /* parsing mode (any, fixed or multi) */
+};
+
+struct string_invalid_str string_invalid_strs[] = {
+               {"invalid", "one"},     /* fixed string */
+               {"invalid", "one#two#three"},   /* multiple choice string */
+               {"invalid", "invalidone"},      /* string that starts the same */
+               {"invalidone", "invalid"},      /* string that starts the same */
+               {"toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!"
+                "toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!"
+                "toolong!!!", NULL },
+               {"toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!"
+                "toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!"
+                "toolong!!!", "fixed" },
+               {"toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!"
+                "toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!"
+                "toolong!!!", "multi#choice#string" },
+               {"invalid",
+                "toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!"
+                "toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!toolong!!!"
+                "toolong!!!" },
+                {"invalid", ""},
+                {"", "invalid"}
+};
+
+
+
+const char * string_help_strs[] = {
+               NULL,
+               "fixed_str",
+               "multi#str",
+};
+
+
+
+#define STRING_PARSE_STRS_SIZE \
+       (sizeof(string_parse_strs) / sizeof(string_parse_strs[0]))
+#define STRING_HELP_STRS_SIZE \
+       (sizeof(string_help_strs) / sizeof(string_help_strs[0]))
+#define STRING_ELT_STRS_SIZE \
+       (sizeof(string_elt_strs) / sizeof(string_elt_strs[0]))
+#define STRING_NB_STRS_SIZE \
+       (sizeof(string_nb_strs) / sizeof(string_nb_strs[0]))
+#define STRING_INVALID_STRS_SIZE \
+       (sizeof(string_invalid_strs) / sizeof(string_invalid_strs[0]))
+
+#define SMALL_BUF 8
+
+/* test invalid parameters */
+int
+test_parse_string_invalid_param(void)
+{
+       cmdline_parse_token_string_t token;
+       int result;
+       char buf[CMDLINE_TEST_BUFSIZE];
+
+       memset(&token, 0, sizeof(token));
+
+       rte_snprintf(buf, sizeof(buf), "buffer");
+
+       /* test null token */
+       if (cmdline_get_help_string(
+               NULL, buf, 0) != -1) {
+               printf("Error: function accepted null token!\n");
+               return -1;
+       }
+       if (cmdline_complete_get_elt_string(
+                       NULL, 0, buf, 0) != -1) {
+               printf("Error: function accepted null token!\n");
+               return -1;
+       }
+       if (cmdline_complete_get_nb_string(NULL) != -1) {
+               printf("Error: function accepted null token!\n");
+               return -1;
+       }
+       if (cmdline_parse_string(NULL, buf, NULL) != -1) {
+               printf("Error: function accepted null token!\n");
+               return -1;
+       }
+       /* test null buffer */
+       if (cmdline_complete_get_elt_string(
+                       (cmdline_parse_token_hdr_t*)&token, 0, NULL, 0) != -1) {
+               printf("Error: function accepted null buffer!\n");
+               return -1;
+       }
+       if (cmdline_parse_string(
+                       (cmdline_parse_token_hdr_t*)&token, NULL, (void*)&result) != -1) {
+               printf("Error: function accepted null buffer!\n");
+               return -1;
+       }
+       if (cmdline_get_help_string(
+                       (cmdline_parse_token_hdr_t*)&token, NULL, 0) != -1) {
+               printf("Error: function accepted null buffer!\n");
+               return -1;
+       }
+       /* test null result */
+       if (cmdline_parse_string(
+                       (cmdline_parse_token_hdr_t*)&token, buf, NULL) == -1) {
+               printf("Error: function rejected null result!\n");
+               return -1;
+       }
+       /* test negative index */
+       if (cmdline_complete_get_elt_string(
+                       (cmdline_parse_token_hdr_t*)&token, -1, buf, 0) != -1) {
+               printf("Error: function accepted negative index!\n");
+               return -1;
+       }
+       return 0;
+}
+
+/* test valid parameters but invalid data */
+int
+test_parse_string_invalid_data(void)
+{
+       cmdline_parse_token_string_t token;
+       cmdline_parse_token_string_t help_token;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       char help_str[CMDLINE_TEST_BUFSIZE];
+       char small_buf[SMALL_BUF];
+       unsigned i;
+
+       /* test parsing invalid strings */
+       for (i = 0; i < STRING_INVALID_STRS_SIZE; i++) {
+               memset(&token, 0, sizeof(token));
+               memset(buf, 0, sizeof(buf));
+
+               /* prepare test token data */
+               token.string_data.str = string_invalid_strs[i].fixed_str;
+
+               if (cmdline_parse_string((cmdline_parse_token_hdr_t*)&token,
+                               string_invalid_strs[i].str, (void*)buf) != -1) {
+                       memset(help_str, 0, sizeof(help_str));
+                       memset(&help_token, 0, sizeof(help_token));
+
+                       help_token.string_data.str = string_invalid_strs[i].fixed_str;
+
+                       /* get parse type so we can give a good error message */
+                       cmdline_get_help_string((cmdline_parse_token_hdr_t*)&token, help_str,
+                                       sizeof(help_str));
+
+                       printf("Error: parsing %s as %s succeeded!\n",
+                                       string_invalid_strs[i].str, help_str);
+                       return -1;
+               }
+       }
+
+       /* misc tests (big comments signify test cases) */
+       memset(&token, 0, sizeof(token));
+       memset(small_buf, 0, sizeof(small_buf));
+
+       /*
+        * try to get element from a null token
+        */
+       token.string_data.str = NULL;
+       if (cmdline_complete_get_elt_string(
+                       (cmdline_parse_token_hdr_t*)&token, 1,
+                       buf, sizeof(buf)) != -1) {
+               printf("Error: getting token from null token string!\n");
+               return -1;
+       }
+
+       /*
+        * try to get element into a buffer that is too small
+        */
+       token.string_data.str = "too_small_buffer";
+       if (cmdline_complete_get_elt_string(
+                       (cmdline_parse_token_hdr_t*)&token, 0,
+                       small_buf, sizeof(small_buf)) != -1) {
+               printf("Error: writing token into too small a buffer succeeded!\n");
+               return -1;
+       }
+
+       /*
+        * get help string written into a buffer smaller than help string
+        * truncation should occur
+        */
+       token.string_data.str = NULL;
+       if (cmdline_get_help_string(
+                       (cmdline_parse_token_hdr_t*)&token,
+                       small_buf, sizeof(small_buf)) == -1) {
+               printf("Error: writing help string into too small a buffer failed!\n");
+               return -1;
+       }
+       /* get help string for "any string" so we can compare it with small_buf */
+       cmdline_get_help_string((cmdline_parse_token_hdr_t*)&token, help_str,
+                       sizeof(help_str));
+       if (strncmp(small_buf, help_str, sizeof(small_buf) - 1)) {
+               printf("Error: help string mismatch!\n");
+               return -1;
+       }
+       /* check null terminator */
+       if (small_buf[sizeof(small_buf) - 1] != '\0') {
+               printf("Error: small buffer doesn't have a null terminator!\n");
+               return -1;
+       }
+
+       /*
+        * try to count tokens in a null token
+        */
+       token.string_data.str = NULL;
+       if (cmdline_complete_get_nb_string(
+                       (cmdline_parse_token_hdr_t*)&token) != 0) {
+               printf("Error: getting token count from null token succeeded!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+/* test valid parameters and data */
+int
+test_parse_string_valid(void)
+{
+       cmdline_parse_token_string_t token;
+       cmdline_parse_token_string_t help_token;
+       char buf[CMDLINE_TEST_BUFSIZE];
+       char help_str[CMDLINE_TEST_BUFSIZE];
+       unsigned i;
+
+       /* test parsing strings */
+       for (i = 0; i < STRING_PARSE_STRS_SIZE; i++) {
+               memset(&token, 0, sizeof(token));
+               memset(buf, 0, sizeof(buf));
+
+               token.string_data.str = string_parse_strs[i].fixed_str;
+
+               if (cmdline_parse_string((cmdline_parse_token_hdr_t*)&token,
+                               string_parse_strs[i].str, (void*)buf) < 0) {
+
+                       /* clean help data */
+                       memset(&help_token, 0, sizeof(help_token));
+                       memset(help_str, 0, sizeof(help_str));
+
+                       /* prepare help token */
+                       help_token.string_data.str = string_parse_strs[i].fixed_str;
+
+                       /* get help string so that we get an informative error message */
+                       cmdline_get_help_string((cmdline_parse_token_hdr_t*)&token, help_str,
+                                       sizeof(help_str));
+
+                       printf("Error: parsing %s as %s failed!\n",
+                                       string_parse_strs[i].str, help_str);
+                       return -1;
+               }
+               if (strncmp(buf, string_parse_strs[i].result,
+                               sizeof(string_parse_strs[i].result) - 1) != 0) {
+                       printf("Error: result mismatch!\n");
+                       return -1;
+               }
+       }
+
+       /* get number of string tokens and verify it's correct */
+       for (i = 0; i < STRING_NB_STRS_SIZE; i++) {
+               memset(&token, 0, sizeof(token));
+
+               token.string_data.str = string_nb_strs[i].str;
+
+               if (cmdline_complete_get_nb_string(
+                               (cmdline_parse_token_hdr_t*)&token) <
+                               string_nb_strs[i].nb_strs) {
+                       printf("Error: strings count mismatch!\n");
+                       return -1;
+               }
+       }
+
+       /* get token at specified position and verify it's correct */
+       for (i = 0; i < STRING_ELT_STRS_SIZE; i++) {
+               memset(&token, 0, sizeof(token));
+               memset(buf, 0, sizeof(buf));
+
+               token.string_data.str = string_elt_strs[i].str;
+
+               if (cmdline_complete_get_elt_string(
+                               (cmdline_parse_token_hdr_t*)&token, string_elt_strs[i].idx,
+                               buf, sizeof(buf)) < 0) {
+                       printf("Error: getting string element failed!\n");
+                       return -1;
+               }
+               if (strncmp(buf, string_elt_strs[i].result,
+                               sizeof(string_elt_strs[i].result)) != 0) {
+                       printf("Error: result mismatch!\n");
+                       return -1;
+               }
+       }
+
+       /* cover all cases with help strings */
+       for (i = 0; i < STRING_HELP_STRS_SIZE; i++) {
+               memset(&help_token, 0, sizeof(help_token));
+               memset(help_str, 0, sizeof(help_str));
+               help_token.string_data.str = string_help_strs[i];
+               if (cmdline_get_help_string((cmdline_parse_token_hdr_t*)&help_token,
+                               help_str, sizeof(help_str)) < 0) {
+                       printf("Error: help operation failed!\n");
+                       return -1;
+               }
+       }
+
+       return 0;
+}