[PATCH xorg-gtest] Add support for starting a process through valgrind
Peter Hutterer
peter.hutterer at who-t.net
Sun Nov 25 21:42:27 PST 2012
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 at who-t.net>
---
README | 4 ++
src/process.cpp | 11 ++++-
test/process-test.cpp | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 123 insertions(+), 1 deletion(-)
diff --git a/README b/README
index 53475db..2409bdb 100644
--- a/README
+++ b/README
@@ -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();
--
1.7.11.7
More information about the xorg-devel
mailing list