From: Kent Gibson <warthog618@gmail.com>
To: linux-gpio@vger.kernel.org, brgl@bgdev.pl
Cc: Kent Gibson <warthog618@gmail.com>
Subject: [libgpiod v2][PATCH v4 3/5] tools: tests for line name focussed rework
Date: Mon, 14 Nov 2022 12:01:00 +0800 [thread overview]
Message-ID: <20221114040102.66031-4-warthog618@gmail.com> (raw)
In-Reply-To: <20221114040102.66031-1-warthog618@gmail.com>
Rework the tools tests and expand to cover new functionality.
Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
tools/gpio-tools-test | 3 -
tools/gpio-tools-test.bats | 2369 ++++++++++++++++++++++++++++++++++--
2 files changed, 2284 insertions(+), 88 deletions(-)
diff --git a/tools/gpio-tools-test b/tools/gpio-tools-test
index 234f9bd..1a012dc 100755
--- a/tools/gpio-tools-test
+++ b/tools/gpio-tools-test
@@ -37,9 +37,6 @@ check_prog() {
# Check all required non-coreutils tools
check_prog bats
check_prog modprobe
-check_prog rmmod
-check_prog udevadm
-check_prog timeout
# Check if we're running a kernel at the required version or later
check_kernel $MIN_KERNEL_VERSION
diff --git a/tools/gpio-tools-test.bats b/tools/gpio-tools-test.bats
index d200df1..78b0959 100755
--- a/tools/gpio-tools-test.bats
+++ b/tools/gpio-tools-test.bats
@@ -1,18 +1,24 @@
#!/usr/bin/env bats
# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com>
+# SPDX-FileCopyrightText: 2022 Kent Gibson <warthog618@gmail.com>
# Simple test harness for the gpio-tools.
-# Where output from coprocesses is stored
-COPROC_OUTPUT=$BATS_TMPDIR/gpio-tools-test-output
+# Where output from the dut is stored
+DUT_OUTPUT=$BATS_TMPDIR/gpio-tools-test-output
# Save the PID of coprocess - otherwise we won't be able to wait for it
# once it exits as the COPROC_PID will be cleared.
-COPROC_SAVED_PID=""
+DUT_PID=""
-GPIOSIM_CHIPS=""
-GPIOSIM_CONFIGFS="/sys/kernel/config/gpio-sim/"
+# mappings from local name to system chip name, path, dev name
+# -g required for the associative arrays, cos BATS...
+declare -g -A GPIOSIM_CHIP_NAME
+declare -g -A GPIOSIM_CHIP_PATH
+declare -g -A GPIOSIM_DEV_NAME
+GPIOSIM_CONFIGFS="/sys/kernel/config/gpio-sim"
GPIOSIM_SYSFS="/sys/devices/platform/"
+GPIOSIM_APP_NAME="gpio-tools-test"
# Run the command in $* and return 0 if the command failed. The way we do it
# here is a workaround for the way bats handles failing processes.
@@ -23,10 +29,7 @@ assert_fail() {
# Check if the string in $2 matches against the pattern in $1.
regex_matches() {
- local PATTERN=$1
- local STRING=$2
-
- [[ $STRING =~ $PATTERN ]]
+ [[ $2 =~ $1 ]] || (echo "Mismatched: \"$2\"" && false)
}
# Iterate over all lines in the output of the last command invoked with bats'
@@ -38,31 +41,39 @@ output_contains_line() {
do
test "$line" = "$LINE" && return 0
done
-
+ echo "Mismatched:"
+ echo "$output"
return 1
}
+output_is() {
+ test "$output" = "$1" || (echo "Mismatched: \"$output\"" && false)
+}
+
+num_lines_is() {
+ test ${#lines[@]} -eq $1 || (echo "Num lines is: ${#lines[@]}" && false)
+}
+
+status_is() {
+ test "$status" -eq "$1"
+}
+
# Same as above but match against the regex pattern in $1.
output_regex_match() {
- local PATTERN=$1
-
for line in "${lines[@]}"
do
- regex_matches "$PATTERN" "$line" && return 0
+ [[ "$line" =~ $1 ]] && return 0
done
-
+ echo "Mismatched:"
+ echo "$output"
return 1
}
-random_name() {
- cat /proc/sys/kernel/random/uuid
-}
-
gpiosim_chip() {
local VAR=$1
- local NAME=$(random_name)
+ local NAME=${GPIOSIM_APP_NAME}-$$-${VAR}
local DEVPATH=$GPIOSIM_CONFIGFS/$NAME
- local BANKPATH=$DEVPATH/$NAME
+ local BANKPATH=$DEVPATH/bank0
mkdir -p $BANKPATH
@@ -87,75 +98,60 @@ gpiosim_chip() {
echo 1 > $DEVPATH/live
- GPIOSIM_CHIPS="$VAR:$NAME $GPIOSIM_CHIPS"
+ local chip_name=$(<$BANKPATH/chip_name)
+ GPIOSIM_CHIP_NAME[$1]=$chip_name
+ GPIOSIM_CHIP_PATH[$1]="/dev/$chip_name"
+ GPIOSIM_DEV_NAME[$1]=$(<$DEVPATH/dev_name)
}
-gpiosim_chip_map_name() {
- local VAR=$1
-
- for CHIP in $GPIOSIM_CHIPS
- do
- KEY=$(echo $CHIP | cut -d":" -f1)
- VAL=$(echo $CHIP | cut -d":" -f2)
-
- if [ "$KEY" = "$VAR" ]
- then
- echo $VAL
- fi
- done
+gpiosim_chip_number() {
+ local NAME=${GPIOSIM_CHIP_NAME[$1]}
+ echo ${NAME#"gpiochip"}
}
-gpiosim_chip_name() {
- local VAR=$1
- local NAME=$(gpiosim_chip_map_name $VAR)
-
- cat $GPIOSIM_CONFIGFS/$NAME/$NAME/chip_name
+gpiosim_chip_symlink() {
+ GPIOSIM_CHIP_LINK="$2/${GPIOSIM_APP_NAME}-$$-lnk"
+ ln -s ${GPIOSIM_CHIP_PATH[$1]} "$GPIOSIM_CHIP_LINK"
}
-gpiosim_dev_name() {
- local VAR=$1
- local NAME=$(gpiosim_chip_map_name $VAR)
-
- cat $GPIOSIM_CONFIGFS/$NAME/dev_name
+gpiosim_chip_symlink_cleanup() {
+ if [ -n "$GPIOSIM_CHIP_LINK" ]
+ then
+ rm "$GPIOSIM_CHIP_LINK"
+ fi
+ unset GPIOSIM_CHIP_LINK
}
gpiosim_set_pull() {
- local VAR=$1
local OFFSET=$2
local PULL=$3
- local DEVNAME=$(gpiosim_dev_name $VAR)
- local CHIPNAME=$(gpiosim_chip_name $VAR)
+ local DEVNAME=${GPIOSIM_DEV_NAME[$1]}
+ local CHIPNAME=${GPIOSIM_CHIP_NAME[$1]}
echo $PULL > $GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/pull
}
gpiosim_check_value() {
- local VAR=$1
local OFFSET=$2
local EXPECTED=$3
- local DEVNAME=$(gpiosim_dev_name $VAR)
- local CHIPNAME=$(gpiosim_chip_name $VAR)
-
- VAL=$(cat $GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/value)
- if [ "$VAL" = "$EXPECTED" ]
- then
- return 0
- fi
+ local DEVNAME=${GPIOSIM_DEV_NAME[$1]}
+ local CHIPNAME=${GPIOSIM_CHIP_NAME[$1]}
- return 1
+ VAL=$(<$GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/value)
+ [ "$VAL" = "$EXPECTED" ]
}
gpiosim_cleanup() {
- for CHIP in $GPIOSIM_CHIPS
+ for CHIP in ${!GPIOSIM_CHIP_NAME[@]}
do
- local NAME=$(echo $CHIP | cut -d":" -f2)
+ local NAME=${GPIOSIM_APP_NAME}-$$-$CHIP
local DEVPATH=$GPIOSIM_CONFIGFS/$NAME
- local BANKPATH=$DEVPATH/$NAME
+ local BANKPATH=$DEVPATH/bank0
echo 0 > $DEVPATH/live
- ls $BANKPATH/line* 2> /dev/null
+ ls $BANKPATH/line* > /dev/null 2>&1
if [ "$?" = "0" ]
then
for LINE in $(find $BANKPATH/ | egrep "line[0-9]+$")
@@ -169,7 +165,11 @@ gpiosim_cleanup() {
rmdir $DEVPATH
done
- GPIOSIM_CHIPS=""
+ gpiosim_chip_symlink_cleanup
+
+ GPIOSIM_CHIP_NAME=()
+ GPIOSIM_CHIP_PATH=()
+ GPIOSIM_DEV_NAME=()
}
run_tool() {
@@ -178,45 +178,2244 @@ run_tool() {
run timeout 10s $BATS_TEST_DIRNAME/"$@"
}
-coproc_run_tool() {
- rm -f $BR_PROC_OUTPUT
- coproc timeout 10s $BATS_TEST_DIRNAME/"$@" > $COPROC_OUTPUT 2> $COPROC_OUTPUT
- COPROC_SAVED_PID=$COPROC_PID
- # FIXME We're giving the background process some time to get up, but really this
- # should be more reliable...
+dut_run() {
+ coproc timeout 10s $BATS_TEST_DIRNAME/"$@" 2>&1
+ DUT_PID=$COPROC_PID
+ read -t1 -n1 -u ${COPROC[0]} DUT_FIRST_CHAR
+}
+
+dut_run_redirect() {
+ coproc timeout 10s $BATS_TEST_DIRNAME/"$@" > $DUT_OUTPUT 2>&1
+ DUT_PID=$COPROC_PID
+ # give the process time to spin up
+ # FIXME - find a better solution
sleep 0.2
}
-coproc_tool_stdin_write() {
+dut_read_redirect() {
+ output=$(<$DUT_OUTPUT)
+ local ORIG_IFS="$IFS"
+ IFS=$'\n' lines=($output)
+ IFS="$ORIG_IFS"
+}
+
+dut_read() {
+ local LINE
+ lines=()
+ while read -t 0.2 -u ${COPROC[0]} LINE;
+ do
+ if [ -n "$DUT_FIRST_CHAR" ]
+ then
+ LINE=${DUT_FIRST_CHAR}${LINE}
+ unset DUT_FIRST_CHAR
+ fi
+ lines+=("$LINE")
+ done
+ output="${lines[@]}"
+}
+
+dut_readable() {
+ read -t 0 -u ${COPROC[0]} LINE
+}
+
+dut_flush() {
+ local JUNK
+ lines=()
+ output=
+ unset DUT_FIRST_CHAR
+ while read -t 0 -u ${COPROC[0]} JUNK;
+ do
+ read -t 0.1 -u ${COPROC[0]} JUNK || true
+ done
+}
+
+# check the next line of output matches the regex
+dut_regex_match() {
+ PATTERN=$1
+
+ read -t 0.2 -u ${COPROC[0]} LINE || (echo Timeout && false)
+ if [ -n "$DUT_FIRST_CHAR" ]
+ then
+ LINE=${DUT_FIRST_CHAR}${LINE}
+ unset DUT_FIRST_CHAR
+ fi
+ [[ $LINE =~ $PATTERN ]] || (echo "Mismatched: \"$LINE\"" && false)
+}
+
+dut_write() {
echo $* >&${COPROC[1]}
}
-coproc_tool_kill() {
+dut_kill() {
SIGNUM=$1
- kill $SIGNUM $COPROC_SAVED_PID
+ kill $SIGNUM $DUT_PID
}
-coproc_tool_wait() {
+dut_wait() {
status="0"
# A workaround for the way bats handles command failures.
- wait $COPROC_SAVED_PID || export status=$?
+ wait $DUT_PID || export status=$?
test "$status" -ne 0 || export status="0"
- output=$(cat $COPROC_OUTPUT)
- local ORIG_IFS="$IFS"
- IFS=$'\n' lines=($output)
- IFS="$ORIG_IFS"
- rm -f $COPROC_OUTPUT
+ unset DUT_PID
}
-teardown() {
- if [ -n "$BG_PROC_PID" ]
- then
- kill -9 $BG_PROC_PID
- run wait $BG_PROC_PID
- BG_PROC_PID=""
- fi
+dut_cleanup() {
+ if [ -n "$DUT_PID" ]
+ then
+ kill -SIGTERM $DUT_PID
+ wait $DUT_PID || false
+ fi
+ rm -f $DUT_OUTPUT
+}
+teardown() {
+ dut_cleanup
gpiosim_cleanup
}
+request_release_line() {
+ $BATS_TEST_DIRNAME/gpioget -c $* >/dev/null
+}
+
+#
+# gpiodetect test cases
+#
+
+@test "gpiodetect: all chips" {
+ gpiosim_chip sim0 num_lines=4
+ gpiosim_chip sim1 num_lines=8
+ gpiosim_chip sim2 num_lines=16
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+ local sim1=${GPIOSIM_CHIP_NAME[sim1]}
+ local sim2=${GPIOSIM_CHIP_NAME[sim2]}
+ local sim0dev=${GPIOSIM_DEV_NAME[sim0]}
+ local sim1dev=${GPIOSIM_DEV_NAME[sim1]}
+ local sim2dev=${GPIOSIM_DEV_NAME[sim2]}
+
+ run_tool gpiodetect
+
+ output_contains_line "$sim0 [${sim0dev}-node0] (4 lines)"
+ output_contains_line "$sim1 [${sim1dev}-node0] (8 lines)"
+ output_contains_line "$sim2 [${sim2dev}-node0] (16 lines)"
+ status_is 0
+
+ # ignoring symlinks
+ local initial_output=$output
+ gpiosim_chip_symlink sim1 /dev
+
+ run_tool gpiodetect
+
+ output_is "$initial_output"
+ status_is 0
+}
+
+@test "gpiodetect: a chip" {
+ gpiosim_chip sim0 num_lines=4
+ gpiosim_chip sim1 num_lines=8
+ gpiosim_chip sim2 num_lines=16
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+ local sim1=${GPIOSIM_CHIP_NAME[sim1]}
+ local sim2=${GPIOSIM_CHIP_NAME[sim2]}
+ local sim0dev=${GPIOSIM_DEV_NAME[sim0]}
+ local sim1dev=${GPIOSIM_DEV_NAME[sim1]}
+ local sim2dev=${GPIOSIM_DEV_NAME[sim2]}
+
+ # by name
+ run_tool gpiodetect $sim0
+
+ output_contains_line "$sim0 [${sim0dev}-node0] (4 lines)"
+ num_lines_is 1
+ status_is 0
+
+ # by path
+ run_tool gpiodetect ${GPIOSIM_CHIP_PATH[sim1]}
+
+ output_contains_line "$sim1 [${sim1dev}-node0] (8 lines)"
+ num_lines_is 1
+ status_is 0
+
+ # by number
+ run_tool gpiodetect $(gpiosim_chip_number sim2)
+
+ output_contains_line "$sim2 [${sim2dev}-node0] (16 lines)"
+ num_lines_is 1
+ status_is 0
+
+ # by symlink
+ gpiosim_chip_symlink sim2 .
+ run_tool gpiodetect $GPIOSIM_CHIP_LINK
+
+ output_contains_line "$sim2 [${sim2dev}-node0] (16 lines)"
+ num_lines_is 1
+ status_is 0
+}
+
+@test "gpiodetect: multiple chips" {
+ gpiosim_chip sim0 num_lines=4
+ gpiosim_chip sim1 num_lines=8
+ gpiosim_chip sim2 num_lines=16
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+ local sim1=${GPIOSIM_CHIP_NAME[sim1]}
+ local sim2=${GPIOSIM_CHIP_NAME[sim2]}
+ local sim0dev=${GPIOSIM_DEV_NAME[sim0]}
+ local sim1dev=${GPIOSIM_DEV_NAME[sim1]}
+ local sim2dev=${GPIOSIM_DEV_NAME[sim2]}
+
+ run_tool gpiodetect $sim0 $sim1 $sim2
+
+ output_contains_line "$sim0 [${sim0dev}-node0] (4 lines)"
+ output_contains_line "$sim1 [${sim1dev}-node0] (8 lines)"
+ output_contains_line "$sim2 [${sim2dev}-node0] (16 lines)"
+ num_lines_is 3
+ status_is 0
+}
+
+@test "gpiodetect: with nonexistent chip" {
+ run_tool gpiodetect nonexistent-chip
+
+ status_is 1
+ output_regex_match \
+".*cannot find GPIO chip character device 'nonexistent-chip'"
+}
+
+#
+# gpioinfo test cases
+#
+
+@test "gpioinfo: all chips" {
+ gpiosim_chip sim0 num_lines=4
+ gpiosim_chip sim1 num_lines=8
+
+ run_tool gpioinfo
+
+ echo "$output"
+ output_contains_line "${GPIOSIM_CHIP_NAME[sim0]} - 4 lines:"
+ output_contains_line "${GPIOSIM_CHIP_NAME[sim1]} - 8 lines:"
+ output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+7:\\s+unnamed\\s+input"
+ status_is 0
+
+ # ignoring symlinks
+ local initial_output=$output
+ gpiosim_chip_symlink sim1 /dev
+
+ run_tool gpioinfo
+
+ output_is "$initial_output"
+ status_is 0
+}
+
+@test "gpioinfo: all chips with some used lines" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar
+ gpiosim_chip sim1 num_lines=8 line_name=3:baz line_name=4:xyz
+
+ dut_run gpioset --banner --active-low foo=1 baz=0
+
+ run_tool gpioinfo
+
+ output_contains_line "${GPIOSIM_CHIP_NAME[sim0]} - 4 lines:"
+ output_contains_line "${GPIOSIM_CHIP_NAME[sim1]} - 8 lines:"
+ output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input"
+ output_regex_match \
+"\\s+line\\s+1:\\s+\"foo\"\\s+output active-low consumer=\"gpioset\""
+ output_regex_match \
+"\\s+line\\s+3:\\s+\"baz\"\\s+output active-low consumer=\"gpioset\""
+ status_is 0
+}
+
+@test "gpioinfo: a chip" {
+ gpiosim_chip sim0 num_lines=8
+ gpiosim_chip sim1 num_lines=4
+
+ local sim1=${GPIOSIM_CHIP_NAME[sim1]}
+
+ # by name
+ run_tool gpioinfo --chip $sim1
+
+ output_contains_line "$sim1 - 4 lines:"
+ output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+1:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+2:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+3:\\s+unnamed\\s+input"
+ num_lines_is 5
+ status_is 0
+
+ # by path
+ run_tool gpioinfo --chip $sim1
+
+ output_contains_line "$sim1 - 4 lines:"
+ output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+1:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+2:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+3:\\s+unnamed\\s+input"
+ num_lines_is 5
+ status_is 0
+
+ # by number
+ run_tool gpioinfo --chip $sim1
+
+ output_contains_line "$sim1 - 4 lines:"
+ output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+1:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+2:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+3:\\s+unnamed\\s+input"
+ num_lines_is 5
+ status_is 0
+
+ # by symlink
+ gpiosim_chip_symlink sim1 .
+ run_tool gpioinfo --chip $GPIOSIM_CHIP_LINK
+
+ output_contains_line "$sim1 - 4 lines:"
+ output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+1:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+2:\\s+unnamed\\s+input"
+ output_regex_match "\\s+line\\s+3:\\s+unnamed\\s+input"
+ num_lines_is 5
+ status_is 0
+}
+
+@test "gpioinfo: a line" {
+ gpiosim_chip sim0 num_lines=8 line_name=5:bar
+ gpiosim_chip sim1 num_lines=4 line_name=2:bar
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+ local sim1=${GPIOSIM_CHIP_NAME[sim1]}
+
+ # by offset
+ run_tool gpioinfo --chip $sim1 2
+
+ output_regex_match "$sim1 2\\s+\"bar\"\\s+input"
+ num_lines_is 1
+ status_is 0
+
+ # by name
+ run_tool gpioinfo bar
+
+ output_regex_match "$sim0 5\\s+\"bar\"\\s+input"
+ num_lines_is 1
+ status_is 0
+
+ # by chip and name
+ run_tool gpioinfo --chip $sim1 2
+
+ output_regex_match "$sim1 2\\s+\"bar\"\\s+input"
+ num_lines_is 1
+ status_is 0
+}
+
+@test "gpioinfo: first matching named line" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \
+ line_name=3:foobar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \
+ line_name=4:xyz line_name=7:foobar
+ gpiosim_chip sim2 num_lines=16
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioinfo foobar
+
+ output_regex_match "$sim0 3\\s+\"foobar\"\\s+input"
+ num_lines_is 1
+ status_is 0
+}
+
+@test "gpioinfo: multiple lines" {
+ gpiosim_chip sim0 num_lines=8 line_name=5:bar
+ gpiosim_chip sim1 num_lines=4 line_name=2:baz
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+ local sim1=${GPIOSIM_CHIP_NAME[sim1]}
+
+ # by offset
+ run_tool gpioinfo --chip $sim1 1 2
+
+ output_regex_match "$sim1 1\\s+unnamed\\s+input"
+ output_regex_match "$sim1 2\\s+\"baz\"\\s+input"
+ num_lines_is 2
+ status_is 0
+
+ # by name
+ run_tool gpioinfo bar baz
+
+ output_regex_match "$sim0 5\\s+\"bar\"\\s+input"
+ output_regex_match "$sim1 2\\s+\"baz\"\\s+input"
+ num_lines_is 2
+ status_is 0
+
+ # by name and offset
+ run_tool gpioinfo --chip $sim0 bar 3
+
+ output_regex_match "$sim0 5\\s+\"bar\"\\s+input"
+ output_regex_match "$sim0 3\\s+unnamed\\s+input"
+ num_lines_is 2
+ status_is 0
+}
+
+@test "gpioinfo: line attribute menagerie" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo
+ gpiosim_chip sim1 num_lines=8 line_name=3:baz
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+ local sim1=${GPIOSIM_CHIP_NAME[sim1]}
+
+ dut_run gpioset --banner --active-low --bias=pull-up --drive=open-source foo=1 baz=0
+
+ run_tool gpioinfo foo baz
+
+ output_regex_match \
+"$sim0 1\\s+\"foo\"\\s+output active-low drive=open-source bias=pull-up consumer=\"gpioset\""
+ output_regex_match \
+"$sim1 3\\s+\"baz\"\\s+output active-low drive=open-source bias=pull-up consumer=\"gpioset\""
+ num_lines_is 2
+ status_is 0
+
+ dut_kill
+ dut_wait
+
+ dut_run gpioset --banner --bias=pull-down --drive=open-drain foo=1 baz=0
+
+ run_tool gpioinfo foo baz
+
+ output_regex_match \
+"$sim0 1\\s+\"foo\"\\s+output drive=open-drain bias=pull-down consumer=\"gpioset\""
+ output_regex_match \
+"$sim1 3\\s+\"baz\"\\s+output drive=open-drain bias=pull-down consumer=\"gpioset\""
+ num_lines_is 2
+ status_is 0
+
+ dut_kill
+ dut_wait
+
+ dut_run gpiomon --banner --bias=disabled --utc -p 10ms foo baz
+
+ run_tool gpioinfo foo baz
+
+ output_regex_match \
+"$sim0 1\\s+\"foo\"\\s+input bias=disabled edges=both event-clock=realtime debounce-period=10ms consumer=\"gpiomon\""
+ output_regex_match \
+"$sim1 3\\s+\"baz\"\\s+input bias=disabled edges=both event-clock=realtime debounce-period=10ms consumer=\"gpiomon\""
+ num_lines_is 2
+ status_is 0
+
+ dut_kill
+ dut_wait
+
+ dut_run gpiomon --banner --edges=rising --localtime foo baz
+
+ run_tool gpioinfo foo baz
+
+ output_regex_match \
+"$sim0 1\\s+\"foo\"\\s+input edges=rising event-clock=realtime consumer=\"gpiomon\""
+ output_regex_match \
+"$sim1 3\\s+\"baz\"\\s+input edges=rising event-clock=realtime consumer=\"gpiomon\""
+ num_lines_is 2
+ status_is 0
+
+ dut_kill
+ dut_wait
+
+ dut_run gpiomon --banner --edges=falling foo baz
+
+ run_tool gpioinfo foo baz
+
+ output_regex_match \
+"$sim0 1\\s+\"foo\"\\s+input edges=falling consumer=\"gpiomon\""
+ output_regex_match \
+"$sim1 3\\s+\"baz\"\\s+input edges=falling consumer=\"gpiomon\""
+ num_lines_is 2
+ status_is 0
+}
+
+@test "gpioinfo: with same line twice" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ # by offset
+ run_tool gpioinfo --chip $sim0 1 1
+
+ output_regex_match "$sim0 1\\s+\"foo\"\\s+input"
+ output_regex_match ".*lines '1' and '1' are the same line"
+ num_lines_is 2
+ status_is 1
+
+ # by name
+ run_tool gpioinfo foo foo
+
+ output_regex_match "$sim0 1\\s+\"foo\"\\s+input"
+ output_regex_match ".*lines 'foo' and 'foo' are the same line"
+ num_lines_is 2
+ status_is 1
+
+ # by name and offset
+ run_tool gpioinfo --chip $sim0 foo 1
+
+ output_regex_match "$sim0 1\\s+\"foo\"\\s+input"
+ output_regex_match ".*lines 'foo' and '1' are the same line"
+ num_lines_is 2
+ status_is 1
+}
+
+@test "gpioinfo: all lines matching name" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \
+ line_name=3:foobar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \
+ line_name=4:xyz line_name=7:foobar
+ gpiosim_chip sim2 num_lines=16
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+ local sim1=${GPIOSIM_CHIP_NAME[sim1]}
+
+ run_tool gpioinfo --strict foobar
+
+ output_regex_match "$sim0 3\\s+\"foobar\"\\s+input"
+ output_regex_match "$sim1 2\\s+\"foobar\"\\s+input"
+ output_regex_match "$sim1 7\\s+\"foobar\"\\s+input"
+ num_lines_is 3
+ status_is 1
+}
+
+@test "gpioinfo: with lines strictly by name" {
+ # not suggesting this setup makes any sense
+ # - just test that we can deal with it
+ gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ # first by offset (to show offsets match first)
+ run_tool gpioinfo --chip $sim0 1 6
+
+ output_regex_match "$sim0 1\\s+\"6\"\\s+input"
+ output_regex_match "$sim0 6\\s+\"1\"\\s+input"
+ num_lines_is 2
+ status_is 0
+
+ # then strictly by name
+ run_tool gpioinfo --by-name --chip $sim0 1
+
+ output_regex_match "$sim0 6\\s+\"1\"\\s+input"
+ num_lines_is 1
+ status_is 0
+}
+
+@test "gpioinfo: with nonexistent chip" {
+ run_tool gpioinfo --chip nonexistent-chip
+
+ output_regex_match \
+".*cannot find GPIO chip character device 'nonexistent-chip'"
+ status_is 1
+}
+
+@test "gpioinfo: with nonexistent line" {
+ gpiosim_chip sim0 num_lines=8
+
+ run_tool gpioinfo nonexistent-line
+
+ output_regex_match ".*cannot find line 'nonexistent-line'"
+ status_is 1
+
+ run_tool gpioinfo --chip ${GPIOSIM_CHIP_NAME[sim0]} nonexistent-line
+
+ output_regex_match ".*cannot find line 'nonexistent-line'"
+ status_is 1
+}
+
+@test "gpioinfo: with offset out of range" {
+ gpiosim_chip sim0 num_lines=4
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioinfo --chip $sim0 0 1 2 3 4 5
+
+ output_regex_match "$sim0 0\\s+unnamed\\s+input"
+ output_regex_match "$sim0 1\\s+unnamed\\s+input"
+ output_regex_match "$sim0 2\\s+unnamed\\s+input"
+ output_regex_match "$sim0 3\\s+unnamed\\s+input"
+ output_regex_match ".*offset 4 is out of range on chip '$sim0'"
+ output_regex_match ".*offset 5 is out of range on chip '$sim0'"
+ num_lines_is 6
+ status_is 1
+}
+
+#
+# gpioget test cases
+#
+
+@test "gpioget: by name" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+
+ gpiosim_set_pull sim0 1 pull-up
+
+ run_tool gpioget foo
+
+ output_is "\"foo\"=active"
+ status_is 0
+
+ run_tool gpioget --unquoted foo
+
+ output_is "foo=active"
+ status_is 0
+}
+
+@test "gpioget: by offset" {
+ gpiosim_chip sim0 num_lines=8
+
+ gpiosim_set_pull sim0 1 pull-up
+
+ run_tool gpioget --chip ${GPIOSIM_CHIP_NAME[sim0]} 1
+
+ output_is "\"1\"=active"
+ status_is 0
+
+ run_tool gpioget --unquoted --chip ${GPIOSIM_CHIP_NAME[sim0]} 1
+
+ output_is "1=active"
+ status_is 0
+}
+
+@test "gpioget: by symlink" {
+ gpiosim_chip sim0 num_lines=8
+ gpiosim_chip_symlink sim0 .
+
+ gpiosim_set_pull sim0 1 pull-up
+
+ run_tool gpioget --chip $GPIOSIM_CHIP_LINK 1
+
+ output_is "\"1\"=active"
+ status_is 0
+}
+
+@test "gpioget: by chip and name" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+ gpiosim_chip sim1 num_lines=8 line_name=3:foo
+
+ gpiosim_set_pull sim1 3 pull-up
+
+ run_tool gpioget --chip ${GPIOSIM_CHIP_NAME[sim1]} foo
+
+ output_is "\"foo\"=active"
+ status_is 0
+
+ run_tool gpioget --unquoted --chip ${GPIOSIM_CHIP_NAME[sim1]} foo
+
+ output_is "foo=active"
+ status_is 0
+}
+
+@test "gpioget: first matching named line" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \
+ line_name=3:foobar line_name=7:foobar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \
+ line_name=4:xyz
+ gpiosim_chip sim2 num_lines=16
+
+ gpiosim_set_pull sim0 3 pull-up
+
+ run_tool gpioget foobar
+
+ output_is "\"foobar\"=active"
+ status_is 0
+}
+
+@test "gpioget: multiple lines" {
+ gpiosim_chip sim0 num_lines=8
+
+ gpiosim_set_pull sim0 2 pull-up
+ gpiosim_set_pull sim0 3 pull-up
+ gpiosim_set_pull sim0 5 pull-up
+ gpiosim_set_pull sim0 7 pull-up
+
+ run_tool gpioget --unquoted --chip ${GPIOSIM_CHIP_NAME[sim0]} 0 1 2 3 4 5 6 7
+
+ output_is \
+"0=inactive 1=inactive 2=active 3=active 4=inactive 5=active 6=inactive 7=active"
+ status_is 0
+}
+
+@test "gpioget: multiple lines by name and offset" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=6:bar
+ gpiosim_chip sim1 num_lines=8 line_name=1:baz line_name=3:bar
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 1 pull-up
+ gpiosim_set_pull sim0 4 pull-up
+ gpiosim_set_pull sim0 6 pull-up
+
+ run_tool gpioget --chip $sim0 0 foo 4 bar
+
+ output_is "\"0\"=inactive \"foo\"=active \"4\"=active \"bar\"=active"
+ status_is 0
+}
+
+@test "gpioget: multiple lines across multiple chips" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=4:xyz
+
+ gpiosim_set_pull sim0 1 pull-up
+ gpiosim_set_pull sim1 4 pull-up
+
+ run_tool gpioget baz bar foo xyz
+
+ output_is "\"baz\"=inactive \"bar\"=inactive \"foo\"=active \"xyz\"=active"
+ status_is 0
+}
+
+@test "gpioget: with numeric values" {
+ gpiosim_chip sim0 num_lines=8
+
+ gpiosim_set_pull sim0 2 pull-up
+ gpiosim_set_pull sim0 3 pull-up
+ gpiosim_set_pull sim0 5 pull-up
+ gpiosim_set_pull sim0 7 pull-up
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioget --numeric --chip $sim0 0 1 2 3 4 5 6 7
+
+ output_is "0 0 1 1 0 1 0 1"
+ status_is 0
+}
+
+@test "gpioget: with active-low" {
+ gpiosim_chip sim0 num_lines=8
+
+ gpiosim_set_pull sim0 2 pull-up
+ gpiosim_set_pull sim0 3 pull-up
+ gpiosim_set_pull sim0 5 pull-up
+ gpiosim_set_pull sim0 7 pull-up
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioget --active-low --unquoted --chip $sim0 0 1 2 3 4 5 6 7
+
+ output_is \
+"0=active 1=active 2=inactive 3=inactive 4=active 5=inactive 6=active 7=inactive"
+ status_is 0
+}
+
+@test "gpioget: with consumer" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar
+ gpiosim_chip sim1 num_lines=8 line_name=3:baz line_name=4:xyz
+
+ dut_run gpionotify --banner -F "%l %E %C" foo baz
+
+ run_tool gpioget --consumer gpio-tools-tests foo baz
+ status_is 0
+
+ dut_read
+ output_regex_match "foo requested gpio-tools-tests"
+ output_regex_match "baz requested gpio-tools-tests"
+}
+
+@test "gpioget: with pull-up" {
+ gpiosim_chip sim0 num_lines=8
+
+ gpiosim_set_pull sim0 2 pull-up
+ gpiosim_set_pull sim0 3 pull-up
+ gpiosim_set_pull sim0 5 pull-up
+ gpiosim_set_pull sim0 7 pull-up
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioget --bias=pull-up --unquoted --chip $sim0 0 1 2 3 4 5 6 7
+
+ output_is \
+"0=active 1=active 2=active 3=active 4=active 5=active 6=active 7=active"
+ status_is 0
+}
+
+@test "gpioget: with pull-down" {
+ gpiosim_chip sim0 num_lines=8
+
+ gpiosim_set_pull sim0 2 pull-up
+ gpiosim_set_pull sim0 3 pull-up
+ gpiosim_set_pull sim0 5 pull-up
+ gpiosim_set_pull sim0 7 pull-up
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioget --bias=pull-down --unquoted --chip $sim0 0 1 2 3 4 5 6 7
+
+ output_is \
+"0=inactive 1=inactive 2=inactive 3=inactive 4=inactive 5=inactive 6=inactive 7=inactive"
+ status_is 0
+}
+
+@test "gpioget: with direction as-is" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ # flip to output
+ run_tool gpioset -t0 foo=1
+
+ status_is 0
+
+ run_tool gpioinfo foo
+ output_regex_match "$sim0 1\\s+\"foo\"\\s+output"
+ status_is 0
+
+ run_tool gpioget --as-is foo
+ # note gpio-sim reverts line to its pull when released
+ output_is "\"foo\"=inactive"
+ status_is 0
+
+ run_tool gpioinfo foo
+ output_regex_match "$sim0 1\\s+\"foo\"\\s+output"
+ status_is 0
+
+ # whereas the default behaviour forces to input
+ run_tool gpioget foo
+ # note gpio-sim reverts line to its pull when released
+ # (defaults to pull-down)
+ output_is "\"foo\"=inactive"
+ status_is 0
+
+ run_tool gpioinfo foo
+ output_regex_match "$sim0 1\\s+\"foo\"\\s+input"
+ status_is 0
+}
+
+@test "gpioget: with hold-period" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+
+ # only test parsing - testing the hold-period itself is tricky
+ run_tool gpioget --hold-period=100ms foo
+ output_is "\"foo\"=inactive"
+ status_is 0
+}
+
+@test "gpioget: with strict named line check" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \
+ line_name=3:foobar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \
+ line_name=4:xyz line_name=7:foobar
+ gpiosim_chip sim2 num_lines=16
+
+ run_tool gpioget --strict foobar
+
+ output_regex_match ".*line 'foobar' is not unique"
+ status_is 1
+}
+
+@test "gpioget: with lines by offset" {
+ # not suggesting this setup makes any sense
+ # - just test that we can deal with it
+ gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1
+
+ gpiosim_set_pull sim0 1 pull-up
+ gpiosim_set_pull sim0 6 pull-down
+
+ run_tool gpioget --chip ${GPIOSIM_CHIP_NAME[sim0]} 1 6
+
+ output_is "\"1\"=active \"6\"=inactive"
+ status_is 0
+
+ run_tool gpioget --unquoted --chip ${GPIOSIM_CHIP_NAME[sim0]} 1 6
+
+ output_is "1=active 6=inactive"
+ status_is 0
+}
+
+@test "gpioget: with lines strictly by name" {
+ # not suggesting this setup makes any sense
+ # - just test that we can deal with it
+ gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1
+
+ gpiosim_set_pull sim0 1 pull-up
+ gpiosim_set_pull sim0 6 pull-down
+
+ run_tool gpioget --by-name --chip ${GPIOSIM_CHIP_NAME[sim0]} 1 6
+
+ output_is "\"1\"=inactive \"6\"=active"
+ status_is 0
+
+ run_tool gpioget --by-name --unquoted --chip ${GPIOSIM_CHIP_NAME[sim0]} 1 6
+
+ output_is "1=inactive 6=active"
+ status_is 0
+}
+
+@test "gpioget: with no arguments" {
+ run_tool gpioget
+
+ output_regex_match ".*at least one GPIO line must be specified"
+ status_is 1
+}
+
+@test "gpioget: with chip but no line specified" {
+ gpiosim_chip sim0 num_lines=8
+
+ run_tool gpioget --chip ${GPIOSIM_CHIP_NAME[sim0]}
+
+ output_regex_match ".*at least one GPIO line must be specified"
+ status_is 1
+}
+
+@test "gpioget: with offset out of range" {
+ gpiosim_chip sim0 num_lines=4
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioget --chip $sim0 0 1 2 3 4 5
+
+ output_regex_match ".*offset 4 is out of range on chip '$sim0'"
+ output_regex_match ".*offset 5 is out of range on chip '$sim0'"
+ status_is 1
+}
+
+@test "gpioget: with nonexistent line" {
+ run_tool gpioget nonexistent-line
+
+ output_regex_match ".*cannot find line 'nonexistent-line'"
+ status_is 1
+}
+
+@test "gpioget: with same line twice" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ # by offset
+ run_tool gpioget --chip $sim0 0 0
+
+ output_regex_match ".*lines '0' and '0' are the same line"
+ status_is 1
+
+ # by name
+ run_tool gpioget foo foo
+
+ output_regex_match ".*lines 'foo' and 'foo' are the same line"
+ status_is 1
+
+ # by chip and name
+ run_tool gpioget --chip $sim0 foo foo
+
+ output_regex_match ".*lines 'foo' and 'foo' are the same line"
+ status_is 1
+
+ # by name and offset
+ run_tool gpioget --chip $sim0 foo 1
+
+ output_regex_match ".*lines 'foo' and '1' are the same line"
+ status_is 1
+
+ # by offset and name
+ run_tool gpioget --chip $sim0 1 foo
+
+ output_regex_match ".*lines '1' and 'foo' are the same line"
+ status_is 1
+}
+
+@test "gpioget: with invalid bias" {
+ gpiosim_chip sim0 num_lines=8
+
+ run_tool gpioget --bias=bad --chip ${GPIOSIM_CHIP_NAME[sim0]} 0 1
+
+ output_regex_match ".*invalid bias.*"
+ status_is 1
+}
+
+@test "gpioget: with invalid hold-period" {
+ gpiosim_chip sim0 num_lines=8
+
+ run_tool gpioget --hold-period=bad --chip ${GPIOSIM_CHIP_NAME[sim0]} 0
+
+ output_regex_match ".*invalid period.*"
+ status_is 1
+}
+
+#
+# gpioset test cases
+#
+
+@test "gpioset: by name" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+
+ dut_run gpioset --banner foo=1
+
+ gpiosim_check_value sim0 1 1
+}
+
+@test "gpioset: by offset" {
+ gpiosim_chip sim0 num_lines=8
+
+ dut_run gpioset --banner --chip ${GPIOSIM_CHIP_NAME[sim0]} 1=1
+
+ gpiosim_check_value sim0 1 1
+}
+
+@test "gpioset: by symlink" {
+ gpiosim_chip sim0 num_lines=8
+ gpiosim_chip_symlink sim0 .
+
+ dut_run gpioset --banner --chip $GPIOSIM_CHIP_LINK 1=1
+
+ gpiosim_check_value sim0 1 1
+}
+
+@test "gpioset: by chip and name" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+ gpiosim_chip sim1 num_lines=8 line_name=3:foo
+
+ dut_run gpioset --banner --chip ${GPIOSIM_CHIP_NAME[sim1]} foo=1
+
+ gpiosim_check_value sim1 3 1
+}
+
+@test "gpioset: first matching named line" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \
+ line_name=3:foobar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \
+ line_name=4:xyz line_name=7:foobar
+ gpiosim_chip sim2 num_lines=16
+
+ dut_run gpioset --banner foobar=1
+
+ gpiosim_check_value sim0 3 1
+}
+
+@test "gpioset: multiple lines" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpioset --banner --chip $sim0 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1
+
+ gpiosim_check_value sim0 0 0
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 2 1
+ gpiosim_check_value sim0 3 1
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 5 1
+ gpiosim_check_value sim0 6 0
+ gpiosim_check_value sim0 7 1
+}
+
+@test "gpioset: multiple lines by name and offset" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar
+
+ dut_run gpioset --banner --chip ${GPIOSIM_CHIP_NAME[sim0]} 0=1 foo=1 bar=1 3=1
+
+ gpiosim_check_value sim0 0 1
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 2 1
+ gpiosim_check_value sim0 3 1
+}
+
+
+@test "gpioset: multiple lines across multiple chips" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=4:xyz
+
+ dut_run gpioset --banner foo=1 bar=1 baz=1 xyz=1
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 2 1
+ gpiosim_check_value sim1 0 1
+ gpiosim_check_value sim1 4 1
+}
+
+@test "gpioset: with active-low" {
+ gpiosim_chip sim0 num_lines=8
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpioset --banner --active-low -c $sim0 \
+ 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1
+
+ gpiosim_check_value sim0 0 1
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 2 0
+ gpiosim_check_value sim0 3 0
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 5 0
+ gpiosim_check_value sim0 6 1
+ gpiosim_check_value sim0 7 0
+}
+
+@test "gpioset: with consumer" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar
+ gpiosim_chip sim1 num_lines=8 line_name=3:baz line_name=4:xyz
+
+ dut_run gpioset --banner --consumer gpio-tools-tests foo=1 baz=0
+
+ run_tool gpioinfo
+
+ output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input"
+ output_regex_match \
+"\\s+line\\s+1:\\s+\"foo\"\\s+output consumer=\"gpio-tools-tests\""
+ output_regex_match \
+"\\s+line\\s+3:\\s+\"baz\"\\s+output consumer=\"gpio-tools-tests\""
+ status_is 0
+}
+
+@test "gpioset: with push-pull" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpioset --banner --drive=push-pull --chip $sim0 \
+ 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1
+
+ gpiosim_check_value sim0 0 0
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 2 1
+ gpiosim_check_value sim0 3 1
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 5 1
+ gpiosim_check_value sim0 6 0
+ gpiosim_check_value sim0 7 1
+}
+
+@test "gpioset: with open-drain" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 2 pull-up
+ gpiosim_set_pull sim0 3 pull-up
+ gpiosim_set_pull sim0 5 pull-up
+ gpiosim_set_pull sim0 7 pull-up
+
+ dut_run gpioset --banner --drive=open-drain --chip $sim0 \
+ 0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1
+
+ gpiosim_check_value sim0 0 0
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 2 1
+ gpiosim_check_value sim0 3 1
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 5 1
+ gpiosim_check_value sim0 6 0
+ gpiosim_check_value sim0 7 1
+}
+
+@test "gpioset: with open-source" {
+ gpiosim_chip sim0 num_lines=8
+
+ gpiosim_set_pull sim0 2 pull-up
+ gpiosim_set_pull sim0 3 pull-up
+ gpiosim_set_pull sim0 5 pull-up
+ gpiosim_set_pull sim0 7 pull-up
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpioset --banner --drive=open-source --chip $sim0 \
+ 0=0 1=0 2=1 3=0 4=1 5=1 6=0 7=1
+
+ gpiosim_check_value sim0 0 0
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 2 1
+ gpiosim_check_value sim0 3 1
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 5 1
+ gpiosim_check_value sim0 6 0
+ gpiosim_check_value sim0 7 1
+}
+
+@test "gpioset: with pull-up" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpioset --banner --bias=pull-up --drive=open-drain \
+ --chip $sim0 0=0 1=0 2=1 3=0 4=1 5=1 6=0 7=1
+
+ gpiosim_check_value sim0 0 0
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 2 1
+ gpiosim_check_value sim0 3 0
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 5 1
+ gpiosim_check_value sim0 6 0
+ gpiosim_check_value sim0 7 1
+}
+
+@test "gpioset: with pull-down" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpioset --banner --bias=pull-down --drive=open-source \
+ --chip $sim0 0=0 1=0 2=1 3=0 4=1 5=1 6=0 7=1
+
+ gpiosim_check_value sim0 0 0
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 2 1
+ gpiosim_check_value sim0 3 0
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 5 1
+ gpiosim_check_value sim0 6 0
+ gpiosim_check_value sim0 7 1
+}
+
+@test "gpioset: with value variants" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 0 pull-up
+ gpiosim_set_pull sim0 1 pull-down
+ gpiosim_set_pull sim0 2 pull-down
+ gpiosim_set_pull sim0 3 pull-up
+ gpiosim_set_pull sim0 4 pull-down
+ gpiosim_set_pull sim0 5 pull-up
+ gpiosim_set_pull sim0 6 pull-up
+ gpiosim_set_pull sim0 7 pull-down
+
+ dut_run gpioset --banner --chip $sim0 0=0 1=1 2=active \
+ 3=inactive 4=on 5=off 6=false 7=true
+
+ gpiosim_check_value sim0 0 0
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 2 1
+ gpiosim_check_value sim0 3 0
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 5 0
+ gpiosim_check_value sim0 6 0
+ gpiosim_check_value sim0 7 1
+}
+
+@test "gpioset: with hold-period" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 5 pull-up
+
+ dut_run gpioset --banner --hold-period=1200ms -t0 --chip $sim0 0=1 5=0 7=1
+
+ gpiosim_check_value sim0 0 1
+ gpiosim_check_value sim0 5 0
+ gpiosim_check_value sim0 7 1
+
+ dut_wait
+
+ status_is 0
+}
+
+@test "gpioset: interactive exit" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpioset --interactive --chip $sim0 1=0 2=1 5=1 6=0 7=1
+
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 2 1
+ gpiosim_check_value sim0 5 1
+ gpiosim_check_value sim0 6 0
+ gpiosim_check_value sim0 7 1
+
+ dut_write "exit"
+ dut_wait
+
+ status_is 0
+}
+
+@test "gpioset: interactive help" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \
+ line_name=7:baz
+
+ dut_run gpioset --interactive foo=1 bar=0 baz=0
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 7 0
+
+ dut_write "help"
+
+ dut_read
+ output_regex_match "COMMANDS:.*"
+ output_regex_match ".*get \[line\]\.\.\..*"
+ output_regex_match ".*set <line=value>\.\.\..*"
+ output_regex_match ".*toggle \[line\]\.\.\..*"
+ output_regex_match ".*sleep <period>.*"
+}
+
+@test "gpioset: interactive get" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \
+ line_name=7:baz
+
+ dut_run gpioset --interactive foo=1 bar=0 baz=0
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 7 0
+
+ dut_write "get"
+
+ dut_read
+ output_regex_match "\"foo\"=active \"bar\"=inactive \"baz\"=inactive"
+
+ dut_write "get bar"
+
+ dut_read
+ output_regex_match "\"bar\"=inactive"
+}
+
+@test "gpioset: interactive get unquoted" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \
+ line_name=7:baz
+
+ dut_run gpioset --interactive --unquoted foo=1 bar=0 baz=0
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 7 0
+
+ dut_write "get"
+
+ dut_read
+ output_regex_match "foo=active bar=inactive baz=inactive"
+
+ dut_write "get bar"
+
+ dut_read
+ output_regex_match "bar=inactive"
+}
+
+@test "gpioset: interactive set" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \
+ line_name=7:baz
+
+ dut_run gpioset --interactive foo=1 bar=0 baz=0
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 7 0
+
+ dut_write "set bar=active"
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 7 0
+ dut_write "get"
+ dut_read
+ output_regex_match "\"foo\"=active \"bar\"=active \"baz\"=inactive"
+}
+
+@test "gpioset: interactive toggle" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \
+ line_name=7:baz
+
+ gpiosim_set_pull sim0 4 pull-up
+ gpiosim_set_pull sim0 7 pull-up
+
+ dut_run gpioset -i foo=1 bar=0 baz=0
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 7 0
+
+ dut_write "toggle"
+
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 7 1
+ dut_write "get"
+ dut_read
+ output_regex_match "\"foo\"=inactive\\s+\"bar\"=active\\s+\"baz\"=active\\s*"
+
+ dut_write "toggle baz"
+
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 7 0
+ dut_write "get"
+ dut_read
+ output_regex_match "\"foo\"=inactive\\s+\"bar\"=active\\s+\"baz\"=inactive\\s*"
+}
+
+@test "gpioset: interactive sleep" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \
+ line_name=7:baz
+
+ dut_run gpioset --interactive foo=1 bar=0 baz=0
+
+ dut_write "sleep 500ms"
+ dut_flush
+
+ assert_fail dut_readable
+
+ sleep 1
+
+ # prompt, but not a full line...
+ dut_readable
+}
+
+@test "gpioset: toggle (continuous)" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \
+ line_name=7:baz
+
+ gpiosim_set_pull sim0 4 pull-up
+ gpiosim_set_pull sim0 7 pull-up
+
+ dut_run gpioset --banner --toggle 1s foo=1 bar=0 baz=0
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 7 0
+
+ sleep 1
+
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 7 1
+
+ sleep 1
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 7 0
+}
+
+@test "gpioset: toggle (terminated)" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \
+ line_name=7:baz
+
+ gpiosim_set_pull sim0 4 pull-up
+
+ # hold-period to allow test to sample before gpioset exits
+ dut_run gpioset --banner --toggle 1s,0 -p 600ms foo=1 bar=0 baz=1
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 7 1
+
+ sleep 1
+
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 4 1
+ gpiosim_check_value sim0 7 0
+
+ dut_wait
+
+ status_is 0
+
+ # using --toggle 0 to exit
+ # hold-period to allow test to sample before gpioset exits
+ dut_run gpioset --banner -t0 -p 600ms foo=1 bar=0 baz=1
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 4 0
+ gpiosim_check_value sim0 7 1
+
+ dut_wait
+
+ status_is 0
+}
+
+@test "gpioset: with invalid toggle period" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \
+ line_name=7:baz
+
+ run_tool gpioset --toggle 1ns foo=1 bar=0 baz=0
+
+ output_regex_match ".*invalid period.*"
+ status_is 1
+}
+
+@test "gpioset: with strict named line check" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \
+ line_name=3:foobar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \
+ line_name=4:xyz line_name=7:foobar
+ gpiosim_chip sim2 num_lines=16
+
+ run_tool gpioset --strict foobar=active
+
+ output_regex_match ".*line 'foobar' is not unique"
+ status_is 1
+}
+
+@test "gpioset: with lines by offset" {
+ # not suggesting this setup makes any sense
+ # - just test that we can deal with it
+ gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1
+
+ gpiosim_set_pull sim0 1 pull-down
+ gpiosim_set_pull sim0 6 pull-up
+
+ dut_run gpioset --banner --chip ${GPIOSIM_CHIP_NAME[sim0]} 6=1 1=0
+
+ gpiosim_check_value sim0 1 0
+ gpiosim_check_value sim0 6 1
+}
+
+@test "gpioset: with lines strictly by name" {
+ # not suggesting this setup makes any sense
+ # - just test that we can deal with it
+ gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1
+
+ gpiosim_set_pull sim0 1 pull-down
+ gpiosim_set_pull sim0 6 pull-up
+
+ dut_run gpioset --banner --by-name --chip ${GPIOSIM_CHIP_NAME[sim0]} 6=1 1=0
+
+ gpiosim_check_value sim0 1 1
+ gpiosim_check_value sim0 6 0
+}
+
+@test "gpioset: interactive after SIGINT" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+
+ dut_run gpioset -i foo=1
+
+ dut_kill -SIGINT
+ dut_wait
+
+ status_is 130
+}
+
+@test "gpioset: interactive after SIGTERM" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+
+ dut_run gpioset -i foo=1
+
+ dut_kill -SIGTERM
+ dut_wait
+
+ status_is 143
+}
+
+@test "gpioset: with no arguments" {
+ run_tool gpioset
+
+ status_is 1
+ output_regex_match ".*at least one GPIO line value must be specified"
+}
+
+@test "gpioset: with chip but no line specified" {
+ gpiosim_chip sim0 num_lines=8
+
+ run_tool gpioset --chip ${GPIOSIM_CHIP_NAME[sim0]}
+
+ output_regex_match ".*at least one GPIO line value must be specified"
+ status_is 1
+}
+
+@test "gpioset: with offset out of range" {
+ gpiosim_chip sim0 num_lines=4
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioset --chip $sim0 0=1 1=1 2=1 3=1 4=1 5=1
+
+ output_regex_match ".*offset 4 is out of range on chip '$sim0'"
+ output_regex_match ".*offset 5 is out of range on chip '$sim0'"
+ status_is 1
+}
+
+@test "gpioset: with invalid hold-period" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioset --hold-period=bad --chip $sim0 0=1
+
+ output_regex_match ".*invalid period.*"
+ status_is 1
+}
+
+@test "gpioset: with invalid value" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ # by name
+ run_tool gpioset --chip $sim0 0=c
+
+ output_regex_match ".*invalid line value.*"
+ status_is 1
+
+ # by value
+ run_tool gpioset --chip $sim0 0=3
+
+ output_regex_match ".*invalid line value.*"
+ status_is 1
+}
+
+@test "gpioset: with invalid offset" {
+ gpiosim_chip sim0 num_lines=8
+
+ run_tool gpioset --chip ${GPIOSIM_CHIP_NAME[sim0]} 4000000000=0
+
+ output_regex_match ".*cannot find line '4000000000'"
+ status_is 1
+}
+
+@test "gpioset: with invalid bias" {
+ gpiosim_chip sim0 num_lines=8
+
+ run_tool gpioset --bias=bad --chip ${GPIOSIM_CHIP_NAME[sim0]} 0=1 1=1
+
+ output_regex_match ".*invalid bias.*"
+ status_is 1
+}
+
+@test "gpioset: with invalid drive" {
+ gpiosim_chip sim0 num_lines=8
+
+ run_tool gpioset --drive=bad --chip ${GPIOSIM_CHIP_NAME[sim0]} 0=1 1=1
+
+ output_regex_match ".*invalid drive.*"
+ status_is 1
+}
+
+@test "gpioset: with daemonize and interactive" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioset --interactive --daemonize --chip $sim0 0=1
+
+ output_regex_match ".*can't combine daemonize with interactive"
+ status_is 1
+}
+
+@test "gpioset: with interactive and toggle" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpioset --interactive --toggle 1s --chip $sim0 0=1
+
+ output_regex_match ".*can't combine interactive with toggle"
+ status_is 1
+}
+
+@test "gpioset: with nonexistent line" {
+ run_tool gpioset nonexistent-line=0
+
+ output_regex_match ".*cannot find line 'nonexistent-line'"
+ status_is 1
+}
+
+@test "gpioset: with same line twice" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ # by offset
+ run_tool gpioset --chip $sim0 0=1 0=1
+
+ output_regex_match ".*lines '0' and '0' are the same line"
+ status_is 1
+
+ # by name
+ run_tool gpioset --chip $sim0 foo=1 foo=1
+
+ output_regex_match ".*lines 'foo' and 'foo' are the same line"
+ status_is 1
+
+ # by name and offset
+ run_tool gpioset --chip $sim0 foo=1 1=1
+
+ output_regex_match ".*lines 'foo' and '1' are the same line"
+ status_is 1
+
+ # by offset and name
+ run_tool gpioset --chip $sim0 1=1 foo=1
+
+ output_regex_match ".*lines '1' and 'foo' are the same line"
+ status_is 1
+}
+
+#
+# gpiomon test cases
+#
+
+@test "gpiomon: by name" {
+ gpiosim_chip sim0 num_lines=8 line_name=4:foo
+
+ gpiosim_set_pull sim0 4 pull-up
+
+ dut_run gpiomon --banner --edges=rising foo
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-down
+ gpiosim_set_pull sim0 4 pull-up
+ gpiosim_set_pull sim0 4 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+\"foo\""
+ assert_fail dut_readable
+}
+
+@test "gpiomon: by offset" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 4 pull-up
+
+ dut_run gpiomon --banner --edges=rising --chip $sim0 4
+ dut_regex_match "Monitoring line .*"
+
+ gpiosim_set_pull sim0 4 pull-down
+ gpiosim_set_pull sim0 4 pull-up
+ gpiosim_set_pull sim0 4 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4"
+ assert_fail dut_readable
+}
+
+@test "gpiomon: by symlink" {
+ gpiosim_chip sim0 num_lines=8
+ gpiosim_chip_symlink sim0 .
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 4 pull-up
+
+ dut_run gpiomon --banner --edges=rising --chip $GPIOSIM_CHIP_LINK 4
+ dut_regex_match "Monitoring line .*"
+
+ gpiosim_set_pull sim0 4 pull-down
+ gpiosim_set_pull sim0 4 pull-up
+ gpiosim_set_pull sim0 4 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0\\s+4"
+ assert_fail dut_readable
+}
+
+
+@test "gpiomon: by chip and name" {
+ gpiosim_chip sim0 num_lines=8 line_name=0:foo
+ gpiosim_chip sim1 num_lines=8 line_name=2:foo
+
+ local sim1=${GPIOSIM_CHIP_NAME[sim1]}
+
+ gpiosim_set_pull sim1 0 pull-up
+
+ dut_run gpiomon --banner --edges=rising --chip $sim1 foo
+ dut_regex_match "Monitoring line .*"
+
+ gpiosim_set_pull sim1 2 pull-down
+ gpiosim_set_pull sim1 2 pull-up
+ gpiosim_set_pull sim1 2 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim1 2 \"foo\""
+ assert_fail dut_readable
+}
+
+@test "gpiomon: first matching named line" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \
+ line_name=3:foobar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \
+ line_name=4:xyz line_name=7:foobar
+ gpiosim_chip sim2 num_lines=16
+
+ dut_run gpiomon --banner foobar
+ dut_regex_match "Monitoring line .*"
+
+ gpiosim_set_pull sim0 3 pull-up
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+\"foobar\""
+ assert_fail dut_readable
+}
+
+@test "gpiomon: rising edge" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 4 pull-up
+
+ dut_run gpiomon --banner --edges=rising --chip $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-down
+ gpiosim_set_pull sim0 4 pull-up
+ gpiosim_set_pull sim0 4 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4"
+ assert_fail dut_readable
+}
+
+@test "gpiomon: falling edge" {
+ gpiosim_chip sim0 num_lines=8
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 4 pull-down
+
+ dut_run gpiomon --banner --edges=falling --chip $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ gpiosim_set_pull sim0 4 pull-down
+ gpiosim_set_pull sim0 4 pull-up
+ dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4"
+ assert_fail dut_readable
+}
+
+@test "gpiomon: both edges" {
+ gpiosim_chip sim0 num_lines=8
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner --edges=both --chip $sim0 4
+ dut_regex_match "Monitoring line .*"
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4"
+
+ gpiosim_set_pull sim0 4 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4"
+}
+
+@test "gpiomon: with pull-up" {
+ gpiosim_chip sim0 num_lines=8
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 4 pull-down
+
+ dut_run gpiomon --banner --bias=pull-up --chip $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4"
+
+ assert_fail dut_readable
+}
+
+@test "gpiomon: with pull-down" {
+ gpiosim_chip sim0 num_lines=8
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 4 pull-up
+
+ dut_run gpiomon --banner --bias=pull-down --chip $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4"
+
+ assert_fail dut_readable
+}
+
+@test "gpiomon: with active-low" {
+ gpiosim_chip sim0 num_lines=8
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 4 pull-up
+
+ dut_run gpiomon --banner --active-low --chip $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4"
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4"
+
+ assert_fail dut_readable
+}
+
+@test "gpiomon: with consumer" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar
+ gpiosim_chip sim1 num_lines=8 line_name=3:baz line_name=4:xyz
+
+ dut_run gpiomon --banner --consumer gpio-tools-tests foo baz
+
+ run_tool gpioinfo
+
+ output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input"
+ output_regex_match \
+"\\s+line\\s+1:\\s+\"foo\"\\s+input edges=both consumer=\"gpio-tools-tests\""
+ output_regex_match \
+"\\s+line\\s+3:\\s+\"baz\"\\s+input edges=both consumer=\"gpio-tools-tests\""
+ status_is 0
+}
+
+@test "gpiomon: with quiet mode" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner --edges=rising --quiet --chip $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ assert_fail dut_readable
+}
+
+@test "gpiomon: with num-events" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ # redirect, as gpiomon exits after 4 events
+ dut_run_redirect gpiomon --num-events=4 --chip $sim0 4
+
+ gpiosim_set_pull sim0 4 pull-up
+ gpiosim_set_pull sim0 4 pull-down
+ gpiosim_set_pull sim0 4 pull-up
+ gpiosim_set_pull sim0 4 pull-down
+
+ dut_wait
+ status_is 0
+ dut_read_redirect
+
+ regex_matches "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4" "${lines[0]}"
+ regex_matches "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4" "${lines[1]}"
+ regex_matches "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 4" "${lines[2]}"
+ regex_matches "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 4" "${lines[3]}"
+ num_lines_is 4
+}
+
+@test "gpiomon: multiple lines" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner --format=%o --chip $sim0 1 3 2 5 4
+ dut_regex_match "Monitoring lines .*"
+
+ gpiosim_set_pull sim0 2 pull-up
+ dut_regex_match "2"
+ gpiosim_set_pull sim0 3 pull-up
+ dut_regex_match "3"
+ gpiosim_set_pull sim0 4 pull-up
+ dut_regex_match "4"
+
+ assert_fail dut_readable
+}
+
+@test "gpiomon: multiple lines by name and offset" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner --format=%o --chip $sim0 foo bar 3
+ dut_regex_match "Monitoring lines .*"
+
+ gpiosim_set_pull sim0 2 pull-up
+ dut_regex_match "2"
+ gpiosim_set_pull sim0 3 pull-up
+ dut_regex_match "3"
+ gpiosim_set_pull sim0 1 pull-up
+ dut_regex_match "1"
+
+ assert_fail dut_readable
+}
+
+@test "gpiomon: multiple lines across multiple chips" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=4:xyz
+
+ dut_run gpiomon --banner --format=%l foo bar baz
+ dut_regex_match "Monitoring lines .*"
+
+ gpiosim_set_pull sim0 2 pull-up
+ dut_regex_match "bar"
+ gpiosim_set_pull sim1 0 pull-up
+ dut_regex_match "baz"
+ gpiosim_set_pull sim0 1 pull-up
+ dut_regex_match "foo"
+
+ assert_fail dut_readable
+}
+
+@test "gpiomon: exit after SIGINT" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner --chip $sim0 4
+ dut_regex_match "Monitoring line .*"
+
+ dut_kill -SIGINT
+ dut_wait
+
+ status_is 130
+}
+
+@test "gpiomon: exit after SIGTERM" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner --chip $sim0 4
+ dut_regex_match "Monitoring line .*"
+
+ dut_kill -SIGTERM
+ dut_wait
+
+ status_is 143
+}
+
+@test "gpiomon: with nonexistent line" {
+ run_tool gpiomon nonexistent-line
+
+ status_is 1
+ output_regex_match ".*cannot find line 'nonexistent-line'"
+}
+
+@test "gpiomon: with same line twice" {
+ gpiosim_chip sim0 num_lines=8 line_name=1:foo
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ # by offset
+ run_tool gpiomon --chip $sim0 0 0
+
+ output_regex_match ".*lines '0' and '0' are the same line"
+ status_is 1
+
+ # by name
+ run_tool gpiomon foo foo
+
+ output_regex_match ".*lines 'foo' and 'foo' are the same line"
+ status_is 1
+
+ # by name and offset
+ run_tool gpiomon --chip $sim0 1 foo
+
+ output_regex_match ".*lines '1' and 'foo' are the same line"
+ status_is 1
+}
+
+@test "gpiomon: with strict named line check" {
+ gpiosim_chip sim0 num_lines=4 line_name=1:foo line_name=2:bar \
+ line_name=3:foobar
+ gpiosim_chip sim1 num_lines=8 line_name=0:baz line_name=2:foobar \
+ line_name=4:xyz line_name=7:foobar
+ gpiosim_chip sim2 num_lines=16
+
+ run_tool gpiomon --strict foobar
+
+ output_regex_match ".*line 'foobar' is not unique"
+ status_is 1
+}
+@test "gpiomon: with lines by offset" {
+ # not suggesting this setup makes any sense
+ # - just test that we can deal with it
+ gpiosim_chip sim0 num_lines=8 line_name=1:6 line_name=6:1
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 1 pull-up
+
+ dut_run gpiomon --banner --chip $sim0 6 1
+ dut_flush
+
+ gpiosim_set_pull sim0 1 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 1"
+
+ gpiosim_set_pull sim0 1 pull-up
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 1"
+
+ gpiosim_set_pull sim0 6 pull-up
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 6"
+
+ gpiosim_set_pull sim0 6 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 6"
+
+ assert_fail dut_readable
+}
+
+@test "gpiomon: with lines strictly by name" {
+ # not suggesting this setup makes sense
+ # - just test that we can deal with it
+ gpiosim_chip sim0 num_lines=8 line_name=1:42 line_name=6:13
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ gpiosim_set_pull sim0 1 pull-up
+
+ dut_run gpiomon --banner --by-name --chip $sim0 42 13
+ dut_flush
+
+ gpiosim_set_pull sim0 1 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 1"
+
+ gpiosim_set_pull sim0 1 pull-up
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 1"
+
+ gpiosim_set_pull sim0 6 pull-up
+ dut_regex_match "[0-9]+\.[0-9]+\\s+rising\\s+$sim0 6"
+
+ gpiosim_set_pull sim0 6 pull-down
+ dut_regex_match "[0-9]+\.[0-9]+\\s+falling\\s+$sim0 6"
+
+ assert_fail dut_readable
+}
+
+@test "gpiomon: with no arguments" {
+ run_tool gpiomon
+
+ output_regex_match ".*at least one GPIO line must be specified"
+ status_is 1
+}
+
+@test "gpiomon: with no line specified" {
+ gpiosim_chip sim0 num_lines=8
+
+ run_tool gpiomon --chip ${GPIOSIM_CHIP_NAME[sim0]}
+
+ output_regex_match ".*at least one GPIO line must be specified"
+ status_is 1
+}
+
+@test "gpiomon: with offset out of range" {
+ gpiosim_chip sim0 num_lines=4
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpiomon --chip $sim0 5
+
+ output_regex_match ".*offset 5 is out of range on chip '$sim0'"
+ status_is 1
+}
+
+@test "gpiomon: with invalid bias" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ run_tool gpiomon --bias=bad -c $sim0 0 1
+
+ output_regex_match ".*invalid bias.*"
+ status_is 1
+}
+
+@test "gpiomon: with custom format (event type + offset)" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=%e %o" -c $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_read
+ output_is "1 4"
+}
+
+@test "gpiomon: with custom format (event type + offset joined)" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=%e%o" -c $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_read
+ output_is "14"
+}
+
+@test "gpiomon: with custom format (edge, chip and line)" {
+ gpiosim_chip sim0 num_lines=8 line_name=4:baz
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=%e %o %E %c %l" -c $sim0 baz
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_regex_match "1 4 rising $sim0 baz"
+}
+
+@test "gpiomon: with custom format (seconds timestamp)" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=%e %o %S" -c $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_regex_match "1 4 [0-9]+\\.[0-9]+"
+}
+
+@test "gpiomon: with custom format (UTC timestamp)" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=%U %e %o " --event-clock=realtime \
+ -c $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_regex_match \
+"[0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9]\\.[0-9]+Z 1 4"
+}
+
+@test "gpiomon: with custom format (localtime timestamp)" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=%L %e %o" --event-clock=realtime \
+ -c $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_regex_match \
+"[0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9]\\.[0-9]+ 1 4"
+}
+
+@test "gpiomon: with custom format (double percent sign)" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=start%%end" -c $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_read
+ output_is "start%end"
+}
+
+@test "gpiomon: with custom format (double percent sign + event type specifier)" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=%%e" -c $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_read
+ output_is "%e"
+}
+
+@test "gpiomon: with custom format (single percent sign)" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=%" -c $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_read
+ output_is "%"
+}
+
+@test "gpiomon: with custom format (single percent sign between other characters)" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=foo % bar" -c $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_read
+ output_is "foo % bar"
+}
+
+@test "gpiomon: with custom format (unknown specifier)" {
+ gpiosim_chip sim0 num_lines=8
+
+ local sim0=${GPIOSIM_CHIP_NAME[sim0]}
+
+ dut_run gpiomon --banner "--format=%x" -c $sim0 4
+ dut_flush
+
+ gpiosim_set_pull sim0 4 pull-up
+ dut_read
+ output_is "%x"
+}
--
2.38.1
next prev parent reply other threads:[~2022-11-14 4:01 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-11-14 4:00 [libgpiod v2][PATCH v4 0/5] tools: improvements for v2 Kent Gibson
2022-11-14 4:00 ` [libgpiod v2][PATCH v4 1/5] tools: remove old code to simplify review Kent Gibson
2022-11-14 4:00 ` [libgpiod v2][PATCH v4 2/5] tools: line name focussed rework Kent Gibson
2022-11-14 4:01 ` Kent Gibson [this message]
2022-11-15 13:08 ` [libgpiod v2][PATCH v4 3/5] tools: tests for " Bartosz Golaszewski
2022-11-15 23:25 ` Kent Gibson
2022-11-14 4:01 ` [libgpiod v2][PATCH v4 4/5] tools: add gpionotify Kent Gibson
2022-11-14 4:01 ` [libgpiod v2][PATCH v4 5/5] tools: gpionotify tests Kent Gibson
2022-11-14 14:26 ` [libgpiod v2][PATCH v4 0/5] tools: improvements for v2 Bartosz Golaszewski
2022-11-14 15:12 ` Kent Gibson
2022-11-14 16:42 ` Bartosz Golaszewski
2022-11-14 23:35 ` Kent Gibson
2022-11-15 3:44 ` Kent Gibson
2022-11-15 14:32 ` Bartosz Golaszewski
2022-11-16 0:12 ` Kent Gibson
2022-11-16 8:14 ` Bartosz Golaszewski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20221114040102.66031-4-warthog618@gmail.com \
--to=warthog618@gmail.com \
--cc=brgl@bgdev.pl \
--cc=linux-gpio@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.