Add C++ methods for accessing binary name etc.
authorTeemu Murtola <teemu.murtola@gmail.com>
Mon, 14 May 2012 09:29:05 +0000 (12:29 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Wed, 16 May 2012 17:34:16 +0000 (20:34 +0300)
- Add a ProgramInfo class that can be used to access global information
  about binary name etc. Move some path manipulation code from
  CommandLineModuleManager to the new class, resolving a TODO item.
- Make initialization methods of this class also call the relevant
  methods in statutil.h.
- Call the new initialization methods in unit tests and in g_ana.
- Use the new method for accessing program names in C++ code.
- Fix ShortProgram() such that it does not segfault if
  set_program_name() has not been called (not needed after the above
  changes, but still better this way).
- Add statutil.c methods that use global storage to valgrind suppression
  list.

Change-Id: I74c906aa5188d2dc4f0e1f6edab89e36393724f3

13 files changed:
cmake/legacy_and_external.supp
src/gromacs/commandline/cmdlinemodulemanager.cpp
src/gromacs/commandline/cmdlinemodulemanager.h
src/gromacs/commandline/tests/cmdlinemodulemanager.cpp
src/gromacs/commandline/tests/cmdlinetest.h
src/gromacs/gmxlib/statutil.c
src/gromacs/utility/errorformat.cpp
src/gromacs/utility/programinfo.cpp [new file with mode: 0644]
src/gromacs/utility/programinfo.h [new file with mode: 0644]
src/programs/g_ana/g_ana.cpp
src/testutils/CMakeLists.txt
src/testutils/testoptions.cpp
src/testutils/unittest_main.cpp

index 6788ba24b8a7e2cf5150d3e65729ef021be99b76..0e18ccd0921c794c2b3644fa96b7020722fccfee 100644 (file)
    fun:inflateInit2_
 }
 
+{
+   set_program_name
+   Memcheck:Leak
+   ...
+   fun:set_program_name
+}
+
+{
+   set_command_line
+   Memcheck:Leak
+   ...
+   fun:set_command_line
+}
+
 {
    read_tps_conf
    Memcheck:Leak
index 646c5d7b88849197261b74306de35b138dc8b349..7a65cc72b9412a576a3fbdafb6441155f34c1088 100644 (file)
  */
 #include "cmdlinemodulemanager.h"
 
-// For GMX_BINARY_SUFFIX
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #include <cstdio>
-#include <cstring>
 
 #include <map>
 #include <string>
 #include <utility>
 
-#include "gromacs/legacyheaders/statutil.h"
-
 #include "gromacs/commandline/cmdlinemodule.h"
 #include "gromacs/utility/gmxassert.h"
-#include "gromacs/utility/path.h"
+#include "gromacs/utility/programinfo.h"
 
 namespace gmx
 {
@@ -76,9 +68,9 @@ class CommandLineModuleManager::Impl
         /*! \brief
          * Initializes the implementation class.
          *
-         * \param[in] realBinaryName  Name of the binary that this manager runs.
+         * \param[in] programInfo  Program information for the running binary.
          */
-        explicit Impl(const char *realBinaryName);
+        explicit Impl(const ProgramInfo &programInfo);
 
         /*! \brief
          * Finds a module that matches a name.
@@ -93,16 +85,21 @@ class CommandLineModuleManager::Impl
         /*! \brief
          * Finds a module that the name of the binary.
          *
-         * \param[in] argv0  argv[0] passed to the program.
+         * \param[in] programInfo  Program information object to use.
          * \throws    std::bad_alloc if out of memory.
          * \returns   Iterator to the found module, or
          *      \c modules_.end() if not found.
          *
          * Checks whether the program is invoked through a symlink whose name
-         * is different from \a realBinaryName_, and if so, checks if a module
-         * name matches the name of the symlink.
+         * is different from ProgramInfo::realBinaryName(), and if so, checks
+         * if a module name matches the name of the symlink.
+         *
+         * Note that the \p programInfo parameter is currently not necessary
+         * (as the program info object is also contained as a member), but it
+         * clarifies the control flow.
          */
-        ModuleMap::const_iterator findModuleFromBinaryName(const std::string &argv0) const;
+        ModuleMap::const_iterator
+        findModuleFromBinaryName(const ProgramInfo &programInfo) const;
 
         //! Prints usage message to stderr.
         void printUsage(bool bModuleList) const;
@@ -115,12 +112,12 @@ class CommandLineModuleManager::Impl
          * Owns the contained modules.
          */
         ModuleMap               modules_;
-        //! Real name of the binary that is running (without suffixes).
-        std::string             realBinaryName_;
+        //! Information about the currently running program.
+        const ProgramInfo      &programInfo_;
 };
 
-CommandLineModuleManager::Impl::Impl(const char *realBinaryName)
-    : realBinaryName_(realBinaryName)
+CommandLineModuleManager::Impl::Impl(const ProgramInfo &programInfo)
+    : programInfo_(programInfo)
 {
 }
 
@@ -132,27 +129,11 @@ CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
 }
 
 CommandLineModuleManager::Impl::ModuleMap::const_iterator
