This tests several aspects of the parser. These tests are primarily adapted
from the *.lil code examples included with upstream LIL. These tests should
probably get their own category, especially if I add additional styles of
tests.

Signed-off-by: Sean Anderson <sean...@gmail.com>
---
Yes, I know checkpatch complains about the quoted string being split, but
that warning is intended for user-visible strings.

 MAINTAINERS       |   1 +
 test/cmd/Makefile |   1 +
 test/cmd/lil.c    | 339 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 341 insertions(+)
 create mode 100644 test/cmd/lil.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 0184de5f93..3bf460127b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -771,6 +771,7 @@ M:  Sean Anderson <sean...@gmail.com>
 S:     Maintained
 F:     common/cli_lil.c
 F:     include/cli_lil.h
+F:     test/cmd/lil.c
 
 LOGGING
 M:     Simon Glass <s...@chromium.org>
diff --git a/test/cmd/Makefile b/test/cmd/Makefile
index 2cfe43a6bd..4f7440cb44 100644
--- a/test/cmd/Makefile
+++ b/test/cmd/Makefile
@@ -5,6 +5,7 @@
 ifdef CONFIG_HUSH_PARSER
 obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o
 endif
+obj-$(CONFIG_LIL_FULL) += lil.o
 obj-y += mem.o
 obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o
 obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o
