Теперь для версии 1.2 добавим измерения покрытия программы тестами. Для этого добавим в
Makefile.am дополнительный рецепт для запуска утилиты
gcov, которая обрабатывает результаты тестирования:
@user:
syscall-master/Makefile.am
SUBDIRS = src doc tests
checklog: check
cat tests/*.log
gcov: check
$(MAKE) -C src gcov
для ручного показа статистики по тестированию добавлен рецепт checklog;
рецепт make gcov в основном Makefile требует предварительного запуска тестов (check), после чего вызывает make gcov в каталоге src.
Собирать статистику о выполнении строк исходного текста можно только если программа особым образом скомпилирована: в её код добавлено постоянное обновление многочисленных счётчиков, проверка достижимости и т. п. Исполняемые файлы, скомпилированные для проверки покрытия, непригодны к эксплуатации: они работают гораздо медленнее, потребляют больше ресурсов и при запуске создают множество временных файлов с отчётами:
@user:
syscall-master/src/Makefile.am
CFLAGS = -Wall -O0 -g
if COND_GCOV
CFLAGS += -fprofile-arcs -ftest-coverage
endif
lib_LTLIBRARIES=libsyscall.la
libsyscall_la_SOURCES=globals.c utility.c basic.c
BUILT_SOURCES = syscall.h systab.h
bin_PROGRAMS = syscall
syscall_SOURCES = syscall.c
syscall_LDADD = libsyscall.la
systab.h:
sh lssyscalls | awk '{printf "{\"%s\", %d},\n", $$1, $$2}' > $@
gcov:
gcov -o .libs basic -t -k -b | grep -v -E '[[:digit:]]:[ /][*]'
gcov -o .libs utility -t -k -b | grep -v -E '[[:digit:]]:[ /][*]'
Утилита gcov принимает на вход имя файла со статистикой покрытия (без расширения) которое соответствует .c-файлу из директории исходных текстов. Файлы статистики формируются в рабочей директории .libs, её надо указать с помощью ключа -o. Эти файлы собирают информацию о количестве отработанных в процессе тестирования строк кода и другие статистические параметры. Из данной статистики нас будут интересовать выводы по количеству выполнения строк кода и, дополнительно (благодаря ключу -b), вариантах обработки условий (в условных операторах / switch-case конструкции и т. д.):
@user
[user@VM syscall-master]$ make COND_GCOV=1 gcov
Making check in src
<...>
make -C src gcov
gcov -o .libs basic -t -k -b | grep -v -E '[[:digit:]]:[ /][*]'
-: 0:Colorization: profile count: zero coverage (exceptional) zero coverage (unexceptional) unexecuted block
-: 0:Source:basic.c
-: 0:Graph:.libs/basic.gcno
-: 0:Data:.libs/basic.gcda
-: 0:Runs:14
-: 1:#include "syscall.h"
-: 2:
function parse_arg called 6 returned 100% blocks executed 81%
6: 3:unsigned long parse_arg(const char *syscall_name, char *arg)
-: 4:{
-: 5: unsigned long num;
6: 6: char *endp = NULL;
-: 7:
-: 8: /* #hello - length of a string */
6: 9: if (arg[0] == '#') {
branch 0 taken 33% (fallthrough)
branch 1 taken 67%
2: 10: return (unsigned long)strlen(arg + 1);
-: 11: }
-: 12:
-: 13: /* $0 - return value of a previous syscall */
4: 14: if (arg[0] == '$') {
branch 0 taken 50% (fallthrough)
branch 1 taken 50%
2: 15: num = strtoul(arg + 1, &endp, 10);
call 0 returned 100%
2: 16: if (num < CMD_MAX - 1) {
branch 0 taken 100% (fallthrough)
branch 1 taken 0%
2: 17: return (unsigned long)ret_values[num];
-: 18: }
-: 19: }
-: 20:
-: 21: /* Try to parse it as a number */
2: 22: endp = NULL;
2: 23: num = strtoul(arg, &endp, 0);
call 0 returned 100%
2: 24: if (errno == ERANGE) {
branch 0 taken 0% (fallthrough)
branch 1 taken 100%
0: 25: errx(1, "%s: argument '%s' is out of range",
call 0 never executed
-: 26: syscall_name, arg);
-: 27: }
2: 28: if (endp && *endp == '\0') {
branch 0 taken 100% (fallthrough)
branch 1 taken 0%
branch 2 taken 100% (fallthrough)
branch 3 taken 0%
-: 29: /* strtoul succeeded */
2: 30: return num;
-: 31: }
-: 32:
-: 33: /* assume it is a string */
-: 34: /* unescape any \n at the end of the string */
0: 35: unescape_nl(arg);
call 0 never executed
0: 36: return (unsigned long)arg;
-: 37:}
-: 38:
gcov -o .libs utility -t -k -b | grep -v -E '[[:digit:]]:[ /][*]'
<...>
[user@VM syscall-master]$ ls -la src/.libs/ src | grep -E "\.gc??"
-rw-r--r-- 1 builder builder 1668 Aug 11 22:58 syscall.gcno
-rw-r--r-- 1 builder builder 228 Aug 11 22:58 basic.gcda
-rw-r--r-- 1 builder builder 5392 Aug 11 22:58 basic.gcno
-rw-r--r-- 1 builder builder 61 Aug 11 22:58 globals.gcno
-rw-r--r-- 1 builder builder 208 Aug 11 22:58 utility.gcda
-rw-r--r-- 1 builder builder 1836 Aug 11 22:58 utility.gcno
Для удобства можно добавить задание COND_GCOV параметром configure:
@user:
syscall-master/configure.ac
<...>
# Variable definitins
AC_SUBST(CK_VERBOSITY, verbose)
AC_ARG_VAR(CK_VERBOSITY, [Default: "verbose", can be "silent", "minimal" or "normal")])
# Enabe/disable things
AC_ARG_ENABLE([gcov],
[AS_HELP_STRING([--enable-gcov], [use gcov to collect test suite coverage])],
[], [enable_gcov=no])
AM_CONDITIONAL([COND_GCOV],[test '!' "$enable_gcov" = no])
# Checks for libraries.
# Checks for header files.
<...>
Поскольку пакет с исполняемыми файлами, скомпилированными для запуска gcov, был бы не пригоден для эксплуатации, необходимо проводить тестирование на независимой сборке. Она будет проводиться в отдельной вспомогательной директории, а запуск проверки будет контролироваться с помощью макросов %def_enable для отслеживания ключа запуска тестирования и %if_enabled / %endif для обработки случая тестирования. В сам пакет же будут добавлены скомпилированные обычным образом файлы:
@user:
syscall-master/.gear/syscall.spec
%def_enable gcov
Name: syscall
Version: 1.2
Release: alt1
<...>
%build
%autoreconf
%if_enabled gcov
mkdir gcov_check && cd gcov_check
../configure --enable-gcov
%make_build
cd ..
%endif
%configure
%make_build
%install
%makeinstall_std
%check
%if_enabled gcov
cd gcov_check
make gcov
cd ..
%else
make check
%endif
%files
%_bindir/%name
%_libdir/*
%_man1dir/*
%changelog
* Mon Aug 11 2025 UsamG1t <usamg1t@altlinux.org> 1.2-alt1
- Add gcov
* Fri Aug 08 2025 UsamG1t <usamg1t@altlinux.org> 1.1-alt1
- Add xUnit check
* Fri Aug 08 2025 UsamG1t <usamg1t@altlinux.org> 1.0-alt1
- Initial Build
@user
[user@VM syscall-master]$ gear-hsh --lazy
<...>
Executing(%check): /bin/sh -e /usr/src/tmp/rpm-tmp.73347
+ umask 022
+ /bin/mkdir -p /usr/src/RPM/BUILD
+ cd /usr/src/RPM/BUILD
+ cd syscall-1.2
+ make check
<...>
checkmk include.ts > include.c
<...>
PASS: include
PASS: upstream
============================================================================
Testsuite summary for syscall 1.0
============================================================================
# TOTAL: 2
# PASS: 2
# SKIP: 0
# XFAIL: 0
# FAIL: 0
# XPASS: 0
# ERROR: 0
============================================================================
<...>
make -C src gcov
make[1]: Entering directory '/usr/src/RPM/BUILD/syscall-1.2/src'
gcov -o .libs basic -t -k -b | grep -v -E '[[:digit:]]:[ /][*]'
<...>