std::list<FileNameData>::const_iterator file;
for (file = fileNameOptions_.begin(); file != fileNameOptions_.end(); ++file)
{
- // FIXME: FF_NOT_READ_NODE should also skip all fexist() calls in
- // FileNameOption. However, it is not currently used, and other
- // commented out code for using it is also outdated, so left this for
- // later.
if (!bReadNode && (file->fnm->flag & ffREAD))
{
continue;
gmx::Options options(NULL, NULL);
gmx::FileNameOptionManager fileOptManager;
+ fileOptManager.disableInputOptionChecking(
+ FF(PCA_NOT_READ_NODE) || FF(PCA_DISABLE_INPUT_FILE_CHECKING));
options.addManager(&fileOptManager);
options.setDescription(gmx::ConstArrayRef<const char *>(desc, ndesc));
#define PCA_BE_NICE (1<<13)
/** Is this node not reading: for parallel all nodes but the master */
#define PCA_NOT_READ_NODE (1<<16)
+/** Don't do any special processing for ffREAD files */
+#define PCA_DISABLE_INPUT_FILE_CHECKING (1<<17)
/*! \brief
* Parse command-line arguments.
const char *const cmdline[] = {
"test", "-f2", "-f3", "other", "-f4", "trj.gro", "-g2", "foo"
};
- parseFromArray(cmdline, 0, fnm, gmx::EmptyArrayRef());
+ parseFromArray(cmdline, PCA_DISABLE_INPUT_FILE_CHECKING, fnm, gmx::EmptyArrayRef());
EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
EXPECT_STREQ("trj.xtc", opt2fn("-f", nfile(), fnm));
EXPECT_STREQ("trj2.xtc", opt2fn("-f2", nfile(), fnm));
done_filenms(nfile(), fnm);
}
+TEST_F(ParseCommonArgsTest, HandlesNonExistentOptionalInputFiles)
+{
+ t_filenm fnm[] = {
+ { efTPS, "-s", NULL, ffOPTRD },
+ { efTRX, "-f", "trj", ffOPTRD }
+ };
+ const char *const cmdline[] = {
+ "test"
+ };
+ parseFromArray(cmdline, 0, fnm, gmx::EmptyArrayRef());
+ EXPECT_STREQ("topol.tpr", ftp2fn(efTPS, nfile(), fnm));
+ EXPECT_STREQ("trj.xtc", opt2fn("-f", nfile(), fnm));
+ done_filenms(nfile(), fnm);
+}
+
TEST_F(ParseCommonArgsTest, HandlesCompressedFiles)
{
t_filenm fnm[] = {
#include "gromacs/options/filenameoptionmanager.h"
#include "gromacs/options/optionmanagercontainer.h"
#include "gromacs/utility/arrayref.h"
-#include "gromacs/utility/file.h"
+#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/stringutil.h"
{ eftGenericData, efDAT }
};
-//! Extensions that are recognized as compressed files.
-const char *const c_compressedExtensions[] =
-{ ".gz", ".Z" };
-
/********************************************************************
* FileTypeHandler
*/
//! Returns the extension with the given index.
const char *extension(int i) const;
- //! Returns whether \p filename has a valid extension for this type.
- bool hasKnownExtension(const std::string &filename) const;
- //! Adds a default extension for this type to \p filename.
- std::string addExtension(const std::string &filename) const;
- /*! \brief
- * Adds an extension to \p filename if it results in an existing file.
- *
- * Tries to add each extension for this file type to \p filename and
- * checks whether this results in an existing file.
- * The first match is returned.
- * Returns an empty string if no existing file is found.
- */
- std::string findFileWithExtension(const std::string &filename) const;
+ //! Returns whether \p fileType (from filenm.h) is accepted for this type.
+ bool isValidType(int fileType) const;
private:
/*! \brief
}
bool
-FileTypeHandler::hasKnownExtension(const std::string &filename) const
-{
- for (int i = 0; i < extensionCount(); ++i)
- {
- if (endsWith(filename, extension(i)))
- {
- return true;
- }
- }
- return false;
-}
-
-std::string
-FileTypeHandler::addExtension(const std::string &filename) const
-{
- if (extensionCount() == 0)
- {
- return filename;
- }
- return filename + extension(0);
-}
-
-std::string
-FileTypeHandler::findFileWithExtension(const std::string &filename) const
-{
- for (int i = 0; i < extensionCount(); ++i)
- {
- std::string testFilename(filename + extension(i));
- if (File::exists(testFilename))
- {
- return testFilename;
- }
- }
- return std::string();
-}
-
-/*! \brief
- * Helper method to complete a file name provided to a file name option.
- *
- * \param[in] value Value provided to the file name option.
- * \param[in] typeHandler Handler for the file type.
- * \param[in] bCompleteToExisting
- * Whether to check existing files when completing the extension.
- * \returns \p value with possible extension added.
- */
-std::string completeFileName(const std::string &value,
- const FileTypeHandler &typeHandler,
- bool bCompleteToExisting)
+FileTypeHandler::isValidType(int fileType) const
{
- if (bCompleteToExisting && File::exists(value))
+ if (genericTypes_ != NULL)
{
- // TODO: This may not work as expected if the value is passed to a
- // function that uses fn2ftp() to determine the file type and the input
- // file has an unrecognized extension.
- ConstArrayRef<const char *> compressedExtensions(c_compressedExtensions);
- ConstArrayRef<const char *>::const_iterator ext;
- for (ext = compressedExtensions.begin(); ext != compressedExtensions.end(); ++ext)
+ for (int i = 0; i < extensionCount(); ++i)
{
- if (endsWith(value, *ext))
+ if (fileType == genericTypes_[i])
{
- return value.substr(0, value.length() - std::strlen(*ext));
+ return true;
}
}
- return value;
+ return false;
}
- if (typeHandler.hasKnownExtension(value))
- {
- return value;
- }
- if (bCompleteToExisting)
+ else
{
- std::string newValue = typeHandler.findFileWithExtension(value);
- if (!newValue.empty())
- {
- return newValue;
- }
+ return fileType == fileType_;
}
- return typeHandler.addExtension(value);
}
//! \}
void FileNameOptionStorage::convertValue(const std::string &value)
{
- const bool bInput = isInputFile() || isInputOutputFile();
- FileTypeHandler typeHandler(fileType_);
- addValue(completeFileName(value, typeHandler, bInput));
+ if (manager_ != NULL)
+ {
+ std::string processedValue = manager_->completeFileName(value, info_);
+ if (!processedValue.empty())
+ {
+ // If the manager returns a value, use it without further checks,
+ // except for sanity checking.
+ if (!isDirectoryOption())
+ {
+ const int fileType = fn2ftp(processedValue.c_str());
+ if (fileType == efNR)
+ {
+ // If the manager returned an invalid file name, assume
+ // that it knows what it is doing. But assert that it
+ // only does that for the only case that it is currently
+ // required for: VMD plugins.
+ GMX_ASSERT(isInputFile() && isTrajectoryOption(),
+ "Manager returned an invalid file name");
+ }
+ else
+ {
+ GMX_ASSERT(isValidType(fileType),
+ "Manager returned an invalid file name");
+ }
+ }
+ addValue(processedValue);
+ return;
+ }
+ }
+ // Currently, directory options are simple, and don't need any
+ // special processing.
+ // TODO: Consider splitting them into a separate DirectoryOption.
+ if (isDirectoryOption())
+ {
+ addValue(value);
+ return;
+ }
+ const int fileType = fn2ftp(value.c_str());
+ if (fileType == efNR)
+ {
+ std::string message
+ = formatString("File '%s' cannot be used by GROMACS because it "
+ "does not have a recognizable extension.\n"
+ "The following extensions are possible for this option:\n %s",
+ value.c_str(), joinStrings(extensions(), ", ").c_str());
+ GMX_THROW(InvalidInputError(message));
+ }
+ else if (!isValidType(fileType))
+ {
+ std::string message
+ = formatString("File name '%s' cannot be used for this option.\n"
+ "Only the following extensions are possible:\n %s",
+ value.c_str(), joinStrings(extensions(), ", ").c_str());
+ GMX_THROW(InvalidInputError(message));
+ }
+ addValue(value);
}
void FileNameOptionStorage::processAll()
{
- FileTypeHandler typeHandler(fileType_);
- if (hasFlag(efOption_HasDefaultValue) && typeHandler.extensionCount() > 0)
+ if (manager_ != NULL && hasFlag(efOption_HasDefaultValue))
{
- const bool bInput = isInputFile() || isInputOutputFile();
- ValueList &valueList = values();
+ ValueList &valueList = values();
GMX_RELEASE_ASSERT(valueList.size() == 1,
"There should be only one default value");
- const bool bGlobalDefault =
- (manager_ != NULL && !manager_->defaultFileName().empty());
- if (!valueList[0].empty() && (typeHandler.extensionCount() > 1 || bGlobalDefault))
+ if (!valueList[0].empty())
{
const std::string &oldValue = valueList[0];
GMX_ASSERT(endsWith(oldValue, defaultExtension()),
"Default value does not have the expected extension");
- std::string prefix = stripSuffixIfPresent(oldValue, defaultExtension());
- if (bGlobalDefault)
- {
- prefix = manager_->defaultFileName();
- }
- std::string newValue = completeFileName(prefix, typeHandler, bInput);
- if (newValue != oldValue)
+ const std::string prefix
+ = stripSuffixIfPresent(oldValue, defaultExtension());
+ const std::string newValue
+ = manager_->completeDefaultFileName(prefix, info_);
+ if (!newValue.empty() && newValue != oldValue)
{
+ GMX_ASSERT(isValidType(fn2ftp(newValue.c_str())),
+ "Manager returned an invalid default value");
valueList[0] = newValue;
refreshValues();
}
return fileType_ == efRND;
}
+bool FileNameOptionStorage::isTrajectoryOption() const
+{
+ return fileType_ == efTRX;
+}
+
const char *FileNameOptionStorage::defaultExtension() const
{
FileTypeHandler typeHandler(fileType_);
return result;
}
+bool FileNameOptionStorage::isValidType(int fileType) const
+{
+ FileTypeHandler typeHandler(fileType_);
+ return typeHandler.isValidType(fileType);
+}
+
+ConstArrayRef<int> FileNameOptionStorage::fileTypes() const
+{
+ if (fileType_ < 0)
+ {
+ return ConstArrayRef<int>();
+ }
+ const int genericTypeCount = ftp2generic_count(fileType_);
+ if (genericTypeCount > 0)
+ {
+ return ConstArrayRef<int>(ftp2generic_list(fileType_), genericTypeCount);
+ }
+ return ConstArrayRef<int>(&fileType_, 1);
+}
+
/********************************************************************
* FileNameOptionInfo
*/
return option().isDirectoryOption();
}
+bool FileNameOptionInfo::isTrajectoryOption() const
+{
+ return option().isTrajectoryOption();
+}
+
const char *FileNameOptionInfo::defaultExtension() const
{
return option().defaultExtension();
return option().extensions();
}
+bool FileNameOptionInfo::isValidType(int fileType) const
+{
+ return option().isValidType(fileType);
+}
+
+ConstArrayRef<int> FileNameOptionInfo::fileTypes() const
+{
+ return option().fileTypes();
+}
+
/********************************************************************
* FileNameOption
*/
//! Whether the option specifies directories.
bool isDirectoryOption() const;
+ //! Whether the option specifies a generic trajectory file.
+ bool isTrajectoryOption() const;
//! Returns the default extension for this option.
const char *defaultExtension() const;
//! Returns the list of extensions this option accepts.
ExtensionList extensions() const;
+ //! Returns whether \p fileType (from filenm.h) is accepted for this option.
+ bool isValidType(int fileType) const;
+ //! Returns the list of file types this option accepts.
+ ConstArrayRef<int> fileTypes() const;
private:
const FileNameOptionStorage &option() const;
*/
#include "filenameoptionmanager.h"
+#include <cstring>
+
#include <string>
+#include "gromacs/fileio/filenm.h"
#include "gromacs/options/basicoptions.h"
#include "gromacs/options/filenameoption.h"
#include "gromacs/options/options.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/file.h"
+#include "gromacs/utility/path.h"
+#include "gromacs/utility/stringutil.h"
namespace gmx
{
+namespace
+{
+
+//! Extensions that are recognized as compressed files.
+const char *const c_compressedExtensions[] =
+{ ".gz", ".Z" };
+
+/********************************************************************
+ * Helper functions
+ */
+
+/*! \brief
+ * Adds an extension to \p prefix if it results in an existing file.
+ *
+ * Tries to add each extension for this file type to \p prefix and
+ * checks whether this results in an existing file.
+ * The first match is returned.
+ * Returns an empty string if no existing file is found.
+ */
+std::string findExistingExtension(const std::string &prefix,
+ const FileNameOptionInfo &option)
+{
+ ConstArrayRef<int> types = option.fileTypes();
+ ConstArrayRef<int>::const_iterator i;
+ for (i = types.begin(); i != types.end(); ++i)
+ {
+ std::string testFilename(prefix + ftp2ext_with_dot(*i));
+ if (File::exists(testFilename))
+ {
+ return testFilename;
+ }
+ }
+ return std::string();
+}
+
+} // namespace
+
/********************************************************************
* FileNameOptionManager::Impl
*/
class FileNameOptionManager::Impl
{
public:
+ Impl() : bInputCheckingDisabled_(false) {}
+
//! Global default file name, if set.
std::string defaultFileName_;
+ //! Whether input option processing has been disabled.
+ bool bInputCheckingDisabled_;
};
/********************************************************************
{
}
+void FileNameOptionManager::disableInputOptionChecking(bool bDisable)
+{
+ impl_->bInputCheckingDisabled_ = bDisable;
+}
+
void FileNameOptionManager::addDefaultFileNameOption(
Options *options, const char *name)
{
.description("Set the default filename for all file options"));
}
-const std::string &FileNameOptionManager::defaultFileName() const
+std::string FileNameOptionManager::completeFileName(
+ const std::string &value, const FileNameOptionInfo &option)
+{
+ const bool bInput = option.isInputFile() || option.isInputOutputFile();
+ // Currently, directory options are simple, and don't need any
+ // special processing.
+ // TODO: Consider splitting them into a separate DirectoryOption.
+ if (option.isDirectoryOption())
+ {
+ if (!impl_->bInputCheckingDisabled_ && bInput && !Directory::exists(value))
+ {
+ std::string message
+ = formatString("Directory '%s' does not exist or is not accessible.",
+ value.c_str());
+ // TODO: Get actual errno value from the attempt to open the file
+ // to provide better feedback to the user.
+ GMX_THROW(InvalidInputError(message));
+ }
+ return value;
+ }
+ const int fileType = fn2ftp(value.c_str());
+ if (bInput && !impl_->bInputCheckingDisabled_)
+ {
+ if (fileType == efNR && File::exists(value))
+ {
+ ConstArrayRef<const char *> compressedExtensions(c_compressedExtensions);
+ ConstArrayRef<const char *>::const_iterator ext;
+ for (ext = compressedExtensions.begin(); ext != compressedExtensions.end(); ++ext)
+ {
+ if (endsWith(value, *ext))
+ {
+ std::string newValue = value.substr(0, value.length() - std::strlen(*ext));
+ if (option.isValidType(fn2ftp(newValue.c_str())))
+ {
+ return newValue;
+ }
+ else
+ {
+ return std::string();
+ }
+ }
+ }
+ // VMD plugins may be able to read the file.
+ if (option.isInputFile() && option.isTrajectoryOption())
+ {
+ return value;
+ }
+ }
+ else if (fileType == efNR)
+ {
+ std::string processedValue = findExistingExtension(value, option);
+ if (!processedValue.empty())
+ {
+ return processedValue;
+ }
+ if (option.isLibraryFile())
+ {
+ // TODO: Treat also library files here.
+ return value + option.defaultExtension();
+ }
+ else
+ {
+ std::string message
+ = formatString("File '%s' does not exist or is not accessible.\n"
+ "The following extensions were tried to complete the file name:\n %s",
+ value.c_str(), joinStrings(option.extensions(), ", ").c_str());
+ GMX_THROW(InvalidInputError(message));
+ }
+ }
+ else if (option.isValidType(fileType))
+ {
+ if (option.isLibraryFile())
+ {
+ // TODO: Treat also library files.
+ }
+ else if (!File::exists(value))
+ {
+ std::string message
+ = formatString("File '%s' does not exist or is not accessible.",
+ value.c_str());
+ // TODO: Get actual errno value from the attempt to open the file
+ // to provide better feedback to the user.
+ GMX_THROW(InvalidInputError(message));
+ }
+ return value;
+ }
+ }
+ else // Not an input file
+ {
+ if (fileType == efNR)
+ {
+ return value + option.defaultExtension();
+ }
+ else if (option.isValidType(fileType))
+ {
+ return value;
+ }
+ }
+ return std::string();
+}
+
+std::string FileNameOptionManager::completeDefaultFileName(
+ const std::string &prefix, const FileNameOptionInfo &option)
{
- return impl_->defaultFileName_;
+ if (option.isDirectoryOption() || impl_->bInputCheckingDisabled_)
+ {
+ return std::string();
+ }
+ const bool bInput = option.isInputFile() || option.isInputOutputFile();
+ const std::string realPrefix
+ = !impl_->defaultFileName_.empty() ? impl_->defaultFileName_ : prefix;
+ if (bInput)
+ {
+ std::string completedName = findExistingExtension(realPrefix, option);
+ if (!completedName.empty())
+ {
+ return completedName;
+ }
+ if (option.isLibraryFile())
+ {
+ // TODO: Treat also library files here.
+ return realPrefix + option.defaultExtension();
+ }
+ else if (option.isSet())
+ {
+ std::string message
+ = formatString("No file name was provided, and the default file "
+ "'%s' does not exist or is not accessible.\n"
+ "The following extensions were tried to complete the file name:\n %s",
+ prefix.c_str(), joinStrings(option.extensions(), ", ").c_str());
+ GMX_THROW(InvalidInputError(message));
+ }
+ else if (option.isRequired())
+ {
+ std::string message
+ = formatString("Required option was not provided, and the default file "
+ "'%s' does not exist or is not accessible.\n"
+ "The following extensions were tried to complete the file name:\n %s",
+ prefix.c_str(), joinStrings(option.extensions(), ", ").c_str());
+ GMX_THROW(InvalidInputError(message));
+ }
+ // We get here with the legacy optional behavior.
+ }
+ return realPrefix + option.defaultExtension();
}
} // namespace gmx
namespace gmx
{
+class FileNameOptionInfo;
class Options;
/*! \brief
* Handles interaction of file name options with global options.
*
- * Currently, this class implements support for a global default file name
- * that overrides any option-specific default.
+ * This class contains all logic that completes file names based on user input
+ * and file system contents. Additionally, this class implements support for a
+ * global default file name that overrides any option-specific default, as well
+ * as additional control over how the completion is done.
*
* \todo
- * Currently, this class has very little logic, and just provides the global
- * values to FileNameOptionStorage implementation. A cleaner design would have
- * most of the non-trivial file name completion logic in this class, so that
- * the customizations would be centralized here.
+ * Most of the functionality in this class is specific to command line parsing,
+ * so it would be cleaner to replace this with an interface, and have the
+ * actual code in the `commandline` module.
*
* Adding a FileNameOptionManager for an Options object is optional, even if
* the Options contains FileNameOption options. Features from the manager are
- * not available if the manager is not created, but otherwise the options work.
+ * not available if the manager is not created, but otherwise the options work:
+ * the values provided to FileNameOption are used as they are, and exceptions
+ * are thrown if they are no valid instead of attempting to complete them.
*
* \see Options::addManager()
*
FileNameOptionManager();
virtual ~FileNameOptionManager();
+ /*! \brief
+ * Disables special input file option handling.
+ *
+ * If disabled, this removes all file system calls from the file
+ * name option parsing.
+ * The values returned by FileNameOption for input and input/output
+ * files are handled with the same simple rule as for output files:
+ * the default extension is added if the file does not end in a
+ * recognized extension, and no other checking is done.
+ *
+ * This changes the following behavior:
+ * - Providing non-existent files does not trigger errors.
+ * - Extensions for input files are not completed to an existing file.
+ * - Compressed input files do not work.
+ */
+ void disableInputOptionChecking(bool bDisable);
+
/*! \brief
* Adds an option for setting the default global file name.
*
*/
void addDefaultFileNameOption(Options *options, const char *name);
- //! Returns the currently set default file name.
- const std::string &defaultFileName() const;
+ /*! \brief
+ * Completes file name option values.
+ *
+ * \param[in] value Value provided by the user.
+ * \param[in] option Option for which the value should be completed.
+ * \returns Value for the file name option.
+ * \throws std::bad_alloc if out of memory.
+ * \throws InvalidInputError if the value is not valid for this
+ * option.
+ *
+ * This method is called for each value that the user provides to
+ * a FileNameOption. The return value (if non-empty) is used as the
+ * value of the option instead of the user-provided one.
+ */
+ std::string completeFileName(const std::string &value,
+ const FileNameOptionInfo &option);
+ /*! \brief
+ * Completes default values for file name options.
+ *
+ * \param[in] prefix Default prefix for the file name.
+ * \param[in] option Option for which the value should be completed.
+ * \returns Value for the file name option.
+ * \throws std::bad_alloc if out of memory.
+ * \throws InvalidInputError if the value is not valid for this
+ * option.
+ *
+ * This method is called for each FileNameOption that has a default
+ * value (either a standard default value, or if the user provided the
+ * option without an explicit value). \p prefix is the default value
+ * without the default extension for the option.
+ * If the return value is non-empty, it is used as the default value
+ * for the option instead of \p prefix + default extension.
+ */
+ std::string completeDefaultFileName(const std::string &prefix,
+ const FileNameOptionInfo &option);
private:
class Impl;
//! \copydoc FileNameOptionInfo::isDirectoryOption()
bool isDirectoryOption() const;
+ //! \copydoc FileNameOptionInfo::isTrajectoryOption()
+ bool isTrajectoryOption() const;
//! \copydoc FileNameOptionInfo::defaultExtension()
const char *defaultExtension() const;
//! \copydoc FileNameOptionInfo::extensions()
std::vector<const char *> extensions() const;
+ //! \copydoc FileNameOptionInfo::isValidType()
+ bool isValidType(int fileType) const;
+ //! \copydoc FileNameOptionInfo::fileTypes()
+ ConstArrayRef<int> fileTypes() const;
private:
virtual void convertValue(const std::string &value);
#
# This file is part of the GROMACS molecular simulation package.
#
-# Copyright (c) 2010,2011,2012, by the GROMACS development team, led by
+# Copyright (c) 2010,2011,2012,2014, by the GROMACS development team, led by
# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
# and including many others, as listed in the AUTHORS file in the
# top-level source directory and at http://www.gromacs.org.
gmx_add_unit_test(OptionsUnitTests options-test
abstractoptionstorage.cpp
filenameoption.cpp
+ filenameoptionmanager.cpp
option.cpp
optionsassigner.cpp
timeunitmanager.cpp)
*/
/*! \internal \file
* \brief
- * Tests file name option implementation.
+ * Tests basic file name option implementation.
*
* \author Teemu Murtola <teemu.murtola@gmail.com>
* \ingroup module_options
*/
-#include <vector>
+#include "gromacs/options/filenameoption.h"
#include <gtest/gtest.h>
-#include "gromacs/options/filenameoption.h"
-#include "gromacs/options/filenameoptionmanager.h"
#include "gromacs/options/options.h"
#include "gromacs/options/optionsassigner.h"
#include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
-#include "gromacs/utility/path.h"
#include "testutils/testasserts.h"
-#include "testutils/testfilemanager.h"
namespace
{
using gmx::FileNameOption;
-using gmx::test::TestFileManager;
-
-TEST(FileNameOptionTest, AddsMissingExtension)
-{
- gmx::Options options(NULL, NULL);
- std::string value;
- ASSERT_NO_THROW_GMX(options.addOption(
- FileNameOption("f").store(&value)
- .filetype(gmx::eftTrajectory).outputFile()));
-
- gmx::OptionsAssigner assigner(&options);
- EXPECT_NO_THROW_GMX(assigner.start());
- EXPECT_NO_THROW_GMX(assigner.startOption("f"));
- EXPECT_NO_THROW_GMX(assigner.appendValue("testfile"));
- EXPECT_NO_THROW_GMX(assigner.finishOption());
- EXPECT_NO_THROW_GMX(assigner.finish());
- EXPECT_NO_THROW_GMX(options.finish());
-
- EXPECT_EQ("testfile.xtc", value);
-}
TEST(FileNameOptionTest, HandlesRequiredDefaultValueWithoutExtension)
{
FileNameOption("f").store(&value).required()
.filetype(gmx::eftGenericData).outputFile()
.defaultBasename("testfile")));
+ EXPECT_EQ("testfile.dat", value);
gmx::OptionsAssigner assigner(&options);
EXPECT_NO_THROW_GMX(assigner.start());
EXPECT_EQ("testfile.ndx", value);
}
-TEST(FileNameOptionTest, AddsMissingExtensionBasedOnExistingFile)
-{
- TestFileManager tempFiles;
- std::string filename(tempFiles.getTemporaryFilePath(".trr"));
- gmx::File::writeFileFromString(filename, "Dummy trajectory file");
- std::string inputValue(gmx::Path::stripExtension(filename));
-
- gmx::Options options(NULL, NULL);
- std::string value;
- ASSERT_NO_THROW_GMX(options.addOption(
- FileNameOption("f").store(&value)
- .filetype(gmx::eftTrajectory).inputFile()));
-
- gmx::OptionsAssigner assigner(&options);
- EXPECT_NO_THROW_GMX(assigner.start());
- EXPECT_NO_THROW_GMX(assigner.startOption("f"));
- EXPECT_NO_THROW_GMX(assigner.appendValue(inputValue));
- EXPECT_NO_THROW_GMX(assigner.finishOption());
- EXPECT_NO_THROW_GMX(assigner.finish());
- EXPECT_NO_THROW_GMX(options.finish());
-
- EXPECT_EQ(filename, value);
-}
-
-TEST(FileNameOptionTest, AddsMissingExtensionForRequiredDefaultNameBasedOnExistingFile)
-{
- TestFileManager tempFiles;
- std::string filename(tempFiles.getTemporaryFilePath(".trr"));
- gmx::File::writeFileFromString(filename, "Dummy trajectory file");
- std::string inputValue(gmx::Path::stripExtension(filename));
-
- gmx::Options options(NULL, NULL);
- std::string value;
- ASSERT_NO_THROW_GMX(options.addOption(
- FileNameOption("f").store(&value).required()
- .filetype(gmx::eftTrajectory).inputFile()
- .defaultBasename(inputValue.c_str())));
- EXPECT_EQ(inputValue + ".xtc", value);
-
- gmx::OptionsAssigner assigner(&options);
- EXPECT_NO_THROW_GMX(assigner.start());
- EXPECT_NO_THROW_GMX(assigner.startOption("f"));
- EXPECT_NO_THROW_GMX(assigner.finishOption());
- EXPECT_NO_THROW_GMX(assigner.finish());
- EXPECT_NO_THROW_GMX(options.finish());
-
- EXPECT_EQ(filename, value);
-}
-
-TEST(FileNameOptionTest, AddsMissingExtensionForOptionalDefaultNameBasedOnExistingFile)
+TEST(FileNameOptionTest, GivesErrorOnUnknownFileSuffix)
{
- TestFileManager tempFiles;
- std::string filename(tempFiles.getTemporaryFilePath(".trr"));
- gmx::File::writeFileFromString(filename, "Dummy trajectory file");
- std::string inputValue(gmx::Path::stripExtension(filename));
-
- gmx::Options options(NULL, NULL);
- std::string value;
+ gmx::Options options(NULL, NULL);
+ std::string value;
ASSERT_NO_THROW_GMX(options.addOption(
FileNameOption("f").store(&value)
- .filetype(gmx::eftTrajectory).inputFile()
- .defaultBasename(inputValue.c_str())));
+ .filetype(gmx::eftIndex).outputFile()));
+ EXPECT_TRUE(value.empty());
gmx::OptionsAssigner assigner(&options);
EXPECT_NO_THROW_GMX(assigner.start());
EXPECT_NO_THROW_GMX(assigner.startOption("f"));
+ EXPECT_THROW_GMX(assigner.appendValue("testfile.foo"), gmx::InvalidInputError);
EXPECT_NO_THROW_GMX(assigner.finishOption());
EXPECT_NO_THROW_GMX(assigner.finish());
EXPECT_NO_THROW_GMX(options.finish());
- EXPECT_EQ(filename, value);
-}
-
-TEST(FileNameOptionTest, AddsMissingExtensionForRequiredFromDefaultNameOptionBasedOnExistingFile)
-{
- TestFileManager tempFiles;
- std::string filename(tempFiles.getTemporaryFilePath(".trr"));
- gmx::File::writeFileFromString(filename, "Dummy trajectory file");
- std::string inputValue(gmx::Path::stripExtension(filename));
-
- gmx::FileNameOptionManager manager;
- gmx::Options options(NULL, NULL);
- std::string value;
- ASSERT_NO_THROW_GMX(options.addManager(&manager));
- ASSERT_NO_THROW_GMX(options.addOption(
- FileNameOption("f").store(&value).required()
- .filetype(gmx::eftTrajectory).inputFile()
- .defaultBasename("foo")));
- ASSERT_NO_THROW_GMX(manager.addDefaultFileNameOption(&options, "deffnm"));
- EXPECT_EQ("foo.xtc", value);
-
- gmx::OptionsAssigner assigner(&options);
- EXPECT_NO_THROW_GMX(assigner.start());
- EXPECT_NO_THROW_GMX(assigner.startOption("deffnm"));
- EXPECT_NO_THROW_GMX(assigner.appendValue(inputValue));
- EXPECT_NO_THROW_GMX(assigner.finishOption());
- EXPECT_NO_THROW_GMX(assigner.finish());
- EXPECT_NO_THROW_GMX(options.finish());
-
- EXPECT_EQ(filename, value);
+ EXPECT_TRUE(value.empty());
}
-TEST(FileNameOptionTest, AddsMissingExtensionForOptionalFromDefaultNameOptionBasedOnExistingFile)
+TEST(FileNameOptionTest, GivesErrorOnInvalidFileSuffix)
{
- TestFileManager tempFiles;
- std::string filename(tempFiles.getTemporaryFilePath(".trr"));
- gmx::File::writeFileFromString(filename, "Dummy trajectory file");
- std::string inputValue(gmx::Path::stripExtension(filename));
-
- gmx::FileNameOptionManager manager;
- gmx::Options options(NULL, NULL);
- std::string value;
- ASSERT_NO_THROW_GMX(options.addManager(&manager));
+ gmx::Options options(NULL, NULL);
+ std::string value;
ASSERT_NO_THROW_GMX(options.addOption(
FileNameOption("f").store(&value)
- .filetype(gmx::eftTrajectory).inputFile()
- .defaultBasename("foo")));
- ASSERT_NO_THROW_GMX(manager.addDefaultFileNameOption(&options, "deffnm"));
+ .filetype(gmx::eftTrajectory).outputFile()));
+ EXPECT_TRUE(value.empty());
gmx::OptionsAssigner assigner(&options);
EXPECT_NO_THROW_GMX(assigner.start());
- EXPECT_NO_THROW_GMX(assigner.startOption("deffnm"));
- EXPECT_NO_THROW_GMX(assigner.appendValue(inputValue));
- EXPECT_NO_THROW_GMX(assigner.finishOption());
EXPECT_NO_THROW_GMX(assigner.startOption("f"));
+ EXPECT_THROW_GMX(assigner.appendValue("testfile.dat"), gmx::InvalidInputError);
EXPECT_NO_THROW_GMX(assigner.finishOption());
EXPECT_NO_THROW_GMX(assigner.finish());
EXPECT_NO_THROW_GMX(options.finish());
- EXPECT_EQ(filename, value);
+ EXPECT_TRUE(value.empty());
}
} // namespace
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * If you want to redistribute modifications to GROMACS, 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 http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests file name option implementation dependent on gmx::FileNameOptionManager.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_options
+ */
+#include "gromacs/options/filenameoptionmanager.h"
+
+#include <gtest/gtest.h>
+
+#include "gromacs/options/filenameoption.h"
+#include "gromacs/options/options.h"
+#include "gromacs/options/optionsassigner.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/file.h"
+#include "gromacs/utility/path.h"
+
+#include "testutils/testasserts.h"
+#include "testutils/testfilemanager.h"
+
+namespace
+{
+
+using gmx::FileNameOption;
+
+class FileNameOptionManagerTest : public ::testing::Test
+{
+ public:
+ FileNameOptionManagerTest()
+ : options_(NULL, NULL)
+ {
+ options_.addManager(&manager_);
+ }
+
+ std::string createDummyFile(const char *suffix)
+ {
+ std::string filename(tempFiles_.getTemporaryFilePath(suffix));
+ gmx::File::writeFileFromString(filename, "Dummy file");
+ return filename;
+ }
+
+ gmx::FileNameOptionManager manager_;
+ gmx::Options options_;
+ gmx::test::TestFileManager tempFiles_;
+};
+
+/********************************************************************
+ * Actual tests
+ */
+
+TEST_F(FileNameOptionManagerTest, AddsMissingExtension)
+{
+ std::string value;
+ ASSERT_NO_THROW_GMX(options_.addOption(
+ FileNameOption("f").store(&value)
+ .filetype(gmx::eftTrajectory).outputFile()));
+
+ gmx::OptionsAssigner assigner(&options_);
+ EXPECT_NO_THROW_GMX(assigner.start());
+ EXPECT_NO_THROW_GMX(assigner.startOption("f"));
+ EXPECT_NO_THROW_GMX(assigner.appendValue("testfile"));
+ EXPECT_NO_THROW_GMX(assigner.finishOption());
+ EXPECT_NO_THROW_GMX(assigner.finish());
+ EXPECT_NO_THROW_GMX(options_.finish());
+
+ EXPECT_EQ("testfile.xtc", value);
+}
+
+TEST_F(FileNameOptionManagerTest, GivesErrorOnMissingInputFile)
+{
+ std::string value;
+ ASSERT_NO_THROW_GMX(options_.addOption(
+ FileNameOption("f").store(&value)
+ .filetype(gmx::eftIndex).inputFile()));
+ EXPECT_TRUE(value.empty());
+
+ gmx::OptionsAssigner assigner(&options_);
+ EXPECT_NO_THROW_GMX(assigner.start());
+ EXPECT_NO_THROW_GMX(assigner.startOption("f"));
+ EXPECT_THROW_GMX(assigner.appendValue("missing.ndx"), gmx::InvalidInputError);
+ EXPECT_NO_THROW_GMX(assigner.finishOption());
+ EXPECT_NO_THROW_GMX(assigner.finish());
+ EXPECT_NO_THROW_GMX(options_.finish());
+
+ EXPECT_TRUE(value.empty());
+}
+
+TEST_F(FileNameOptionManagerTest, GivesErrorOnMissingGenericInputFile)
+{
+ std::string value;
+ ASSERT_NO_THROW_GMX(options_.addOption(
+ FileNameOption("f").store(&value)
+ .filetype(gmx::eftTrajectory).inputFile()));
+ EXPECT_TRUE(value.empty());
+
+ gmx::OptionsAssigner assigner(&options_);
+ EXPECT_NO_THROW_GMX(assigner.start());
+ EXPECT_NO_THROW_GMX(assigner.startOption("f"));
+ EXPECT_THROW_GMX(assigner.appendValue("missing.trr"), gmx::InvalidInputError);
+ EXPECT_NO_THROW_GMX(assigner.finishOption());
+ EXPECT_NO_THROW_GMX(assigner.finish());
+ EXPECT_NO_THROW_GMX(options_.finish());
+
+ EXPECT_TRUE(value.empty());
+}
+
+TEST_F(FileNameOptionManagerTest, GivesErrorOnMissingDefaultInputFile)
+{
+ std::string value;
+ ASSERT_NO_THROW_GMX(options_.addOption(
+ FileNameOption("f").store(&value)
+ .filetype(gmx::eftIndex).inputFile()
+ .defaultBasename("missing")));
+
+ gmx::OptionsAssigner assigner(&options_);
+ EXPECT_NO_THROW_GMX(assigner.start());
+ EXPECT_NO_THROW_GMX(assigner.startOption("f"));
+ EXPECT_NO_THROW_GMX(assigner.finishOption());
+ EXPECT_NO_THROW_GMX(assigner.finish());
+ EXPECT_THROW_GMX(options_.finish(), gmx::InvalidInputError);
+}
+
+TEST_F(FileNameOptionManagerTest, GivesErrorOnMissingRequiredInputFile)
+{
+ std::string value;
+ ASSERT_NO_THROW_GMX(options_.addOption(
+ FileNameOption("f").store(&value).required()
+ .filetype(gmx::eftIndex).inputFile()
+ .defaultBasename("missing")));
+ EXPECT_EQ("missing.ndx", value);
+
+ gmx::OptionsAssigner assigner(&options_);
+ EXPECT_NO_THROW_GMX(assigner.start());
+ EXPECT_NO_THROW_GMX(assigner.finish());
+ EXPECT_THROW_GMX(options_.finish(), gmx::InvalidInputError);
+}
+
+TEST_F(FileNameOptionManagerTest, AddsMissingExtensionBasedOnExistingFile)
+{
+ std::string filename(createDummyFile(".trr"));
+ std::string inputValue(gmx::Path::stripExtension(filename));
+
+ std::string value;
+ ASSERT_NO_THROW_GMX(options_.addOption(
+ FileNameOption("f").store(&value)
+ .filetype(gmx::eftTrajectory).inputFile()));
+
+ gmx::OptionsAssigner assigner(&options_);
+ EXPECT_NO_THROW_GMX(assigner.start());
+ EXPECT_NO_THROW_GMX(assigner.startOption("f"));
+ EXPECT_NO_THROW_GMX(assigner.appendValue(inputValue));
+ EXPECT_NO_THROW_GMX(assigner.finishOption());
+ EXPECT_NO_THROW_GMX(assigner.finish());
+ EXPECT_NO_THROW_GMX(options_.finish());
+
+ EXPECT_EQ(filename, value);
+}
+
+TEST_F(FileNameOptionManagerTest,
+ AddsMissingExtensionForRequiredDefaultNameBasedOnExistingFile)
+{
+ std::string filename(createDummyFile(".trr"));
+ std::string inputValue(gmx::Path::stripExtension(filename));
+
+ std::string value;
+ ASSERT_NO_THROW_GMX(options_.addOption(
+ FileNameOption("f").store(&value).required()
+ .filetype(gmx::eftTrajectory).inputFile()
+ .defaultBasename(inputValue.c_str())));
+ EXPECT_EQ(inputValue + ".xtc", value);
+
+ gmx::OptionsAssigner assigner(&options_);
+ EXPECT_NO_THROW_GMX(assigner.start());
+ EXPECT_NO_THROW_GMX(assigner.startOption("f"));
+ EXPECT_NO_THROW_GMX(assigner.finishOption());
+ EXPECT_NO_THROW_GMX(assigner.finish());
+ EXPECT_NO_THROW_GMX(options_.finish());
+
+ EXPECT_EQ(filename, value);
+}
+
+TEST_F(FileNameOptionManagerTest,
+ AddsMissingExtensionForOptionalDefaultNameBasedOnExistingFile)
+{
+ std::string filename(createDummyFile(".trr"));
+ std::string inputValue(gmx::Path::stripExtension(filename));
+
+ std::string value;
+ ASSERT_NO_THROW_GMX(options_.addOption(
+ FileNameOption("f").store(&value)
+ .filetype(gmx::eftTrajectory).inputFile()
+ .defaultBasename(inputValue.c_str())));
+
+ gmx::OptionsAssigner assigner(&options_);
+ EXPECT_NO_THROW_GMX(assigner.start());
+ EXPECT_NO_THROW_GMX(assigner.startOption("f"));
+ EXPECT_NO_THROW_GMX(assigner.finishOption());
+ EXPECT_NO_THROW_GMX(assigner.finish());
+ EXPECT_NO_THROW_GMX(options_.finish());
+
+ EXPECT_EQ(filename, value);
+}
+
+TEST_F(FileNameOptionManagerTest,
+ AddsMissingExtensionForRequiredFromDefaultNameOptionBasedOnExistingFile)
+{
+ std::string filename(createDummyFile(".trr"));
+ std::string inputValue(gmx::Path::stripExtension(filename));
+
+ std::string value;
+ ASSERT_NO_THROW_GMX(options_.addOption(
+ FileNameOption("f").store(&value).required()
+ .filetype(gmx::eftTrajectory).inputFile()
+ .defaultBasename("foo")));
+ ASSERT_NO_THROW_GMX(manager_.addDefaultFileNameOption(&options_, "deffnm"));
+ EXPECT_EQ("foo.xtc", value);
+
+ gmx::OptionsAssigner assigner(&options_);
+ EXPECT_NO_THROW_GMX(assigner.start());
+ EXPECT_NO_THROW_GMX(assigner.startOption("deffnm"));
+ EXPECT_NO_THROW_GMX(assigner.appendValue(inputValue));
+ EXPECT_NO_THROW_GMX(assigner.finishOption());
+ EXPECT_NO_THROW_GMX(assigner.finish());
+ EXPECT_NO_THROW_GMX(options_.finish());
+
+ EXPECT_EQ(filename, value);
+}
+
+TEST_F(FileNameOptionManagerTest,
+ AddsMissingExtensionForOptionalFromDefaultNameOptionBasedOnExistingFile)
+{
+ std::string filename(createDummyFile(".trr"));
+ std::string inputValue(gmx::Path::stripExtension(filename));
+
+ std::string value;
+ ASSERT_NO_THROW_GMX(options_.addOption(
+ FileNameOption("f").store(&value)
+ .filetype(gmx::eftTrajectory).inputFile()
+ .defaultBasename("foo")));
+ ASSERT_NO_THROW_GMX(manager_.addDefaultFileNameOption(&options_, "deffnm"));
+
+ gmx::OptionsAssigner assigner(&options_);
+ EXPECT_NO_THROW_GMX(assigner.start());
+ EXPECT_NO_THROW_GMX(assigner.startOption("deffnm"));
+ EXPECT_NO_THROW_GMX(assigner.appendValue(inputValue));
+ EXPECT_NO_THROW_GMX(assigner.finishOption());
+ EXPECT_NO_THROW_GMX(assigner.startOption("f"));
+ EXPECT_NO_THROW_GMX(assigner.finishOption());
+ EXPECT_NO_THROW_GMX(assigner.finish());
+ EXPECT_NO_THROW_GMX(options_.finish());
+
+ EXPECT_EQ(filename, value);
+}
+
+} // namespace
#include "gromacs/commandline/cmdlinemodulemanager.h"
#include "gromacs/commandline/cmdlineparser.h"
#include "gromacs/fileio/trx.h"
+#include "gromacs/options/filenameoptionmanager.h"
#include "gromacs/options/options.h"
#include "gromacs/pbcutil/pbc.h"
#include "gromacs/selection/selectioncollection.h"
SelectionCollection *selections,
int *argc, char *argv[])
{
+ FileNameOptionManager fileoptManager;
SelectionOptionManager seloptManager(selections);
Options options(NULL, NULL);
Options moduleOptions(module_->name(), module_->description());
Options commonOptions("common", "Common analysis control");
Options selectionOptions("selection", "Common selection control");
+ options.addManager(&fileoptManager);
options.addManager(&seloptManager);
options.addSubSection(&commonOptions);
options.addSubSection(&selectionOptions);
#endif
#include <stdio.h>
+#include <string.h>
#include "gromacs/legacyheaders/checkpoint.h"
#include "gromacs/legacyheaders/copyrite.h"
#include "gromacs/fileio/filenm.h"
#include "gromacs/utility/fatalerror.h"
+static bool is_multisim_option_set(int argc, const char *const argv[])
+{
+ for (int i = 0; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "-multi") == 0 || strcmp(argv[i], "-multidir") == 0)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
int gmx_mdrun(int argc, char *argv[])
{
const char *desc[] = {
int rc;
char **multidir = NULL;
-
cr = init_commrec();
PCA_Flags = (PCA_CAN_SET_DEFFNM | (MASTER(cr) ? 0 : PCA_QUIET));
+ // With -multi or -multidir, the file names are going to get processed
+ // further (or the working directory changed), so we can't check for their
+ // existence during parsing. It isn't useful to do any completion based on
+ // file system contents, either.
+ if (is_multisim_option_set(argc, argv))
+ {
+ PCA_Flags |= PCA_DISABLE_INPUT_FILE_CHECKING;
+ }
/* Comment this in to do fexist calls only on master
* works not with rerun or tables at the moment