[cfe-dev] Instrumentation for unit testing
Gábor Márton via cfe-dev
cfe-dev at lists.llvm.org
Mon Nov 21 12:15:44 PST 2016
Hi,
I am a PhD student and as part of my research I'd like to ease testing
of legacy code.
There are many legacy enterprise applications that were written
without automated unit tests. It is often very difficult to maintain
and modify such code, since we cannot verify the changes. A frequently
used approach in this situation is to write additional tests without
modifying the original source code. There are several techniques to do
this [1]. However, these techniques have their own limitations and
disadvantages [1,2].
My aim is to provide an alternative technique without those
limitations. I used the Clang compiler sanitizer infrastructure to
implement testing specific instrumentation (prototype). The
instrumentation makes it possible to replace any C/C++ function with a
corresponding test double function.
[1] https://github.com/martong/finstrument_mock/blob/master/README.md#alternatives
[2] https://accu.org/index.php/journals/1927
Here are a few motivating examples:
### Replace template functions
// unit_under_test.hpp
template <typename T>
T FunTemp(T t) {
return t;
}
inline int foo(int p) {
return FunTemp(p);
}
// test.cpp
TEST_F(FooFixture, CallFunT) {
SUBSTITUTE(&FunTemp<int>, &fake_FunTemp);
int p = 13;
auto res = foo(p);
EXPECT_EQ(res, 39);
}
### Replace functions in class templates
// unit_under_test.hpp
template <typename T>
struct TemplateS {
int foo(int p) { return bar(p); }
int bar(int p) { return p; }
};
// test.cpp
int fake_bar_mem_fun(TemplateS<int>* self, int p) { return p * 3; }
TEST_F(FooFixture, ClassT) {
SUBSTITUTE(&TemplateS<int>::bar, &fake_bar_mem_fun);
TemplateS<int> t;
auto res = t.foo(13);
EXPECT_EQ(res, 39);
}
### Replace (always inline) functions in STL
Consider the following concurrent `Entity`:
// Entity.hpp
class Entity {
public:
int process(int i) const;
void add(int i);
private:
std::vector<int> v;
mutable std::mutex m;
};
// Entity.cpp
int Entity::process(int i) const {
std::unique_lock<std::mutex> lock{m, std::try_to_lock};
if (lock.owns_lock()) {
auto result = std::accumulate(v.begin(), v.end(), i);
return result;
} else {
return -1;
}
return 0;
}
void Entity::add(int i) {
std::lock_guard<std::mutex> lock{m};
v.push_back(i);
}
We can test the behaviour based on whether the lock is already owned
by another thread or not:
// test.cpp
#include "Entity.hpp"
bool owns_lock_result;
using Lock = std::unique_lock<std::mutex>;
bool fake_owns_lock(Lock*) { return owns_lock_result; }
TEST_F(FooFixture, MutexTest) {
SUBSTITUTE(&Lock::owns_lock, &fake_owns_lock);
Entity e;
owns_lock_result = false;
EXPECT_EQ(e.process(1), -1);
owns_lock_result = true;
EXPECT_EQ(e.process(1), 1);
}
You can find other motivating examples about
- Replacing calls in libc - fopen(), fread()
- Replacing system calls - time()
- Eliminating death tests, replace `[[noreturn]]` functions
at https://github.com/martong/finstrument_mock/blob/master/README.md#motivating-examples
How does it work?
The idea is to replace each and every function call expression with
the following pseudo code (let's suppose, the callee is foo):
char* funptr = __fake_hook(&foo);
auto ret = result_of(&foo);
if (funptr) {
ret = funptr(args...);
} else {
ret = foo(args...);
}
return ret;
`__fake_hook` is defined in a runtime library, which shall be linked
after we switched on this compiler option. The result of `__fake_hook`
can be set runtime, with the `SUBSTITUTE` macro.
I had to refactor `CodeGenFunction::EmitCall` in order to be able to
modify the generated IR for the call expressions.
You can find more implementations details (difficulties with virtuals,
performance, future work, etc) here:
https://github.com/martong/finstrument_mock
Also the diff of the patched compiler is here:
https://github.com/martong/clang/compare/finstrument_mock_0...martong:finstrument_mock
I'd like to ask from the Clang community
- whether you find this as a useful contribution,
- would you like to integrate such experimental instrumentation to the
Clang tree?
If the answer is yes then what are the next steps (review, open questions, etc)?
Thanks,
Gabor
More information about the cfe-dev
mailing list