diff --git a/.travis.yml b/.travis.yml index 09b01a9..57a64c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,9 +37,9 @@ before_install: export TARGET=all; fi -# build using the Makefile +# build (and test) using the Makefile, with a single overall status script: - - make ${TARGET} && make misc + - make ${TARGET} && make misc && make check # run/simulate a test install after_success: diff --git a/Makefile b/Makefile index 65bdcdc..75fc27b 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin .PHONY: all clean tools target-tools install install-tools install-target-tools +.PHONY: check tools: $(TOOLS) $(FEXC_LINKS) target-tools: $(TARGET_TOOLS) @@ -94,6 +95,7 @@ install-misc: $(MISC_TOOLS) clean: + make -C tests/ clean @rm -vf $(TOOLS) $(FEXC_LINKS) $(TARGET_TOOLS) $(MISC_TOOLS) @rm -vf version.h *.o *.elf *.sunxi *.bin *.nm *.orig @@ -186,3 +188,6 @@ version.h: @for x in $(TOOLS) $(FEXC_LINKS) $(TARGET_TOOLS) version.h '*.o' '*.swp'; do \ echo "$$x"; \ done | sort -V > $@ + +check: $(FEXC_LINKS) + make -C tests/ diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..f3560a3 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,48 @@ +# +# tests/Makefile +# + +BOARDS_URL := https://github.com/linux-sunxi/sunxi-boards/archive/master.zip +BOARDS_DIR := sunxi-boards + +check: check_all_fex coverage + +# Conversion cycle (.fex -> .bin -> .fex) test for all sunxi-boards +check_all_fex: $(BOARDS_DIR)/README unify-fex + ./test_all_fex.sh $(BOARDS_DIR) + +coverage: + # Usage help / invocation with no args + ../sunxi-fexc -? 2> /dev/null ; exit 0 + # Improve code coverage for corner cases (e.g. erroneous parameters) + ./test_fex2bin_corner_cases.sh + ./test_bin2fex_corner_cases.sh + +# Retrieve and extract sunxi-boards archive (containing all .fex) +$(BOARDS_DIR).zip: + curl -fLsS -o $@ $(BOARDS_URL) +$(BOARDS_DIR)/README: $(BOARDS_DIR).zip + @echo Extracting $< ... + unzip -q $< + mv sunxi-boards-master $(BOARDS_DIR) + touch -r $(BOARDS_DIR) $< + cat patches/*.patch | patch -p1 + +unify-fex: unify-fex.c + $(CC) -Wall -Werror -o $@ $< + +clean: + rm -rf $(BOARDS_DIR).zip $(BOARDS_DIR) unify-fex + +# +# Dedicated rule for Travis CI test of sunxi-boards. This assumes that the +# sunxi-tools source (archive) was extracted into a subdir below the working +# directory, meaning that BOARDS_DIR should be "../.." +# +sunxi-boards_CI: unify-fex + # compile sunxi-fexc, link bin2fex and fex2bin + make -C .. bin2fex fex2bin + # apply patches to BOARDS_DIR, ignore mismatches + cat patches/*.patch | patch --forward -r- -p2 -d $(BOARDS_DIR) || true + # and finally run the tests + ./test_all_fex.sh $(BOARDS_DIR) diff --git a/tests/fextest.sh b/tests/fextest.sh new file mode 100755 index 0000000..bd647cf --- /dev/null +++ b/tests/fextest.sh @@ -0,0 +1,19 @@ +#!/bin/bash +echo $0 $* +FEX2BIN=../fex2bin +BIN2FEX=../bin2fex +FEX=$1 +BIN=${FEX/%.fex/.bin} +REVERSE=${FEX/%.fex/.new} +${FEX2BIN} ${FEX} ${BIN} +${BIN2FEX} ${BIN} ${REVERSE} +# preprocess .fex, compare it to the bin2fex output +if ./unify-fex ${FEX} | diff -uwB - ${REVERSE}; then + # if successful, clean up the output files + rm -f ${BIN} ${REVERSE} +else + echo '***' + echo "*** ERROR processing ${FEX}" + echo '***' + exit 1 +fi diff --git a/tests/patches/a-star_kv49l.patch b/tests/patches/a-star_kv49l.patch new file mode 100644 index 0000000..799ae1a --- /dev/null +++ b/tests/patches/a-star_kv49l.patch @@ -0,0 +1,55 @@ +See https://github.com/linux-sunxi/sunxi-boards/issues/51 + +--- orig/sunxi-boards/sys_config/a33/a-star_kv49l.fex ++++ new/sunxi-boards/sys_config/a33/a-star_kv49l.fex +@@ -830,25 +830,25 @@ + + [Vdevice] + Vdevice_used = 0 +-Vdevice_0 = port:P@00<0><0><0><0> +-Vdevice_1 = port:P@00<0><0><0><0> ++;Vdevice_0 = port:P@00<0><0><0><0> ++;Vdevice_1 = port:P@00<0><0><0><0> + + [s_uart0] + s_uart_used = 0 +-s_uart_tx = port:P@00<0><0><0><0> +-s_uart_rx = port:P@00<0><0><0><0> ++;s_uart_tx = port:P@00<0><0><0><0> ++;s_uart_rx = port:P@00<0><0><0><0> + + [s_rsb0] + s_rsb_used = 0 +-s_rsb_sck = port:P@00<0><0><0><0> +-s_rsb_sda = port:P@00<0><0><0><0> ++;s_rsb_sck = port:P@00<0><0><0><0> ++;s_rsb_sda = port:P@00<0><0><0><0> + + [s_jtag0] + s_jtag_used = 0 +-s_jtag_tms = port:P@00<0><0><0><0> +-s_jtag_tck = port:P@00<0><0><0><0> +-s_jtag_tdo = port:P@00<0><0><0><0> +-s_jtag_tdi = port:P@00<0><0><0><0> ++;s_jtag_tms = port:P@00<0><0><0><0> ++;s_jtag_tck = port:P@00<0><0><0><0> ++;s_jtag_tdo = port:P@00<0><0><0><0> ++;s_jtag_tdi = port:P@00<0><0><0><0> + + [s_powchk] + s_powchk_used = 0 +@@ -875,9 +875,9 @@ + + [leds_para] + leds_used = 0 +-;red_led = port:P@00<0><0><0><0> +-;red_led_active_low = 0 +-;green_led_active_low = 0 +-;blue_led = +-;blue_led_active_low = 0 ++;red_led = port:P@00<0><0><0><0> ++;red_led_active_low = 0 ++;green_led_active_low = 0 ++;blue_led = ++;blue_led_active_low = 0 + diff --git a/tests/test_all_fex.sh b/tests/test_all_fex.sh new file mode 100755 index 0000000..f22325e --- /dev/null +++ b/tests/test_all_fex.sh @@ -0,0 +1,7 @@ +#!/bin/sh +FEXFILES=fexfiles.lst +find $1 -name '*.fex' > ${FEXFILES} +while read fex; do + ./fextest.sh ${fex} || exit +done <${FEXFILES} +rm -f ${FEXFILES} diff --git a/tests/test_bin2fex_corner_cases.sh b/tests/test_bin2fex_corner_cases.sh new file mode 100755 index 0000000..b10746b --- /dev/null +++ b/tests/test_bin2fex_corner_cases.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# +# === Test errors / corner cases of "bin2fex", improving on code coverage === +# +BIN2FEX=../bin2fex +TESTFILE=sunxi-boards/sys_config/a10/a10-olinuxino-lime +# use sunxi-fexc in "fex2bin" mode, testing explicit parameters at the same time +FEX2BIN="../sunxi-fexc -v -q -I fex -O bin" + +${FEX2BIN} ${TESTFILE}.fex ${TESTFILE}.bin +# have bin2fex explicitly read /dev/stdin, to force use of fexc.c's "read_all()" +cat ${TESTFILE}.bin | ${BIN2FEX} /dev/stdin > /dev/null +rm -f ${TESTFILE}.bin diff --git a/tests/test_fex2bin_corner_cases.sh b/tests/test_fex2bin_corner_cases.sh new file mode 100755 index 0000000..2fe7ebe --- /dev/null +++ b/tests/test_fex2bin_corner_cases.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# +# === Test errors / corner cases of "fex2bin", improving on code coverage === +# +FEX2BIN=../fex2bin + +function expect () { + OUT=`${FEX2BIN} 2>&1` + if (! echo ${OUT} | grep -q "$1"); then + echo ERROR: Expected substring \"$1\" not found in output: + echo ${OUT} + exit 1 + fi + #echo ${OUT} +} + +# missing section, CRLF line ending +echo -e "foobar\r\n" | expect "data must follow a section" + +# malformed sections +expect "incomplete section declaration" <<-EOF + [foobar +EOF +expect "invalid character at 5" <<-EOF + [foo#bar] +EOF + +# invalid entry +expect "invalid character at 4" <<-EOF + [foo] + bar +EOF + +# bad port specifiers +expect "parse error at 12" <<-EOF + [foo] + bar = port:P@0 +EOF +expect "invalid character at 14" <<-EOF + [foo] + bar = port:PA* +EOF +expect "port out of range at 14" <<-EOF + [foo] + bar = port:PA666 +EOF +expect "value out of range at 17" <<-EOF + [foo] + bar = port:PA00<-1> +EOF +expect "invalid character at 18" <<-EOF + [foo] + bar = port:PA00<0 > +EOF + +# bad = pairs +expect "invalid character at 8" <<-EOF + [foo] + bar = 0* +EOF +expect "value out of range" <<-EOF + [foo] + bar = 4294967296 +EOF +expect "unquoted value 'bad', assuming string" <<-EOF + [foo] + bar = bad +EOF + +# test truncation of very long identifiers +${FEX2BIN} > /dev/null <<-EOF + [an_overly_long_section_name_to_truncate] + an_overly_long_entry_name_to_truncate = 0 +EOF diff --git a/tests/unify-fex.c b/tests/unify-fex.c new file mode 100644 index 0000000..05f2acd --- /dev/null +++ b/tests/unify-fex.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2016 Bernhard Nortmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * unify-fex.c + * + * A utility program to do some standard transformations on .fex files, + * to allow simpler (diff) comparison with the output of bin2fex. + */ + +#include +#include +#include +#include + +/* string macro to determine if str starts with a given literal */ +#define starts(str, literal) \ + (strncmp(str, "" literal, sizeof(literal) - 1) == 0) + +int main(int argc, char **argv) +{ + FILE *input = stdin; + char line[1024]; + char *c, *p; + long long num; + + if (argc >= 2) { + input = fopen(argv[1], "r"); + if (!input) { + perror("failed to open input file"); + exit(EXIT_FAILURE); + } + } + + /* loop over all input lines, output goes to stdout */ + while (fgets(line, sizeof(line), input)) { + + /* strip all whitespace (even CR/LF) from the input line */ + for (c = p = line; *p; p++) { + if (!isspace(*p)) + *c++ = *p; + } + *c = '\0'; + + if (*line == ';' || *line == '#') + /* This is a comment line, simply ignore it */ + continue; + if (*line == ':') + continue; /* suspect malformed comment, ignore */ + + if ((p = strchr(line, '='))) { + /* This is a = line, reformat it */ + c = strdup(p + 1); + sprintf(p, " = %s", c); + free(c); + p += 3; /* have p point to the beginning of */ + + if (starts(p, "port:")) { + if (p[5] == 'P') { /* port:P... */ + /* get pin number (including bank) */ + num = ((p[6] - 'A') << 5) + strtoll(p + 7, &c, 10); + c = strdup(c); + sprintf(p, "port:P%c%02lld%s", 'A' + (int)(num >> 5), (num & 0x1F), c); + free(c); + + /* check angle brackets to determine options count */ + num = 0; + for (c = p + 9; *c; c++) { + if (*c == '<') + num++; + } + /* append "" for missing options */ + c = strrchr(p, '\0'); + while (num < 4) { + c += sprintf(c, ""); + num++; + } + } + } else { + /* + * fix formatting of numeric values, depending on the keyword + * these are a bit nasty, since they vary wildly between hex + * and decimal - see decompile_single_mode() in script_fex.c + */ + num = strtoll(p, NULL, 0); + if (num || *p == '0') { + int hex = starts(line, "csi_twi_addr"); + hex |= starts(line, "ctp_twi_addr"); + hex |= starts(line, "dram_baseaddr"); + hex |= starts(line, "dram_emr"); + hex |= starts(line, "dram_tpr"); + hex |= starts(line, "dram_zq"); + hex |= starts(line, "g2d_size"); + hex |= starts(line, "gsensor_twi_addr"); + hex |= starts(line, "lcd_gamma_tbl_"); + hex |= starts(line, "rtp_press_threshold "); + hex |= starts(line, "rtp_sensitive_level"); + hex |= starts(line, "tkey_twi_addr"); + + /* large decimals will be decompiled as negative */ + if (!hex && num >= 2147483648LL) + num -= 4294967296LL; + + sprintf(p, hex ? "0x%llx" : "%lld", num); + } else { + /* + * We expect all other (= non-numeric) values + * to be strings, always quote them. + */ + if (*p && (*p != '"')) { + c = strdup(p); + sprintf(p, "\"%s\"", c); + free(c); + } + } + } + } + + puts(line); + } + + if (ferror(input)) { + perror("file read error"); + exit(EXIT_FAILURE); + } + + fclose(input); + return EXIT_SUCCESS; +}