[testing] Extract StreamCapture test utility (flutter/engine#47774)

Factors out an RAII-based class that can be used to capture std::cout, std::cerr, or technically any other std::ostream, though that's unlikely to be useful.

This makes the logic reusable but more importantly, ensures the capture is cleaned up at the end of the test.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
Chris Bracken 2023-11-07 17:18:16 -08:00 committed by GitHub
parent b82cf7549f
commit 229331bcff
5 changed files with 91 additions and 20 deletions

View File

@ -25,6 +25,7 @@
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/embedder/embedder_engine.h"
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
#include "flutter/testing/stream_capture.h"
#include "flutter/testing/test_dart_native_resolver.h"
#include "gtest/gtest.h"
@ -189,9 +190,7 @@ TEST_F(FlutterEngineTest, CanLogToStdout) {
CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
// Replace stdout stream buffer with our own.
std::stringstream buffer;
std::streambuf* old_buffer = std::cout.rdbuf();
std::cout.rdbuf(buffer.rdbuf());
StreamCapture stdout_capture(&std::cout);
// Launch the test entrypoint.
FlutterEngine* engine = GetFlutterEngine();
@ -200,12 +199,10 @@ TEST_F(FlutterEngineTest, CanLogToStdout) {
latch.Wait();
// Restore old stdout stream buffer.
std::cout.rdbuf(old_buffer);
stdout_capture.Stop();
// Verify hello world was written to stdout.
std::string logs = buffer.str();
EXPECT_TRUE(logs.find("Hello logging") != std::string::npos);
EXPECT_TRUE(stdout_capture.GetOutput().find("Hello logging") != std::string::npos);
}
// TODO(cbracken): Needs deflaking. https://github.com/flutter/flutter/issues/124677

View File

@ -15,6 +15,7 @@
#include "flutter/shell/platform/windows/testing/windows_test.h"
#include "flutter/shell/platform/windows/testing/windows_test_config_builder.h"
#include "flutter/shell/platform/windows/testing/windows_test_context.h"
#include "flutter/testing/stream_capture.h"
#include "gtest/gtest.h"
#include "third_party/tonic/converter/dart_converter.h"
@ -45,27 +46,20 @@ TEST_F(WindowsTest, LaunchMain) {
// Verify there is no unexpected output from launching main.
TEST_F(WindowsTest, LaunchMainHasNoOutput) {
// Replace stdout & stderr stream buffers with our own.
std::stringstream cout_buffer;
std::stringstream cerr_buffer;
std::streambuf* old_cout_buffer = std::cout.rdbuf();
std::streambuf* old_cerr_buffer = std::cerr.rdbuf();
std::cout.rdbuf(cout_buffer.rdbuf());
std::cerr.rdbuf(cerr_buffer.rdbuf());
StreamCapture stdout_capture(&std::cout);
StreamCapture stderr_capture(&std::cerr);
auto& context = GetContext();
WindowsConfigBuilder builder(context);
ViewControllerPtr controller{builder.Run()};
ASSERT_NE(controller, nullptr);
// Restore original stdout & stderr stream buffer.
std::cout.rdbuf(old_cout_buffer);
std::cerr.rdbuf(old_cerr_buffer);
stdout_capture.Stop();
stderr_capture.Stop();
// Verify stdout & stderr have no output.
std::string cout = cout_buffer.str();
std::string cerr = cerr_buffer.str();
EXPECT_TRUE(cout.empty());
EXPECT_TRUE(cerr.empty());
EXPECT_TRUE(stdout_capture.GetOutput().empty());
EXPECT_TRUE(stderr_capture.GetOutput().empty());
}
// Verify we can successfully launch a custom entry point.

View File

@ -25,6 +25,8 @@ source_set("testing_lib") {
"mock_canvas.h",
"post_task_sync.cc",
"post_task_sync.h",
"stream_capture.cc",
"stream_capture.h",
"test_args.cc",
"test_args.h",
"test_timeout_listener.cc",

View File

@ -0,0 +1,31 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/testing/stream_capture.h"
namespace flutter {
namespace testing {
StreamCapture::StreamCapture(std::ostream* ostream)
: ostream_(ostream), old_buffer_(ostream_->rdbuf()) {
ostream_->rdbuf(buffer_.rdbuf());
}
StreamCapture::~StreamCapture() {
Stop();
}
void StreamCapture::Stop() {
if (old_buffer_) {
ostream_->rdbuf(old_buffer_);
old_buffer_ = nullptr;
}
}
std::string StreamCapture::GetOutput() const {
return buffer_.str();
}
} // namespace testing
} // namespace flutter

View File

@ -0,0 +1,47 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_TESTING_STREAM_CAPTURE_H_
#define FLUTTER_TESTING_STREAM_CAPTURE_H_
#include <ostream>
#include <sstream>
#include <string>
namespace flutter {
namespace testing {
// Temporarily replaces the specified stream's output buffer to capture output.
//
// Example:
// StreamCapture captured_stdout(&std::cout);
// ... code that writest to std::cout ...
// std::string output = captured_stdout.GetCapturedOutput();
class StreamCapture {
public:
// Begins capturing output to the specified stream.
StreamCapture(std::ostream* ostream);
// Stops capturing output to the specified stream, and restores the original
// output buffer, if |Stop| has not already been called.
~StreamCapture();
// Stops capturing output to the specified stream, and restores the original
// output buffer.
void Stop();
// Returns any output written to the captured stream between construction and
// the first call to |Stop|, if any, or now.
std::string GetOutput() const;
private:
std::ostream* ostream_;
std::stringstream buffer_;
std::streambuf* old_buffer_;
};
} // namespace testing
} // namespace flutter
#endif // FLUTTER_TESTING_STREAM_CAPTURE_H_