google-tests-conan-sample/readme.md

11 KiB
Raw Blame History

Базовая настройка тестирования.

Используемые технологии

  • C++11
  • CMake
  • GTest (Google test framework for C++)
  • Conan

Структура проекта

- Project/
-- build/
--- <caches>
--- bin/
---- program
-- include/
--- algos.h
--- algos.c
-- src/
--- main.c
-- tests/
--- test.cpp
-- conanfile.txt
-- CMakeLists.txt

conanfile

Для подключения тестового фреймворка используется система менеджмента артефактов Conan, которая позволяет осуществлять подключение заранее скомпилированных библиотек без необходимости их установки на ОС разработчика. Подключение осуществляется описанием корректных настроек в файле conanfile.txt:

[requires]
gtest/cci.20210126

[generators]
cmake

Таким образом, будет подключена последняя на момент написания данного документа сборка фреймворка тестирования.

sources

Исходные коды проекта разделены на две части: собственно исходники и включаемые файлы. Исходники - это код самой программы, использующей алгоритмы, описанные во включаемых заголовочных файлах.

src/main.c

В файле с программой подключается файл с тестируемым алгоритмом, который используется в коде программы:

#include <stdio.h>
#include "../include/algos.h"

void swap(char* a, char* b);

int main(int argc, const char** argv) {
    char a = -11;
    char b = 15;
    printf("a=%d; b=%d\n", a, b);
    swap(&a, &b);
    printf("a=%d; b=%d\n", a, b);
    return 0;
}

Выводом данного кода будет:

user@debian: ./build/bin/prog 
a=-11; b=15
a=15; b=-11

include/algos.h

Подключаемый в код программ и тестов заголовочный файл должен содержать только одно объявление функций:

#ifndef algos
#define algos

void swap(char* a, char* b);

#endif

include/algos.c

файл с реализацией лаконичен:

#include "algos.h"

void swap(char* a, char* b) {
    *a ^= *b;
    *b ^= *a;
    *a ^= *b;
}

tests

Тесты описываются в отдельном файле, из которого будет собран бинарный файл. Важно, что фреймворк работает только с С++11 или новее, поэтому использовать более старый стандарт не получится, а исходные файлы на С нужно включать в секции extern "C". Каждый тест описывается в специальной макрофункции TEST в параметрах которой указывается детальное название теста. В данном примере будет использоваться только один ассерт - EXPECT_EQ, который проверяет значение переданной первым аргументом переменной на равенство второму аргументу

tests/test.cpp

#include <gtest/gtest.h>
#include <iostream>
extern "C" {
#include "../includes/algos.h"
}

TEST(HelloTest, WeTestHere) {
    char a = -11;
    char b = 15;
    swap(&a, &b);
    EXPECT_EQ(a, 15);
    EXPECT_EQ(b, -11);
}

TEST(AnotherTest, WeTestThere) {
    char a = -11;
    char b = -11;
    swap(&a, &b);
    EXPECT_EQ(a, -11);
    EXPECT_EQ(b, -11);
}

CMake

Сборщик CMake описывает правила, по которым будет сформировал Makefile для дальнейшей сборки утилитой GNU Automake или Ninja. В файл настроек необходимо включить строки подключения артефактов Conan, поскольку мы используем готовую сборку фреймворка тестирования, а не устанавливаем его на компьютер. Также в скрипте указывается два исполняемых файла - для сборки программы и тестов, соответственно.

cmake_minimum_required(VERSION 3.5)
project(BasicC)
enable_testing()

# GoogleTest requires at least C++11
set(CMAKE_CXX_STANDARD 11)

# conan dependencies
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

# test executable
add_executable(check tests/test.cpp include/algos.h include/algos.c)
target_link_libraries(check ${CONAN_LIBS} gtest_main)

# let make know we use tests
include(GoogleTest)
gtest_discover_tests(check)

# application executable
add_executable(prog src/main.c include/algos.h include/algos.c)

Запуск

