libpayload: Add unit-tests framework and first test case
This commit adds a unit-tests framework ported from coreboot, and test for drivers/speaker. Usage of the unit-tests framework is same as for the coreboot one. Change-Id: Iaa94ee4dcdc3f74af830113813df0e8fb0b31e4f Signed-off-by: Jakub Czapiga <jacz@semihalf.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/58242 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Paul Fagerburg <pfagerburg@chromium.org> Reviewed-by: Yu-Ping Wu <yupingso@google.com>
This commit is contained in:
		
				
					committed by
					
						 Felix Held
						Felix Held
					
				
			
			
				
	
			
			
			
						parent
						
							e8b6b07bfc
						
					
				
				
					commit
					12ae850dfc
				
			| @@ -114,17 +114,35 @@ ARCH-$(CONFIG_LP_ARCH_ARM64)   := arm64 | |||||||
| ARCH-$(CONFIG_LP_ARCH_X86)     := x86_32 | ARCH-$(CONFIG_LP_ARCH_X86)     := x86_32 | ||||||
| ARCH-$(CONFIG_LP_ARCH_MOCK)    := mock | ARCH-$(CONFIG_LP_ARCH_MOCK)    := mock | ||||||
|  |  | ||||||
| # Three cases where we don't need fully populated $(obj) lists: | # Five cases where we don't need fully populated $(obj) lists: | ||||||
| # 1. when no .config exists | # 1. when no .config exists | ||||||
| # 2. when make config (in any flavour) is run | # 2. when make config (in any flavour) is run | ||||||
| # 3. when make distclean is run | # 3. when make distclean is run | ||||||
|  | # 4. when make help% or make clean% is run | ||||||
|  | # 5. when make %-test or make %-tests or make %coverage-report is run | ||||||
| # Don't waste time on reading all Makefile.incs in these cases | # Don't waste time on reading all Makefile.incs in these cases | ||||||
| ifeq ($(strip $(HAVE_DOTCONFIG)),) | ifeq ($(strip $(HAVE_DOTCONFIG)),) | ||||||
| NOCOMPILE:=1 | NOCOMPILE := 1 | ||||||
| endif | endif | ||||||
| ifneq ($(MAKECMDGOALS),) | ifneq ($(MAKECMDGOALS),) | ||||||
| ifneq ($(filter %config %clean,$(MAKECMDGOALS)),) | ifneq ($(filter %config %clean clean-% help%,$(MAKECMDGOALS)),) | ||||||
| NOCOMPILE:=1 | NOCOMPILE := 1 | ||||||
|  | endif | ||||||
|  | ifneq ($(filter %clean help% clean%, $(MAKECMDGOALS)),) | ||||||
|  | UNIT_TEST := 1 | ||||||
|  | endif | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | ifneq ($(filter help%, $(MAKECMDGOALS)),) | ||||||
|  | NOCOMPILE := 1 | ||||||
|  | UNIT_TEST := 1 | ||||||
|  | else | ||||||
|  | ifneq ($(filter %-test %-tests %coverage-report, $(MAKECMDGOALS)),) | ||||||
|  | ifneq ($(filter-out %-test %-tests %coverage-report, $(MAKECMDGOALS)),) | ||||||
|  | $(error Cannot mix unit-tests targets with other targets) | ||||||
|  | endif | ||||||
|  | NOCOMPILE := | ||||||
|  | UNIT_TEST := 1 | ||||||
| endif | endif | ||||||
| endif | endif | ||||||
|  |  | ||||||
| @@ -135,6 +153,7 @@ $(xcompile): $(top)/../../util/xcompile/xcompile | |||||||
|  |  | ||||||
| ifeq ($(NOCOMPILE),1) | ifeq ($(NOCOMPILE),1) | ||||||
| include $(TOPLEVEL)/Makefile.inc | include $(TOPLEVEL)/Makefile.inc | ||||||
|  | include $(TOPLEVEL)/tests/Makefile.inc | ||||||
| real-all: config | real-all: config | ||||||
|  |  | ||||||
| else | else | ||||||
| @@ -283,9 +302,13 @@ evaluate_subdirs= \ | |||||||
| 		$(eval $(call includemakefiles,$(dir)/Makefile.inc))) \ | 		$(eval $(call includemakefiles,$(dir)/Makefile.inc))) \ | ||||||
| 	$(if $(subdirs),$(eval $(call evaluate_subdirs))) | 	$(if $(subdirs),$(eval $(call evaluate_subdirs))) | ||||||
|  |  | ||||||
| # collect all object files eligible for building | # collect all object files eligible for building or run unit-tests | ||||||
|  | ifneq ($(UNIT_TEST),1) | ||||||
| subdirs:=$(TOPLEVEL) | subdirs:=$(TOPLEVEL) | ||||||
| $(eval $(call evaluate_subdirs)) | $(eval $(call evaluate_subdirs)) | ||||||
|  | else | ||||||
|  | include $(TOPLEVEL)/tests/Makefile.inc | ||||||
|  | endif | ||||||
|  |  | ||||||
| src-to-obj=$(addsuffix .$(1).o, $(basename $(addprefix $(obj)/, $($(1)-srcs)))) | src-to-obj=$(addsuffix .$(1).o, $(basename $(addprefix $(obj)/, $($(1)-srcs)))) | ||||||
| $(foreach class,$(classes),$(eval $(class)-objs:=$(call src-to-obj,$(class)))) | $(foreach class,$(classes),$(eval $(class)-objs:=$(call src-to-obj,$(class)))) | ||||||
|   | |||||||
| @@ -135,7 +135,7 @@ prepare: | |||||||
|  |  | ||||||
| junit.xml: | junit.xml: | ||||||
| 	echo '<?xml version="1.0" encoding="utf-8"?><testsuite>' > $@.tmp | 	echo '<?xml version="1.0" encoding="utf-8"?><testsuite>' > $@.tmp | ||||||
| 	for i in $(filter-out %.old,$(wildcard configs/*)); do \ | 	for i in $(filter-out %.old %.unit-tests,$(wildcard configs/*)); do \ | ||||||
| 		$(MAKE) clean; \ | 		$(MAKE) clean; \ | ||||||
| 		echo "Building libpayload for $$i"; \ | 		echo "Building libpayload for $$i"; \ | ||||||
| 		cp "$$i" junit_config; \ | 		cp "$$i" junit_config; \ | ||||||
| @@ -158,7 +158,7 @@ junit.xml: | |||||||
| 	mv $@.tmp $@ | 	mv $@.tmp $@ | ||||||
|  |  | ||||||
| test-configs: | test-configs: | ||||||
| 	for config in $(filter-out %.old,$(wildcard configs/*)); do \ | 	for config in $(filter-out %.old %.unit-tests,$(wildcard configs/*)); do \ | ||||||
| 		$(MAKE) clean; \ | 		$(MAKE) clean; \ | ||||||
| 		cp "$$config" test_config; \ | 		cp "$$config" test_config; \ | ||||||
| 		echo "*** Making libpayload config $$config ***"; \ | 		echo "*** Making libpayload config $$config ***"; \ | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								payloads/libpayload/configs/config.unit-tests
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								payloads/libpayload/configs/config.unit-tests
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | CONFIG_LP_ARCH_MOCK=y | ||||||
|  | CONFIG_LP_ARCH_BIG_ENDIAN=n | ||||||
							
								
								
									
										277
									
								
								payloads/libpayload/tests/Makefile.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								payloads/libpayload/tests/Makefile.inc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,277 @@ | |||||||
|  | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
|  | testsrc := $(top)/tests | ||||||
|  |  | ||||||
|  | # Place the build output in one of two places depending on COV, so that code | ||||||
|  | # built with code coverage never mixes with code built without code coverage. | ||||||
|  | ifeq ($(COV),1) | ||||||
|  | testobj := $(obj)/coverage | ||||||
|  | else | ||||||
|  | testobj := $(obj)/tests | ||||||
|  | endif | ||||||
|  | coverage-dir := $(testobj)/coverage_reports | ||||||
|  |  | ||||||
|  | coreboottop := ../../ | ||||||
|  |  | ||||||
|  | cmockasrc := $(coreboottop)/3rdparty/cmocka | ||||||
|  | cmockaobj := $(objutil)/cmocka | ||||||
|  | CMOCKA_LIB := $(cmockaobj)/src/libcmocka.so | ||||||
|  |  | ||||||
|  | CMAKE := cmake | ||||||
|  |  | ||||||
|  | TEST_DEFAULT_CONFIG := $(top)/configs/config.unit-tests | ||||||
|  | TEST_DOTCONFIG := $(testobj)/.config | ||||||
|  | TEST_KCONFIG_AUTOHEADER := $(testobj)/libpayload-config.src.h | ||||||
|  | TEST_KCONFIG_AUTOCONFIG := $(testobj)/auto.conf | ||||||
|  | TEST_KCONFIG_DEPENDENCIES := $(testobj)/auto.conf.cmd | ||||||
|  | TEST_KCONFIG_SPLITCONFIG := $(testobj)/config/ | ||||||
|  | TEST_KCONFIG_TRISTATE := $(testobj)/tristate.conf | ||||||
|  | TEST_KCONFIG_NEGATIVES := 1 | ||||||
|  | TEST_KBUILD_KCONFIG := $(top)/Kconfig | ||||||
|  | TEST_CONFIG_ := CONFIG_LP_ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Default includes | ||||||
|  | TEST_CFLAGS := -include include/kconfig.h -include include/compiler.h | ||||||
|  | TEST_CFLAGS += -Iinclude -Iinclude/mock | ||||||
|  | TEST_CFLAGS += -I$(dir $(TEST_KCONFIG_AUTOHEADER)) | ||||||
|  |  | ||||||
|  | # Test specific includes | ||||||
|  | TEST_CFLAGS += -I$(testsrc)/include -I$(testsrc)/include/mocks | ||||||
|  | TEST_CFLAGS += -I$(cmockasrc)/include | ||||||
|  |  | ||||||
|  | # Minimal subset of warnings and errors. Tests can be less strict than actual build. | ||||||
|  | TEST_CFLAGS += -Wall -Wundef -Wstrict-prototypes -Wvla | ||||||
|  | TEST_CFLAGS += -Wwrite-strings -Wno-trigraphs -Wimplicit-fallthrough | ||||||
|  | TEST_CFLAGS += -Wstrict-aliasing -Wshadow -Werror | ||||||
|  |  | ||||||
|  | TEST_CFLAGS += -std=gnu11 -Os -ffunction-sections -fdata-sections -fno-builtin | ||||||
|  |  | ||||||
|  | # Make unit-tests detectable by the code | ||||||
|  | TEST_CFLAGS += -D__TEST__ | ||||||
|  |  | ||||||
|  | # Link against CMocka | ||||||
|  | TEST_LDFLAGS := -L$(dir $(CMOCKA_LIB)) -lcmocka -Wl,-rpath=$(dir $(CMOCKA_LIB)) | ||||||
|  |  | ||||||
|  | TEST_LDFLAGS += -Wl,--gc-sections | ||||||
|  |  | ||||||
|  | # Disable userspace relocations | ||||||
|  | TEST_CFLAGS += -fno-pie -fno-pic | ||||||
|  | TEST_LDFLAGS += -no-pie | ||||||
|  |  | ||||||
|  | ifeq ($(COV),1) | ||||||
|  | TEST_CFLAGS += --coverage | ||||||
|  | TEST_LDFLAGS += --coverage | ||||||
|  | endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Extra attributes for unit tests. Declated per each test. Only `srcs` is required. | ||||||
|  | attributes := cflags config mocks srcs | ||||||
|  |  | ||||||
|  | alltests := | ||||||
|  | subdirs := tests/crypto tests/curses tests/drivers tests/gdb tests/libc tests/libcbfs | ||||||
|  | subdirs += tests/liblz4 tests/liblzma tests/libpci | ||||||
|  |  | ||||||
|  | define tests-handler | ||||||
|  | alltests += $(1)$(2) | ||||||
|  | $(foreach attribute,$(attributes), \ | ||||||
|  | 	$(eval $(1)$(2)-$(attribute) += $($(2)-$(attribute)))) | ||||||
|  | $(foreach attribute,$(attributes), \ | ||||||
|  | 	$(eval $(2)-$(attribute) := )) | ||||||
|  | endef | ||||||
|  |  | ||||||
|  | # Copy attributes of one test to another | ||||||
|  | # $1 - input test name | ||||||
|  | # $2 - output test name | ||||||
|  | copy-test = $(foreach attribute,$(attributes), \ | ||||||
|  | 		$(eval $(strip $(2))-$(attribute) := $($(strip $(1))-$(attribute)))) | ||||||
|  |  | ||||||
|  | $(call add-special-class,tests) | ||||||
|  | $(call evaluate_subdirs) | ||||||
|  |  | ||||||
|  | # Create actual targets for unit test binaries | ||||||
|  | # $1 - test name | ||||||
|  | define TEST_CC_template | ||||||
|  |  | ||||||
|  | # Generate custom config.h redefining given config symbols, and declaring mocked | ||||||
|  | # functions weak. It is important that the compiler already sees that they are | ||||||
|  | # weak (and they aren't just turned weak at a later stage) to prevent certain | ||||||
|  | # optimizations that would break if the function gets replaced. (For clang this | ||||||
|  | # file needs to be marked `system_header` to prevent it from warning about | ||||||
|  | # `#pragma weak` entries without a matching function declaration, since there is | ||||||
|  | # no -Wno-xxx commandline for that.) | ||||||
|  | $(1)-config-file := $(testobj)/$(1)/libpayload-config.h | ||||||
|  | $$($(1)-config-file): $(TEST_KCONFIG_AUTOHEADER) | ||||||
|  | 	mkdir -p $$(dir $$@); | ||||||
|  | 	printf '// File generated by tests/Makefile.inc\n// Do not change\n' > $$@; | ||||||
|  | 	printf '#ifndef TEST_LIBPAYLOAD_CONFIG_H_\n' >> $$@; | ||||||
|  | 	printf '#define TEST_LIBPAYLOAD_CONFIG_H_\n' >> $$@; | ||||||
|  | 	printf '#include <%s>\n\n' "$(notdir $(TEST_KCONFIG_AUTOHEADER))" >> $$@; | ||||||
|  | 	for kv in $$($(1)-config); do \ | ||||||
|  | 		key="`echo $$$$kv | cut -d '=' -f -1`"; \ | ||||||
|  | 		value="`echo $$$$kv | cut -d '=' -f 2-`"; \ | ||||||
|  | 		printf '#undef %s\n' "$$$$key" >> $$@; \ | ||||||
|  | 		printf '#define %s %s\n\n' "$$$$key" "$$$$value" >> $$@; \ | ||||||
|  | 	done | ||||||
|  | 	printf '#ifdef __clang__\n' >> $$@; | ||||||
|  | 	printf '#pragma clang system_header\n' >> $$@; | ||||||
|  | 	printf '#endif\n\n' >> $$@; | ||||||
|  | 	printf '#ifdef __TEST_SRCOBJ__\n' >> $$@; | ||||||
|  | 	for m in $$($(1)-mocks); do \ | ||||||
|  | 		printf '#pragma weak %s\n' "$$$$m" >> $$@; \ | ||||||
|  | 	done | ||||||
|  | 	printf '#endif\n\n' >> $$@; | ||||||
|  | 	printf '#endif\n' >> $$@; | ||||||
|  |  | ||||||
|  | $($(1)-objs): TEST_CFLAGS += -I$$(dir $$($(1)-config-file)) \ | ||||||
|  | 	-D__TEST_NAME__=\"$(subst /,_,$(1))\" | ||||||
|  |  | ||||||
|  | # Give us a way to distinguish between libpayload source files and test files in the code. | ||||||
|  | $($(1)-srcobjs): TEST_CFLAGS += -D__TEST_SRCOBJ__ | ||||||
|  |  | ||||||
|  | # Compile sources and apply mocking/wrapping for selected symbols. | ||||||
|  | # For each listed mock add new symbol with prefix `__real_`, | ||||||
|  | # pointing to the same section:address. This will keep original | ||||||
|  | # function accessible if required. | ||||||
|  | $($(1)-objs): $(testobj)/$(1)/%.o: $$$$*.c $$($(1)-config-file) | ||||||
|  | 	mkdir -p $$(dir $$@) | ||||||
|  | 	$(HOSTCC) $$(TEST_CFLAGS) $($(1)-cflags) -MMD \ | ||||||
|  | 		-MF $$(basename $$@).d -MT $$@ -c $$< -o $$@.orig | ||||||
|  | 	objcopy_wrap_flags=''; \ | ||||||
|  | 	for sym in $$($(1)-mocks); do \ | ||||||
|  | 		sym_line="$$$$($(HOSTOBJDUMP) -t $$@.orig \ | ||||||
|  | 			| grep -E \"[0-9a-fA-F]+\\s+w\\s+F\\s+.*\\s$$$$sym$$$$\")"; \ | ||||||
|  | 		if [ ! -z "$$$$sym_line" ] ; then \ | ||||||
|  | 			addr="$$$$(echo \"$$$$sym_line\" | awk '{ print $$$$1 }')"; \ | ||||||
|  | 			section="$$$$(echo \"$$$$sym_line\" | awk '{ print $$$$(NF - 2) }')"; \ | ||||||
|  | 			objcopy_wrap_flags="$$$$objcopy_wrap_flags --add-symbol __real_$$$${sym}=$$$${section}:0x$$$${addr},function,global"; \ | ||||||
|  | 		fi \ | ||||||
|  | 	done ; \ | ||||||
|  | 	$(HOSTOBJCOPY) $$@.orig $$$$objcopy_wrap_flags $$@ | ||||||
|  |  | ||||||
|  | $($(1)-bin): $($(1)-objs) $(CMOCKA_LIB) | ||||||
|  | 	$(HOSTCC) $$^ $($(1)-cflags) $$(TEST_LDFLAGS) -o $$@ | ||||||
|  |  | ||||||
|  | endef | ||||||
|  |  | ||||||
|  | $(foreach test,$(alltests), \ | ||||||
|  | 	$(eval $(test)-srcobjs := $(addprefix $(testobj)/$(test)/, \ | ||||||
|  | 		$(patsubst %.c,%.o,$(filter-out tests/%,$($(test)-srcs))))) \ | ||||||
|  | 	$(eval $(test)-objs := $(addprefix $(testobj)/$(test)/, \ | ||||||
|  | 		$(patsubst %.c,%.o,$($(test)-srcs)))) \ | ||||||
|  | 	$(eval $(test)-bin := $(testobj)/$(test)/run)) | ||||||
|  | $(foreach test,$(alltests), \ | ||||||
|  | 	$(eval $(call TEST_CC_template,$(test)))) | ||||||
|  | $(foreach test,$(alltests), \ | ||||||
|  | 	$(eval all-test-objs += $($(test)-objs)) \ | ||||||
|  | 	$(eval test-bins += $($(test)-bin))) | ||||||
|  |  | ||||||
|  | DEPENDENCIES += $(addsuffix .d,$(basename $(all-test-objs))) | ||||||
|  | -include $(DEPENDENCIES) | ||||||
|  |  | ||||||
|  | # Build CMocka | ||||||
|  | $(CMOCKA_LIB): | ||||||
|  | 	echo "*** Building CMOCKA ***" | ||||||
|  | 	mkdir -p $(cmockaobj) | ||||||
|  | 	cd $(cmockaobj) && $(CMAKE) $(abspath $(cmockasrc)) | ||||||
|  | 	$(MAKE) -C $(cmockaobj) | ||||||
|  |  | ||||||
|  | # Kconfig targets | ||||||
|  | $(TEST_DOTCONFIG): | ||||||
|  | 	mkdir -p $(dir $@) | ||||||
|  | 	cp $(TEST_DEFAULT_CONFIG) $(TEST_DOTCONFIG) | ||||||
|  |  | ||||||
|  | $(TEST_KCONFIG_AUTOHEADER): TEST_KCONFIG_FLAGS := DOTCONFIG=$(TEST_DOTCONFIG) \ | ||||||
|  | 	KCONFIG_AUTOHEADER=$(TEST_KCONFIG_AUTOHEADER) \ | ||||||
|  | 	KCONFIG_AUTOCONFIG=$(TEST_KCONFIG_AUTOCONFIG) \ | ||||||
|  | 	KCONFIG_DEPENDENCIES=$(TEST_KCONFIG_DEPENDENCIES) \ | ||||||
|  | 	KCONFIG_SPLITCONFIG=$(TEST_KCONFIG_SPLITCONFIG) \ | ||||||
|  | 	KCONFIG_TRISTATE=$(TEST_KCONFIG_TRISTATE) \ | ||||||
|  | 	KCONFIG_NEGATIVES=$(TEST_KCONFIG_NEGATIVES) \ | ||||||
|  | 	KBUILD_KCONFIG=$(TEST_KBUILD_KCONFIG) \ | ||||||
|  | 	KBUILD_DEFCONFIG=$(TEST_DEFAULT_CONFIG) \ | ||||||
|  | 	CONFIG_=$(TEST_CONFIG_) | ||||||
|  |  | ||||||
|  | $(TEST_KCONFIG_AUTOHEADER): $(TEST_DOTCONFIG) $(objk)/conf | ||||||
|  | 	mkdir -p $(dir $@) | ||||||
|  | 	$(MAKE) $(TEST_KCONFIG_FLAGS) olddefconfig V=$(V) | ||||||
|  | 	$(MAKE) $(TEST_KCONFIG_FLAGS) syncconfig V=$(V) | ||||||
|  |  | ||||||
|  | $(TEST_KCONFIG_AUTOCONFIG): $(TEST_KCONFIG_AUTOHEADER) | ||||||
|  | 	true | ||||||
|  |  | ||||||
|  | .PHONY: $(alltests) $(addprefix clean-,$(alltests)) | ||||||
|  | .PHONY: unit-tests build-unit-tests run-unit-tests clean-unit-tests | ||||||
|  |  | ||||||
|  | ifeq ($(JUNIT_OUTPUT),y) | ||||||
|  | $(alltests): export CMOCKA_MESSAGE_OUTPUT=xml | ||||||
|  | $(alltests): export CMOCKA_XML_FILE=$(testobj)/junit-%g.xml | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | $(alltests): $$($$(@)-bin) | ||||||
|  | 	rm -f $(testobj)/junit-libpayload-$(subst /,_,$(patsubst $(testobj)/%/,%,$(dir $^)))\(*\).xml | ||||||
|  | 	rm -f $(testobj)/$(subst /,_,$^).failed | ||||||
|  | 	-$^ || echo failed > $(testobj)/$(subst /,_,$^).failed | ||||||
|  |  | ||||||
|  | # Build a code coverage report by collecting all the gcov files into a single | ||||||
|  | # report. If COV is not set, this might be a user error, and they're trying | ||||||
|  | # to generate a coverage report without first having built and run the code | ||||||
|  | # with code coverage. absence of COV=1 will be corrected. | ||||||
|  |  | ||||||
|  | .PHONY: coverage-report clean-coverage-report | ||||||
|  |  | ||||||
|  | ifeq ($(COV),1) | ||||||
|  | coverage-report: | ||||||
|  | 	lcov -o $(testobj)/tests.info -c -d $(testobj) --exclude '$(testsrc)/*' | ||||||
|  | 	genhtml -q -o $(coverage-dir) -t "coreboot unit tests" -s $(testobj)/tests.info | ||||||
|  |  | ||||||
|  | clean-coverage-report: | ||||||
|  | 	rm -Rf $(coverage-dir) | ||||||
|  | else | ||||||
|  | coverage-report: | ||||||
|  | 	COV=1 V=$(V) $(MAKE) coverage-report | ||||||
|  |  | ||||||
|  | clean-coverage-report: | ||||||
|  | 	COV=1 V=$(V) $(MAKE) clean-coverage-report | ||||||
|  | endif | ||||||
|  |  | ||||||
|  | unit-tests: build-unit-tests run-unit-tests | ||||||
|  |  | ||||||
|  | build-unit-tests: $(test-bins) | ||||||
|  |  | ||||||
|  | run-unit-tests: $(alltests) | ||||||
|  | 	if [ `find $(testobj) -name '*.failed' | wc -l` -gt 0 ]; then \ | ||||||
|  | 		echo "**********************"; \ | ||||||
|  | 		echo "     TESTS FAILED"; \ | ||||||
|  | 		echo "**********************"; \ | ||||||
|  | 		exit 1; \ | ||||||
|  | 	else \ | ||||||
|  | 		echo "**********************"; \ | ||||||
|  | 		echo "   ALL TESTS PASSED"; \ | ||||||
|  | 		echo "**********************"; \ | ||||||
|  | 		exit 0; \ | ||||||
|  | 	fi | ||||||
|  |  | ||||||
|  | $(addprefix clean-,$(alltests)): clean-% | ||||||
|  | 	rm -rf $(testobj)/$* | ||||||
|  |  | ||||||
|  | clean-unit-tests: | ||||||
|  | 	rm -rf $(testobj) | ||||||
|  |  | ||||||
|  | list-unit-tests: | ||||||
|  | 	@echo "unit-tests:" | ||||||
|  | 	for t in $(sort $(alltests)); do \ | ||||||
|  | 		echo "  $$t"; \ | ||||||
|  | 	done | ||||||
|  |  | ||||||
|  | help-unit-tests help:: | ||||||
|  | 	@echo  '*** libpayload unit-tests targets ***' | ||||||
|  | 	@echo  '  Use "COV=1 make [target]" to enable code coverage for unit tests' | ||||||
|  | 	@echo  '  unit-tests            - Run all unit-tests from tests/' | ||||||
|  | 	@echo  '  clean-unit-tests      - Remove unit-tests build artifacts' | ||||||
|  | 	@echo  '  list-unit-tests       - List all unit-tests' | ||||||
|  | 	@echo  '  <unit-test>           - Build and run single unit-test' | ||||||
|  | 	@echo  '  clean-<unit-test>     - Remove single unit-test build artifacts' | ||||||
|  | 	@echo  '  coverage-report       - Generate a code coverage report' | ||||||
|  | 	@echo  '  clean-coverage-report - Remove the code coverage report' | ||||||
|  | 	@echo | ||||||
							
								
								
									
										9
									
								
								payloads/libpayload/tests/drivers/Makefile.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								payloads/libpayload/tests/drivers/Makefile.inc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
|  | tests-y += speaker-test | ||||||
|  |  | ||||||
|  | speaker-test-srcs += tests/drivers/speaker-test.c | ||||||
|  | speaker-test-mocks += inb | ||||||
|  | speaker-test-mocks += outb | ||||||
|  | speaker-test-mocks += arch_ndelay | ||||||
|  | speaker-test-cflags += -include $(testsrc)/include/mocks/x86_io.h | ||||||
							
								
								
									
										148
									
								
								payloads/libpayload/tests/drivers/speaker-test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								payloads/libpayload/tests/drivers/speaker-test.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | |||||||
|  | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  |  | ||||||
|  | #include <libpayload.h> | ||||||
|  | #include <mocks/x86_io.h> | ||||||
|  |  | ||||||
|  | /* Include source to gain access to private defines */ | ||||||
|  | #include "../drivers/speaker.c" | ||||||
|  |  | ||||||
|  | #include <tests/test.h> | ||||||
|  |  | ||||||
|  | void outb(unsigned char val, int port) | ||||||
|  | { | ||||||
|  | 	check_expected(val); | ||||||
|  | 	check_expected(port); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | unsigned char inb(int port) | ||||||
|  | { | ||||||
|  | 	check_expected(port); | ||||||
|  | 	return mock_type(unsigned char); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void setup_speaker_enable_calls(u16 freq, u8 port_val) | ||||||
|  | { | ||||||
|  | 	/* Minimal correct value should be at leaset 256. For values lowe than that, | ||||||
|  | 	   counter will have an incorrect value. Regardless, there is  */ | ||||||
|  | 	u16 reg16 = 1193180 / freq; | ||||||
|  |  | ||||||
|  | 	/* Select counter 2 */ | ||||||
|  | 	expect_value(outb, val, 0xb6); | ||||||
|  | 	expect_value(outb, port, I82C54_CONTROL_WORD_REGISTER); | ||||||
|  |  | ||||||
|  | 	/* Write freq. [LSB, MSB] */ | ||||||
|  | 	expect_value(outb, val, (u8)(reg16 & 0xff)); | ||||||
|  | 	expect_value(outb, port, I82C54_COUNTER2); | ||||||
|  | 	expect_value(outb, val, (u8)(reg16 >> 8)); | ||||||
|  | 	expect_value(outb, port, I82C54_COUNTER2); | ||||||
|  |  | ||||||
|  | 	/* Enable PC speaker */ | ||||||
|  | 	expect_value(inb, port, PC_SPEAKER_PORT); | ||||||
|  | 	will_return(inb, port_val); | ||||||
|  | 	expect_value(outb, val, port_val | 0x3); | ||||||
|  | 	expect_value(outb, port, PC_SPEAKER_PORT); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void test_speaker_enable(void **state) | ||||||
|  | { | ||||||
|  | 	setup_speaker_enable_calls(1, 0); | ||||||
|  | 	speaker_enable(1); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(1, 0xff); | ||||||
|  | 	speaker_enable(1); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(1, 123); | ||||||
|  | 	speaker_enable(1); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(1, -1); | ||||||
|  | 	speaker_enable(1); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(-1, 0); | ||||||
|  | 	speaker_enable(-1); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(-1, 0xff); | ||||||
|  | 	speaker_enable(-1); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(-1, 222); | ||||||
|  | 	speaker_enable(-1); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(-1, -1); | ||||||
|  | 	speaker_enable(-1); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(10000, 0); | ||||||
|  | 	speaker_enable(10000); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(10000, 0xff); | ||||||
|  | 	speaker_enable(10000); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(10000, 91); | ||||||
|  | 	speaker_enable(10000); | ||||||
|  |  | ||||||
|  | 	setup_speaker_enable_calls(10000, -1); | ||||||
|  | 	speaker_enable(10000); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void setup_speaker_disable_calls(u8 value) | ||||||
|  | { | ||||||
|  | 	expect_value(inb, port, PC_SPEAKER_PORT); | ||||||
|  | 	will_return(inb, value); | ||||||
|  | 	expect_value(outb, val, value & 0xfc); | ||||||
|  | 	expect_value(outb, port, PC_SPEAKER_PORT); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void test_speaker_disable(void **state) | ||||||
|  | { | ||||||
|  | 	setup_speaker_disable_calls(0); | ||||||
|  | 	speaker_disable(); | ||||||
|  |  | ||||||
|  | 	setup_speaker_disable_calls(0xfc); | ||||||
|  | 	speaker_disable(); | ||||||
|  |  | ||||||
|  | 	setup_speaker_disable_calls(0xff); | ||||||
|  | 	speaker_disable(); | ||||||
|  |  | ||||||
|  | 	setup_speaker_disable_calls(0xff - 0xfc); | ||||||
|  | 	speaker_disable(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void arch_ndelay(uint64_t ns) | ||||||
|  | { | ||||||
|  | 	check_expected(ns); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void setup_speaker_tone_calls(u16 freq, unsigned int duration) | ||||||
|  | { | ||||||
|  | 	setup_speaker_enable_calls(freq, ~freq & 0xff); | ||||||
|  | 	expect_value(arch_ndelay, ns, (uint64_t)duration * NSECS_PER_MSEC); | ||||||
|  | 	setup_speaker_disable_calls(0xff); | ||||||
|  | 	expect_any(arch_ndelay, ns); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void test_speaker_tone(void **state) | ||||||
|  | { | ||||||
|  | 	setup_speaker_tone_calls(500, 100); | ||||||
|  | 	speaker_tone(500, 100); | ||||||
|  |  | ||||||
|  | 	setup_speaker_tone_calls(4321, 0); | ||||||
|  | 	speaker_tone(4321, 0); | ||||||
|  |  | ||||||
|  | 	setup_speaker_tone_calls(-1, -1); | ||||||
|  | 	speaker_tone(-1, -1); | ||||||
|  |  | ||||||
|  | 	setup_speaker_tone_calls(10000, 1000); | ||||||
|  | 	speaker_tone(10000, 1000); | ||||||
|  |  | ||||||
|  | 	setup_speaker_tone_calls(433, 890); | ||||||
|  | 	speaker_tone(433, 890); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int main(void) | ||||||
|  | { | ||||||
|  | 	const struct CMUnitTest tests[] = { | ||||||
|  | 		cmocka_unit_test(test_speaker_enable), | ||||||
|  | 		cmocka_unit_test(test_speaker_disable), | ||||||
|  | 		cmocka_unit_test(test_speaker_tone), | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	return lp_run_group_tests(tests, NULL, NULL); | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								payloads/libpayload/tests/include/mocks/x86_io.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								payloads/libpayload/tests/include/mocks/x86_io.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  |  | ||||||
|  | #ifndef TESTS_MOCKS_X86_IO_H_ | ||||||
|  | #define TESTS_MOCKS_X86_IO_H_ | ||||||
|  |  | ||||||
|  | unsigned int inl(int port); | ||||||
|  |  | ||||||
|  | unsigned short inw(int port); | ||||||
|  |  | ||||||
|  | unsigned char inb(int port); | ||||||
|  |  | ||||||
|  | void outl(unsigned int val, int port); | ||||||
|  |  | ||||||
|  | void outw(unsigned short val, int port); | ||||||
|  |  | ||||||
|  | void outb(unsigned char val, int port); | ||||||
|  |  | ||||||
|  | void outsl(int port, const void *addr, unsigned long count); | ||||||
|  |  | ||||||
|  | void outsw(int port, const void *addr, unsigned long count); | ||||||
|  |  | ||||||
|  | void outsb(int port, const void *addr, unsigned long count); | ||||||
|  |  | ||||||
|  | void insl(int port, void *addr, unsigned long count); | ||||||
|  |  | ||||||
|  | void insw(int port, void *addr, unsigned long count); | ||||||
|  |  | ||||||
|  | void insb(int port, void *addr, unsigned long count); | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										52
									
								
								payloads/libpayload/tests/include/tests/test.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								payloads/libpayload/tests/include/tests/test.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  |  | ||||||
|  | #ifndef _TESTS_TEST_H | ||||||
|  | #define _TESTS_TEST_H | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Standard test header that should be included in all tests. For now it just encapsulates the | ||||||
|  |  * include dependencies for Cmocka. Test-specific APIs that are so generic we would want them | ||||||
|  |  * available everywhere could also be added here. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <arch/types.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <setjmp.h> | ||||||
|  | #include <cmocka.h> | ||||||
|  |  | ||||||
|  | /* Helper macro to aviud checkpatch errors for some macros */ | ||||||
|  | #define EMPTY_WRAP(...) __VA_ARGS__ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Set symbol value and make it global. | ||||||
|  |  */ | ||||||
|  | #define TEST_SYMBOL(symbol, value) asm(".set " #symbol ", " #value "\n\t.globl " #symbol) | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Define memory region for testing purpose. | ||||||
|  |  * | ||||||
|  |  * Create buffer with specified name and size. | ||||||
|  |  * Create end symbol for it. | ||||||
|  |  */ | ||||||
|  | #define TEST_REGION(region, size) uint8_t _##region[size];                                     \ | ||||||
|  | 	TEST_SYMBOL(_e##region, _##region + size);                                             \ | ||||||
|  | 	TEST_SYMBOL(_##region##_size, size) | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Set start, end and size symbols describing region without allocating memory for it. | ||||||
|  |  */ | ||||||
|  | #define TEST_REGION_UNALLOCATED(region, start, size) EMPTY_WRAP(                               \ | ||||||
|  | 	TEST_SYMBOL(_##region, start);                                                         \ | ||||||
|  | 	TEST_SYMBOL(_e##region, _##region + size);                                             \ | ||||||
|  | 	TEST_SYMBOL(_##region##_size, size)                                                    \ | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | /* Wrapper for running cmocka test groups using name provided by build system in __TEST_NAME__ | ||||||
|  |    This should be used instead of cmocka_run_group_tests(). If there is a need to use custom | ||||||
|  |    group name, then please use cmocka_run_group_tests_name(). */ | ||||||
|  | #define lp_run_group_tests(group_tests, group_setup, group_teardown)                           \ | ||||||
|  | 	cmocka_run_group_tests_name((__TEST_NAME__ "(" #group_tests ")"), group_tests,         \ | ||||||
|  | 				    group_setup, group_teardown) | ||||||
|  |  | ||||||
|  | #endif /* _TESTS_TEST_H */ | ||||||
| @@ -97,6 +97,7 @@ endif | |||||||
| 	$(MAKE) CPUS=$(CPUS) V=$(V) Q=$(Q) BLD_DIR=src/soc/nvidia/tegra124/lp0 BLD=tegra124_lp0 MFLAGS= MAKEFLAGS=xcompile=$(COREBOOT_BUILD_DIR)/xcompile MAKETARGET=all junit.xml | 	$(MAKE) CPUS=$(CPUS) V=$(V) Q=$(Q) BLD_DIR=src/soc/nvidia/tegra124/lp0 BLD=tegra124_lp0 MFLAGS= MAKEFLAGS=xcompile=$(COREBOOT_BUILD_DIR)/xcompile MAKETARGET=all junit.xml | ||||||
| 	$(MAKE) CPUS=$(CPUS) V=$(V) Q=$(Q) BLD_DIR=src/soc/nvidia/tegra210/lp0 BLD=tegra120_lp0 MFLAGS= MAKEFLAGS=xcompile=$(COREBOOT_BUILD_DIR)/xcompile MAKETARGET=all junit.xml | 	$(MAKE) CPUS=$(CPUS) V=$(V) Q=$(Q) BLD_DIR=src/soc/nvidia/tegra210/lp0 BLD=tegra120_lp0 MFLAGS= MAKEFLAGS=xcompile=$(COREBOOT_BUILD_DIR)/xcompile MAKETARGET=all junit.xml | ||||||
| 	$(MAKE) unit-tests JUNIT_OUTPUT=y | 	$(MAKE) unit-tests JUNIT_OUTPUT=y | ||||||
|  | 	(cd payloads/libpayload; unset COREBOOT_BUILD_DIR; $(MAKE) unit-tests JUNIT_OUTPUT=y) | ||||||
|  |  | ||||||
| test-basic: test-lint test-tools test-abuild test-payloads test-cleanup | test-basic: test-lint test-tools test-abuild test-payloads test-cleanup | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user