diff --git a/test/cmd/lil.c b/test/cmd/lil.c
new file mode 100644
index 0000000000..896b2fed15
--- /dev/null
+++ b/test/cmd/lil.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0+ AND Zlib
+/*
+ * Copyright (C) 2021 Sean Anderson <sean...@gmail.com>
+ * Copyright (C) 2010-2021 Kostas Michalopoulos
+ *
+ * This file contains code which originated from the LIL project, licensed 
under
+ * Zlib. All modifications are licensed under GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/global_data.h>
+#include <cli_lil.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+const char helpers[] =
+       "proc assert {cond} {"
+               "if not [upeval expr [set cond]] {"
+                       "error [set cond]"
+               "}"
+       "};"
+       "proc assert_err {cmd} {"
+               "set ok 1;"
+               "try {upeval $cmd; set ok 0} {};"
+               "assert {$ok};"
+       "};"
+       "proc asserteq {expr1 expr2} {"
+               "set val1 [upeval 'expr \"$expr1\"'];"
+               "set val2 [upeval 'expr \"$expr2\"'];"
+               "if {$val1 != $val2} {"
+                       "error '$expr1 == ${expr2}: "
+                               "Expected ${val1}, got $val2'"
+               "}"
+       "};"
+       "proc asserteq_str {expr1 expr2} {"
+               "set val1 [upeval 'subst \"$expr1\"'];"
+               "set val2 [upeval 'subst \"$expr2\"'];"
+               "if not [streq $val1 $val2] {"
+                       "error '$expr1 == ${expr2}: "
+                               "Expected ${val1}, got $val2'"
+               "}"
+       "};"
+       "proc asserteq_list {xs ys} {"
+               "set xlen [count $xs];"
+               "set ylen [count $ys];"
+               "if not {$xlen == $ylen} {"
+                       "error '\\[count ${xs}\\] == \\[count ${ys}\\]: "
+                               "Expected ${xlen}, got $ylen'\n"
+               "};"
+               "for {set i 0} {$i < $xlen} {incr i} {"
+                       "set x [index $xs $i];"
+                       "set y [index $ys $i];"
+                       "if not {[streq $x $y]} {"
+                               "error '$xs == ${ys}: "
+                                       "Expected $x at ${i}, got $y'"
+                       "}"
+               "}"
+       "}";
+
+static const struct {
+       const char *name;
+       const char *cmd;
+} lil_tests[] = {
+       {"and",
+               "proc and args {"
+                       "foreach [slice $args 1] {"
+                               "upeval 'downeval \\'set v \\'\\[${i}\\]';"
+                               "if not $v { return 0 }"
+                       "};"
+                       "return 1"
+               "};"
+               "set a 0;"
+               "set final [and {set a 3} {return 0} {set a 32}];"
+               "asserteq 0 {$final};"
+               "assert 3 {$a};"
+       },
+       {"assert",
+               "assert 1;"
+               "assert_err {assert 0};"
+               "asserteq 1 1;"
+               "assert_err {asserteq 1 0};"
+               "asserteq_str {string one} {string one};"
+               "assert_err {asserteq_str {string one} {string two}};"
+               "asserteq_list [list 1 2 3] [list 1 2 3];"
+               "assert_err {asserteq_list [list 1 2] [list 1 2 3]};"
+               "assert_err {asserteq_list [list 1 2 3] [list 1 2]};"
+               "assert_err {asserteq_list [list 1 2 3] [list 1 2 4]};"
+       },
+       {"downeval",
+               "proc grab-some-list {} {"
+                       "set items {};"
+                       "upeval {"
+                               "foreach $some-list {"
+                                       "downeval 'append items $i'"
+                               "}"
+                       "};"
+                       "return $items"
+               "};"
+               "set some-list [list foo bar baz blah moo boo];"
+               "asserteq_list $some-list [grab-some-list]"
+       },
+       {"expr",
+               "asserteq 7 {1 + ( 2 * 3 )};"
+               "asserteq 7 {1+(2*3)};"
+               "asserteq -6 {1+ ~(2*3)};"
+               "asserteq -6 {1 + ~( 2 * 3 )};"
+               "asserteq -6 {1 +~ (2*3 )};"
+               "asserteq -6 {~(2*3)+1};"
+               "asserteq 0 {1*!(2+2)};"
+               "asserteq -1 {~!(!{})};"
+               "asserteq 1 {1 +~*(2*3)};"
+               "asserteq 1 {'hello'};"
+               "asserteq 0 {0};"
+               "asserteq 0 {{}};"
+               "asserteq 1 {()};"
+               "asserteq 1 {( )};"
+               "asserteq_str '' {[expr]};"
+       },
+       {"factorial",
+               "proc fact {n} {"
+                       "if {$n} {"
+                               "expr {$n * [fact [expr {$n - 1}]]}"
+                       "} {"
+                               "return 1"
+                       "}"
+               "};"
+               "asserteq 1 {[fact 0]};"
+               "asserteq 1 {[fact 1]};"
+               "asserteq 6 {[fact 3]};"
+               "asserteq 3628800 {[fact 10]};"
+               "asserteq 2432902008176640000 {[fact 20]}"
+       },
+       {"filter",
+               "set short_procs [filter [reflect procs] {[length $x] < 5}];"
+               "foreach $short_procs {assert {[length $i] < 5}}"
+       },
+       {"funcs",
+               "proc lapply {list proc} {"
+                       "set ret {};"
+                       "foreach $list {"
+                               "append ret [$proc $i];"
+                       "};"
+                       "return $ret"
+               "};"
+               "set list [list {bad's day} {good's day} eh??];"
+               "asserteq_list [lapply $list split] [list "
+                       "[list {bad's} day] "
+                       "[list {good's} day] "
+                       "[list eh??]"
+               "];"
+               "asserteq_list [lapply $list length] [list 9 10 4];"
+               "asserteq_list [lapply $list [proc {a} {"
+                       "return [index [split $a] 0]"
+               "}]] [list {bad's} {good's} eh??]"
+       },
+       {"lists",
+               "set l [list foo bar baz bad];"
+               "asserteq_str baz {[index $l 2]};"
+               "append l 'Hello, world!';"
+               "asserteq_list $l [list foo bar baz bad 'Hello, world!'];"
+               "set l [subst $l];"
+               "asserteq_list $l [list foo bar baz bad Hello, world!];"
+               "lmap $l foox barx bamia;"
+               "asserteq_str foo {$foox};"
+               "asserteq_str bar {$barx};"
+               "asserteq_str baz {$bamia};"
+               "set l {one     # linebreaks are ignored in list parsing mode\n"
+               "\n"
+               "two;three      # a semicolon still counts as line break\n"
+               "               # (which in list mode is treated as a\n"
+               "               # separator for list entries)\n"
+               "# of course a semicolon inside quotes is treated like normal\n"
+               "three';'and';a;half'\n"
+               "# like in code mode, a semicolon will stop the comment; four\n"
+               "\n"
+               "# below we have a quote, square brackets for inline\n"
+               "# expansions are still taken into consideration\n"
+               "[quote {this line will be ignored completely\n"
+               "        as will this line and instead be replaced\n"
+               "        with the 'five' below since while in code\n"
+               "        mode (that is, inside the brackets here)\n"
+               "        linebreaks are still processed}\n"
+               " quote five]\n"
+               "\n"
+               "# The curly brackets are also processed so the next three\n"
+               "# lines will show up as three separate lines\n"
+               "{six\n"
+               "seven\n"
+               "eight}}\n"
+               "asserteq_list $l [list one two three 'three;and;a;half' four "
+               "five 'six\\nseven\\neight'];"
+       },
+       {"local",
+               "proc bits-for {x} {"
+                       "local y bits;"
+                       "set y 0 bits 0;"
+                       "while {$y <= $x} {"
+                               "incr bits;"
+                               "set y [expr 1 << $bits]"
+                       "};"
+                       "return $bits"
+               "};"
+               "set y 1001;"
+               "set bits [bits-for $y];"
+               "set x 45;"
+               "set bitsx [bits-for $x];"
+               "asserteq 1001 {$y};"
+               "asserteq 10 {$bits};"
+               "asserteq 45 {$x};"
+               "asserteq 6 {$bitsx}"
+       },
+       {"multiline comment",
+               "# this line will not be executed, but the following will\n"
+               "set ok1 1\n"
+               "## This is a multiline comment\n"
+               "   which, as the name implies,\n"
+               "   spans multiple lines.\n"
+               "set ok2 1\n"
+               "   the code above wouldn't execute,\n"
+               "   but this will --> ##set ok3 1\n"
+               "### more than two #s will not count as multiline comments\n"
+               "set ok4 1\n"
+               "# Note that semicolons can be used as linebreaks so\n"
+               "# this code will be executed: ; set ok5 1\n"
+               "##\n"
+               "   ...however inside multiline comments semicolons do not\n"
+               "   stop the comment section (pretty much like linebreaks)\n"
+               "   and this code will not be executed: ; set ok6 1\n"
+               "##\n"
+               "# Also note that unlike in regular code, semicolons cannot\n"
+               "# be escaped in single-line comments, e.g.: ; set ok7 1\n"
+               "asserteq_str 1 {$ok1};"
+               "assert {![reflect has-var ok2]}"
+               "asserteq_str 1 {$ok3};"
+               "asserteq_str 1 {$ok4};"
+               "asserteq_str 1 {$ok5};"
+               "assert {![reflect has-var ok6]}"
+               "asserteq_str 1 {$ok7};"
+       },
+       {"multiline code",
+               "asserteq_list [list hello \\\n"
+               "       world] [list hello world]"
+       },
+       {"return",
+               "proc uses_return {} {"
+                       "return 1;"
+                       "return 0;"
+               "};"
+               "proc doesnt_use_return {} {"
+                       "quote 1;"
+               "};"
+               "proc uses_result {} {"
+                       "result 1;"
+                       "quote 0;"
+               "};"
+               "assert {[uses_return]};"
+               "assert {[doesnt_use_return]};"
+               "assert {[uses_result]}"
+       },
+       {"strings",
+               "set a 'This is a string';"
+               "set b 'This is another string';"
+               "asserteq 16 {[length $a]};"
+               "asserteq 22 {[length $b]};"
+               "asserteq_str a {[charat $a [expr [length $a] / 2]]};"
+               "asserteq_str t {[charat $b [expr [length $b] / 2]]};"
+               "asserteq 97 {[codeat $a [expr [length $a] / 2]]};"
+               "asserteq 116 {[codeat $b [expr [length $b] / 2]]};"
+               "asserteq 10 {[strpos $a string]};"
+               "asserteq 16 {[strpos $b string]};"
+               "asserteq -78 {[compare $a $b]};"
+               "assert {![streq $a $b]};"
+               "asserteq_str 'This is a foo' {[repstr $a string foo]};"
+               "asserteq_str 'This is another foo' {[repstr $b string foo]};"
+               "asserteq_list [split $a] [list This is a string];"
+               "asserteq_list [split $b] [list This is another string];"
+       },
+       {"topeval",
+               "proc does-something {} {"
+                       "topeval {"
+                               "asserteq 10 {$x};"
+                               "set x 42;"
+                               "downeval {set y [expr $x * 10]}"
+                       "};"
+                       "asserteq 420 {$y}"
+               "};"
+               "proc calls-something {} {"
+                       "local x;"
+                       "set x 33;"
+                       "does-something;"
+                       "asserteq 33 {$x};"
+                       "asserteq 420 {$y}"
+               "};"
+               "set x 10;"
+               "set y 20;"
+               "calls-something;"
+               "asserteq 42 {$x};"
+               "asserteq 420 {$y}"
+       },
+       {"trim",
+               "set str '  Hello,  world! ';"
+               "asserteq_str 'Hello,  world!' {[trim $str]};"
+               "asserteq_str 'Hello,  world! ' {[ltrim $str]};"
+               "asserteq_str '  Hello,  world!' {[rtrim $str]};"
+               "asserteq_str 'Hello world' {[foreach [split $str] {"
+                       "quote [trim $i {,!}]"
+               "}]};"
+               "asserteq_str 'Hello world' {[filter [split $str {,! }] {"
+                       "[length $x] > 0"
+               "}]};"
+       },
+};
+
+static int lib_test_lil(struct unit_test_state *uts)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(lil_tests); i++) {
+               const char *err_msg;
+               enum lil_error err;
+               struct lil *lil = lil_new(NULL);
+
+               lil_free_value(lil_parse(lil, helpers, sizeof(helpers) - 1, 0));
+               ut_asserteq(LIL_ERR_NONE, lil_error(lil, &err_msg));
+               lil_free_value(lil_parse(lil, lil_tests[i].cmd, 0, 0));
+               err = lil_error(lil, &err_msg);
+               if (err) {
+                       ut_failf(uts, __FILE__, __LINE__, __func__,
+                                lil_tests[i].name, "err=%d: %s", err, err_msg);
+                       lil_free(lil);
+                       return CMD_RET_FAILURE;
+               };
+               lil_free(lil);
+       }
+
+       return 0;
+}
+LIB_TEST(lib_test_lil, 0);
-- 
2.32.0

Reply via email to