Здравствуйте, товарищи программисты и все кто им сочувствует. Я хотел бы предложить обзор возможностей сборочной системы QBS для интеграции покрытия кода в Qt автотесты QtTest с использованием утилит gcov/lcov. Кому эта тема интересна, добро пожаловать по кат.   Итак, давайте разберемся, что означает термин "покрытие кода". Вкратце, это когда используется некая утилита (или комплекс ПО) которая позволяет отследить и отобразить в той или иной форме результаты того, насколько тот или иной автотест покрывает исходный код чего-либо.  Обычно, эта утилита встраивает часть некоего своего кода (например, в виде библиотеки, слинкованной с автотестом) для генерации дополнительной информации в процессе работы автотеста. При этом, полученную информацию можно отобразить в любой удобной форме (обычно это HTML страницы).  Содержимое этой информации включает процентное соотношение количества строк и ветвей кода, обработанных автотестом, а также полную информацию о состоянии всех строк и ветвей. В нашем текущем примере мы будем идти по пути "наименьшего сопротивления" и использовать общедоступное и свободное ПО (т.к. мы все в душе лентяи): Компонент Что это такое Причина выбора GNU/Linux Операционная система. Доступность, бесплатность, простота. GCC Набор компиляторов. Доступность, бесплатность, простота.  GCOV Утилита, собирающая предварительную информацию о покрытии. Уже содержится в наборе компиляторов GCC. LCOV Утилита конвертирующая результаты покрытия в удобной форме. Доступность, бесплатность, простота. GENHTML Утилита конвертирующая результаты покрытия в HTML формат (входит в состав lcov). Доступность, бесплатность, простота. Qt Кросс-платформенный С++ фреймворк. Доступность, бесплатность, простота. Уже содержит свой собственный тестовый фреймворк QtTest. QtCreator Кросс-платформенное и универсальное IDE. Доступность, бесплатность, простота. Поддерживает автотесты QtTest и сборочную систему QBS "из коробки". QBS Кросс-платформенная система сборки. Доступность, бесплатность, простота, модерн и удобство (да и вообще, няшка). Примечание: Я здесь не буду расписывать как устанавливать те или иные пакеты, т.к. считаем, что пользователь разберется сам (например, погуглит). Вкратце, чтобы включить поддержку покрытия кода в автотестах, используя компилятор GCC (или CLang), достаточно собрать приложение с отключенной оптимизацией (чтобы компилятор не выкидывал секции, ветвления и т.д., а оставлял "как есть") и передать ему флаг --coverage как в опции компилятора так и в опции линковщика. Примечание: Именно так и никак иначе, т.к.приложение автотеста просто не скомпилируется. При этом, линковщик автоматически слинкует с исполняемым приложением автотеста некий дополнительный код, который будет заниматься сбором информации об использованных строках и ветвях в коде (я так предполагаю). Все это хозяйство заработает только после реального запуска приложения автотеста. По завершению работы приложения автотеста будет автоматически сгенерирован бинарный файл с результатами покрытия в специальном формате.  Далее, этот выходной файл результатов покрытия можно передать, например, утилите lcov для генерации "дружелюбной" информации для пользователя. Например, как набор красивых HTML страниц с подробной информацией о покрытии. Ниже приведу минимальный список шагов что и как мы будем делать для достижения результата: Пишем некий код, который надо проверить. Пишем некий автотест который линкуется с кодом и проверяет его. Запускаем автотест и проверяем что он работает. Добавляем опцию --coverage при сборке автотеста. Запускаем автотест и проверяем что он сгенерировал выходную информацию о покрытии в выходной файл.  Передаем сгенерированный выходной файл в утилиту lcov для генерации красивых результатов в HTML формате. Открываем HTML результаты и любуемся. Давайте для простоты создадим простейшее дерево проекта COVERAGE-EXAMPLE со следующей структурой:  , где:  coverage-example.qbs - это корневой файл проекта: src/- директория содержащая исходные коды под-проекты главного проекта, которые перечислены в файле src.qbs (в нашем случае это будут только под-проекты библиотек): libs/ - директория содержащая исходные коды под-проектов библиотек, которые перечислены в файле libs.qbs (в нашем случае это будет только один продукт библиотеки foo): libs/foo/ - директория содержащая исходные коды продукта нашей библиотеки, конфигурация которой описана в файле foo.qbs : Библиотека foo пусть будет статической и пусть содержит всего лишь один класс Foo с одним методом encode(): Этот метод принимает на вход какое-то значение из перечисления и преобразует его в некоторое имя: Чтобы проверить что целевой метод: работает правильно, необходимо ему на вход подавать каждое значение из перечисления Number и сравнивать результат, возвращаемый методом, с ожидаемой строкой. Для простоты эксперимента возьмем тестовый фреймворк QtTest из состава Qt, для чего немного расширим структуру проекта, добавив директорию tests: При этом, файл coverage-example.qbs пополнится до такого содержимого: , где: tests/tests.qbs - директория содержащая исходные коды под-проектов тестов, которые перечислены в файле tests.qbs (в нашем случае это будут только под-проекты автотестов): auto/ - директория содержащая только под-проекты автотестов, которые перечислены в файле auto.qbs (на данный момент содержит только один автотест foo для нашей библиотеки foo): auto/foo/ - директория содержащая продукт приложения автотеста для библиотеки foo, конфигурация которого описана в файле foo.qbs : Приложение автотеста tst_foo.cpp содержит следующий код: Здесь: метод теста tst_Foo:encode_data() формирует набор датасетов, подаваемых в проверяемый метод Foo::encode() библиотеки.  метод теста tst_Foo::encode() запускает по очереди целевую функцию из библиотеки с каджым из датасетов. Примечание: Это все магия QtTest, с которой подробнее можно ознакомиться из оффициальной документации Qt. Для проверки того, что наш автотест работает как надо, нам просто необходимо его запустить (автотест - это просто обычное приложение). В процессе работы он выдаст результат, похожий на этот: Чтобы информация о покрытии генерировалась в процессе работы автотеста, необходимо добавить опцию --coverage и компилятору и линковщику, а также собирать проект с отключенной оптимизацией (например, это дебаг режим).  В нашем случае при сборке статической библиотеки до линковки дело не доходит, т.к. здесь работает только компилятор и архиватор. А вот уже приложение автотеста и компилируется и линкуется. Поэтому опцию --coverage нужно добавить в два разных продукта в разные шаги сборки:  и в библиотеку (только компилятору)  и в автотест (и компилятору и линковщику).  Для этого QBS предоставляет модуль cpp, который имеет сециально предназначенные для этого свойства. В нашем случае, свойство cpp.driverFlags - то что нужно, оно передаст опцию --coverage и компилятору и линковщику. Простой и самый напрашивающийся вариант - это продублировать свойство cpp.driverFlags и в продукт библиоеки и в продукт автотеста. Но это решение некрасивое, и есть более изящный подход, используя мощь QBS. ;) Мы просто создадим дополнительный QBS модуль, и назовем его для примера как coverage. Этот модуль будет подставлять нужные опции сам автоматически. Достаточно только его подключить как зависимость к продукту. Для этого придется немного расширить дерево проекта: , где: qbs/ - директория в которой находятся наши вспомогательные QBS модули или файлы импорта. qbs/modules/ - директория содержащая вспомогательные модули (должна иметь имя modules). qbs/modules/coverage/ - директория, содержащая исходный QBS код нашего вспомогательного модуля coverage описанного в файле coverage.qbs :  Далее, добавляем этот модуль как зависимость к продукту библиотеки в файле foo.qbs: Теперь при сборке библиотеки, будет добавлена опция --coverage компилятору. А благодаря тому, что мы экспортировали зависимость от модуля coverage из библиотеки foo с помощью Export , то теперь любой продукт, подключивший библиотеку foo также получит эту опцию --coverage. Таким образом, мы убиваем сразу двух (и более зайцев). И почти последгий штрих - надо указать QBS где искать наш новый модуль coverage. Для этого нужно добавить в корневой файл проекта coverage-example.qbs одну строчку: На этом, казалось бы, этот шаг должен был быть завершен - но нет. После включения опции --coverage после сборки библиотеки и приложения автотеста, в директории с объектными файлами автоматически будут создаваться файлы с расширением *.gcno: Но мы не хотим держать эту помойку и пускать все на самотек. Мы хотим, чтобы при операции clean очищалась вся директория сборки. Для этого нужно добавить в модуль coverage правило, которое обрабатывало бы файлы *.gcno как артефакты (т.е. включить их в граф сборки QBS).  Примечание: Т.к. выходной артифакт gcno никому не нужен (он не включен ни в какую зависимость) то чтобы правило заработало, необходимо явно сказать QBS-у чтобы он всегда запускал это правило с помощью опции модуля additionalProductTypes. Т.к. наше правило ничего не делает (просто регистрирует файлы *.gcno как артефакты), то и его код должен ничего не делать тоже. Теперь запускаем снова наше приложение автотеста. И видим, что он отработал как обычно (вывел в консоль результаты): Но теперь рядом с объектными файлами библиотеки и приложения автотеста автоматически сгенерировались бинарные файлы отчетов *.gcda: Но мы лентяи не не хотим вручную запускать автотесты. Для этого у QBS уже имеется специальный объект AutotestRunner, который достаточно добавить в корневой проект coverage-example.qbs : И всем продуктам приложениям автотестов просто добавить тег autotest (в нашем случае у нас один объект автотеста в tests/auto/foo/foo.qbs): И тепрерь, при сборке нашего раннера, QBS автоматически отследит все его зависимости (они являются автотестами), соберет их (если еще не собраны), и запустит по очереди. Итак, чтобы сгенерировать красивые результаты покрытия в виде HTML страниц, необходимо обработать все бинарные файлы результатов утилитой lcov.  Чтобы автоматизировать этот процесс - напишем свой зарускатель автотестов, т.к. стандартный AutotestRunner не годится для этой цели. Причина в том, что нам нужно не только запускать автотест, но и написать правила, которые будут отслеживать генерируемые файлы *.gcda как выходные артефакты и подавать их на вход утилите lcov.  Назовем наш новый запускатель как CoverageRunner и поместим его реализацию в директорию imports: , где: qbs/imports/ - диретория, содержащая разные реализации вспомогательных объектов.   В нашем случае она содержит только одну реализацию объекта нашего запускателя в файле CoverageRunner.qbs : Этот запускатель реализует набор правил, выстроенных в цепочку в графе сборки. При сборке запускателя будут выполненны следующие действия: Запуск автотестов для генерации бинарных файлов *.gcda . Подача всех файлов *.gcda в утилиту lcov для генерации текстовых фалов *.info. Подача всех текстовых файлов *.info в утилиту lcov для их объединения в один результирующий файл *.info. Подача результирующего текстового файла *.info в утилиту genhtml для генерации HTML страниц о покрытии кода. Далее, необходимо модифицировать корневой файл проекта coverage-example.qbs, заменив в нем AutotestRunner на наш CoverageRunner : Теперь, для генерации результатов покрытия в виде HTML страниц достаточно просто выполнить сборку нашего продукта - запускателя тестов coverage-runner. После сборки нашего запускателя, он сгенерирует результаты покрытия в виде HTML страниц в выходную директорию сборки запускателя, например: Теперь мы можем открыть файл index.html и посмотреть что получилось: Конечно, здесь добавляется и лишняя информация. Но это не проблема, т.к. ее можно исключить на этапе генерации файлов *.info, но мы не будем касаться этого в данной статье. Результаты покрытия по нашей библиотеки примерно следующие: Теперь можете сами поиграться с содержимым автотеста, например, закомментировав некоторые строчки дата-сетов, перегенерировать HTML и посмотреть что поменяется. ;) В этой статье краттко рассмотрели всю мощь и гибкость QBS для реализации самых разнообразных задач. И, конечно, ссылки: Исходный код примера на github.  Что такое QBS.