diff options
author | Jeroen Vermeulen <jtv@precisiontranslationtools.com> | 2015-04-17 18:57:55 +0300 |
---|---|---|
committer | Jeroen Vermeulen <jtv@precisiontranslationtools.com> | 2015-04-17 18:57:55 +0300 |
commit | d56f317f2ee844beffc7f83e3e6ae2aeb80add61 (patch) | |
tree | 7719c4cc3ea2648c423ec80630342c21f76d5477 /util | |
parent | c5e9a58ae2acb70d05a59722989b526fcce31e9f (diff) |
New helper classes: temp_dir & temp_file.
I'm adding these because boost::filesystem::unique_path introduces
encoding issues: on Windows the path is in wchar_t, breaking use of
those strings in various places! Encoding the strings is just too
much work.
It's still possible that the current temp_file implementation won't
build on Windows (it uses POSIX mkstemp() and close()) but that can
be fixed underneath the API.
Diffstat (limited to 'util')
-rw-r--r-- | util/Jamfile | 2 | ||||
-rw-r--r-- | util/tempfile.hh | 79 | ||||
-rw-r--r-- | util/tempfile_test.cc | 119 |
3 files changed, 199 insertions, 1 deletions
diff --git a/util/Jamfile b/util/Jamfile index 18b20a33a..a82a5e23d 100644 --- a/util/Jamfile +++ b/util/Jamfile @@ -32,5 +32,5 @@ import testing ; run file_piece_test.o kenutil /top//boost_unit_test_framework : : file_piece.cc ; for local t in [ glob *_test.cc : file_piece_test.cc read_compressed_test.cc ] { local name = [ MATCH "(.*)\.cc" : $(t) ] ; - unit-test $(name) : $(t) kenutil /top//boost_unit_test_framework /top//boost_system ; + unit-test $(name) : $(t) kenutil /top//boost_unit_test_framework /top//boost_filesystem /top//boost_system ; } diff --git a/util/tempfile.hh b/util/tempfile.hh new file mode 100644 index 000000000..228238823 --- /dev/null +++ b/util/tempfile.hh @@ -0,0 +1,79 @@ +#ifndef UTIL_TEMPFILE_H +#define UTIL_TEMPFILE_H + +// Utilities for creating temporary files and directories. + +#include <cstdio> +#include <cstdlib> +#include <string> + +#include <boost/filesystem.hpp> +#include <boost/noncopyable.hpp> + +#include "util/exception.hh" + +namespace util +{ + +/** Temporary directory. + * + * Automatically creates, and on destruction deletes, a temporary directory. + * The actual directory in the filesystem will only exist while the temp_dir + * object exists. + * + * If the directory no longer exists by the time the temp_dir is destroyed, + * no cleanup happens. + */ +class temp_dir : boost::noncopyable +{ +public: + temp_dir() + { + char buf[] = "tmpdir.XXXXXX"; + m_path = std::string(mkdtemp(buf)); + } + + ~temp_dir() + { + boost::filesystem::remove_all(path()); + } + + /// Return the temporary directory's full path. + const std::string &path() const { return m_path; } + +private: + std::string m_path; +}; + + +/** Temporary file. + * + * Automatically creates, and on destruction deletes, a temporary file. + */ +class temp_file : boost::noncopyable +{ +public: + temp_file() + { + char buf[] = "tmp.XXXXXX"; + const int fd = mkstemp(buf); + if (fd == -1) throw ErrnoException(); + close(fd); + m_path = buf; + } + + ~temp_file() + { + boost::filesystem::remove(path()); + } + + /// Return the temporary file's full path. + const std::string &path() const { return m_path; } + +private: + std::string m_path; +}; + +} // namespace util + +#endif diff --git a/util/tempfile_test.cc b/util/tempfile_test.cc new file mode 100644 index 000000000..49736fe0c --- /dev/null +++ b/util/tempfile_test.cc @@ -0,0 +1,119 @@ +#include "util/tempfile.hh" + +#include <fstream> + +#include <boost/filesystem.hpp> + +#define BOOST_TEST_MODULE TempFileTest +#include <boost/test/unit_test.hpp> + +namespace util +{ +namespace +{ + +BOOST_AUTO_TEST_CASE(temp_dir_has_path) +{ + BOOST_CHECK(temp_dir().path().size() > 0); +} + +BOOST_AUTO_TEST_CASE(temp_dir_creates_temp_directory) +{ + const temp_dir t; + BOOST_CHECK(boost::filesystem::exists(t.path())); + BOOST_CHECK(boost::filesystem::is_directory(t.path())); +} + +BOOST_AUTO_TEST_CASE(temp_dir_creates_unique_directory) +{ + BOOST_CHECK(temp_dir().path() != temp_dir().path()); +} + +BOOST_AUTO_TEST_CASE(temp_dir_cleans_up_directory) +{ + std::string path; + { + const temp_dir t; + path = t.path(); + } + BOOST_CHECK(!boost::filesystem::exists(path)); +} + +BOOST_AUTO_TEST_CASE(temp_dir_cleanup_succeeds_if_directory_contains_file) +{ + std::string path; + { + const temp_dir t; + path = t.path(); + boost::filesystem::create_directory(path + "/directory"); + std::ofstream file((path + "/file").c_str()); + file << "Text"; + file.flush(); + } + BOOST_CHECK(!boost::filesystem::exists(path)); +} + +BOOST_AUTO_TEST_CASE(temp_dir_cleanup_succeeds_if_directory_is_gone) +{ + std::string path; + { + const temp_dir t; + path = t.path(); + boost::filesystem::remove_all(path); + } + BOOST_CHECK(!boost::filesystem::exists(path)); +} + +BOOST_AUTO_TEST_CASE(temp_file_has_path) +{ + BOOST_CHECK(temp_file().path().size() > 0); +} + +BOOST_AUTO_TEST_CASE(temp_file_creates_temp_file) +{ + const temp_file f; + BOOST_CHECK(boost::filesystem::exists(f.path())); + BOOST_CHECK(boost::filesystem::is_regular_file(f.path())); +} + +BOOST_AUTO_TEST_CASE(temp_file_creates_unique_file) +{ + BOOST_CHECK(temp_file().path() != temp_file().path()); +} + +BOOST_AUTO_TEST_CASE(temp_file_creates_writable_file) +{ + const std::string data = "Test-data-goes-here"; + const temp_file f; + std::ofstream outfile(f.path().c_str()); + outfile << data; + outfile.flush(); + std::string read_data; + std::ifstream infile(f.path().c_str()); + infile >> read_data; + BOOST_CHECK_EQUAL(data, read_data); +} + +BOOST_AUTO_TEST_CASE(temp_file_cleans_up_file) +{ + std::string path; + { + const temp_file f; + path = f.path(); + } + BOOST_CHECK(!boost::filesystem::exists(path)); +} + +BOOST_AUTO_TEST_CASE(temp_file_cleanup_succeeds_if_file_is_gone) +{ + std::string path; + { + const temp_file t; + path = t.path(); + boost::filesystem::remove(path); + } + BOOST_CHECK(!boost::filesystem::exists(path)); +} + +} // namespace anonymous +} // namespace util |