Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppress a bunch of unnecessary test output #442

Merged
merged 14 commits into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion test/performance/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ endif()

gz_build_tests(
TYPE PERFORMANCE
SOURCES ${tests})
SOURCES ${tests}
LIB_DEPS gz-common${GZ_COMMON_VER}-testing
)

if(TARGET PERFORMANCE_plugin_specialization)
# We add this dependency to make sure that DummyPlugins gets generated
Expand Down
9 changes: 8 additions & 1 deletion test/performance/logging.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
#include <thread>

#include <gz/common/Console.hh>
#include <gz/common/testing/RedirectConsoleStream.hh>

using RedirectConsoleStream = gz::common::testing::RedirectConsoleStream;
using StreamSource = gz::common::testing::StreamSource;

namespace {
// Lower value than spdlog to keep CI from flaking
Expand All @@ -38,7 +42,6 @@ void WriteToFile(std::string result_filename, std::string content)
std::cerr << "Error writing to " << result_filename << std::endl;
}
out << content << std::flush;
std::cout << content;
}

void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t> &result)
Expand Down Expand Up @@ -162,6 +165,8 @@ void run(size_t number_of_threads)
<< filename_result << std::endl;
WriteToFile(filename_result, oss.str());

auto stream = gz::common::testing::RedirectStdout();

auto start_time_application_total =
std::chrono::high_resolution_clock::now();
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
Expand All @@ -175,6 +180,8 @@ void run(size_t number_of_threads)
}
auto stop_time_application_total = std::chrono::high_resolution_clock::now();

auto output = stream.GetString();

uint64_t total_time_in_us =
std::chrono::duration_cast<std::chrono::microseconds>(
stop_time_application_total - start_time_application_total)
Expand Down
79 changes: 79 additions & 0 deletions testing/include/gz/common/testing/RedirectConsoleStream.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef GZ_COMMON_TESTING_REDIRECTCONSOLE_STREAM_HH
#define GZ_COMMON_TESTING_REDIRECTCONSOLE_STREAM_HH

#include <string>

#include <gz/utils/ImplPtr.hh>
#include <gz/common/testing/TestPaths.hh>

#include "gz/common/testing/Export.hh"

namespace gz::common::testing
{

enum class StreamSource
{
STDOUT,
STDERR,
};

class GZ_COMMON_TESTING_VISIBLE RedirectConsoleStream
{
/// \brief Constructor
///
/// Redirect a choosen stream to a temporary file that can be read back.
/// Upon destruction, any redirection will be removed.
/// Upon destruction, the temporary file will be removed.
/// Access contents before destruction using GetString
///
/// \param[in] _source Console source to redirect (eg stdout or stderr)
/// \param[in] _destination Destination filename
public: RedirectConsoleStream(const StreamSource &_source,
const std::string &_destination);

/// \brief Get the contents of the redirected console output
/// This will additionally remove any redirection
public: std::string GetString();

/// \brief Get if the redirection is valid and enabled
/// \return bool True if the redirection is valid
public: bool Active() const;

/// \brief Implementation pointer
GZ_UTILS_UNIQUE_IMPL_PTR(dataPtr)
};

/// \brief Redirect standard out to a test-specific temporary path
inline RedirectConsoleStream RedirectStdout()
{
auto path = gz::common::testing::TempPath("stdout.out");
return RedirectConsoleStream(StreamSource::STDOUT, path);
}

/// \brief Redirect standard error to a test-specific temporary path
inline RedirectConsoleStream RedirectStderr()
{
auto path = gz::common::testing::TempPath("stderr.out");
return RedirectConsoleStream(StreamSource::STDERR, path);
}


} // namespace gz::common::testing
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved

