In a project I am working on, we are using Google Test to write and run our unit tests. Google Test is a really nice unit test framework, but that is another story. The thing you need to know for this post, is that it has a flag
--gtest_repeat, which will make it run the test several times.
The other day, we ran into a strange problem. The following would work:
$ ./Test; ./Test
whereas the following would break:
$ ./Test --gtest_repeat=2
The code that was being exercised involves quite a bit of socket programming, so at first we wasted some time looking for improper shutdowns and reuse of sockets, but that was a dead end. We also confirmed that our
TearDown() was executed properly for each time the test ran. Then we discovered that running the test manually two times in a row worked, and ended up a bit puzzled.
If you read the title, you might have figured out our problem already, but here it is:
Non-local static variables are always initialized before
main() is executed. We used a static variable to keep track of the state in a dummy object for our test, and relied on the runtime system to initialize it for us. When we ran the test twice manually, the binary was actually executed twice, which resulted in the variable also being initialized twice. But when passing
--gtest_repeat to Google Tests
main(), the binary was only executed once, resulting in the variable only being initialized once, again resulting in keeping state from one pass to the next. Initializing the variable manually in a
SetUp() function fixed the problem.
Appendix A: Why non-local static variables are initialized before
Imagine you have a global static variable defined in one translation unit (a translation unit is basically one .cc-file with headers included), but declared and used in several others. There is no way for the compiler to know where it will be used first, without having access to all translation units, which only the linker has. The best C++ can do is to have the runtime system initialize all such variables before
main() is executed. Also note that due to the same argument, there is no guaranteed order of initialization of non-local static variables.
But why can’t the variable just be automatically wrapped with code that initializes it the first time it is accessed? Think about it for a moment. Where would that code need to be placed? In all the places that accesses that variable! And again, this might be in many separate translation units.
When variables are initialized in C++ is an interesting topic in itself, and I guess I will be coming back to that in a later post.