-CommandLineModuleManager::Impl::findModuleFromBinaryName(const std::string &argv0) const
+CommandLineModuleManager::Impl::findModuleFromBinaryName(
+        const ProgramInfo &programInfo) const
 {
-    // TODO: Move this logic into a common place in utility/ and remove
-    // dependency on config.h from this file.
-    // (most natural place would be in a location that wraps Program() etc.)
-    std::string binaryName = Path::splitToPathAndFilename(argv0).second;
-    if (binaryName.length() >= 4
-        && binaryName.compare(binaryName.length() - 4, 4, ".exe") == 0)
-    {
-        binaryName.erase(binaryName.length() - 4);
-    }
-#ifdef GMX_BINARY_SUFFIX
-    size_t suffixLength = std::strlen(GMX_BINARY_SUFFIX);
-    if (suffixLength > 0 && binaryName.length() >= suffixLength
-        && binaryName.compare(binaryName.length() - suffixLength, suffixLength,
-                              GMX_BINARY_SUFFIX) == 0)
-    {
-        binaryName.erase(binaryName.length() - suffixLength);
-    }
-#endif
-    if (binaryName == realBinaryName_)
+    std::string binaryName = programInfo.invariantProgramName();
+    if (binaryName == programInfo.realBinaryName())
     {
         return modules_.end();
     }
@@ -165,7 +146,7 @@ CommandLineModuleManager::Impl::findModuleFromBinaryName(const std::string &argv
 
 void CommandLineModuleManager::Impl::printUsage(bool bModuleList) const
 {
-    const char *program = ShortProgram();
+    const char *program = programInfo_.programName().c_str();
     fprintf(stderr, "Usage: %s <command> [<args>]\n\n", program);
     if (bModuleList)
     {
@@ -258,8 +239,8 @@ int CommandLineHelpModule::run(int argc, char *argv[])
  * CommandLineModuleManager
  */
 
-CommandLineModuleManager::CommandLineModuleManager(const char *realBinaryName)
-    : impl_(new Impl(realBinaryName))
+CommandLineModuleManager::CommandLineModuleManager(const ProgramInfo &programInfo)
+    : impl_(new Impl(programInfo))
 {
     addModule(CommandLineModulePointer(new internal::CommandLineHelpModule(*this)));
 }
@@ -280,7 +261,7 @@ int CommandLineModuleManager::run(int argc, char *argv[])
 {
     int argOffset = 0;
     Impl::ModuleMap::const_iterator module
-        = impl_->findModuleFromBinaryName(argv[0]);
+        = impl_->findModuleFromBinaryName(impl_->programInfo_);
     if (module == impl_->modules_.end())
     {
         if (argc < 2)
index eb3499b9a6b107121f60f0f8c05025fc3ad38852..050946f737f4e4d6343b432c9ce3a3236fc0500c 100644 (file)
@@ -46,6 +46,7 @@ namespace gmx
 {
 
 class CommandLineModuleInterface;
+class ProgramInfo;
 
 //! Smart pointer type for managing a CommandLineModuleInterface.
 typedef gmx_unique_ptr<CommandLineModuleInterface>::type
@@ -64,10 +65,12 @@ class CommandLineHelpModule;
 int
 main(int argc, char *argv[])
 {
+    const gmx::ProgramInfo &programInfo =
+        gmx::ProgramInfo::init("g_ana", argc, argv);
     CopyRight(stderr, argv[0]);
     try
     {
-        gmx::CommandLineModuleManager manager("g_ana");
+        gmx::CommandLineModuleManager manager(programInfo);
         // <register all necessary modules>
         return manager.run(argc, argv);
     }
@@ -88,14 +91,13 @@ class CommandLineModuleManager
         /*! \brief
          * Initializes a command-line module manager.
          *
-         * \param[in] realBinaryName  Name of the binary that this manager runs
-         *     (without Gromacs binary suffix or .exe on Windows).
+         * \param[in] programInfo  Program information for the running binary.
          * \throws    std::bad_alloc if out of memory.
          *
          * The binary name is used to detect when the binary is run through a
          * symlink, and automatically invoke a matching module in such a case.
          */
-        explicit CommandLineModuleManager(const char *realBinaryName);
+        explicit CommandLineModuleManager(const ProgramInfo &programInfo);
         ~CommandLineModuleManager();
 
         /*! \brief
index 85c2382b681de7ba561a448bd5642ee5c8169160..72a2b1cef6c525fde55809d4f32a7bd38261c084 100644 (file)
@@ -46,6 +46,7 @@
 
 #include "gromacs/commandline/cmdlinemodule.h"
 #include "gromacs/commandline/cmdlinemodulemanager.h"
+#include "gromacs/utility/programinfo.h"
 
 #include "cmdlinetest.h"
 
@@ -89,23 +90,28 @@ MockModule::MockModule(const char *name, const char *description)
 class CommandLineModuleManagerTest : public ::testing::Test
 {
     public:
-        CommandLineModuleManagerTest();
-
+        void initManager(const gmx::test::CommandLine &args);
         MockModule &addModule(const char *name, const char *description);
 
-        gmx::CommandLineModuleManager manager_;
+        gmx::CommandLineModuleManager &manager() { return *manager_; }
+
+    private:
+        boost::scoped_ptr<gmx::ProgramInfo>              programInfo_;
+        boost::scoped_ptr<gmx::CommandLineModuleManager> manager_;
 };
 
-CommandLineModuleManagerTest::CommandLineModuleManagerTest()
-    : manager_("g_test")
+void CommandLineModuleManagerTest::initManager(const gmx::test::CommandLine &args)
 {
+    manager_.reset();
+    programInfo_.reset(new gmx::ProgramInfo("g_test", args.argc(), args.argv()));
+    manager_.reset(new gmx::CommandLineModuleManager(*programInfo_));
 }
 
 MockModule &
 CommandLineModuleManagerTest::addModule(const char *name, const char *description)
 {
     MockModule *module = new MockModule(name, description);
-    manager_.addModule(gmx::CommandLineModulePointer(module));
+    manager().addModule(gmx::CommandLineModulePointer(module));
     return *module;
 }
 
@@ -119,6 +125,7 @@ TEST_F(CommandLineModuleManagerTest, RunsModule)
         "g_test", "module", "-flag", "yes"
     };
     gmx::test::CommandLine args(cmdline);
+    initManager(args);
     MockModule &mod1 = addModule("module", "First module");
     addModule("other", "Second module");
     using ::testing::_;
@@ -127,7 +134,7 @@ TEST_F(CommandLineModuleManagerTest, RunsModule)
     EXPECT_CALL(mod1, run(_, _))
         .With(Args<1, 0>(ElementsAreArray(args.argv() + 1, args.argc() - 1)));
     int rc = 0;
-    ASSERT_NO_THROW(rc = manager_.run(args.argc(), args.argv()));
+    ASSERT_NO_THROW(rc = manager().run(args.argc(), args.argv()));
     ASSERT_EQ(0, rc);
 }
 
@@ -137,6 +144,7 @@ TEST_F(CommandLineModuleManagerTest, RunsModuleBasedOnBinaryName)
         "g_module", "-flag", "yes"
     };
     gmx::test::CommandLine args(cmdline);
+    initManager(args);
     MockModule &mod1 = addModule("module", "First module");
     addModule("other", "Second module");
     using ::testing::_;
@@ -145,7 +153,7 @@ TEST_F(CommandLineModuleManagerTest, RunsModuleBasedOnBinaryName)
     EXPECT_CALL(mod1, run(_, _))
         .With(Args<1, 0>(ElementsAreArray(args.argv(), args.argc())));
     int rc = 0;
-    ASSERT_NO_THROW(rc = manager_.run(args.argc(), args.argv()));
+    ASSERT_NO_THROW(rc = manager().run(args.argc(), args.argv()));
     ASSERT_EQ(0, rc);
 }
 
@@ -155,6 +163,7 @@ TEST_F(CommandLineModuleManagerTest, RunsModuleBasedOnBinaryNameWithPathAndSuffi
         "/usr/local/gromacs/bin/g_module" GMX_BINARY_SUFFIX ".exe", "-flag", "yes"
     };
     gmx::test::CommandLine args(cmdline);
+    initManager(args);
     MockModule &mod1 = addModule("module", "First module");
     addModule("other", "Second module");
     using ::testing::_;
@@ -163,7 +172,7 @@ TEST_F(CommandLineModuleManagerTest, RunsModuleBasedOnBinaryNameWithPathAndSuffi
     EXPECT_CALL(mod1, run(_, _))
         .With(Args<1, 0>(ElementsAreArray(args.argv(), args.argc())));
     int rc = 0;
-    ASSERT_NO_THROW(rc = manager_.run(args.argc(), args.argv()));
+    ASSERT_NO_THROW(rc = manager().run(args.argc(), args.argv()));
     ASSERT_EQ(0, rc);
 }
 
@@ -173,6 +182,7 @@ TEST_F(CommandLineModuleManagerTest, HandlesConflictingBinaryAndModuleNames)
         "g_test", "test", "-flag", "yes"
     };
     gmx::test::CommandLine args(cmdline);
+    initManager(args);
     MockModule &mod1 = addModule("test", "Test module");
     addModule("other", "Second module");
     using ::testing::_;
@@ -181,7 +191,7 @@ TEST_F(CommandLineModuleManagerTest, HandlesConflictingBinaryAndModuleNames)
     EXPECT_CALL(mod1, run(_, _))
         .With(Args<1, 0>(ElementsAreArray(args.argv() + 1, args.argc() - 1)));
     int rc = 0;
-    ASSERT_NO_THROW(rc = manager_.run(args.argc(), args.argv()));
+    ASSERT_NO_THROW(rc = manager().run(args.argc(), args.argv()));
     ASSERT_EQ(0, rc);
 }
 
index 42aff01d394c7f0661290b6059c614d081622a8d..8516e39d109e6e9888d04d973b6763416e1e589a 100644 (file)
@@ -86,6 +86,10 @@ class CommandLine
         int &argc() { return argc_; }
         //! Returns argv for passing into C-style command-line handling.
         char **argv() { return argv_; }
+        //! Returns argc for passing into C-style command-line handling.
+        int argc() const { return argc_; }
+        //! Returns argv for passing into C-style command-line handling.
+        const char *const *argv() const { return argv_; }
 
     private:
         //! Internal helper method used to implement the constructor.
index 0e245660d9b0c91e46cbf0a3891ac03ccca50a28..a3678d412636309a41ed8276f87201b4c210e2d4 100644 (file)
@@ -110,6 +110,8 @@ const char *ShortProgram(void)
 #ifdef GMX_THREAD_MPI
     tMPI_Thread_mutex_unlock(&init_mutex);
 #endif
+    if (ret == NULL)
+        return "GROMACS";
     if ((pr=strrchr(ret,DIR_SEPARATOR)) != NULL)
         ret=pr+1;
     return ret;
index 4c8ebe814bf569507eafcf2c7a9e3d72fd2bebcf..a9cbfaf74264fe13f72ad1c4946f2d4d6faeffa2 100644 (file)
@@ -40,9 +40,9 @@
 #include <string>
 
 #include "gromacs/legacyheaders/copyrite.h"
-#include "gromacs/legacyheaders/statutil.h"
 
 #include "gromacs/utility/format.h"
+#include "gromacs/utility/programinfo.h"
 
 namespace gmx
 {
@@ -56,8 +56,8 @@ std::string formatFatalError(const char *title, const char *details,
 {
     std::string result;
     result.append("\n-------------------------------------------------------\n");
-    // TODO: Make the program name work also for unit tests
-    result.append(formatString("Program %s, %s\n", "TEST", GromacsVersion()));
+    const char *programName = ProgramInfo::getInstance().programName().c_str();
+    result.append(formatString("Program %s, %s\n", programName, GromacsVersion()));
     if (func != NULL)
     {
         result.append(formatString("In function %s\n", func));
diff --git a/src/gromacs/utility/programinfo.cpp b/src/gromacs/utility/programinfo.cpp
new file mode 100644 (file)
index 0000000..94abd7e
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::ProgramInfo.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_utility
+ */
+#include "programinfo.h"
+
+// For GMX_BINARY_SUFFIX
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cstdlib>
+#include <cstring>
+
+#include <new>
+#include <string>
+
+#include <boost/scoped_ptr.hpp>
+
+#include "gromacs/legacyheaders/statutil.h"
+
+#include "gromacs/utility/path.h"
+
+namespace gmx
+{
+
+namespace
+{
+boost::scoped_ptr<ProgramInfo> g_programInfo;
+} // namespace
+
+/********************************************************************
+ * ProgramInfo::Impl
+ */
+
+class ProgramInfo::Impl
+{
+    public:
+        Impl();
+        Impl(const char *realBinaryName, int argc, const char *const argv[]);
+
+        std::string realBinaryName_;
+        std::string fullInvokedProgram_;
+        std::string programName_;
+        std::string invariantProgramName_;
+};
+
+ProgramInfo::Impl::Impl()
+    : realBinaryName_("GROMACS"), fullInvokedProgram_("GROMACS")
+{
+}
+
+ProgramInfo::Impl::Impl(const char *realBinaryName,
+                        int argc, const char *const argv[])
+    : realBinaryName_(realBinaryName != NULL ? realBinaryName : ""),
+      fullInvokedProgram_(argv[0]),
+      programName_(Path::splitToPathAndFilename(fullInvokedProgram_).second),
+      invariantProgramName_(programName_)
+{
+    if (invariantProgramName_.length() >= 4
+        && invariantProgramName_.compare(invariantProgramName_.length() - 4, 4,
+                                         ".exe") == 0)
+    {
+        invariantProgramName_.erase(invariantProgramName_.length() - 4);
+    }
+#ifdef GMX_BINARY_SUFFIX
+    size_t suffixLength = std::strlen(GMX_BINARY_SUFFIX);
+    if (suffixLength > 0 && invariantProgramName_.length() >= suffixLength
+        && invariantProgramName_.compare(
+                invariantProgramName_.length() - suffixLength, suffixLength,
+                GMX_BINARY_SUFFIX) == 0)
+    {
+        invariantProgramName_.erase(invariantProgramName_.length() - suffixLength);
+    }
+#endif
+    if (realBinaryName == NULL)
+    {
+        realBinaryName_ = invariantProgramName_;
+    }
+}
+
+/********************************************************************
+ * ProgramInfo
+ */
+
+// static
+const ProgramInfo &ProgramInfo::getInstance()
+{
+    // TODO: For completeness, this should be thread-safe.
+    if (g_programInfo.get() == NULL)
+    {
+        g_programInfo.reset(new ProgramInfo());
+    }
+    return *g_programInfo;
+}
+
+// static
+const ProgramInfo &ProgramInfo::init(int argc, const char *const argv[])
+{
+    return init(NULL, argc, argv);
+}
+
+// static
+const ProgramInfo &ProgramInfo::init(const char *realBinaryName,
+                                     int argc, const char *const argv[])
+{
+    try
+    {
+        set_program_name(argv[0]);
+        // TODO: Constness should not be cast away.
+        set_command_line(argc, const_cast<char **>(argv));
+        // TODO: For completeness, this should be thread-safe.
+        g_programInfo.reset(new ProgramInfo(realBinaryName, argc, argv));
+        return *g_programInfo;
+    }
+    catch (const std::bad_alloc &)
+    {
+        // TODO: Report error.
+        std::exit(1);
+    }
+}
+
+ProgramInfo::ProgramInfo()
+    : impl_(new Impl)
+{
+}
+
+ProgramInfo::ProgramInfo(const char *realBinaryName)
+    : impl_(new Impl(realBinaryName, 1, &realBinaryName))
+{
+}
+
+ProgramInfo::ProgramInfo(int argc, const char *const argv[])
+    : impl_(new Impl(NULL, argc, argv))
+{
+}
+
+ProgramInfo::ProgramInfo(const char *realBinaryName,
+                         int argc, const char *const argv[])
+    : impl_(new Impl(realBinaryName, argc, argv))
+{
+}
+
+ProgramInfo::~ProgramInfo()
+{
+}
+
+std::string ProgramInfo::realBinaryName() const
+{
+    return impl_->realBinaryName_;
+}
+
+std::string ProgramInfo::programNameWithPath() const
+{
+    return impl_->fullInvokedProgram_;
+}
+
+std::string ProgramInfo::programName() const
+{
+    return impl_->programName_;
+}
+
+std::string ProgramInfo::invariantProgramName() const
+{
+    return impl_->invariantProgramName_;
+}
+
+} // namespace gmx
diff --git a/src/gromacs/utility/programinfo.h b/src/gromacs/utility/programinfo.h
new file mode 100644 (file)
index 0000000..001ec73
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::ProgramInfo.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_PROGRAMINFO_H
+#define GMX_UTILITY_PROGRAMINFO_H
+
+#include <string>
+
+#include "common.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Helper class for managing information about the running binary.
+ *
+ * This class provides access to the name of the binary currently running, as
+ * well as other information derived from it.
+ *
+ * ProgramInfo::init() should be called before any other (C++) Gromacs calls in
+ * a command-line program, as the information is used for printing error
+ * messages.
+ *
+ * Constructors are provided mostly for unit testing purposes; in normal usage,
+ * a single ProgramInfo object is constructed with init() in the beginning of
+ * the program.  The returned object can be explicitly passed to other methods,
+ * or accessed through getInstance().
+ *
+ * Unless explicitly noted otherwise, methods in this class may throw
+ * std::bad_alloc on out-of-memory conditions, but do not throw other
+ * exceptions.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class ProgramInfo
+{
+    public:
+        /*! \brief
+         * Returns the singleton ProgramInfo object.
+         *
+         * \returns The same object as initialized with the last call to init().
+         * \throws  std::bad_alloc if out of memory (only if this is the first
+         *      call and init() has not been called either).
+         */
+        static const ProgramInfo &getInstance();
+        /*! \brief
+         * Initializes global program information.
+         *
+         * \param[in] argc  argc value passed to main().
+         * \param[in] argv  argv array passed to main().
+         * \returns   Reference to initialized program information object.
+         *
+         * The return value of realBinaryName() is the same as
+         * invariantProgramName().
+         *
+         * Does not throw. Terminates the program on out-of-memory error.
+         */
+        static const ProgramInfo &init(int argc, const char *const argv[]);
+        /*! \brief
+         * Initializes global program information with explicit binary name.
+         *
+         * \param[in] realBinaryName  Name of the binary
+         *     (without Gromacs binary suffix or .exe on Windows).
+         * \param[in] argc  argc value passed to main().
+         * \param[in] argv  argv array passed to main().
+         * \returns   Reference to initialized program information object.
+         *
+         * This overload is provided for cases where the program may be invoked
+         * through a symlink, and it is necessary to know the real name of the
+         * binary.
+         *
+         * Does not throw. Terminates the program on out-of-memory error.
+         */
+        static const ProgramInfo &init(const char *realBinaryName,
+                                       int argc, const char *const argv[]);
+
+        /*! \brief
+         * Constructs an empty program info objects.
+         *
+         * All methods in the constructed object return dummy values.
+         */
+        ProgramInfo();
+        /*! \brief
+         * Initializes a program information object with binary name only.
+         *
+         * \param[in] realBinaryName  Name of the binary
+         *     (without Gromacs binary suffix or .exe on Windows).
+         *
+         * This is needed for unit testing purposes.
+         * The constructed object works as if the command line consisted of
+         * only of the binary name.
+         */
+        explicit ProgramInfo(const char *realBinaryName);
+        /*! \brief
+         * Initializes a program information object based on command line.
+         *
+         * \param[in] argc  argc value passed to main().
+         * \param[in] argv  argv array passed to main().
+         */
+        ProgramInfo(int argc, const char *const argv[]);
+        /*! \brief
+         * Initializes a program information object based on binary name and
+         * command line.
+         *
+         * \param[in] realBinaryName  Name of the binary
+         *     (without Gromacs binary suffix or .exe on Windows).
+         * \param[in] argc  argc value passed to main().
+         * \param[in] argv  argv array passed to main().
+         */
+        ProgramInfo(const char *realBinaryName,
+                    int argc, const char *const argv[]);
+        ~ProgramInfo();
+
+        /*! \brief
+         * Returns the real name of the binary.
+         *
+         * The returned value is comparable to invariantProgramName(), i.e., it
+         * has suffixes and OS-specific extensions removed.
+         */
+        std::string realBinaryName() const;
+        /*! \brief
+         * Returns the path and name of the binary as it was invoked.
+         */
+        std::string programNameWithPath() const;
+        /*! \brief
+         * Returns the name of the binary as it was invoked without any path.
+         */
+        std::string programName() const;
+        /*! \brief
+         * Returns an invariant name of the binary.
+         *
+         * The returned value has OS-specific extensions (.exe on Windows)
+         * removed, as well as any binary suffix that was configured.
+         */
+        std::string invariantProgramName() const;
+
+    private:
+        class Impl;
+
+        PrivateImplPointer<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
index 7b5183acae429bbcb4c4ffa8c4c3511c48c8c5f0..5767947ace46e411a56f3eda2d967d3747b24411 100644 (file)
 #include "gromacs/commandline/cmdlinemodulemanager.h"
 #include "gromacs/trajectoryanalysis/modules.h"
 #include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/programinfo.h"
 
 int
 main(int argc, char *argv[])
 {
+    const gmx::ProgramInfo &info =
+        gmx::ProgramInfo::init("g_ana", argc, argv);
+    // TODO: With the addition of ProgramInfo above, this no longer needs to
+    // be here, so think where it would best go.
     CopyRight(stderr, argv[0]);
     try
     {
-        gmx::CommandLineModuleManager manager("g_ana");
+        gmx::CommandLineModuleManager manager(info);
         registerTrajectoryAnalysisModules(&manager);
         return manager.run(argc, argv);
     }
index 1b59a872d8889dc2e2d2da3714580b1ccd93d01a..aa79fb8e5ada63c6bf4459e81d4e47a307975178 100644 (file)
@@ -1,12 +1,12 @@
-include_directories(${GTEST_INCLUDE_DIRS})
+include_directories(${GMOCK_INCLUDE_DIRS})
 set(COMMON_SOURCES datapath.cpp
                    refdata.cpp
                    stringtest.cpp
                    testoptions.cpp)
 
 add_library(testutils STATIC ${COMMON_SOURCES})
-set(TESTUTILS_LIBS testutils ${GTEST_LIBRARIES} ${LIBXML2_LIBRARIES})
-target_link_libraries(testutils libgromacs ${GTEST_LIBRARIES} ${LIBXML2_LIBRARIES})
+set(TESTUTILS_LIBS testutils ${GMOCK_LIBRARIES} ${LIBXML2_LIBRARIES})
+target_link_libraries(testutils libgromacs ${GMOCK_LIBRARIES} ${LIBXML2_LIBRARIES})
 
 set(TESTUTILS_DIR ${CMAKE_CURRENT_SOURCE_DIR})
 set(TESTUTILS_DIR ${TESTUTILS_DIR} PARENT_SCOPE)
index f2505787702c35ac3431ebda577d61018408863f..2ec782ce4475dd87a47cd99c2e55e9f5d1c89a9c 100644 (file)
 #include <vector>
 
 #include <boost/scoped_ptr.hpp>
+#include <gmock/gmock.h>
 
 #include "gromacs/commandline/cmdlineparser.h"
 #include "gromacs/options/options.h"
 #include "gromacs/utility/errorcodes.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/programinfo.h"
 
 #include "datapath.h"
 #include "refdata.h"
@@ -64,13 +66,15 @@ namespace test
 
 void initTestUtils(const char *dataPath, int *argc, char *argv[])
 {
-    if (dataPath != NULL)
-    {
-        setTestDataPath(dataPath);
-    }
-    initReferenceData(argc, argv);
     try
     {
+        ProgramInfo::init(*argc, argv);
+        ::testing::InitGoogleMock(argc, argv);
+        if (dataPath != NULL)
+        {
+            setTestDataPath(dataPath);
+        }
+        initReferenceData(argc, argv);
         boost::scoped_ptr<std::vector<std::string> > commandLine(
                 new std::vector<std::string>());
         for (int i = 0; i < *argc; ++i)
index fecce9654db704533a8b9039aadb5f41e2913be6..8b5292543f45dd9c98de544e2fb7d45f793efe29 100644 (file)
  */
 /*! \libinternal \file
  * \brief
- * main() for unit tests that use Google C++ Mocking Framework.
+ * main() for unit tests that use \ref module_testutils.
  *
  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
  * \ingroup module_testutils
  */
-#include <gmock/gmock.h>
+#include <gtest/gtest.h>
 
 #include "testutils/testoptions.h"
 
 #endif
 
 /*! \brief
- * Initializes unit testing with Google C++ Mocking Framework.
+ * Initializes unit testing for \ref module_testutils.
  */
 int main(int argc, char *argv[])
 {
-    ::testing::InitGoogleMock(&argc, argv);
+    // Calls ::testing::InitGoogleMock()
     ::gmx::test::initTestUtils(TEST_DATA_PATH, &argc, argv);
     return RUN_ALL_TESTS();
 }