#endif // GZ_COMMON_TESTING_REDIRECTCONSOLE_STREAM_HH
2 changes: 2 additions & 0 deletions testing/include/gz/common/testing/TestPaths.hh
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ template <typename... Args>
std::string TempPath(Args const &... args)
{
auto testPaths = TestPathFactory(kTestingProjectSourceDir);
assert(nullptr != testPaths);

std::string dataDir;
testPaths->TestTmpPath(dataDir);
return common::joinPaths(dataDir, args...);
Expand Down
2 changes: 2 additions & 0 deletions testing/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
set(sources
BazelTestPaths.cc
CMakeTestPaths.cc
RedirectConsoleStream.cc
TestPaths.cc
Utils.cc
)

set(test_sources
AutoLogFixture_TEST.cc
CMakeTestPaths_TEST.cc
RedirectConsoleStream_TEST.cc
Utils_TEST.cc
)

Expand Down
174 changes: 174 additions & 0 deletions testing/src/RedirectConsoleStream.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright (C) 2022 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#include <cstring>
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved
#include <cstdio>
#include <cstdlib>

#include "gz/common/testing/RedirectConsoleStream.hh"
#include "gz/common/Console.hh"
#include <gz/common/testing/TestPaths.hh>

#include <fstream>
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved
#include <gz/utils/SuppressWarning.hh>
#include <sstream>

#ifdef _WIN32
#include <io.h>
#include <windows.h>
#else
#include <unistd.h>
#endif

#include <fcntl.h>
#include <sys/stat.h>

namespace gz::common::testing
{

class RedirectConsoleStream::Implementation
{
/// \brief Console source being redirected
public: StreamSource source;

/// \brief Filename to write console output to
public: std::string sink {""};

/// \brief Holds the redirected file descriptor
public: int fd {-1};

/// \brief Holds the original source file descriptor
public: int originalFd {-1};

/// \brief Remove any console redirection, restoring original sink
public: void RemoveRedirection();

/// \brief Destructor
public: ~Implementation();
};

//////////////////////////////////////////////////
void RedirectConsoleStream::Implementation::RemoveRedirection()
{
// MSVC treats dup and dup2 as deprecated, preferring _dup and _dup2
// We can safely ignore that here.
GZ_UTILS_WARN_IGNORE__DEPRECATED_DECLARATION;
/// Restore the orignal source file descriptor
if (this->originalFd != -1)
{
fflush(nullptr);
dup2(this->originalFd, this->fd);
close(this->originalFd);
this->originalFd = -1;
}
GZ_UTILS_WARN_RESUME__DEPRECATED_DECLARATION;
mjcarroll marked this conversation as resolved.
Show resolved Hide resolved
}

//////////////////////////////////////////////////
RedirectConsoleStream::Implementation::~Implementation()
{
this->RemoveRedirection();

if (!this->sink.empty() && common::exists(this->sink))
{
common::removeFile(this->sink);
}
}

//////////////////////////////////////////////////
int GetSourceFd(const StreamSource &_source)
{
switch(_source)
{
case StreamSource::STDOUT:
return fileno(stdout);
break;
case StreamSource::STDERR:
return fileno(stderr);
break;
default:
return -1;
}
}

//////////////////////////////////////////////////
RedirectConsoleStream::RedirectConsoleStream(const StreamSource &_source,
const std::string &_destination):
dataPtr(gz::utils::MakeUniqueImpl<Implementation>())
{
// MSVC treats dup and dup2 as deprecated, preferring _dup and _dup2
// We can safely ignore that here.
GZ_UTILS_WARN_IGNORE__DEPRECATED_DECLARATION;
this->dataPtr->sink = _destination;

if (this->dataPtr->sink.empty())
{
gzerr << "Failed to open sink file: console redirection disabled"
<< "(empty filename)" << std::endl;
return;
}

if (common::exists(this->dataPtr->sink))
{
gzerr << "Failed to open sink file: console redirection disabled"
<< "(destination exists)" << std::endl;
return;
}

this->dataPtr->fd = GetSourceFd(_source);

// Store the fd so that it can be restored upon destruction
this->dataPtr->originalFd = dup(this->dataPtr->fd);

int sinkFd;
// Create a file with read/write permissions and exclusive access
if ((sinkFd = open(this->dataPtr->sink.c_str(),
O_EXCL | O_RDWR | O_CREAT,
S_IREAD | S_IWRITE)) < 0)
{
gzerr << "Failed to open sink file, console redirection disabled"
<< "(" << strerror(sinkFd) << ")" << std::endl;
return;
}

fflush(nullptr);
// Duplicate the sink file descriptor onto the source
dup2(sinkFd, this->dataPtr->fd);
// Close the file handle;
close(sinkFd);
GZ_UTILS_WARN_RESUME__DEPRECATED_DECLARATION;
}

//////////////////////////////////////////////////
std::string RedirectConsoleStream::GetString()
{
this->dataPtr->RemoveRedirection();

// Read the file contents and return to the user
std::ifstream input(this->dataPtr->sink);
std::stringstream buffer;
buffer << input.rdbuf();
return buffer.str();
}

//////////////////////////////////////////////////
bool RedirectConsoleStream::Active() const
{
return !this->dataPtr->sink.empty() && this->dataPtr->originalFd != -1;
}

} // namespace gz::common::testing
Loading