From 6914ff6113f1667308293fe37e1ba4a4dcba850f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 5 Jun 2023 21:51:28 -0400 Subject: SL-18837: Use Boost.Filesystem for NamedTempFile, instead of APR. --- indra/test/namedtempfile.h | 136 +++++++++++---------------------------------- indra/test/test.cpp | 10 ++-- 2 files changed, 38 insertions(+), 108 deletions(-) (limited to 'indra/test') diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index 7d59cad32c..927e2b4fd9 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -13,15 +13,15 @@ #define LL_NAMEDTEMPFILE_H #include "llerror.h" -#include "llapr.h" -#include "apr_file_io.h" +#include "stringize.h" #include -#include -#include -#include #include +#include +#include +#include #include #include +#include /** * Create a text file with specified content "somewhere in the @@ -31,41 +31,36 @@ class NamedTempFile: public boost::noncopyable { LOG_CLASS(NamedTempFile); public: - NamedTempFile(const std::string& pfx, const std::string& content, apr_pool_t* pool=gAPRPoolp): - mPool(pool) + NamedTempFile(const std::string_view& pfx, + const std::string_view& content, + const std::string_view& sfx=std::string_view("")) { - createFile(pfx, boost::phoenix::placeholders::arg1 << content); - } - - // Disambiguate when passing string literal - NamedTempFile(const std::string& pfx, const char* content, apr_pool_t* pool=gAPRPoolp): - mPool(pool) - { - createFile(pfx, boost::phoenix::placeholders::arg1 << content); + createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx); } // Function that accepts an ostream ref and (presumably) writes stuff to // it, e.g.: // (boost::phoenix::placeholders::arg1 << "the value is " << 17 << '\n') - typedef boost::function Streamer; + typedef std::function Streamer; - NamedTempFile(const std::string& pfx, const Streamer& func, apr_pool_t* pool=gAPRPoolp): - mPool(pool) + NamedTempFile(const std::string_view& pfx, + const Streamer& func, + const std::string_view& sfx=std::string_view("")) { - createFile(pfx, func); + createFile(pfx, func, sfx); } virtual ~NamedTempFile() { - ll_apr_assert_status(apr_file_remove(mPath.c_str(), mPool)); + boost::filesystem::remove(mPath); } - virtual std::string getName() const { return mPath; } + virtual std::string getName() const { return mPath.native(); } void peep() { std::cout << "File '" << mPath << "' contains:\n"; - std::ifstream reader(mPath.c_str()); + boost::filesystem::ifstream reader(mPath); std::string line; while (std::getline(reader, line)) std::cout << line << '\n'; @@ -73,92 +68,40 @@ public: } protected: - void createFile(const std::string& pfx, const Streamer& func) + void createFile(const std::string_view& pfx, + const Streamer& func, + const std::string_view& sfx) { // Create file in a temporary place. - const char* tempdir = NULL; - ll_apr_assert_status(apr_temp_dir_get(&tempdir, mPool)); - - // Construct a temp filename template in that directory. - char *tempname = NULL; - ll_apr_assert_status(apr_filepath_merge(&tempname, - tempdir, - (pfx + "XXXXXX").c_str(), - 0, - mPool)); - - // Create a temp file from that template. - apr_file_t* fp = NULL; - ll_apr_assert_status(apr_file_mktemp(&fp, - tempname, - APR_CREATE | APR_WRITE | APR_EXCL, - mPool)); - // apr_file_mktemp() alters tempname with the actual name. Not until - // now is it valid to capture as our mPath. - mPath = tempname; + boost::filesystem::path tempname{ boost::filesystem::temp_directory_path() }; + // unique_path() recommended model + tempname += stringize(pfx, "%%%%-%%%%-%%%%-%%%%", sfx); + mPath = boost::filesystem::unique_path(tempname); + boost::filesystem::ofstream out{ mPath }; // Write desired content. - std::ostringstream out; - // Stream stuff to it. func(out); - - std::string data(out.str()); - apr_size_t writelen(data.length()); - ll_apr_assert_status(apr_file_write(fp, data.c_str(), &writelen)); - ll_apr_assert_status(apr_file_close(fp)); - llassert_always(writelen == data.length()); } - std::string mPath; - apr_pool_t* mPool; + boost::filesystem::path mPath; }; /** * Create a NamedTempFile with a specified filename extension. This is useful * when, for instance, you must be able to use the file in a Python import * statement. - * - * A NamedExtTempFile actually has two different names. We retain the original - * no-extension name as a placeholder in the temp directory to ensure - * uniqueness; to that we link the name plus the desired extension. Naturally, - * both must be removed on destruction. */ class NamedExtTempFile: public NamedTempFile { LOG_CLASS(NamedExtTempFile); public: - NamedExtTempFile(const std::string& ext, const std::string& content, apr_pool_t* pool=gAPRPoolp): - NamedTempFile(remove_dot(ext), content, pool), - mLink(mPath + ensure_dot(ext)) - { - linkto(mLink); - } + NamedExtTempFile(const std::string& ext, const std::string_view& content): + NamedTempFile(remove_dot(ext), content, ensure_dot(ext)) + {} - // Disambiguate when passing string literal - NamedExtTempFile(const std::string& ext, const char* content, apr_pool_t* pool=gAPRPoolp): - NamedTempFile(remove_dot(ext), content, pool), - mLink(mPath + ensure_dot(ext)) - { - linkto(mLink); - } - - NamedExtTempFile(const std::string& ext, const Streamer& func, apr_pool_t* pool=gAPRPoolp): - NamedTempFile(remove_dot(ext), func, pool), - mLink(mPath + ensure_dot(ext)) - { - linkto(mLink); - } - - virtual ~NamedExtTempFile() - { - ll_apr_assert_status(apr_file_remove(mLink.c_str(), mPool)); - } - - // Since the caller has gone to the trouble to create the name with the - // extension, that should be the name we return. In this class, mPath is - // just a placeholder to ensure that future createFile() calls won't - // collide. - virtual std::string getName() const { return mLink; } + NamedExtTempFile(const std::string& ext, const Streamer& func): + NamedTempFile(remove_dot(ext), func, ensure_dot(ext)) + {} static std::string ensure_dot(const std::string& ext) { @@ -175,7 +118,7 @@ public: { return ext; } - return std::string(".") + ext; + return "." + ext; } static std::string remove_dot(const std::string& ext) @@ -187,19 +130,6 @@ public: } return ext.substr(found); } - -private: - void linkto(const std::string& path) - { - // This method assumes that since mPath (without extension) is - // guaranteed by apr_file_mktemp() to be unique, then (mPath + any - // extension) is also unique. This is likely, though not guaranteed: - // files could be created in the same temp directory other than by - // this class. - ll_apr_assert_status(apr_file_link(mPath.c_str(), path.c_str())); - } - - std::string mLink; }; #endif /* ! defined(LL_NAMEDTEMPFILE_H) */ diff --git a/indra/test/test.cpp b/indra/test/test.cpp index bb48216b2b..04f32831b7 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -97,10 +97,10 @@ public: class RecordToTempFile : public LLError::Recorder, public boost::noncopyable { public: - RecordToTempFile(apr_pool_t* pPool) + RecordToTempFile() : LLError::Recorder(), boost::noncopyable(), - mTempFile("log", "", pPool), + mTempFile("log", ""), mFile(mTempFile.getName().c_str()) { } @@ -141,11 +141,11 @@ private: class LLReplayLogReal: public LLReplayLog, public boost::noncopyable { public: - LLReplayLogReal(LLError::ELevel level, apr_pool_t* pool) + LLReplayLogReal(LLError::ELevel level) : LLReplayLog(), boost::noncopyable(), mOldSettings(LLError::saveAndResetSettings()), - mRecorder(new RecordToTempFile(pool)) + mRecorder(new RecordToTempFile()) { LLError::setFatalFunction(wouldHaveCrashed); LLError::setDefaultLevel(level); @@ -624,7 +624,7 @@ int main(int argc, char **argv) if (LOGFAIL && *LOGFAIL) { LLError::ELevel level = LLError::decodeLevel(LOGFAIL); - replayer.reset(new LLReplayLogReal(level, gAPRPoolp)); + replayer.reset(new LLReplayLogReal(level)); } } LLError::setFatalFunction(wouldHaveCrashed); -- cgit v1.2.3 From aab769e9d7a9b11256572f8adb69d586d9ff8d3d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 5 Jun 2023 23:24:34 -0400 Subject: SL-18837: Make NamedTempFile revert to boost::function from std::function, since some consumers still use (e.g.) boost::phoenix::placeholders::arg1 to generate an inline callable. --- indra/test/namedtempfile.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/test') diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index 927e2b4fd9..df7495fc13 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -15,10 +15,10 @@ #include "llerror.h" #include "stringize.h" #include -#include +#include #include #include -#include +#include #include #include #include @@ -41,7 +41,7 @@ public: // Function that accepts an ostream ref and (presumably) writes stuff to // it, e.g.: // (boost::phoenix::placeholders::arg1 << "the value is " << 17 << '\n') - typedef std::function Streamer; + typedef boost::function Streamer; NamedTempFile(const std::string_view& pfx, const Streamer& func, -- cgit v1.2.3 From 26ca3e14d623e4094dde76ad88e3da2a209483b5 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 5 Jun 2023 23:46:43 -0400 Subject: SL-18837: NamedTempFile must still disambiguate string literals. --- indra/test/namedtempfile.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'indra/test') diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index df7495fc13..acfc048b7a 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -38,6 +38,15 @@ public: createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx); } + // Disambiguate when passing string literal -- unclear why a string + // literal should be ambiguous wrt std::string_view and Streamer + NamedTempFile(const std::string_view& pfx, + const char* content, + const std::string_view& sfx=std::string_view("")) + { + createFile(pfx, [&content](std::ostream& out){ out << content; }, sfx); + } + // Function that accepts an ostream ref and (presumably) writes stuff to // it, e.g.: // (boost::phoenix::placeholders::arg1 << "the value is " << 17 << '\n') @@ -99,6 +108,11 @@ public: NamedTempFile(remove_dot(ext), content, ensure_dot(ext)) {} + // Disambiguate when passing string literal + NamedExtTempFile(const std::string& ext, const char* content): + NamedTempFile(remove_dot(ext), content, ensure_dot(ext)) + {} + NamedExtTempFile(const std::string& ext, const Streamer& func): NamedTempFile(remove_dot(ext), func, ensure_dot(ext)) {} -- cgit v1.2.3 From 6516c9d07db42beba5ba9c0c41a33925794a249c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Jun 2023 07:44:42 -0400 Subject: SL-18837: NamedTempFile back to std::function, use boost::phoenix << It seems the problem addressed by aab769e wasn't some synergy between Boost.Phoenix and Boost.Function, but rather the lack of a Phoenix header file introducing operator<<(). --- indra/test/namedtempfile.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/test') diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index acfc048b7a..84b62a0945 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -15,10 +15,10 @@ #include "llerror.h" #include "stringize.h" #include -#include #include #include #include +#include #include #include #include @@ -50,7 +50,7 @@ public: // Function that accepts an ostream ref and (presumably) writes stuff to // it, e.g.: // (boost::phoenix::placeholders::arg1 << "the value is " << 17 << '\n') - typedef boost::function Streamer; + typedef std::function Streamer; NamedTempFile(const std::string_view& pfx, const Streamer& func, -- cgit v1.2.3 From 1db7ac7139adf505be12308fd7ba2920f5beecde Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Jun 2023 10:02:57 -0400 Subject: SL-18837: Make NamedTempFile name template compatible with Python. The recommended template uses hyphens; change to underscores to be valid Python temp module names. --- indra/test/namedtempfile.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra/test') diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index 84b62a0945..8cd6c990dc 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -83,8 +83,9 @@ protected: { // Create file in a temporary place. boost::filesystem::path tempname{ boost::filesystem::temp_directory_path() }; - // unique_path() recommended model - tempname += stringize(pfx, "%%%%-%%%%-%%%%-%%%%", sfx); + // unique_path() recommended template, but with underscores instead of + // hyphens: some use cases involve temporary Python scripts + tempname += stringize(pfx, "%%%%_%%%%_%%%%_%%%%", sfx); mPath = boost::filesystem::unique_path(tempname); boost::filesystem::ofstream out{ mPath }; -- cgit v1.2.3 From e63c571d1336e3c521e1fc3a5e27bb77fc667790 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Jun 2023 13:34:01 -0400 Subject: SL-18837: On Windows, NamedTempFile must convert from wstring --- indra/test/namedtempfile.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra/test') diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index 8cd6c990dc..da4fec97c8 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -13,6 +13,7 @@ #define LL_NAMEDTEMPFILE_H #include "llerror.h" +#include "llstring.h" #include "stringize.h" #include #include @@ -64,7 +65,8 @@ public: boost::filesystem::remove(mPath); } - virtual std::string getName() const { return mPath.native(); } + // On Windows, path::native() returns a wstring + std::string getName() const { return ll_convert(mPath.native()); } void peep() { -- cgit v1.2.3 From 004150a5305d0df06c52a51a0df3ac26dd4a63cd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Jun 2023 16:27:43 -0400 Subject: SL-18837: Concat path part with / rather than +=. Using concatenation appends the intended filename to the parent directory name, instead of putting the filename in the parent directory. --- indra/test/namedtempfile.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'indra/test') diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index da4fec97c8..c215c50f3f 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -84,10 +84,12 @@ protected: const std::string_view& sfx) { // Create file in a temporary place. - boost::filesystem::path tempname{ boost::filesystem::temp_directory_path() }; - // unique_path() recommended template, but with underscores instead of - // hyphens: some use cases involve temporary Python scripts - tempname += stringize(pfx, "%%%%_%%%%_%%%%_%%%%", sfx); + boost::filesystem::path tempname{ + boost::filesystem::temp_directory_path() / + // unique_path() recommended template, but with underscores + // instead of hyphens: some use cases involve temporary Python + // scripts + stringize(pfx, "%%%%_%%%%_%%%%_%%%%", sfx) }; mPath = boost::filesystem::unique_path(tempname); boost::filesystem::ofstream out{ mPath }; -- cgit v1.2.3 From f54c1215676f26480d88b4588bb0eeb9c05f50d9 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 7 Jul 2023 10:06:02 -0400 Subject: SL-18837: Try putting generated Python scripts in RUNNER_TEMP dir. The claim is that the Windows Python interpreter is integrated somehow with the OS such that a command line that tries to run Python with a script that "looks suspicious" (i.e. in a system temp directory) fails with "Access denied" without even loading the interpreter. At least that theory would explain the "Access denied" errors we've been getting trying to run Python scripts generated into the system temp directory by our integration tests. Our hope is that generating such scripts into the GitHub RUNNER_TEMP directory will work better. As this test is specific to Windows, don't even bother running Mac builds. --- indra/test/namedtempfile.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'indra/test') diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index c215c50f3f..4343361f41 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -84,12 +84,19 @@ protected: const std::string_view& sfx) { // Create file in a temporary place. + // This variable is set by GitHub actions and is the recommended place + // to put temp files belonging to an actions job. + const char* RUNNER_TEMP = getenv("RUNNER_TEMP"); + boost::filesystem::path tempdir{ + // if RUNNER_TEMP is set and not empty + (RUNNER_TEMP && *RUNNER_TEMP)? + boost::filesystem::path(RUNNER_TEMP) : // use RUNNER_TEMP if available + boost::filesystem::temp_directory_path()}; // else canonical temp dir boost::filesystem::path tempname{ - boost::filesystem::temp_directory_path() / - // unique_path() recommended template, but with underscores - // instead of hyphens: some use cases involve temporary Python - // scripts - stringize(pfx, "%%%%_%%%%_%%%%_%%%%", sfx) }; + // use filename template recommended by unique_path() doc, but + // with underscores instead of hyphens: some use cases involve + // temporary Python scripts + tempdir / stringize(pfx, "%%%%_%%%%_%%%%_%%%%", sfx) }; mPath = boost::filesystem::unique_path(tempname); boost::filesystem::ofstream out{ mPath }; -- cgit v1.2.3 From e933ace53b24b732d4111169e3c5964a8591a29e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 7 Jul 2023 14:07:12 -0400 Subject: SL-18837: Try to bypass Windows perm problem with Python indirection. --- indra/test/namedtempfile.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'indra/test') diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index 4343361f41..525a35000d 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -65,8 +65,7 @@ public: boost::filesystem::remove(mPath); } - // On Windows, path::native() returns a wstring - std::string getName() const { return ll_convert(mPath.native()); } + std::string getName() const { return mPath.string(); } void peep() { @@ -78,12 +77,9 @@ public: std::cout << "---\n"; } -protected: - void createFile(const std::string_view& pfx, - const Streamer& func, - const std::string_view& sfx) + static boost::filesystem::path temp_path(const std::string_view& pfx="", + const std::string_view& sfx="") { - // Create file in a temporary place. // This variable is set by GitHub actions and is the recommended place // to put temp files belonging to an actions job. const char* RUNNER_TEMP = getenv("RUNNER_TEMP"); @@ -97,9 +93,17 @@ protected: // with underscores instead of hyphens: some use cases involve // temporary Python scripts tempdir / stringize(pfx, "%%%%_%%%%_%%%%_%%%%", sfx) }; - mPath = boost::filesystem::unique_path(tempname); - boost::filesystem::ofstream out{ mPath }; + return boost::filesystem::unique_path(tempname); + } +protected: + void createFile(const std::string_view& pfx, + const Streamer& func, + const std::string_view& sfx) + { + // Create file in a temporary place. + mPath = temp_path(pfx, sfx); + boost::filesystem::ofstream out{ mPath }; // Write desired content. func(out); } -- cgit v1.2.3 From eb8458587537e06df23447db56b9910a0d4e451e Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 8 Sep 2023 09:50:38 -0400 Subject: SL-18837: NamedTempFile must be binary mode on Windows. --- indra/test/namedtempfile.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/test') diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index 525a35000d..3a994ae798 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -70,7 +70,7 @@ public: void peep() { std::cout << "File '" << mPath << "' contains:\n"; - boost::filesystem::ifstream reader(mPath); + boost::filesystem::ifstream reader(mPath, std::ios::binary); std::string line; while (std::getline(reader, line)) std::cout << line << '\n'; @@ -103,7 +103,7 @@ protected: { // Create file in a temporary place. mPath = temp_path(pfx, sfx); - boost::filesystem::ofstream out{ mPath }; + boost::filesystem::ofstream out{ mPath, std::ios::binary }; // Write desired content. func(out); } -- cgit v1.2.3 From c7546ea65e55143ff3d2d82d8c289bbac7fffe0f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 8 Sep 2023 14:14:09 -0400 Subject: SL-18837: Make llsdserialize_test debug output conditional. Move hexdump() and hexmix() stream formatters to new hexdump.h for potential use by other tests. In toPythonUsing() helper function, add a temp file to receive Python script debug output, and direct debug output to that file. On test failure, dump the contents of that file to the log. Give NamedTempFile::peep() an optional target std::ostream; refactor implementation as peep_via() that accepts a callable to process each text line. Add operator<<() to stream the contents of a NamedTempFile object to ostream -- but don't use that with LL_DEBUGS(), as it flattens the file contents into a single log line. Instead add peep_log(), which streams each individual text line to LL_DEBUGS(). --- indra/test/hexdump.h | 97 ++++++++++++++++++++++++++++++++++++++++++++++ indra/test/namedtempfile.h | 25 ++++++++++-- 2 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 indra/test/hexdump.h (limited to 'indra/test') diff --git a/indra/test/hexdump.h b/indra/test/hexdump.h new file mode 100644 index 0000000000..dd7cbaaa3c --- /dev/null +++ b/indra/test/hexdump.h @@ -0,0 +1,97 @@ +/** + * @file hexdump.h + * @author Nat Goodspeed + * @date 2023-09-08 + * @brief Provide hexdump() and hexmix() ostream formatters + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Copyright (c) 2023, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_HEXDUMP_H) +#define LL_HEXDUMP_H + +#include +#include +#include +#include + +// Format a given byte string as 2-digit hex values, no separators +// Usage: std::cout << hexdump(somestring) << ... +class hexdump +{ +public: + hexdump(const std::string_view& data): + hexdump(data.data(), data.length()) + {} + + hexdump(const char* data, size_t len): + hexdump(reinterpret_cast(data), len) + {} + + hexdump(const unsigned char* data, size_t len): + mData(data, data + len) + {} + + friend std::ostream& operator<<(std::ostream& out, const hexdump& self) + { + auto oldfmt{ out.flags() }; + auto oldfill{ out.fill() }; + out.setf(std::ios_base::hex, std::ios_base::basefield); + out.fill('0'); + for (auto c : self.mData) + { + out << std::setw(2) << unsigned(c); + } + out.setf(oldfmt, std::ios_base::basefield); + out.fill(oldfill); + return out; + } + +private: + std::vector mData; +}; + +// Format a given byte string as a mix of printable characters and, for each +// non-printable character, "\xnn" +// Usage: std::cout << hexmix(somestring) << ... +class hexmix +{ +public: + hexmix(const std::string_view& data): + mData(data) + {} + + hexmix(const char* data, size_t len): + mData(data, len) + {} + + friend std::ostream& operator<<(std::ostream& out, const hexmix& self) + { + auto oldfmt{ out.flags() }; + auto oldfill{ out.fill() }; + out.setf(std::ios_base::hex, std::ios_base::basefield); + out.fill('0'); + for (auto c : self.mData) + { + // std::isprint() must be passed an unsigned char! + if (std::isprint(static_cast(c))) + { + out << c; + } + else + { + out << "\\x" << std::setw(2) << unsigned(c); + } + } + out.setf(oldfmt, std::ios_base::basefield); + out.fill(oldfill); + return out; + } + +private: + std::string mData; +}; + +#endif /* ! defined(LL_HEXDUMP_H) */ diff --git a/indra/test/namedtempfile.h b/indra/test/namedtempfile.h index 3a994ae798..ad14cebbd1 100644 --- a/indra/test/namedtempfile.h +++ b/indra/test/namedtempfile.h @@ -67,14 +67,31 @@ public: std::string getName() const { return mPath.string(); } - void peep() + template + void peep_via(CALLABLE&& callable) const { - std::cout << "File '" << mPath << "' contains:\n"; + std::forward(callable)(stringize("File '", mPath, "' contains:")); boost::filesystem::ifstream reader(mPath, std::ios::binary); std::string line; while (std::getline(reader, line)) - std::cout << line << '\n'; - std::cout << "---\n"; + std::forward(callable)(line); + std::forward(callable)("---"); + } + + void peep_log() const + { + peep_via([](const std::string& line){ LL_DEBUGS() << line << LL_ENDL; }); + } + + void peep(std::ostream& out=std::cout) const + { + peep_via([&out](const std::string& line){ out << line << '\n'; }); + } + + friend std::ostream& operator<<(std::ostream& out, const NamedTempFile& self) + { + self.peep(out); + return out; } static boost::filesystem::path temp_path(const std::string_view& pfx="", -- cgit v1.2.3