coolxv / cpp-stub Goto Github PK
View Code? Open in Web Editor NEWC++ unit test stub(not mock) and awesome.Surpported ISA x86,x86-64,arm64,arm32,arm thumb,mips64,riscv,loongarch64.
License: MIT License
C++ unit test stub(not mock) and awesome.Surpported ISA x86,x86-64,arm64,arm32,arm thumb,mips64,riscv,loongarch64.
License: MIT License
核心**就是为lambda表达式生成独一无二的类和执行函数。
#include <cassert>
#include <utility>
#include "cpp_stub/stub.h"
/***
* FakeFunction 可以封装一个函数或lambda表达式,用于替换目标函数。
* 需要注意的是模板参数中的N和S,对于同一个测试用例中的FakeFunction,一定不能有相同的N、S组合。
* 如果测试用例中只有一个FakeFunction或传入的函数声明都不相同时,可以省略N或S。
* 如果想要通过某个类型区分FakeFunction,请设置模板参数S。
*
* 使用示例:
* auto fun = []() { prstd::size_tf("hehe\n"); };
* FakeFunction<void(void), 1> fl(fun);
*
* decltype(fl)::Exec 即为lambda表达式的函数指针,可以用于Stub::set函数。
*/
template <class T, std::size_t N = 0, class S = void>
class FakeFunction;
template <class Res, class... Args, std::size_t N, class S>
class FakeFunction<Res(Args...), N, S> {
public:
template <class Function>
explicit FakeFunction(Function f)
: m_Call(Call<Function>), m_Release(Release<Function>), m_Function(new Function(f)) {
assert(m_Instance == nullptr);
m_Instance = this;
}
template <class Method, class Function>
explicit FakeFunction(Stub* stub, Method m, Function f) : FakeFunction(f) {
stub->set(m, Exec);
}
template <class Method, class Function, class ExitFunction>
explicit FakeFunction(Stub* stub, Method m, Function f, ExitFunction e) : FakeFunction(stub, m, f) {
m_CallWhenExit = CallWhenExit<ExitFunction>;
m_FunctionCallWhenExit = new ExitFunction(e);
}
~FakeFunction() {
if (m_CallWhenExit != nullptr) {
m_CallWhenExit(this);
m_CallWhenExit = nullptr;
}
m_Release(this);
m_Release = nullptr;
m_Instance = nullptr;
}
FakeFunction(const FakeFunction&) = delete;
FakeFunction& operator=(const FakeFunction&) = delete;
static Res Exec(Args... args) { return m_Instance->m_Call(m_Instance, std::forward<Args>(args)...); }
private:
template <class Function>
static Res Call(FakeFunction* that, Args... args) {
++(that->CallTimes);
Function* f = static_cast<Function*>(that->m_Function);
return (*f)(std::forward<Args>(args)...);
}
template <class Function>
static void Release(FakeFunction* that) {
delete static_cast<Function*>(that->m_Function);
}
template <class Function>
static void CallWhenExit(FakeFunction* that) {
(*static_cast<Function*>(that->m_FunctionCallWhenExit))(that->CallTimes);
delete static_cast<Function*>(that->m_FunctionCallWhenExit);
}
public:
int CallTimes = 0;
private:
static FakeFunction* m_Instance;
Res (*m_Call)(FakeFunction*, Args...) = nullptr;
void (*m_Release)(FakeFunction*) = nullptr;
void (*m_CallWhenExit)(FakeFunction*) = nullptr;
void* m_Function = nullptr;
void* m_FunctionCallWhenExit = nullptr;
};
template <class Res, class... Args, std::size_t N, class S>
FakeFunction<Res(Args...), N, S>* FakeFunction<Res(Args...), N, S>::m_Instance = nullptr;
class Test {
private:
static int func1(int a);
static in func2(int a,int b);
};
There is a macro ACCESS_PRIVATE_FUN to access private function, but how to access protected function.
remove the line "Type Class::Name;" can fix the failure
/*#define ACCESS_PRIVATE_STATIC_FIELD(Class, Type, Name)
**Type Class::Name; / *
#define ACCESS_PRIVATE_STATIC_FIELD(Class, Type, Name)
PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FIELD(
PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, Class, Type, Name)
/builddir/build/BUILD/skupper-router/tests/cpp-stub/cpp_stub.h:47:11: error: 'uint32_t' was not declared in this scope
47 | ((uint32_t*)fn)[0] = 0x58000040 | 9;\
| ^~~~~~~~
/builddir/build/BUILD/skupper-router/tests/cpp-stub/cpp_stub.h:248:13: note: in expansion of macro 'REPLACE_FAR'
248 | REPLACE_FAR(this, fn, fn_stub);
| ^~~~~~~~~~~
/builddir/build/BUILD/skupper-router/tests/cpp-stub/cpp_stub.h:26:1: note: 'uint32_t' is defined in header '<cstdint>'; did you forget to '#include <cstdint>'?
25 | #include <map>
+++ |+#include <cstdint>
The above is my copy of the files. The corresponding lines in this project are at
Lines 40 to 41 in 691b625
here is an eg:
template<typename X, typename Y>
class Foo {
void foo(X x,Y y) {}
};
how to stub for foo(X x,Y y) method?
I am using void Stub::set
to install my stub. The program crashes when compiled in GCC with Thread Sanitizer.
ThreadSanitizer:DEADLYSIGNAL
==4759==ERROR: ThreadSanitizer: SEGV on unknown address 0x000000001297 (pc 0x7f5971a10868 bp 0x1000010d4a00 sp 0x7ffff03b4e90 T4759)
==4759==The signal is caused by a WRITE memory access.
#0 __tsan_write1 <null> (libtsan.so.0+0x91868)
#1 void Stub::set<int (*)(char*, unsigned long, char const*, __va_list_tag*), int (*)(char*, unsigned long, char const*, ...)>(int (*)(char*, unsigned long, char const*, __va_list_tag*), int (*)(char*, unsigned long, char const*, ...)) /home/runner/work/qpid-dispatch/qpid-dispatch/qpid-dispatch/tests/cpp-stub/cpp_stub.h:247 (c_unittests+0x490ea4)
#2 _DOCTEST_ANON_FUNC_2 /home/runner/work/qpid-dispatch/qpid-dispatch/qpid-dispatch/tests/c_unittests/test_terminus.cpp:88 (c_unittests+0x490ea4)
#3 doctest::Context::run() /home/runner/work/qpid-dispatch/qpid-dispatch/qpid-dispatch/tests/c_unittests/doctest.h:6486 (c_unittests+0x458747)
#4 main /home/runner/work/qpid-dispatch/qpid-dispatch/qpid-dispatch/tests/c_unittests/doctest.h:6571 (c_unittests+0x43b762)
#5 __libc_start_call_main <null> (libc.so.6+0x2d55f)
#6 __libc_start_main_impl <null> (libc.so.6+0x2d60b)
#7 _start <null> (c_unittests+0x43c8e4)
My guess is that the address is not instrumented correctly by TSan and that therefore it is correct to resolve this by disabling TSan instrumentation for Stub::set
. The Address Sanitizer tool is not complaining, given the exactly same code.
First, thanks for this tool. It's a hidden gem.
Can you add a license file to the repository?
I think we can make use of this tool in a project, but won't be able to unless it is available under an open-source license like MIT, GPL2, or GPL3.
hello,
my project is very complex.I just want ut some function in project, whats build to lib or so.
In samples, include *.cc
你好,我有个C的嵌入式工程,应该说比较复杂,我想只对部分函数做测试,在工程内是被编译为动态库、静态库的。
我看了示例都是将源码引入到测试文件内,不知道是否可以直接对库内函数进行MOCK。
How to replace function that has more than one parameters
I am using amd64 architecture. I enabled AddressSanitizer and UndefinedBehavior sanitizer in GCC. My tests produce the following undefined behavior error:
cpp_stub.h:244:13: runtime error: store to misaligned address 0x0000004bc2e1 for type 'int', which requires 4 byte alignment
0x0000004bc2e1: note: pointer points here
fd ff ff e9 25 8a 3e 69 00 68 2b 00 00 00 e9 30 fd ff ff ff 25 82 3e 69 00 68 2c 00 00 00 e9 20
^
pc_0x9534d7###func_void Stub::set<int (*)(char*, unsigned long, char const*, __va_list_tag*), int (*)(char*, unsigned long, char const*, ...)>(int (*)(char*, unsigned long, char const*, __va_list_tag*), int (*)(char*, unsigned long, char const*, ...))###file_/home/jdanek/repos/qpid/qpid-dispatch/tests/cpp-stub/cpp_stub.h###line_244###obj_(c_unittests+0x9534d7)
pc_0x94cb4b###func__DOCTEST_ANON_FUNC_2###file_/home/jdanek/repos/qpid/qpid-dispatch/tests/c_unittests/test_terminus.cpp###line_88###obj_(c_unittests+0x94cb4b)
pc_0x7fc0ce###func_doctest::Context::run()###file_/home/jdanek/repos/qpid/qpid-dispatch/tests/c_unittests/doctest.h###line_6486###obj_(c_unittests+0x7fc0ce)
pc_0x7ff7c0###func_main###file_/home/jdanek/repos/qpid/qpid-dispatch/tests/c_unittests/doctest.h###line_6571###obj_(c_unittests+0x7ff7c0)
pc_0x7f11e90db55f###func___libc_start_call_main###file_<null>###line_0###obj_(libc.so.6+0x2d55f)
pc_0x7f11e90db60b###func___libc_start_main_impl###file_<null>###line_0###obj_(libc.so.6+0x2d60b)
pc_0x4bec64###func__start###file_<null>###line_0###obj_(c_unittests+0x4bec64)
The stub.cpp
code is
REPLACE_NEAR(this, fn, fn_stub);
The macro is
//5 byte(jmp rel32)
#define REPLACE_NEAR(t, fn, fn_stub)\
*fn = 0xE9;\
*(int *)(fn + 1) = (int)(fn_stub - fn - CODESIZE_MIN);\
//CACHEFLUSH((char *)fn, CODESIZE);
Looking into code, there is more statements suffering from this, e.g. *(long long *)(fn + 2) = (long long)fn_stub;\
in the same file.
For a solution, I am thinking that std::copy or memcpy should solve this, but it feels more clumsy than the current code and it may need helper variable.
您好,
感谢您提供那么好用的开源库,我有个问题想请教一下您,希望您能解答一二。
我现在有个场景需要在Stub函数中调用原函数(例如下面的例子,malloc
函数已经被替换成malloc_hook函数
,malloc_hook
函数会额外维护一些关于分配的内存地址和内存大小等信息,并调用原函数“malloc”分配内存)。以下代码这种方式hook了malloc函数后,所以malloc_hook
内部的malloc
函数也会被hook,实际变成了递归函数,与预期不符。
有没有办法在执行了stub.set(malloc, malloc_hook)
;的情况下,仍有一种方式可以调用原来的malloc
函数?
void* malloc_hook(size_t size) {
printf("Malloc tries to allocate %ld size of memory\n", size);
//stub.reset(malloc); // 1. 性能影响; 2.多线程有锁和信号量等同步机制时会发生时序问题
void* = malloc(size);
//stub.set(malloc, malloc_stub);
return ret;
}
int main( ) {
Stub stub;
stub.set(malloc, malloc_hook);
A();
stub.reset(A);
return 0;
arm platform contructor stub across segmentation fault
test for windows + docker, success
test for mac(arm) + docker, fails, segment fault when contructor called
I guess function get_dtor_addr may have bug.
在stub.h代码中,会通过 mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)去修改函数指针所在的内存页的读写权限,在这里我有个疑惑,请教下您,函数指针所在的页应该属于代码段,属于不可修改的部分,为什么这里会修改成功呢?
Given these static template functions...
template
void foo(int) {}
template<typename X, typename Y>
void foo(int, int, int) {}
Is it possible to stub this? The examples show a class template function, but not a standalone template function.
please provide a makefile
The dependent submodule injectorpp is not accessible now.
compile command: arm-linux-gnueabi-g++ test_object_member_function_linux.cpp -std=c++11 -I ../src -o test_object_member_funciton_linux
then move to arm32 run it .
[root@DMD3A ~/bin]#./test_object_member_funciton_linux
Segmentation fault
Hello,
I did some translation for zh, and would like make following changes for README_zh.md
diff in following link;
README_zh.md.txt
Thanks,
Mark
root@centos:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# ll
total 12
drwxr-xr-x. 4 root root 87 Feb 4 07:57 ./
drwxr-xr-x. 5 root root 43 Feb 3 10:12 ../
-rw-r--r--. 1 root root 588 Feb 4 07:31 Makefile
drwxr-xr-x. 2 root root 25 Feb 4 07:57 fixtures/
-rw-r--r--. 1 root root 220 Feb 3 09:56 main.cpp
drwxr-xr-x. 2 root root 26 Feb 4 07:57 mocks/
-rw-r--r--. 1 root root 3233 Feb 4 07:27 test_obj.cpp
root@centos:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# make
g++ -std=c++11 -g -no-pie -fno-stack-protector -Wall -Wno-unused-function -Wno-unused-variable -Wno-pmf-conversions -I../include -Imocks -Ifixtures -I../../src -I../../../../src -c -MMD test_obj.cpp -o test_obj.o
g++ -std=c++11 -g -no-pie -fno-stack-protector -Wall -Wno-unused-function -Wno-unused-variable -Wno-pmf-conversions -I../include -Imocks -Ifixtures -I../../src -I../../../../src -c -MMD main.cpp -o main.o
g++ -std=c++11 -g -no-pie -fno-stack-protector -Wall -Wno-unused-function -Wno-unused-variable -Wno-pmf-conversions ./test_obj.o ./main.o -L../lib -lgtest -lgmock -lm -lpthread -o ut
root@centos69203:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# ./ut --gtest_filter=get_number_test.returns_correct_value
Note: Google Test filter = get_number_test.returns_correct_value
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from get_number_test
[ RUN ] get_number_test.returns_correct_value
Segmentation fault (core dumped)
root@centos:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# ll
total 4340
drwxr-xr-x. 4 root root 179 Feb 4 07:58 ./
drwxr-xr-x. 5 root root 43 Feb 3 10:12 ../
-rw-r--r--. 1 root root 588 Feb 4 07:31 Makefile
-rw-------. 1 root root 2031616 Feb 4 07:58 core.90826
drwxr-xr-x. 2 root root 25 Feb 4 07:57 fixtures/
-rw-r--r--. 1 root root 220 Feb 3 09:56 main.cpp
-rw-r--r--. 1 root root 1700 Feb 4 07:58 main.d
-rw-r--r--. 1 root root 116440 Feb 4 07:58 main.o
drwxr-xr-x. 2 root root 26 Feb 4 07:57 mocks/
-rw-r--r--. 1 root root 3233 Feb 4 07:27 test_obj.cpp
-rw-r--r--. 1 root root 1850 Feb 4 07:58 test_obj.d
-rw-r--r--. 1 root root 1479800 Feb 4 07:58 test_obj.o
-rwxr-xr-x. 1 root root 844208 Feb 4 07:58 ut*
root@centos:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# gdb ut core.90826
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "aarch64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
http://www.gnu.org/software/gdb/bugs/.
Find the GDB manual and other documentation resources online at:
http://www.gnu.org/software/gdb/documentation/.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ut...done.
[New LWP 90826]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
Core was generated by `./ut --gtest_filter=get_number_test.returns_correct_value'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000000000040cf88 in Stub::set<int (Obj::)(), int ()(void*)> (this=0xffffdb3e9b10, addr=&virtual Obj::get_number(), addr_stub=0x40a17c <get_number_stub(void*)>) at ../../../../src/stub.h:305
warning: Source file is more recent than executable.
305 std::memcpy(pstub->code_buf, fn, CODESIZE_MIN);
(gdb) bt
#0 0x000000000040cf88 in Stub::set<int (Obj::)(), int ()(void*)> (this=0xffffdb3e9b10, addr=&virtual Obj::get_number(), addr_stub=0x40a17c <get_number_stub(void*)>) at ../../../../src/stub.h:305
#1 0x000000000040a1cc in get_number_test_returns_correct_value_Test::TestBody (this=0x1b103450) at test_obj.cpp:75
#2 0x0000ffff7f434548 in void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::)(), char const) () from /usr/local/gtest/lib/libgtest.so
#3 0x0000ffff7f42dbd8 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::)(), char const) () from /usr/local/gtest/lib/libgtest.so
#4 0x0000ffff7f40c050 in testing::Test::Run() () from /usr/local/gtest/lib/libgtest.so
#5 0x0000ffff7f40c93c in testing::TestInfo::Run() () from /usr/local/gtest/lib/libgtest.so
#6 0x0000ffff7f40cff0 in testing::TestCase::Run() () from /usr/local/gtest/lib/libgtest.so
#7 0x0000ffff7f417160 in testing::internal::UnitTestImpl::RunAllTests() () from /usr/local/gtest/lib/libgtest.so
#8 0x0000ffff7f4357cc in bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::)(), char const) () from /usr/local/gtest/lib/libgtest.so
#9 0x0000ffff7f42eb10 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::)(), char const) () from /usr/local/gtest/lib/libgtest.so
#10 0x0000ffff7f415ce0 in testing::UnitTest::Run() () from /usr/local/gtest/lib/libgtest.so
#11 0x00000000004182ac in RUN_ALL_TESTS () at ../include/gtest/gtest.h:2341
#12 0x0000000000418218 in main (argc=1, argv=0xffffdb3ea008) at main.cpp:12
(gdb) q
root@centos:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# readelf -h ut
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: EXEC (Executable file)
Machine: AArch64
Version: 0x1
Entry point address: 0x409e70
Start of program headers: 64 (bytes into file)
Start of section headers: 841840 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 37
Section header string table index: 36
In the following example, test_stub will not be called. But test_stub will be call when fun1 and fun2 are in two diff files.
main.c:
int test_stub(int a){
return 0;
}
stub.set(fun1, test_stub);
fun2(1);
test.c:
fun1(int a){return a;}
fun2(int a){ return fun1(a);}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.