{
public:
//! Creates the implementation class and the low-level context.
- Impl(File *file, HelpOutputFormat format, const HelpLinks *links)
- : writerContext_(file, format, links), moduleDisplayName_("gmx"),
+ Impl(TextOutputStream *stream, HelpOutputFormat format,
+ const HelpLinks *links)
+ : writerContext_(stream, format, links), moduleDisplayName_("gmx"),
completionWriter_(NULL), bHidden_(false)
{
}
};
CommandLineHelpContext::CommandLineHelpContext(
- File *file, HelpOutputFormat format, const HelpLinks *links,
- const std::string &programName)
- : impl_(new Impl(file, format, links))
+ TextOutputStream *stream, HelpOutputFormat format,
+ const HelpLinks *links, const std::string &programName)
+ : impl_(new Impl(stream, format, links))
{
impl_->writerContext_.setReplacement("[PROGRAM]", programName);
}
CommandLineHelpContext::CommandLineHelpContext(
ShellCompletionWriter *writer)
- : impl_(new Impl(writer->outputFile(), eHelpOutputFormat_Other, NULL))
+ : impl_(new Impl(&writer->outputStream(), eHelpOutputFormat_Other, NULL))
{
impl_->completionWriter_ = writer;
}
*
* Wraps the constructor of HelpWriterContext.
*/
- CommandLineHelpContext(File *file, HelpOutputFormat format,
- const HelpLinks *links,
+ CommandLineHelpContext(TextOutputStream *stream,
+ HelpOutputFormat format, const HelpLinks *links,
const std::string &programName);
//! Creates a context for a particular HelpWriterContext.
explicit CommandLineHelpContext(const HelpWriterContext &writerContext);
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/programcontext.h"
#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
#include "shellcompletions.h"
context.writeTextBlock(
"Usage: [PROGRAM] [<options>] <command> [<args>][PAR]"
"Available commands:");
- File &file = context.outputFile();
+ TextWriter &file = context.outputFile();
TextTableFormatter formatter;
formatter.addColumn(NULL, maxNameLength + 1, false);
formatter.addColumn(NULL, 72 - maxNameLength, true);
FileOutputRedirectorInterface *outputRedirector_;
const std::string &binaryName_;
HelpLinks links_;
- boost::scoped_ptr<File> indexFile_;
- boost::scoped_ptr<File> manPagesFile_;
+ boost::scoped_ptr<TextWriter> indexFile_;
+ boost::scoped_ptr<TextWriter> manPagesFile_;
};
HelpExportReStructuredText::HelpExportReStructuredText(
void HelpExportReStructuredText::startModuleExport()
{
indexFile_.reset(
- new File(outputRedirector_->openFileForWriting("fragments/byname.rst")));
+ new TextWriter(
+ outputRedirector_->openTextOutputFile("fragments/byname.rst")));
indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
binaryName_.c_str(), binaryName_.c_str(),
RootHelpText::title));
manPagesFile_.reset(
- new File(outputRedirector_->openFileForWriting("conf-man.py")));
+ new TextWriter(
+ outputRedirector_->openTextOutputFile("conf-man.py")));
manPagesFile_->writeLine("man_pages = [");
}
{
// TODO: Ideally, the file would only be touched if it really changes.
// This would make Sphinx reruns much faster.
- File file(outputRedirector_->openFileForWriting("onlinehelp/" + tag + ".rst"));
- file.writeLine(formatString(".. _%s:", displayName.c_str()));
+ TextOutputStreamPointer file
+ = outputRedirector_->openTextOutputFile("onlinehelp/" + tag + ".rst");
+ TextWriter writer(file);
+ writer.writeLine(formatString(".. _%s:", displayName.c_str()));
if (0 == displayName.compare(binaryName_ + " mdrun"))
{
// Make an extra link target for the convenience of
// MPI-specific documentation
- file.writeLine(".. _mdrun_mpi:");
+ writer.writeLine(".. _mdrun_mpi:");
}
- file.writeLine();
+ writer.writeLine();
- CommandLineHelpContext context(&file, eHelpOutputFormat_Rst, &links_, binaryName_);
+ CommandLineHelpContext context(file.get(), eHelpOutputFormat_Rst, &links_, binaryName_);
context.enterSubSection(displayName);
context.setModuleDisplayName(displayName);
module.writeHelp(context);
- file.writeLine();
- file.writeLine(".. only:: man");
- file.writeLine();
- file.writeLine(" See also");
- file.writeLine(" --------");
- file.writeLine();
- file.writeLine(formatString(" :manpage:`%s(1)`", binaryName_.c_str()));
- file.writeLine();
- file.writeLine(" More information about |Gromacs| is available at <http://www.gromacs.org/>.");
- file.close();
+ writer.writeLine();
+ writer.writeLine(".. only:: man");
+ writer.writeLine();
+ writer.writeLine(" See also");
+ writer.writeLine(" --------");
+ writer.writeLine();
+ writer.writeLine(formatString(" :manpage:`%s(1)`", binaryName_.c_str()));
+ writer.writeLine();
+ writer.writeLine(" More information about |Gromacs| is available at <http://www.gromacs.org/>.");
+ file->close();
indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
displayName.c_str(), tag.c_str(),
void HelpExportReStructuredText::startModuleGroupExport()
{
indexFile_.reset(
- new File(outputRedirector_->openFileForWriting("fragments/bytopic.rst")));
+ new TextWriter(
+ outputRedirector_->openTextOutputFile("fragments/bytopic.rst")));
manPagesFile_.reset(
- new File(outputRedirector_->openFileForWriting("fragments/bytopic-man.rst")));
+ new TextWriter(
+ outputRedirector_->openTextOutputFile("fragments/bytopic-man.rst")));
}
void HelpExportReStructuredText::exportModuleGroup(
void HelpExportReStructuredText::exportTopic(const HelpTopicInterface &topic)
{
- const std::string path("onlinehelp/" + std::string(topic.name()) + ".rst");
- File file(outputRedirector_->openFileForWriting(path));
- CommandLineHelpContext context(&file, eHelpOutputFormat_Rst, &links_,
- binaryName_);
- HelpManager manager(topic, context.writerContext());
+ const std::string path("onlinehelp/" + std::string(topic.name()) + ".rst");
+ TextOutputStreamPointer file(outputRedirector_->openTextOutputFile(path));
+ CommandLineHelpContext context(file.get(), eHelpOutputFormat_Rst, &links_,
+ binaryName_);
+ HelpManager manager(topic, context.writerContext());
manager.writeCurrentTopic();
+ file->close();
}
/********************************************************************
return 0;
}
- File &outputFile = impl_->outputRedirector_->standardOutput();
+ TextOutputStream &outputFile = impl_->outputRedirector_->standardOutput();
HelpLinks links(eHelpOutputFormat_Console);
initProgramLinks(&links, *impl_);
CommandLineHelpContext context(&outputFile, eHelpOutputFormat_Console, &links,
#include "gromacs/options/timeunitmanager.h"
#include "gromacs/utility/arrayref.h"
#include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
#include "shellcompletions.h"
{
currentLength_ = std::strlen(name) + 1;
indent_ = std::min(currentLength_, 13);
- File &file = context_.outputFile();
+ TextWriter &file = context_.outputFile();
switch (context_.outputFormat())
{
case eHelpOutputFormat_Console:
void SynopsisFormatter::finish()
{
- File &file = context_.outputFile();
+ TextWriter &file = context_.outputFile();
file.writeLine();
file.writeLine();
}
}
fullOptionText.append(bFormatted_ ? "`]" : "]");
- File &file = context_.outputFile();
+ TextWriter &file = context_.outputFile();
currentLength_ += totalLength;
if (currentLength_ >= lineLength_)
{
*
* Copyright (c) 1991-2000, University of Groningen, The Netherlands.
* Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015, 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.
#include "gromacs/options/optionsvisitor.h"
#include "gromacs/utility/arrayref.h"
#include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
namespace gmx
{
class OptionCompletionWriter : public OptionsVisitor
{
public:
- explicit OptionCompletionWriter(File *out) : out_(*out) {}
+ explicit OptionCompletionWriter(TextWriter *out) : out_(*out) {}
virtual void visitSubSection(const Options §ion)
{
void writeOptionCompletion(const OptionInfo &option,
const std::string &completion);
- File &out_;
+ TextWriter &out_;
};
void OptionCompletionWriter::visitOption(const OptionInfo &option)
return formatString("_%s_%s_compl", binaryName_.c_str(), moduleName);
}
- std::string binaryName_;
- boost::scoped_ptr<File> file_;
+ std::string binaryName_;
+ boost::scoped_ptr<TextWriter> file_;
};
ShellCompletionWriter::ShellCompletionWriter(const std::string &binaryName,
{
}
-File *ShellCompletionWriter::outputFile()
+TextOutputStream &ShellCompletionWriter::outputStream()
{
- return impl_->file_.get();
+ return impl_->file_->stream();
}
void ShellCompletionWriter::startCompletions()
{
- impl_->file_.reset(new File(impl_->binaryName_ + "-completion.bash", "w"));
+ impl_->file_.reset(new TextWriter(impl_->binaryName_ + "-completion.bash"));
impl_->file_->writeLine("shopt -s extglob");
}
const char *moduleName,
const Options &options)
{
- File &out = *impl_->file_;
+ TextWriter &out = *impl_->file_;
out.writeLine(formatString("%s() {", impl_->completionFunctionName(moduleName).c_str()));
out.writeLine("local IFS=$'\\n'");
out.writeLine("local c=${COMP_WORDS[COMP_CWORD]}");
*
* Copyright (c) 1991-2000, University of Groningen, The Netherlands.
* Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015, 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.
{
class CommandLineHelpContext;
-class File;
class Options;
+class TextOutputStream;
//! \cond internal
//! \addtogroup module_commandline
ShellCompletionFormat format);
~ShellCompletionWriter();
- File *outputFile();
+ TextOutputStream &outputStream();
void startCompletions();
void writeModuleCompletions(const char *moduleName,
};
CommandLine args(cmdline);
initManager(args, "test");
- redirectManagerOutput();
addModule("module", "First module");
addModule("other", "Second module");
addHelpTopic("topic", "Test topic");
int rc = 0;
ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
ASSERT_EQ(0, rc);
- checkRedirectedOutputFiles();
+ checkRedirectedOutput();
}
TEST_F(CommandLineHelpModuleTest, PrintsHelpOnTopic)
};
CommandLine args(cmdline);
initManager(args, "test");
- redirectManagerOutput();
addModule("module", "First module");
MockHelpTopic &topic = addHelpTopic("topic", "Test topic");
topic.addSubTopic("sub1", "Subtopic 1", "");
int rc = 0;
ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
ASSERT_EQ(0, rc);
- checkRedirectedOutputFiles();
+ checkRedirectedOutput();
}
/*! \brief
};
// TODO: Find a more elegant solution, or get rid of the links.dat altogether.
gmx::File::writeFileFromString("links.dat", "");
- CommandLine args(cmdline);
+ CommandLine args(cmdline);
initManager(args, "test");
- redirectManagerOutput();
MockOptionsModule &mod1 = addOptionsModule("module", "First module");
MockOptionsModule &mod2 = addOptionsModule("other", "Second module");
{
int rc = 0;
ASSERT_NO_THROW_GMX(rc = manager().run(args.argc(), args.argv()));
ASSERT_EQ(0, rc);
- checkRedirectedOutputFiles();
+ checkRedirectedOutput();
std::remove("links.dat");
}
#include "gromacs/options/basicoptions.h"
#include "gromacs/options/filenameoption.h"
#include "gromacs/options/options.h"
-#include "gromacs/utility/file.h"
+#include "gromacs/utility/stringstream.h"
#include "testutils/stringtest.h"
-#include "testutils/testfilemanager.h"
namespace
{
void checkHelp(gmx::CommandLineHelpWriter *writer);
- gmx::test::TestFileManager tempFiles_;
bool bHidden_;
};
void CommandLineHelpWriterTest::checkHelp(gmx::CommandLineHelpWriter *writer)
{
- std::string filename = tempFiles_.getTemporaryFilePath("helptext.txt");
- gmx::File file(filename, "w");
- gmx::CommandLineHelpContext context(&file, gmx::eHelpOutputFormat_Console,
+ gmx::StringOutputStream stream;
+ gmx::CommandLineHelpContext context(&stream, gmx::eHelpOutputFormat_Console,
NULL, "test");
context.setShowHidden(bHidden_);
writer->writeHelp(context);
- file.close();
+ stream.close();
- checkFileContents(filename, "HelpText");
+ checkText(stream.toString(), "HelpText");
}
#include "gromacs/onlinehelp/tests/mock_helptopic.h"
#include "testutils/cmdlinetest.h"
-#include "testutils/testfilemanager.h"
+#include "testutils/testfileredirector.h"
namespace gmx
{
class CommandLineModuleManagerTestBase::Impl
{
public:
+ TestFileOutputRedirector redirector_;
boost::scoped_ptr<CommandLineProgramContext> programContext_;
boost::scoped_ptr<CommandLineModuleManager> manager_;
- TestFileManager fileManager_;
};
CommandLineModuleManagerTestBase::CommandLineModuleManagerTestBase()
impl_->manager_.reset(new gmx::CommandLineModuleManager(
realBinaryName, impl_->programContext_.get()));
impl_->manager_->setQuiet(true);
+ impl_->manager_->setOutputRedirector(&impl_->redirector_);
}
MockModule &
return *impl_->manager_;
}
-void CommandLineModuleManagerTestBase::redirectManagerOutput()
+void CommandLineModuleManagerTestBase::checkRedirectedOutput()
{
- impl_->manager_->setOutputRedirector(&initOutputRedirector(&impl_->fileManager_));
+ impl_->redirector_.checkRedirectedFiles(&checker());
}
} // namespace test
class CommandLine;
class MockHelpTopic;
+class TestFileOutputRedirector;
/*! \internal \brief
* Mock implementation of gmx::CommandLineModuleInterface.
CommandLineModuleManager &manager();
/*! \brief
- * Redirects all manager output to files.
+ * Checks all output from the manager using reference data.
*
- * Can be used to silence tests that would otherwise print out
- * something, and/or checkRedirectedFileContents() from the base class
- * can be used to check the output.
+ * Both output to `stdout` and to files is checked.
*
* The manager is put into quiet mode by default, so the manager will
* only print out information if, e.g., help is explicitly requested.
*/
- void redirectManagerOutput();
+ void checkRedirectedOutput();
private:
class Impl;
#include "gromacs/onlinehelp/helpformat.h"
#include "gromacs/onlinehelp/helpwritercontext.h"
#include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
namespace gmx
{
{
return false;
}
- File &file = context.outputFile();
+ TextWriter &file = context.outputFile();
TextTableFormatter formatter;
formatter.addColumn(NULL, maxNameLength + 1, false);
formatter.addColumn(NULL, 72 - maxNameLength, true);
#include "gromacs/onlinehelp/helpformat.h"
#include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/programcontext.h"
#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
#include "rstparser.h"
{
public:
//! Initializes the state with the given parameters.
- SharedState(File *file, HelpOutputFormat format,
+ SharedState(TextOutputStream *stream, HelpOutputFormat format,
const HelpLinks *links)
- : file_(*file), format_(format), links_(links)
+ : file_(stream), format_(format), links_(links)
{
}
return *consoleOptionsFormatter_;
}
- //! Output file to which the help is written.
- File &file_;
+ //! Writer for writing the help.
+ TextWriter file_;
//! Output format for the help output.
HelpOutputFormat format_;
//! Links to use.
* HelpWriterContext
*/
-HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format)
- : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, NULL)), 0))
+HelpWriterContext::HelpWriterContext(TextOutputStream *stream, HelpOutputFormat format)
+ : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(stream, format, NULL)), 0))
{
}
-HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format,
+HelpWriterContext::HelpWriterContext(TextOutputStream *stream, HelpOutputFormat format,
const HelpLinks *links)
- : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, links)), 0))
+ : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(stream, format, links)), 0))
{
if (links != NULL)
{
return impl_->state_->format_;
}
-File &HelpWriterContext::outputFile() const
+TextWriter &HelpWriterContext::outputFile() const
{
- return impl_->state_->file_;
+ // TODO: Consider how to deal with the const/non-const difference better.
+ return const_cast<TextWriter &>(impl_->state_->file_);
}
void HelpWriterContext::enterSubSection(const std::string &title)
{
return;
}
- File &file = outputFile();
+ TextWriter &file = outputFile();
switch (outputFormat())
{
case eHelpOutputFormat_Console:
const std::string &info,
const std::string &description) const
{
- File &file = outputFile();
+ TextWriter &file = outputFile();
switch (outputFormat())
{
case eHelpOutputFormat_Console:
namespace gmx
{
-class File;
class TextLineWrapperSettings;
+class TextOutputStream;
+class TextWriter;
/*! \cond libapi */
//! \libinternal Output format for help writing.
{
public:
/*! \brief
- * Initializes a context with the given output file and format.
+ * Initializes a context with the given output stream and format.
*
* \throws std::bad_alloc if out of memory.
*/
- HelpWriterContext(File *file, HelpOutputFormat format);
+ HelpWriterContext(TextOutputStream *stream, HelpOutputFormat format);
/*! \brief
- * Initializes a context with the given output file, format and links.
+ * Initializes a context with the given output stream, format and links.
*
* \throws std::bad_alloc if out of memory.
*
* is destructed. The caller is responsible for ensuring that the
* links object remains valid long enough.
*/
- HelpWriterContext(File *file, HelpOutputFormat format,
+ HelpWriterContext(TextOutputStream *stream, HelpOutputFormat format,
const HelpLinks *links);
//! Creates a copy of the context.
HelpWriterContext(const HelpWriterContext &other);
*/
HelpOutputFormat outputFormat() const;
/*! \brief
- * Returns the raw output file for writing the help.
+ * Returns the raw writer for writing the help.
*
- * Using this file directly should be avoided, as it requires one to
+ * Using this writer directly should be avoided, as it requires one to
* have different code for each output format.
* Using other methods in this class should be preferred.
*
* Does not throw.
*/
- File &outputFile() const;
+ TextWriter &outputFile() const;
/*! \brief
* Creates a subsection in the output help.
#include "gromacs/onlinehelp/helptopic.h"
#include "gromacs/onlinehelp/helpwritercontext.h"
#include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
+#include "gromacs/utility/stringstream.h"
#include "gromacs/onlinehelp/tests/mock_helptopic.h"
#include "testutils/stringtest.h"
#include "testutils/testasserts.h"
-#include "testutils/testfilemanager.h"
namespace
{
public:
HelpTestBase();
- gmx::test::TestFileManager tempFiles_;
MockHelpTopic rootTopic_;
- std::string filename_;
- gmx::File helpFile_;
+ gmx::StringOutputStream helpFile_;
gmx::HelpWriterContext context_;
gmx::HelpManager manager_;
};
HelpTestBase::HelpTestBase()
: rootTopic_("", NULL, "Root topic text"),
- filename_(tempFiles_.getTemporaryFilePath("helptext.txt")),
- helpFile_(filename_, "w"),
context_(&helpFile_, gmx::eHelpOutputFormat_Console),
manager_(rootTopic_, context_)
{
ASSERT_NO_THROW_GMX(manager_.writeCurrentTopic());
helpFile_.close();
- checkFileContents(filename_, "HelpText");
+ checkText(helpFile_.toString(), "HelpText");
}
TEST_F(HelpTopicFormattingTest, FormatsSimpleTopic)
#include "gromacs/topology/topology.h"
#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/file.h"
+#include "gromacs/utility/filestream.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/smalloc.h"
#include "gromacs/utility/stringutil.h"
{
sc->rootHelp = createSelectionHelpTopic();
}
- HelpWriterContext context(&File::standardError(),
+ HelpWriterContext context(&TextOutputFile::standardError(),
eHelpOutputFormat_Console);
HelpManager manager(*sc->rootHelp, context);
try
#include "gromacs/onlinehelp/helptopic.h"
#include "gromacs/onlinehelp/helpwritercontext.h"
#include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
#include "selmethod.h"
#include "symrec.h"
e_selvalue_t type,
bool bModifiers) const
{
- File &file = context.outputFile();
+ TextWriter &file = context.outputFile();
MethodList::const_iterator iter;
for (iter = methods_.begin(); iter != methods_.end(); ++iter)
{
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014,2015, 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.
* containers to simplify implementation of other code. Contents of the module
* are discussed in more details under the different headings below.
* Some of the code in installed headers in the module is intended for use
- * directly from code outside the Gromacs library, but a significant portion is
- * exposed only because other public headers depend on it.
+ * directly from code outside the \Gromacs library, but a significant portion
+ * is exposed only because other public headers depend on it.
*
* Since this module implements error handling, it should be at the lowest
* level: it should not depend on other modules. Any functionality needed by
* the error handling code should also be kept in this module.
*
- * <H3>Error Handling</H3>
+ * <H3>Error handling</H3>
*
* Exception classes used in the library are declared in the exceptions.h header
- * file. Most Gromacs-specific exceptions derive from gmx::GromacsException.
+ * file. Most \Gromacs-specific exceptions derive from gmx::GromacsException.
*
* This header also declares a ::GMX_THROW macro that should be used for
* throwing exceptions. ::GMX_THROW_WITH_ERRNO is also provided for reporting
* \endif
*
*
- * <H3>Basic %File Handling</H3>
+ * \if libapi
+ *
+ * <H3>Basic file handling and streams</H3>
*
- * The header file.h declares a gmx::File class for basic I/O support.
+ * The header textstream.h declares interfaces for simple text format streams.
+ * Headers filestream.h and stringstream.h provide implementations for these
+ * streams for reading/writing files and for writing to in-memory strings.
*
- * The header path.h declares helpers for manipulating paths and for managing
- * directories.
+ * The header fileredirector.h provides interfaces for redirecting file input
+ * and/or output to alternative streams, for use in testing, as well as default
+ * implementations for these interfaces that just use the file system.
*
- * The fate of these headers depends on what is decided in Redmine issue #950.
+ * The header textwriter.h provides gmx::TextWriter for more formatting support
+ * when writing to a text stream.
*
+ * The header path.h declares helpers for manipulating paths as strings and for
+ * managing directories and files.
+ * The fate of this header depends on what is decided in Redmine issue #950.
+ *
+ * \endif
*
- * <H3>Implementation Helpers</H3>
+ * <H3>Implementation helpers</H3>
*
* The header basedefinitions.h contains common definitions and macros used
* throughout \Gromacs. It includes fixed-width integer types (`gmx_int64_t`
* safety when using bit flag fields.
*
*
- * <H3>Other Functionality</H3>
+ * <H3>Other functionality</H3>
*
* The header init.h declares gmx::init() and gmx::finalize() for initializing
* and deinitializing the \Gromacs library.
open(filename, mode);
}
-File::File(const FileInitializer &initializer)
- : impl_(new Impl(NULL, true))
-{
- open(initializer.filename_, initializer.mode_);
-}
-
File::File(FILE *fp, bool bClose)
: impl_(new Impl(fp, bClose))
{
return stdinObject;
}
-// static
-File &File::standardOutput()
-{
- static File stdoutObject(stdout, false);
- return stdoutObject;
-}
-
-// static
-File &File::standardError()
-{
- static File stderrObject(stderr, false);
- return stderrObject;
-}
-
// static
std::string File::readToString(const char *filename)
{
namespace gmx
{
-class File;
-
-/*! \brief
- * Parameters for creating a File object.
- *
- * This class (mostly) replaces the ability to return a File object from a
- * function (since File is not copyable): returning a FileInitializer instead
- * allows the caller to construct the File object.
- *
- * \inpublicapi
- * \ingroup module_utility
- */
-class FileInitializer
-{
- public:
- /*! \brief
- * Creates the initializer with given parameters.
- *
- * The passed strings must remain valid until the initializer is used
- * to construct a File object.
- */
- FileInitializer(const char *filename, const char *mode)
- : filename_(filename), mode_(mode)
- {
- }
-
- private:
- const char *filename_;
- const char *mode_;
-
- /*! \brief
- * Needed to allow access to the parameters without otherwise
- * unnecessary accessors.
- */
- friend class File;
-};
-
/*! \brief
* Basic file object.
*
File(const char *filename, const char *mode);
//! \copydoc File(const char *, const char *)
File(const std::string &filename, const char *mode);
- /*! \brief
- * Creates a file object and opens a file.
- *
- * \param[in] initializer Parameters to open the file.
- * \throws std::bad_alloc if out of memory.
- * \throws FileIOError on any I/O error.
- */
- File(const FileInitializer &initializer);
/*! \brief
* Destroys the file object.
*
* \throws std::bad_alloc if out of memory (only on first call).
*/
static File &standardInput();
- /*! \brief
- * Returns a File object for accessing stdout.
- *
- * \throws std::bad_alloc if out of memory (only on first call).
- */
- static File &standardOutput();
- /*! \brief
- * Returns a File object for accessing stderr.
- *
- * \throws std::bad_alloc if out of memory (only on first call).
- */
- static File &standardError();
/*! \brief
* Reads contents of a file to a std::string.
* \param[in] bClose Whether this object should close its file handle.
* \throws std::bad_alloc if out of memory.
*
- * Used internally to implement standardOutput() and standardError().
+ * Used internally to implement standardInput().
*/
File(FILE *fp, bool bClose);
#include "fileredirector.h"
#include "gromacs/utility/file.h"
+#include "gromacs/utility/filestream.h"
namespace gmx
{
class DefaultOutputRedirector : public FileOutputRedirectorInterface
{
public:
- virtual File &standardOutput()
+ virtual TextOutputStream &standardOutput()
{
- return File::standardOutput();
+ return TextOutputFile::standardOutput();
}
- virtual FileInitializer openFileForWriting(const char *filename)
+ virtual TextOutputStreamPointer openTextOutputFile(const char *filename)
{
- return FileInitializer(filename, "w");
+ return TextOutputStreamPointer(new TextOutputFile(filename));
}
};
#include <string>
-#include "gromacs/utility/file.h"
+#include "gromacs/utility/textstream.h"
namespace gmx
{
*
* The calling code should take in this interface and use the methods in it
* all file system operations that need to support this redirection.
- * By default, the code can then use defaultFileInputRedirector() in case no
- * redirection is needed.
*
* This allows tests to override the file existence checks without actually
- * using the file system.
+ * using the file system. See FileOutputRedirectorInterface for notes on
+ * a typical usage pattern.
*
* With some further refactoring of the File class, this could also support
* redirecting input files from in-memory buffers as well, but for now the
/*! \libinternal \brief
* Allows capturing `stdout` and file output from code that supports it.
*
- * The calling code should take in this interface and use the File objects
+ * The calling code should take in this interface and use the stream objects
* it returns for all output that needs to support this redirection.
- * By default, the code can then use defaultFileOutputRedirector() in case no
- * redirection is needed.
*
- * This allows tests to capture the file output without duplicating the
- * knowledge of which files are actually produced. With some further
- * refactoring of the File class, this could support capturing the output into
- * in-memory buffers as well, but for now the current capabilities are
- * sufficient.
+ * Currently, the (nearly) only purpose for this interface is for unit tests to
+ * capture the file output without duplicating the knowledge of which files are
+ * actually produced. The tests can also replace actual files with in-memory
+ * streams (e.g., a StringOutputStream), and test the output without actually
+ * accessing the file system and managing actual files.
+ *
+ * As the main user for non-default implementation of this interface is tests,
+ * code using this interface generally uses a pattern where the redirector is
+ * initialized to defaultFileOutputRedirector(), and a separate setter is
+ * provided for tests to change the default. This allows code outside the
+ * tests (and outside the code actually calling the redirector) to be written
+ * as if this interface did not exist (i.e., they do not need to pass the
+ * default instance).
+ *
+ * Also, the interface only supports text files, but can be generalized if/when
+ * there is a need for binary streams (see also TextOutputStream).
*
* \inlibraryapi
* \ingroup module_utility
virtual ~FileOutputRedirectorInterface();
/*! \brief
- * Returns a File object to use for `stdout` output.
+ * Returns a stream to use for `stdout` output.
*/
- virtual File &standardOutput() = 0;
+ virtual TextOutputStream &standardOutput() = 0;
/*! \brief
- * Returns a File object to use for output to a given file.
+ * Returns a stream to use for output to a file at a given path.
*
* \param[in] filename Requested file name.
*/
- virtual FileInitializer openFileForWriting(const char *filename) = 0;
+ virtual TextOutputStreamPointer openTextOutputFile(const char *filename) = 0;
- //! Convenience method to open a file using an std::string path.
- FileInitializer openFileForWriting(const std::string &filename)
+ //! Convenience method to open a stream using an std::string path.
+ TextOutputStreamPointer openTextOutputFile(const std::string &filename)
{
- return openFileForWriting(filename.c_str());
+ return openTextOutputFile(filename.c_str());
}
};
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, 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
+ * Implements classes from filestream.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_utility
+ */
+#include "gmxpre.h"
+
+#include "filestream.h"
+
+#include <cerrno>
+#include <cstdio>
+
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+
+namespace internal
+{
+
+/********************************************************************
+ * FileStreamImpl
+ */
+
+class FileStreamImpl
+{
+ public:
+ explicit FileStreamImpl(FILE *fp)
+ : fp_(fp), bClose_(false)
+ {
+ }
+ FileStreamImpl(const char *filename, const char *mode)
+ : fp_(NULL), bClose_(true)
+ {
+ fp_ = std::fopen(filename, mode);
+ if (fp_ == NULL)
+ {
+ GMX_THROW_WITH_ERRNO(
+ FileIOError(formatString("Could not open file '%s'", filename)),
+ "fopen", errno);
+ }
+ }
+ ~FileStreamImpl()
+ {
+ if (fp_ != NULL && bClose_)
+ {
+ if (std::fclose(fp_) != 0)
+ {
+ // TODO: Log the error somewhere
+ }
+ }
+ }
+
+ FILE *handle()
+ {
+ GMX_RELEASE_ASSERT(fp_ != NULL,
+ "Attempted to access a file object that is not open");
+ return fp_;
+ }
+
+ void close()
+ {
+ GMX_RELEASE_ASSERT(fp_ != NULL,
+ "Attempted to close a file object that is not open");
+ GMX_RELEASE_ASSERT(bClose_,
+ "Attempted to close a file object that should not be");
+ const bool bOk = (std::fclose(fp_) == 0);
+ fp_ = NULL;
+ if (!bOk)
+ {
+ GMX_THROW_WITH_ERRNO(
+ FileIOError("Error while closing file"), "fclose", errno);
+ }
+ }
+
+ private:
+ //! File handle for this object (NULL if the stream has been closed).
+ FILE *fp_;
+ //! Whether \p fp_ should be closed by this object.
+ bool bClose_;
+};
+
+} // namespace internal
+
+using internal::FileStreamImpl;
+
+/********************************************************************
+ * TextOutputFile
+ */
+
+TextOutputFile::TextOutputFile(const std::string &filename)
+ : impl_(new FileStreamImpl(filename.c_str(), "w"))
+{
+}
+
+TextOutputFile::TextOutputFile(FILE *fp)
+ : impl_(new FileStreamImpl(fp))
+{
+}
+
+TextOutputFile::~TextOutputFile()
+{
+}
+
+void TextOutputFile::write(const char *str)
+{
+ if (std::fprintf(impl_->handle(), "%s", str) < 0)
+ {
+ GMX_THROW_WITH_ERRNO(FileIOError("Writing to file failed"),
+ "fprintf", errno);
+ }
+}
+
+void TextOutputFile::close()
+{
+ impl_->close();
+}
+
+// static
+TextOutputFile &TextOutputFile::standardOutput()
+{
+ static TextOutputFile stdoutObject(stdout);
+ return stdoutObject;
+}
+
+// static
+TextOutputFile &TextOutputFile::standardError()
+{
+ static TextOutputFile stderrObject(stderr);
+ return stderrObject;
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, 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.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares implementations for textstream.h interfaces for file input/output.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_FILESTREAM_H
+#define GMX_UTILITY_FILESTREAM_H
+
+#include <cstdio>
+
+#include <string>
+
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+namespace internal
+{
+class FileStreamImpl;
+}
+
+/*! \libinternal \brief
+ * Text output stream implementation for writing to a file.
+ *
+ * Implementations for the TextOutputStream methods throw FileIOError on any
+ * I/O error.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextOutputFile : public TextOutputStream
+{
+ public:
+ /*! \brief
+ * Opens a text file as a stream.
+ *
+ * \param[in] filename Path to the file to open.
+ * \throws std::bad_alloc if out of memory.
+ * \throws FileIOError on any I/O error.
+ */
+ explicit TextOutputFile(const std::string &filename);
+ /*! \brief
+ * Initializes file object from an existing file handle.
+ *
+ * \param[in] fp File handle to use.
+ * \throws std::bad_alloc if out of memory.
+ *
+ * The caller is responsible of closing the file; close() does nothing
+ * for an object constructed this way.
+ */
+ explicit TextOutputFile(FILE *fp);
+ virtual ~TextOutputFile();
+
+ // From TextOutputStream
+ virtual void write(const char *text);
+ virtual void close();
+
+ /*! \brief
+ * Returns a stream for accessing `stdout`.
+ *
+ * \throws std::bad_alloc if out of memory (only on first call).
+ */
+ static TextOutputFile &standardOutput();
+ /*! \brief
+ * Returns a stream for accessing `stderr`.
+ *
+ * \throws std::bad_alloc if out of memory (only on first call).
+ */
+ static TextOutputFile &standardError();
+
+ private:
+ PrivateImplPointer<internal::FileStreamImpl> impl_;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, 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.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares no_delete deleter for boost::shared_ptr.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_NODELETE_H
+#define GMX_UTILITY_NODELETE_H
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Deleter for boost::shared_ptr that does nothing.
+ *
+ * This is useful for cases where a class needs to keep a reference to another
+ * class, and optionally also manage the lifetime of that other class.
+ * The simplest construct (that does not force all callers to use heap
+ * allocation and boost::shared_ptr for the referenced class) is to use a
+ * single boost::shared_ptr to hold that reference, and use no_delete as the
+ * deleter if the lifetime is managed externally.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+template <class T>
+struct no_delete
+{
+ //! Deleter that does nothing.
+ void operator()(T *) {}
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, 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
+ * Implements classes from stringstream.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_utility
+ */
+#include "gmxpre.h"
+
+#include "stringstream.h"
+
+#include <string>
+
+namespace gmx
+{
+
+void StringOutputStream::write(const char *str)
+{
+ str_.append(str);
+}
+
+void StringOutputStream::close()
+{
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, 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.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares implementations for textstream.h interfaces for input/output to
+ * in-memory strings.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_STRINGSTREAM_H
+#define GMX_UTILITY_STRINGSTREAM_H
+
+#include <string>
+
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Text output stream implementation for writing to an in-memory string.
+ *
+ * Implementations for the TextOutputStream methods throw std::bad_alloc if
+ * reallocation of the string fails.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class StringOutputStream : public TextOutputStream
+{
+ public:
+ //! Returns the text written to the stream so far.
+ const std::string &toString() const { return str_; }
+
+ // From TextOutputStream
+ virtual void write(const char *text);
+ virtual void close();
+
+ private:
+ std::string str_;
+};
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, 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.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares interfaces for simple input/output streams.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_TEXTSTREAM_H
+#define GMX_UTILITY_TEXTSTREAM_H
+
+#include <boost/shared_ptr.hpp>
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Interface for writing text.
+ *
+ * Concrete implementations can write the text to, e.g., a file or an in-memory
+ * string. The main use is to allow unit tests to inject in-memory buffers
+ * instead of reading in files produced by the code under test, but there are
+ * also use cases outside the tests where it is useful to abstract out whether
+ * the output is into a real file or something else.
+ *
+ * To use more advanced formatting than writing plain strings, use TextWriter.
+ *
+ * The current implementation assumes text-only output in several places, but
+ * this interface could possibly be generalized also for binary files.
+ * However, since all binary files currently written by \Gromacs are either
+ * XDR- or TNG-based, they may require a different approach. Also, it is worth
+ * keeping the distinction between text and binary files clear, since Windows
+ * does transparent `LF`-`CRLF` newline translation for text files, so mixing
+ * modes when reading and/or writing the same file can cause subtle issues.
+ *
+ * Both methods in the interface can throw std::bad_alloc or other exceptions
+ * that indicate failures to write to the stream.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextOutputStream
+{
+ public:
+ virtual ~TextOutputStream() {}
+
+ /*! \brief
+ * Writes a given string to the stream.
+ */
+ virtual void write(const char *text) = 0;
+ /*! \brief
+ * Closes the stream.
+ *
+ * It is not allowed to write to a stream after it has been closed.
+ * A method separate from the destructor is provided such that errors
+ * that occur while closing the stream (e.g., when closing the file)
+ * can be handled using exceptions.
+ * The destructor is not allowed to throw, so code that wants to
+ * observe such errors needs to call close() after it has finished
+ * writing to the stream.
+ */
+ virtual void close() = 0;
+};
+
+//! Shorthand for a smart pointer to a TextOutputStream.
+typedef boost::shared_ptr<TextOutputStream> TextOutputStreamPointer;
+
+} // namespace gmx
+
+#endif
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, 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
+ * Implements gmx::TextWriter.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_utility
+ */
+#include "gmxpre.h"
+
+#include "textwriter.h"
+
+#include <cstring>
+
+#include "gromacs/utility/filestream.h"
+#include "gromacs/utility/nodelete.h"
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+class TextWriter::Impl
+{
+ public:
+ explicit Impl(const TextOutputStreamPointer &stream)
+ : stream_(stream)
+ {
+ }
+
+ TextOutputStreamPointer stream_;
+};
+
+TextWriter::TextWriter(const std::string &filename)
+ : impl_(new Impl(TextOutputStreamPointer(new TextOutputFile(filename))))
+{
+}
+
+TextWriter::TextWriter(TextOutputStream *stream)
+ : impl_(new Impl(TextOutputStreamPointer(stream, no_delete<TextOutputStream>())))
+{
+}
+
+TextWriter::TextWriter(const TextOutputStreamPointer &stream)
+ : impl_(new Impl(stream))
+{
+}
+
+TextWriter::~TextWriter()
+{
+}
+
+TextOutputStream &TextWriter::stream()
+{
+ return *impl_->stream_;
+}
+
+void TextWriter::writeString(const char *str)
+{
+ impl_->stream_->write(str);
+}
+
+void TextWriter::writeString(const std::string &str)
+{
+ impl_->stream_->write(str.c_str());
+}
+
+void TextWriter::writeLine(const char *line)
+{
+ const size_t length = std::strlen(line);
+ writeString(line);
+ if (length == 0 || line[length-1] != '\n')
+ {
+ writeLine();
+ }
+}
+
+void TextWriter::writeLine(const std::string &line)
+{
+ writeLine(line.c_str());
+}
+
+void TextWriter::writeLine()
+{
+ writeString("\n");
+}
+
+void TextWriter::close()
+{
+ impl_->stream_->close();
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, 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.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::TextWriter.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_TEXTWRITER_H
+#define GMX_UTILITY_TEXTWRITER_H
+
+#include <string>
+
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Writes text into a TextOutputStream.
+ *
+ * This class provides more formatting and line-oriented writing capabilities
+ * than writing raw strings into the stream.
+ *
+ * All methods that write to the stream can throw any exceptions that the
+ * underlying stream throws.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextWriter
+{
+ public:
+ /*! \brief
+ * Creates a writer that writes to specified file.
+ *
+ * \param[in] filename Path to the file to open.
+ * \throws std::bad_alloc if out of memory.
+ * \throws FileIOError on any I/O error.
+ *
+ * This constructor is provided for convenience for writing directly to
+ * a file, without the need to construct multiple objects.
+ */
+ explicit TextWriter(const std::string &filename);
+ /*! \brief
+ * Creates a writer that writes to specified stream.
+ *
+ * \param[in] stream Stream to write to.
+ * \throws std::bad_alloc if out of memory.
+ *
+ * The caller is responsible of the lifetime of the stream (should
+ * remain in existence as long as the writer exists).
+ *
+ * This constructor is provided for convenience for cases where the
+ * stream is not allocated with `new` and/or not managed by a
+ * boost::shared_ptr (e.g., if the stream is an object on the stack).
+ */
+ explicit TextWriter(TextOutputStream *stream);
+ /*! \brief
+ * Creates a writer that writes to specified stream.
+ *
+ * \param[in] stream Stream to write to.
+ * \throws std::bad_alloc if out of memory.
+ *
+ * The writer keeps a reference to the stream, so the caller can pass
+ * in a temporary if necessary.
+ */
+ explicit TextWriter(const TextOutputStreamPointer &stream);
+ ~TextWriter();
+
+ //! Returns the underlying stream for this writer.
+ TextOutputStream &stream();
+
+ /*! \brief
+ * Writes a string to the stream.
+ *
+ * \param[in] str String to write.
+ */
+ void writeString(const char *str);
+ //! \copydoc writeString(const char *)
+ void writeString(const std::string &str);
+ /*! \brief
+ * Writes a line to the stream.
+ *
+ * \param[in] line Line to write.
+ *
+ * If \p line does not end in a newline, one newline is appended.
+ * Otherwise, works as writeString().
+ */
+ void writeLine(const char *line);
+ //! \copydoc writeLine(const char *)
+ void writeLine(const std::string &line);
+ //! Writes a newline to the stream.
+ void writeLine();
+
+ /*! \brief
+ * Closes the underlying stream.
+ */
+ void close();
+
+ private:
+ class Impl;
+
+ PrivateImplPointer<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014,2015, 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.
#include "gromacs/utility/file.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
#include "testutils/refdata.h"
#include "testutils/testfilemanager.h"
GMX_ASSERT(extension[0] != '.', "Extension should not contain a dot");
std::string fullFilename = impl_->fileManager_.getTemporaryFilePath(
formatString("%d.%s", args->argc(), extension));
- File file(fullFilename, "w");
+ TextWriter file(fullFilename);
ConstArrayRef<const char *>::const_iterator i;
for (i = contents.begin(); i != contents.end(); ++i)
{
#include "stringtest.h"
-#include <algorithm>
#include <string>
-#include <utility>
-#include <vector>
#include <boost/scoped_ptr.hpp>
#include "gromacs/options/basicoptions.h"
#include "gromacs/options/options.h"
-#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/file.h"
-#include "gromacs/utility/fileredirector.h"
#include "testutils/refdata.h"
-#include "testutils/testexceptions.h"
-#include "testutils/testfilemanager.h"
#include "testutils/testoptions.h"
namespace gmx
{
//! Stores the -stdout flag value to print out values instead of checking them.
bool g_bWriteToStdOut = false;
-
-/*! \brief
- * Helper for checking a block of text, e.g., implementing the `-stdout`
- * option.
- *
- * \ingroup module_testutils
- */
-void checkTextImpl(TestReferenceChecker *checker, const std::string &text,
- const char *id)
-{
- if (g_bWriteToStdOut)
- {
- printf("%s:\n", id);
- printf("%s[END]\n", text.c_str());
- }
- else
- {
- checker->checkStringBlock(text, id);
- }
-}
-
}
// TODO: Only add this option to those test binaries that actually need it
}
//! \endcond
-/********************************************************************
- * TestFileOutputRedirector
- */
-
-/*! \internal
- * \brief
- * Implementation of FileOutputRedirectorInterface for tests.
- *
- * This class redirects all output files to temporary files managed by a
- * TestFileManager, and supports checking the contents of these files using the
- * reference data framework.
- *
- * \ingroup module_testutils
- */
-class TestFileOutputRedirector : public FileOutputRedirectorInterface
-{
- public:
- //! Initializes the redirector with the given file manager.
- explicit TestFileOutputRedirector(TestFileManager *fileManager)
- : fileManager_(*fileManager)
- {
- }
-
- virtual File &standardOutput()
- {
- if (!stdoutFile_)
- {
- const std::string path = fileManager_.getTemporaryFilePath("stdout.txt");
- stdoutFile_.reset(new File(path, "w"));
- fileList_.push_back(FileListEntry("<stdout>", path));
- }
- return *stdoutFile_;
- }
- virtual FileInitializer openFileForWriting(const char *filename)
- {
- std::string suffix = filename;
- std::replace(suffix.begin(), suffix.end(), '/', '_');
- const std::string path = fileManager_.getTemporaryFilePath(suffix);
- fileList_.push_back(FileListEntry(filename, path));
- return FileInitializer(fileList_.back().second.c_str(), "w");
- }
-
- /*! \brief
- * Checks the contents of all redirected files.
- */
- void checkRedirectedFiles(TestReferenceChecker *checker)
- {
- if (stdoutFile_)
- {
- stdoutFile_->close();
- stdoutFile_.reset();
- }
- std::vector<FileListEntry>::const_iterator i;
- for (i = fileList_.begin(); i != fileList_.end(); ++i)
- {
- const std::string text = File::readToString(i->second);
- checkTextImpl(checker, text, i->first.c_str());
- }
- }
-
- private:
- typedef std::pair<std::string, std::string> FileListEntry;
-
- TestFileManager &fileManager_;
- boost::scoped_ptr<File> stdoutFile_;
- std::vector<FileListEntry> fileList_;
-};
-
/********************************************************************
* StringTestBase::Impl
*/
public:
TestReferenceData data_;
boost::scoped_ptr<TestReferenceChecker> checker_;
- boost::scoped_ptr<TestFileOutputRedirector> redirector_;
};
/********************************************************************
* StringTestBase
*/
-StringTestBase::StringTestBase()
- : impl_(new Impl)
+// static
+void StringTestBase::checkText(TestReferenceChecker *checker,
+ const std::string &text, const char *id)
{
+ if (g_bWriteToStdOut)
+ {
+ printf("%s:\n", id);
+ printf("%s[END]\n", text.c_str());
+ }
+ else
+ {
+ checker->checkStringBlock(text, id);
+ }
}
-StringTestBase::~StringTestBase()
+StringTestBase::StringTestBase()
+ : impl_(new Impl)
{
}
-FileOutputRedirectorInterface &
-StringTestBase::initOutputRedirector(TestFileManager *fileManager)
+StringTestBase::~StringTestBase()
{
- if (impl_->redirector_)
- {
- GMX_THROW(TestException("initOutputRedirector() called more than once"));
- }
- impl_->redirector_.reset(new TestFileOutputRedirector(fileManager));
- return *impl_->redirector_;
}
TestReferenceChecker &
void
StringTestBase::checkText(const std::string &text, const char *id)
{
- checkTextImpl(&checker(), text, id);
+ checkText(&checker(), text, id);
}
void
checkText(text, id);
}
-void
-StringTestBase::checkRedirectedOutputFiles()
-{
- if (!impl_->redirector_)
- {
- GMX_THROW(TestException("initOutputRedirector() not called"));
- }
- impl_->redirector_->checkRedirectedFiles(&checker());
-}
-
} // namespace test
} // namespace gmx
namespace gmx
{
-class FileOutputRedirectorInterface;
-
namespace test
{
-class TestFileManager;
class TestReferenceChecker;
/*! \libinternal \brief
class StringTestBase : public ::testing::Test
{
public:
- StringTestBase();
- ~StringTestBase();
-
/*! \brief
- * Creates a redirector that directs all output to temporary files.
+ * Checks a block of text.
*
- * \param[in] fileManager File manager to use for temporary files.
- *
- * Can only be called once in a test.
- *
- * \see checkRedirectedOutputFiles()
+ * This static method is provided for code that does not derive from
+ * StringTestBase to use the same functionality, e.g., implementing the
+ * `-stdout` option.
*/
- FileOutputRedirectorInterface &
- initOutputRedirector(TestFileManager *fileManager);
+ static void checkText(TestReferenceChecker *checker,
+ const std::string &text, const char *id);
+
+ StringTestBase();
+ ~StringTestBase();
/*! \brief
* Returns the root checker for this test's reference data.
* single string and calls checkText().
*/
void checkFileContents(const std::string &filename, const char *id);
- /*! \brief
- * Checks contents of all files redirected with initOutputRedirector().
- *
- * Uses the same logic as checkFileContents() to check each file
- * (including `stdout`) that has been created using the redirector
- * returned by initOutputRedirector().
- *
- * initOutputRedirector() must have been called.
- * This method should not be called if the redirector will still be
- * used for further output in the test. Behavior is not designed for
- * checking in the middle of the test, although that could potentially
- * be changed if necessary.
- */
- void checkRedirectedOutputFiles();
private:
class Impl;
#include <set>
#include <string>
+#include <utility>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include "gromacs/utility/stringstream.h"
+
+#include "testutils/stringtest.h"
namespace gmx
{
namespace test
{
+/********************************************************************
+ * TestFileInputRedirector
+ */
+
TestFileInputRedirector::TestFileInputRedirector()
{
}
return existingFiles_.count(filename) > 0;
}
+/********************************************************************
+ * TestFileOutputRedirector::Impl
+ */
+
+class TestFileOutputRedirector::Impl
+{
+ public:
+ typedef boost::shared_ptr<StringOutputStream> StringStreamPointer;
+ typedef std::pair<std::string, StringStreamPointer> FileListEntry;
+
+ StringStreamPointer stdoutStream_;
+ std::vector<FileListEntry> fileList_;
+};
+
+/********************************************************************
+ * TestFileOutputRedirector
+ */
+
+TestFileOutputRedirector::TestFileOutputRedirector()
+ : impl_(new Impl)
+{
+}
+
+TestFileOutputRedirector::~TestFileOutputRedirector()
+{
+}
+
+TextOutputStream &TestFileOutputRedirector::standardOutput()
+{
+ if (!impl_->stdoutStream_)
+ {
+ impl_->stdoutStream_.reset(new StringOutputStream);
+ impl_->fileList_.push_back(Impl::FileListEntry("<stdout>", impl_->stdoutStream_));
+ }
+ return *impl_->stdoutStream_;
+}
+
+TextOutputStreamPointer
+TestFileOutputRedirector::openTextOutputFile(const char *filename)
+{
+ Impl::StringStreamPointer stream(new StringOutputStream);
+ impl_->fileList_.push_back(Impl::FileListEntry(filename, stream));
+ return stream;
+}
+
+void TestFileOutputRedirector::checkRedirectedFiles(TestReferenceChecker *checker)
+{
+ std::vector<Impl::FileListEntry>::const_iterator i;
+ for (i = impl_->fileList_.begin(); i != impl_->fileList_.end(); ++i)
+ {
+ StringTestBase::checkText(checker, i->second->toString(), i->first.c_str());
+ }
+}
+
} // namespace test
} // namespace gmx
namespace test
{
+class TestReferenceChecker;
+
/*! \libinternal \brief
* In-memory implementation for FileInputRedirectorInterface for tests.
*
GMX_DISALLOW_COPY_AND_ASSIGN(TestFileInputRedirector);
};
+/*! \libinternal \brief
+ * In-memory implementation of FileOutputRedirectorInterface for tests.
+ *
+ * This class redirects all output files to in-memory buffers, and supports
+ * checking the contents of these files using the reference data framework.
+ *
+ * \ingroup module_testutils
+ */
+class TestFileOutputRedirector : public FileOutputRedirectorInterface
+{
+ public:
+ TestFileOutputRedirector();
+ virtual ~TestFileOutputRedirector();
+
+ /*! \brief
+ * Checks contents of all redirected files (including stdout).
+ *
+ * This method should not be called if the redirector will still be
+ * used for further output in the test. Behavior is not designed for
+ * checking in the middle of the test, although that could potentially
+ * be changed if necessary.
+ */
+ void checkRedirectedFiles(TestReferenceChecker *checker);
+
+ // From FileOutputRedirectorInterface
+ virtual TextOutputStream &standardOutput();
+ virtual TextOutputStreamPointer openTextOutputFile(const char *filename);
+
+ private:
+ class Impl;
+
+ PrivateImplPointer<Impl> impl_;
+};
+
} // namespace test
} // namespace gmx
#include "gromacs/options/options.h"
#include "gromacs/utility/errorcodes.h"
#include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
+#include "gromacs/utility/filestream.h"
#include "gromacs/utility/futil.h"
#include "gromacs/utility/path.h"
#include "gromacs/utility/programcontext.h"
std::fprintf(stderr,
"\nYou can use the following GROMACS-specific command-line flags\n"
"to control the behavior of the tests:\n\n");
- CommandLineHelpContext context(&File::standardError(),
+ CommandLineHelpContext context(&TextOutputFile::standardError(),
eHelpOutputFormat_Console, NULL, program);
context.setModuleDisplayName(program);
CommandLineHelpWriter(options).writeHelp(context);
* - gmx::test::TestFileInputRedirector (in testfileredirector.h) provides
* functionality for capturing file existence checks in code that uses
* gmx::FileInputRedirectorInterface.
+ * - gmx::test::TestFileOutputRedirector (in testfileredirector.h) provides
+ * functionality for capturing file output (including `stdout`) from code
+ * that uses gmx::FileOutputRedirectorInterface, and checking that output
+ * against reference data.
* - #GMX_TEST_OPTIONS macro provides facilities for adding custom command
* line options for the test binary.
* - testasserts.h provides several custom test assertions for better