[RFC PATCH 17/28] test: Add tests for LIL

Sean Anderson seanga2 at gmail.com
Thu Jul 1 08:16:00 CEST 2021


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 <seanga2 at 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 <seanga2 at gmail.com>
 S:	Maintained
 F:	common/cli_lil.c
 F:	include/cli_lil.h
+F:	test/cmd/lil.c
 
 LOGGING
 M:	Simon Glass <sjg at 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 <seanga2 at 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



More information about the U-Boot mailing list