Вы должны создать все необходимые сигналы SystemC, модули SystemC и установить между ними связь, прежде чем запускать любой тест в GTest.Это требует создания собственной реализации gtest_main.cc
.Естественно, в SystemC вы должны поместить все в sc_main
функцию.
Для этого я бы использовал шаблон проектирования реестра.
Сначала создайте класс реестра (Registry + Factory + Singleton).Этот класс будет отвечать за хранение зарегистрированных конструкторов с использованием динамического выделения с новым и умным указателем в лямбда-выражении (см. factory::add
класс).Создайте все объекты, используя метод factory::create()
перед запуском всех тестов.Затем вы можете получить объект, используя метод factory::get()
при выполнении теста.
factory.hpp
#ifndef FACTORY_HPP
#define FACTORY_HPP
#include <map>
#include <string>
#include <memory>
#include <functional>
class factory {
public:
static factory& get_instance();
template<typename T, typename ...Args>
class add {
public:
add(Args&&... args);
add(const std::string& name, Args&&... args);
};
template<typename T>
static T* get(const std::string& name = "");
void create();
void destroy();
private:
using destructor = std::function<void(void*)>;
using object = std::unique_ptr<void, destructor>;
using constructor = std::function<object(void)>;
factory();
factory(const factory& other) = delete;
factory& operator=(const factory& other) = delete;
void add_object(const std::string& name, constructor create);
void* get_object(const std::string& name);
std::map<std::string, constructor> m_constructors;
std::map<std::string, object> m_objects;
};
template<typename T, typename ...Args>
factory::add<T, Args...>::add(Args&&... args) {
add("", args...);
}
template<typename T, typename ...Args>
factory::add<T, Args...>::add(const std::string& name, Args&&... args) {
factory::get_instance().add_object(name,
[args...] () -> object {
return object{
new T(std::forward<Args>(args)...),
[] (void* obj) {
delete static_cast<T*>(obj);
}
};
}
);
}
template<typename T> auto
factory::get(const std::string& name) -> T* {
return static_cast<T*>(factory::get_instance().get_object(name));
}
#endif /* FACTORY_HPP */
factory.cpp
#include "factory.hpp"
#include <stdexcept>
auto factory::get_instance() -> factory& {
static factory instance{};
return instance;
}
factory::factory() :
m_constructors{},
m_objects{}
{ }
void factory::create() {
for (const auto& item : m_constructors) {
m_objects[item.first] = item.second();
}
}
void factory::destroy() {
m_objects.clear();
}
void factory::add_object(const std::string& name, constructor create) {
auto it = m_constructors.find(name);
if (it == m_constructors.cend()) {
m_constructors[name] = create;
}
else {
throw std::runtime_error("factory::add(): "
+ name + " object already exist in factory");
}
}
auto factory::get_object(const std::string& name) -> void* {
auto it = m_objects.find(name);
if (it == m_objects.cend()) {
throw std::runtime_error("factory::get(): "
+ name + " object doesn't exist in factory");
}
return it->second.get();
}
Создайте собственную версию реализации gtest_main.cc
.Вызовите метод factory::create()
для создания всех сигналов SystemC и модулей SystemC перед выполнением любых тестов RUN_ALL_TESTS()
.Поскольку фабричный класс является одноэлементным шаблоном проектирования, после завершения всех тестов вызовите метод factory::destroy()
, чтобы уничтожить все созданные объекты SystemC.
main.cpp
#include "factory.hpp"
#include <systemc>
#include <gtest/gtest.h>
int sc_main(int argc, char* argv[]) {
factory::get_instance().create();
testing::InitGoogleTest(&argc, argv);
int status = RUN_ALL_TESTS();
factory::get_instance().destroy();
return status;
}
Затемопределите класс dut в своем тесте, чем создадите сигналы SystemC и модули SystemC.В конструкторе сделайте связь между созданными сигналами SystemC и модулями.Зарегистрируйте определенный dut класс в объекте реестра, используя глобальный конструктор, подобный этому factory :: add g .После этого вы можете получить свой объект, используя простой метод factory :: get () .
test.cpp
#include "my_module.h"
#include "factory.hpp"
#include <gtest/gtest.h>
#include <systemc>
class dut {
public:
sc_core::sc_clock aclk{"aclk"};
sc_core::sc_signal<bool> areset_n{"areset_n"};
sc_core::sc_signal<bool> in{"in"};
sc_core::sc_signal<bool> out{"out"};
dut() {
m_dut.aclk(aclk);
m_dut.areset_n(areset_n);
m_dut.in(in);
m_dut.out(out);
}
private:
my_module m_dut{"my_module"};
};
static factory::add<dut> g;
TEST(my_module, simple) {
auto test = factory::get<dut>();
test->areset_n = 0;
test->in = 0;
sc_start(3, SC_NS);
test->areset_n = 1;
test->in = 1;
sc_start(3, SC_NS);
EXPECT_TRUE(test->out.read());
}
my_module.h
#ifndef MY_MODULE_H
#define MY_MODULE_H
#include <systemc>
struct my_module : public sc_core::sc_module {
my_module(const sc_core::sc_module_name& name): sc_core::sc_module(name) {
SC_HAS_PROCESS(my_module);
SC_METHOD(flip_flop_impl);
sensitive << aclk.pos();
<< areset_n.neg();
dont_initialize();
}
void flip_flop_impl() {
if(areset_n.read()) {
out.write(in.read());
} else {
out.write(false);
}
}
sc_core::sc_in<bool> aclk{"aclk"};
sc_core::sc_in<bool> areset_n{"areset_n"};
sc_core::sc_in<bool> in{"in"};
sc_core::sc_out<bool> out{"out"};
}; //< my_module
#endif /* MY_MODULE_H */
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(factory_gtest)
find_package(SystemCLanguage CONFIG REQUIRED)
set(CMAKE_CXX_STANDARD ${SystemC_CXX_STANDARD})
find_package(GTest REQUIRED)
enable_testing()
add_executable(${PROJECT_NAME} main.cpp factory.cpp test.cpp)
target_link_libraries(${PROJECT_NAME} ${GTEST_LIBRARIES} SystemC::systemc)
target_include_directories(${PROJECT_NAME} PRIVATE ${GTEST_INCLUDE_DIRS})
add_test(SystemCGTestExample ${PROJECT_NAME})
Для получения дополнительной информации вы можете проверить мою библиотеку logic для проверки SystemC: https://github.com/tymonx/logic