diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2012-11-22 11:40:21 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2012-12-14 08:48:17 +1000 |
commit | c82885534d9658e56b4b221d8f36efef570cbd25 (patch) | |
tree | 9a3e0e50e8a0c60a78006ca4744c2189dfed1a93 | |
parent | 60f2cfd3b4aa3bea60a26cdc4cc6a169d8669999 (diff) |
Add support for starting a process through valgrind
export XORG_GTEST_USE_VALGRIND="valgrind --leak-check=full"
./run-some-test
But really, can be used with any wrapper binary. Given that valgrind is the
main use-case here, keep the USE_VALGRIND naming instead of something more
generic like USE_PROCESS_WRAPPER or so.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
-rw-r--r-- | README | 4 | ||||
-rw-r--r-- | src/process.cpp | 11 | ||||
-rw-r--r-- | test/process-test.cpp | 109 |
3 files changed, 123 insertions, 1 deletions
@@ -85,3 +85,7 @@ XORG_GTEST_XSERVER_KEEPALIVE XORG_GTEST_CHILD_STDOUT If set to any value, Process::Start() will _not_ close stdout/stdin/stderr for the forked child. +XORG_GTEST_USE_VALGRIND + Set to the valgrind command to use when starting a process. Options must + be space-separated, e.g. "valgrind --leak-check=full --someotherarg" + Not limited to valgrind, you can specify any executable here. diff --git a/src/process.cpp b/src/process.cpp index a743346..43ac0d6 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -100,13 +100,22 @@ void xorg::testing::Process::Start(const std::string &program, const std::vector std::vector<char*> args; std::vector<std::string>::const_iterator it; + char *valgrind = getenv("XORG_GTEST_USE_VALGRIND"); + if (valgrind) { + char *tok = strtok(valgrind, " "); + while(tok) { + args.push_back(strdup(tok)); + tok = strtok(NULL, " "); + } + } + args.push_back(strdup(program.c_str())); for (it = argv.begin(); it != argv.end(); it++) args.push_back(strdup(it->c_str())); args.push_back(NULL); - execvp(program.c_str(), &args[0]); + execvp(args[0], &args[0]); d_->state = ERROR; throw std::runtime_error("Failed to start process"); diff --git a/test/process-test.cpp b/test/process-test.cpp index 59f96c2..3446fe1 100644 --- a/test/process-test.cpp +++ b/test/process-test.cpp @@ -1,3 +1,6 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif #include <errno.h> #include <unistd.h> #include <sys/types.h> @@ -264,6 +267,112 @@ TEST(Process, ForkedChildDoubleStart) } } +class ProcessValgrindWrapper : public ::testing::Test, + public ::testing::WithParamInterface<std::string> { +public: + virtual void SetUp() { + CheckForValgrind(); + } + + virtual void CheckForValgrind() { + Process valgrind; + + /* check if valgrind actually exists */ + valgrind.Start("valgrind", "--version", NULL); + int status; + ASSERT_EQ(waitpid(valgrind.Pid(), &status, 0), valgrind.Pid()); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(WEXITSTATUS(status), 0) << "valgrind failed to start\n"; + } +}; + +TEST_P(ProcessValgrindWrapper, ValgrindWrapper) +{ + XORG_TESTCASE("Use the valgrind wrapper to start valgrind"); + + std::string executable = GetParam(); + + /* now set the env and fire up valgrind */ + setenv("XORG_GTEST_USE_VALGRIND", executable.c_str(), 1); + Process p; + p.Start("ls", NULL); + unsetenv("XORG_GTEST_USE_VALGRIND"); + + /* Check /proc/<pid>/comm to make sure valgrind + was started. But comm takes a while to update, it's first our binary + then valgrind, then memcheck-amd64 (or whatever applies) + */ + char buff[1024] = {0}; + char fname[128]; + sprintf(fname, "/proc/%d/comm", p.Pid()); + + do { + FILE *fp = fopen(fname, "r"); + ASSERT_TRUE(fp); + fgets(buff, sizeof(buff), fp); + fclose(fp); + } while(strstr(buff, program_invocation_short_name)); + + if (executable.compare("valgrind") == 0) + ASSERT_TRUE(strstr(buff, "memcheck") || strstr(buff, "valgrind")); + else + ASSERT_TRUE(strstr(buff, executable.c_str())); +} + +class ProcessValgrindArgsWrapper : public ProcessValgrindWrapper {}; + +TEST_P(ProcessValgrindArgsWrapper, ValgrindWrapperWithArgs) +{ + XORG_TESTCASE("Use the valgrind wrapper with additional args to start valgrind"); + + std::string vargs = GetParam(); + std::vector<std::string> valgrind_args; + char *all_args = strdup(vargs.c_str()); + char *tok = strtok(all_args, " "); + while(tok) { + valgrind_args.push_back(std::string(tok)); + tok = strtok(NULL, " "); + } + free(all_args); + + /* now set the env and fire up valgrind */ + setenv("XORG_GTEST_USE_VALGRIND", vargs.c_str(), 1); + Process p; + p.Start(TEST_ROOT_DIR "process-test-helper", NULL); + unsetenv("XORG_GTEST_USE_VALGRIND"); + + ASSERT_EQ(p.GetState(), Process::RUNNING); + + char buff[1024] = {0}; + char fname[128]; + sprintf(fname, "/proc/%d/cmdline", p.Pid()); + + do { + FILE *fp = fopen(fname, "r"); + ASSERT_TRUE(fp); + fgets(buff, sizeof(buff), fp); + fclose(fp); + } while(strstr(buff, program_invocation_short_name)); + + const char * arg = buff + strlen(buff) + 1; + std::vector<std::string>::const_iterator it = valgrind_args.begin(); + + it++; /* first one is "valgrind" */ + + while(strlen(arg) && it != valgrind_args.end()) { + ASSERT_EQ(it->compare(arg), 0); + arg += strlen(arg) + 1; + it++; + } + + ASSERT_EQ(it, valgrind_args.end()); + p.Kill(100); +} + +INSTANTIATE_TEST_CASE_P(, ProcessValgrindWrapper, ::testing::Values("valgrind", "ls")); +INSTANTIATE_TEST_CASE_P(, ProcessValgrindArgsWrapper, + ::testing::Values("valgrind --leak-check=full", "valgrind -q --trace-children=yes", "valgrind ")); + int main(int argc, char *argv[]) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); |