Add a byte to hex function with unit test
This commit is contained in:
parent
ff3e779f22
commit
c059620b09
6 changed files with 356 additions and 2 deletions
14
Makefile
14
Makefile
|
@ -3,14 +3,26 @@ AS=riscv64-unknown-elf-as
|
|||
ASFLAGS=-g -mabi=ilp32e -march=rv32ec
|
||||
CFLAGS=$(ASFLAGS)
|
||||
LD=riscv64-unknown-elf-ld
|
||||
export JQ?=jaq
|
||||
export RV32EMU?=$(HOME)/Source/github.com/sysprog21/rv32emu/build/rv32emu
|
||||
|
||||
all: calc.elf
|
||||
|
||||
check: tests
|
||||
@tests/unittest
|
||||
|
||||
hello.elf: hello.o
|
||||
$(LD) -m elf32lriscv $^ -o $@
|
||||
|
||||
calc.elf: calc.o
|
||||
calc.elf: hex.o calc.o
|
||||
$(LD) -m elf32lriscv -T link.ld $^ -o $@
|
||||
|
||||
tests: tests/btohex.elf
|
||||
|
||||
tests/btohex.elf: hex.o tests/btohex.o
|
||||
$(LD) -m elf32lriscv -T link.ld $^ -o $@
|
||||
|
||||
%.o : %.s
|
||||
$(AS) $(ASFLAGS) $< -o $@
|
||||
|
||||
.PHONY: check tests
|
||||
|
|
13
calc.s
13
calc.s
|
@ -20,7 +20,7 @@ regnames:
|
|||
|
||||
.section .bss
|
||||
buf: .skip 20 # room for 20 byte string
|
||||
# x10: ABCDEFG\n is 23 chars
|
||||
# x10: ABCDEFGH\n is 14 chars
|
||||
|
||||
.section .data
|
||||
buflen: .byte 0 # length of buf string
|
||||
|
@ -160,3 +160,14 @@ memcpy_loop:
|
|||
j memcpy_loop
|
||||
memcpy_done:
|
||||
ret
|
||||
|
||||
# Write hex representation into buffer
|
||||
# arguments:
|
||||
# a0: value to format
|
||||
# a1: address of buffer to write to
|
||||
# temporaries used:
|
||||
# t0
|
||||
# return:
|
||||
# none
|
||||
tohex:
|
||||
|
||||
|
|
29
hex.s
Normal file
29
hex.s
Normal file
|
@ -0,0 +1,29 @@
|
|||
.global btohex
|
||||
|
||||
.section .rodata
|
||||
hexchars:
|
||||
.ascii "0123456789ABCDEF"
|
||||
|
||||
.text
|
||||
|
||||
# Convert byte to ASCII hex
|
||||
# arguments:
|
||||
# a0: value to format
|
||||
# temporaries used:
|
||||
# t0, t1
|
||||
# return:
|
||||
# a0: value in ASCII hex
|
||||
btohex:
|
||||
andi t0, a0, 0xF # Mask off lower nybble
|
||||
la a1, hexchars # load address of hexchars
|
||||
add a2, a1, t0 # offset to nybble
|
||||
lbu t0, 0(a2) # load hex char at offset
|
||||
srli t1, a0, 4 # shift upper nybble down
|
||||
andi t1, t1, 0xF # mask off upper nybble
|
||||
add a2, a1, t1 # offset to nybble
|
||||
lbu t1, 0(a2) # load hex char at offset
|
||||
mv a0, t1 # copy upper char to a0
|
||||
slli a0, a0, 8 # shuft upper char up
|
||||
or a0, a0, t0 # OR the lower char into a0
|
||||
ret
|
||||
|
20
tests/btohex.s
Normal file
20
tests/btohex.s
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Test for btohex
|
||||
|
||||
.org 0
|
||||
# Provide program starting address to linker
|
||||
.global _start
|
||||
|
||||
.extern btohex
|
||||
|
||||
/* newlib system calls */
|
||||
.set SYSEXIT, 93
|
||||
|
||||
.text
|
||||
_start:
|
||||
li a0, 0xA5
|
||||
jal btohex
|
||||
mv a1, a0
|
||||
|
||||
li t0, SYSEXIT # "exit" syscall
|
||||
la a0, 0 # Use 0 return code
|
||||
ecall # invoke syscall to terminate the program
|
10
tests/test_btohex.sh
Normal file
10
tests/test_btohex.sh
Normal file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_btohex() {
|
||||
# FIXME: Remove grep when this bug is fixed:
|
||||
# https://github.com/sysprog21/rv32emu/issues/561
|
||||
result=$("${RV32EMU}" -d - -q tests/btohex | grep -v '^\d' | "${JQ}" .x11)
|
||||
|
||||
test $? -eq 0 && test "${result}" -eq 16693 # 16693 is A5 in ASCII
|
||||
}
|
||||
|
272
tests/unittest
Executable file
272
tests/unittest
Executable file
|
@ -0,0 +1,272 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# unittest - unit tests framework for shell scripts.
|
||||
#
|
||||
# https://github.com/macie/unittest.sh
|
||||
#
|
||||
# Copyright (c) 2014-2023 Maciej Żok <maciek.zok@gmail.com>
|
||||
# MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
|
||||
#
|
||||
# MESSAGES
|
||||
#
|
||||
|
||||
unittest__print_result() {
|
||||
# Write test status message to stdout
|
||||
# $1 - test location
|
||||
# $2 - test status
|
||||
# prefix: utt9t_
|
||||
utt9t_color_default=''
|
||||
utt9t_color_location=''
|
||||
utt9t_color_status=''
|
||||
|
||||
if { [ -n "${CLICOLOR_FORCE}" ] && [ "${CLICOLOR_FORCE}" -ne 0 ]; } || { [ -t 1 ] && [ -z "${NO_COLOR}" ]; }; then # stdout is interactive terminal
|
||||
utt9t_color_default='\033[0m'
|
||||
case $2 in
|
||||
PASS) # location: default; status: green
|
||||
utt9t_color_location='\033[0m'
|
||||
utt9t_color_status='\033[32m'
|
||||
;;
|
||||
FAIL) # location: red; status: white on red
|
||||
utt9t_color_location='\033[31m'
|
||||
utt9t_color_status='\033[97;41m'
|
||||
;;
|
||||
SKIP) # location: gray; status: gray
|
||||
utt9t_color_location='\033[37m'
|
||||
utt9t_color_status='\033[37m'
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
printf "${utt9t_color_location}%s\t${utt9t_color_status}%s${utt9t_color_default}\n" "$1" "$2"
|
||||
|
||||
unset -v utt9t_color_default utt9t_color_location utt9t_color_status
|
||||
return 0
|
||||
}
|
||||
|
||||
unittest__print_debug() {
|
||||
# $1 - category
|
||||
# $2-... - paragraphs
|
||||
# prefix: utt13o_
|
||||
utt13o_color=''
|
||||
utt13o_color_default=''
|
||||
|
||||
if { [ -n "${CLICOLOR_FORCE}" ] && [ "${CLICOLOR_FORCE}" -ne 0 ]; } || { [ -t 2 ] && [ -z "${NO_COLOR}" ]; }; then # stderr is interactive terminal
|
||||
utt13o_color='\033[34m' # blue
|
||||
utt13o_color_default='\033[0m'
|
||||
utt13o_color_quote='\033[37m' # gray
|
||||
fi
|
||||
|
||||
printf "\n${utt13o_color}-- %s${utt13o_color_default}\n\n" "$1" >&2
|
||||
|
||||
shift 1
|
||||
for utt13o_paragraph in "$@"; do
|
||||
# shellcheck disable=SC2059
|
||||
case ${utt13o_paragraph} in
|
||||
' '*) printf "${utt13o_color_quote}" >&2 ;;
|
||||
*) ;;
|
||||
esac
|
||||
printf "%s${utt13o_color_default}\n\n" "${utt13o_paragraph}" >&2
|
||||
done
|
||||
|
||||
unset -v utt13o_paragraph utt13o_color utt13o_color_default
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# ASSERTIONS
|
||||
#
|
||||
|
||||
# shellcheck disable=SC2317
|
||||
test() {
|
||||
# same arguments as command test(1)
|
||||
UT4e1_test_error_msg=$(/bin/test "$@" 2>&1)
|
||||
case $? in
|
||||
0) ;;
|
||||
1)
|
||||
unittest__print_debug "FAILED TEST [${UNITTEST_CURRENT}]" \
|
||||
'I expected:' \
|
||||
" test$(printf " '%s'" "$@")" \
|
||||
'to be true, but the result was false.'
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
unittest__print_debug "INVALID ASSERTION [${UNITTEST_CURRENT}]" \
|
||||
'I tried to check' \
|
||||
" test$(printf " '%s'" "$@")" \
|
||||
'but I got error with message:' \
|
||||
" ${UT4e1_test_error_msg}" \
|
||||
'Did you use proper operator?' \
|
||||
"Hint: Some operators requires specific type of values. Read 'man test' to learn more."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# FUNCTIONS
|
||||
#
|
||||
|
||||
##
|
||||
# Find files with tests (test_*.sh).
|
||||
# SYNOPSIS:
|
||||
# unittest__test_files [directory...]
|
||||
# OPERANDS:
|
||||
# directory - A pathname of directory to search in. If no directory is
|
||||
# given, it will look for 'tests' directory inside current one. If
|
||||
# a directory is '-', it will use stdin.
|
||||
# STDIN:
|
||||
# (optional) List of directories to search in. Used when call with '-' argument.
|
||||
# STDOUT:
|
||||
# List of 'test_*.sh' file paths.
|
||||
# STDERR:
|
||||
# (optional) Debug/error message.
|
||||
# EXIT STATUS:
|
||||
# 0 - Successfully traversed all directories.
|
||||
# >0 - An error occurred.
|
||||
# EXAMPLES:
|
||||
# unittest__test_files ./unit_tests ./integration_tests
|
||||
# ls ../ | unittest__test_files -
|
||||
# CAVEATS:
|
||||
# Calling it inside pipeline without '-' will disregards standard input and
|
||||
# use defaults instead.
|
||||
unittest__test_files() {
|
||||
{
|
||||
if [ "$1" = '-' ]; then
|
||||
cat -
|
||||
else
|
||||
printf '%s\n' "$@"
|
||||
fi
|
||||
} |
|
||||
while read -r unittest_dir; do
|
||||
if ! find "${unittest_dir:-./}" -path "*${unittest_dir:-tests/}*" -name 'test_*.sh' 2>/dev/null; then
|
||||
unittest__print_debug 'TESTS NOT FOUND' \
|
||||
"I was looking for 'test_*.sh' files inside '${unittest_dir:-tests/}' directory using:" \
|
||||
" $ find \"${unittest_dir:-./}\" -path \"*${unittest_dir:-tests/}*\" -name 'test_*.sh' -print" \
|
||||
'but instead of files I got an error with message:' \
|
||||
" $(find "${unittest_dir:-./}" -path "*${unittest_dir:-tests/}*" -name 'test_*.sh' -print 2>&1)"
|
||||
unset -v unittest_dir
|
||||
return 1
|
||||
fi
|
||||
unset -v unittest_dir
|
||||
done
|
||||
|
||||
return $?
|
||||
}
|
||||
|
||||
##
|
||||
# Run tests from given files.
|
||||
# STDIN: List of files.
|
||||
# STDOUT: Test name with status.
|
||||
# STDERR: (optional) Debug/error message.
|
||||
# EXIT STATUS:
|
||||
# 0 - All tests passed.
|
||||
# >0 - Some tests failed.
|
||||
##
|
||||
unittest__run() {
|
||||
# prefix: utt8r_
|
||||
|
||||
UNITTEST_STATUS=0
|
||||
while read -r utt8r_testfile; do
|
||||
(
|
||||
utt8r_beforeAll=$(sed -n 's/^[ \t]*\(beforeAll\)[ \t]*(.*/\1/p' "${utt8r_testfile}")
|
||||
utt8r_afterAll=$(sed -n 's/^[ \t]*\(afterAll\)[ \t]*(.*/\1/p' "${utt8r_testfile}")
|
||||
utt8r_beforeEach=$(sed -n -e 's/^[ \t]*\(beforeEach\)[ \t]*(.*/\1/p' -e 's/^[ \t]*\(setUp\)[ \t]*(.*/\1/p' "${utt8r_testfile}")
|
||||
utt8r_afterEach=$(sed -n -e 's/^[ \t]*\(afterEach\)[ \t]*(.*/\1/p' -e 's/^[ \t]*\(tearDown\)[ \t]*(.*/\1/p' "${utt8r_testfile}")
|
||||
utt8r_tests=$(sed -n 's/^[ \t]*\(x\{0,1\}test_[^(=]*\)(.*/\1/p' "${utt8r_testfile}")
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "${utt8r_testfile}"
|
||||
${utt8r_beforeAll}
|
||||
for _current_testcase in ${utt8r_tests}; do
|
||||
UNITTEST_CURRENT="${utt8r_testfile#./}:${_current_testcase}"
|
||||
|
||||
case ${_current_testcase} in
|
||||
x*)
|
||||
unittest__print_result "${UNITTEST_CURRENT}" 'SKIP'
|
||||
;;
|
||||
*)
|
||||
${utt8r_beforeEach}
|
||||
# test result is status of last command in test
|
||||
if ${_current_testcase}; then
|
||||
unittest__print_result "${UNITTEST_CURRENT}" 'PASS'
|
||||
else
|
||||
# last command in test failed
|
||||
UNITTEST_STATUS=1
|
||||
unittest__print_result "${UNITTEST_CURRENT}" 'FAIL'
|
||||
fi
|
||||
${utt8r_afterEach}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
${utt8r_afterAll}
|
||||
unset -v UNITTEST_CURRENT
|
||||
exit ${UNITTEST_STATUS}
|
||||
)
|
||||
UNITTEST_STATUS=$?
|
||||
done
|
||||
return ${UNITTEST_STATUS}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# MAIN ROUTINE
|
||||
#
|
||||
|
||||
{
|
||||
# Color output by default: on supported terminals when NO_COLOR is not set.
|
||||
# Supported terminals are recognized based on TERM variable. When TERM is
|
||||
# not set (for example inside CI environment) we assume that terminal is dumb.
|
||||
if [ -z "${NO_COLOR}" ] && [ "$(TERM=${TERM:-dumb} tput colors)" -lt 8 ]; then
|
||||
NO_COLOR='YES'
|
||||
fi
|
||||
|
||||
case $# in
|
||||
0) # discovery mode
|
||||
unittest__test_files | unittest__run
|
||||
exit $?
|
||||
;;
|
||||
1)
|
||||
case $1 in
|
||||
-h|--help)
|
||||
cat >&2 <<-'EOF'
|
||||
unittest - unit tests framework for shell scripts.
|
||||
|
||||
Usage:
|
||||
unittest [options] [test_directory | test_file]
|
||||
|
||||
Options:
|
||||
-h, --help Show this help and exit.
|
||||
-v, --version Show version number and exit.
|
||||
|
||||
Without any arguments it will run all tests from 'tests' directory.
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
|
||||
-v|--version)
|
||||
printf 'unittest 23.11\n' >&2
|
||||
exit 0
|
||||
;;
|
||||
*) # specified directory/file
|
||||
if [ -d "$1" ]; then
|
||||
unittest__test_files "$1" | unittest__run
|
||||
exit $?
|
||||
elif [ -f "$1" ]; then
|
||||
printf '%s\n' "$1" | unittest__run
|
||||
exit $?
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
|
||||
unittest__print_debug 'INVALID USAGE' \
|
||||
"I cannot understand '$*' option. Did you want to use option or did you misspell file/directory?" \
|
||||
'Hint: Find valid usage with:' \
|
||||
' $ unittest -h'
|
||||
exit 64 # EX_USAGE
|
||||
}
|
Loading…
Reference in a new issue