在SystemC中使用现有的单元测试框架 [英] Using existing unit test frameworks with SystemC

查看:126
本文介绍了在SystemC中使用现有的单元测试框架的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在SystemC中从事一个项目,并希望合并单元测试.是否可以在SystemC中使用现有的单元测试框架?

I am working on a project in SystemC and want to incorporate unit testing. Is it possible to use existing unit test frameworks with SystemC?

之所以这样问,是因为似乎SystemC模块只能通过仿真内核执行,并且我想对模块本身使用单元测试.

I ask this because it seems like the SystemC modules only get executed with the simulation kernel, and I want to use unit tests on the modules themselves.

推荐答案

在GTest中运行任何测试之前,必须创建所有必要的SystemC信号,SystemC模块并在它们之间建立连接.这需要创建自己的gtest_main.cc实现.自然,在SystemC中,您必须将所有内容都放入sc_main函数中.

You must create all necessary SystemC signals, SystemC modules and make connection between them before you run any test in GTest. This requires to create own gtest_main.cc implementation. Naturally in SystemC you must put everything in sc_main function.

为此,我将使用注册表设计模式.

For this, I would use registry design pattern.

首先创建注册表类(注册表+工厂+单例).此类将负责在lambda表达式中使用带有新指针和智能指针的动态分配来存储注册的构造函数(请参见factory::add类).在运行所有测试之前,请使用factory::create()方法创建所有对象.然后,您可以在测试执行中使用factory::get()方法获取对象.

First create registry class (registry + factory + singleton). This class will be responsible for storing registered constructors using dynamic allocation with new and smart pointer in lambda expression (see factory::add class). Create all objects using factory::create() method before running all tests. Then you can get object using factory::get()method in you test execution.

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实现版本.在运行任何测试RUN_ALL_TESTS()之前,调用factory::create()方法来创建所有SystemC信号和SystemC模块.因为工厂类是单例设计模式,所以在完成所有测试以销毁所有创建的SystemC对象之后,请调用factory::destroy()方法.

Create your own version of gtest_main.cc implementation. Call factory::create() method to create all SystemC signals and SystemC modules before running any tests RUN_ALL_TESTS(). Because factory class is a singleton design pattern, call factory::destroy() method after finishing all tests to destroy all created SystemC objects.

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信号和模块之间进行连接.使用诸如 factory :: add g 这样的全局构造函数将定义的 dut 类注册到注册表对象.之后,您可以使用简单的 factory :: get()方法获取待测对象.

Then define dut class in your test than will create SystemC signals and SystemC modules. In constructor do connection between created SystemC signals and modules. Register defined dut class to registry object using global constructor like this factory::add g. After than you can get your dut object using simple factory::get() method.

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

For more inspiration, you can check my logic library for SystemC verification: https://github.com/tymonx/logic

这篇关于在SystemC中使用现有的单元测试框架的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