Запуск осуществляется из папки build, соответственно:

  • conan install .. (установить зависимости conan, описанные в файле папкой выше)
  • cmake .. (подготовить Makefile по правилам из файла папкой выше)
  • make/ninja (собрать проект)
  • ctest (запустить тесты)
  • ./bin/prog (запустить программу) В результате запуска тестов получится следующий вывод:
Test project /home/user/Documents/book-c-basic/build
    Start 1: HelloTest.WeTestHere
1/2 Test #1: HelloTest.WeTestHere .............   Passed    0.01 sec
    Start 2: AnotherTest.WeTestThere
2/2 Test #2: AnotherTest.WeTestThere ..........   Passed    0.00 sec

100% tests passed, 0 tests failed out of 2

Total Test time (real) =   0.01 sec

Сборка артефакта

Данная часть руководства рассматривается в случае, если собранные артефакты не предназначены под конфигурацию Вашей ОС. В качестве примера создаётся артефакт GTest под MinGW, который отсутствует на данный момент на офф репозиториях conan center.

  • Инициализация репозитория в папке, где будет находиться скаченный с Интернета проект;
  • Скачать .zip git googletest;
  • Распаковать в папку, где инициализирован репозиторий;
  • Создать проект на внутреннем gitlab и заремоутить;
  • Теперь нужно создать проект тестирования артефакта, conan позволяет уже создать готовый sample package recipe для тестирования после сборки артефакта;
  • Создание рецепта пакета (package recipe) - conan create . testg/testing;
  • После чего появится следующий проект:
- Project/
-- test_package/
--- CMakeLists.txt
--- conanfile.txt
--- example.cpp
-- conanfile.py
  • Поменять во внешнем файле conanfile(py) в методе source команды git clone ссылку на соответствующий проект, а также следующие изменения в данном файле:

В методе source поменять название проекта и путь до CMakeLists.txt:

tools.replace_in_file("hello/CMakeLists.txt", "PROJECT(HelloWorld)",
                              '''PROJECT(HelloWorld)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()''')

На:

tools.replace_in_file("googletest/CMakeLists.txt", "project(googletest-distribution)",
                              '''project(googletest-distribution)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()''')

В методе build меняем соурс папку:

def build(self):
    cmake = CMake(self)
    cmake.configure(source_folder="hello")
    cmake.build()

На:

def build(self):
    cmake = CMake(self)
    cmake.configure(source_folder="googletest")
    cmake.build()

В методе package изменить название папки и пути до заголовков:

def package(self):
    self.copy("*.h", dst="include", src="hello")
    self.copy("*hello.lib", dst="lib", keep_path=False)
    self.copy("*.dll", dst="bin", keep_path=False)
    self.copy("*.so", dst="lib", keep_path=False)
    self.copy("*.dylib", dst="lib", keep_path=False)
    self.copy("*.a", dst="lib", keep_path=False)

На:

def package(self):
    self.copy("*.h", dst="include/gtest", src="googletest/googletest/include/gtest")
    self.copy("*gtest.lib", dst="lib", keep_path=False)
    self.copy("*.dll", dst="bin", keep_path=False)
    self.copy("*.so", dst="lib", keep_path=False)
    self.copy("*.dylib", dst="lib", keep_path=False)
    self.copy("*.a", dst="lib", keep_path=False)

Последний метод будет package_info:

def package_info(self):
    self.cpp_info.libs = ["hello"]

На:

def package_info(self):
    self.cpp_info.libs = ["gtest"]
  • После выполнения настроек .py файла можно выполнить - conan create . testg/testing;
  • Затем, можно выполнить upload артефакта и использовать его по мануалу, который описан выше. Лабо можно использовать данный артефакт локально, conan автоматически вначале проверяет cache, и только потом обращается в remote, где указаны соответствующие ссылки (в нашем случае на Nexus).

Также хотелось бы упомянуть один нюанс. Если Вы загружаете уже собранный артефакт, убедитесь, что в файле conan'а настроек конфигураций не были сбиты пути в [env] разделе. Была ситуация, когда был загружен артефакт, собранный на ОС GNU Linux, и в данном файле изменились пути под вид данной ОС (C:\MinGW... -> /c/MinGW/).