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
|
ASFLAGS=-g -mabi=ilp32e -march=rv32ec
|
||||||
CFLAGS=$(ASFLAGS)
|
CFLAGS=$(ASFLAGS)
|
||||||
LD=riscv64-unknown-elf-ld
|
LD=riscv64-unknown-elf-ld
|
||||||
|
export JQ?=jaq
|
||||||
|
export RV32EMU?=$(HOME)/Source/github.com/sysprog21/rv32emu/build/rv32emu
|
||||||
|
|
||||||
all: calc.elf
|
all: calc.elf
|
||||||
|
|
||||||
|
check: tests
|
||||||
|
@tests/unittest
|
||||||
|
|
||||||
hello.elf: hello.o
|
hello.elf: hello.o
|
||||||
$(LD) -m elf32lriscv $^ -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 $@
|
$(LD) -m elf32lriscv -T link.ld $^ -o $@
|
||||||
|
|
||||||
%.o : %.s
|
%.o : %.s
|
||||||
$(AS) $(ASFLAGS) $< -o $@
|
$(AS) $(ASFLAGS) $< -o $@
|
||||||
|
|
||||||
|
.PHONY: check tests
|
||||||
|
|
13
calc.s
13
calc.s
|
@ -20,7 +20,7 @@ regnames:
|
||||||
|
|
||||||
.section .bss
|
.section .bss
|
||||||
buf: .skip 20 # room for 20 byte string
|
buf: .skip 20 # room for 20 byte string
|
||||||
# x10: ABCDEFG\n is 23 chars
|
# x10: ABCDEFGH\n is 14 chars
|
||||||
|
|
||||||
.section .data
|
.section .data
|
||||||
buflen: .byte 0 # length of buf string
|
buflen: .byte 0 # length of buf string
|
||||||
|
@ -160,3 +160,14 @@ memcpy_loop:
|
||||||
j memcpy_loop
|
j memcpy_loop
|
||||||
memcpy_done:
|
memcpy_done:
|
||||||
ret
|
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