Skip to content

Commit

Permalink
[testing] Extract StreamCapture test utility (#47774)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
cbracken authored Nov 8, 2023
1 parent c8195b4 commit 5224cae
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -189,9 +190,7 @@ + (void)registerWithRegistrar:(id<FlutterPluginRegistrar>)registrar {
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();
Expand All @@ -200,12 +199,10 @@ + (void)registerWithRegistrar:(id<FlutterPluginRegistrar>)registrar {

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
Expand Down
20 changes: 7 additions & 13 deletions shell/platform/windows/flutter_windows_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions testing/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
31 changes: 31 additions & 0 deletions testing/stream_capture.cc
Original file line number Diff line number Diff line change
@@ -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
47 changes: 47 additions & 0 deletions testing/stream_capture.h
Original file line number Diff line number Diff line change
@@ -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_

0 comments on commit 5224cae

Please sign in to comment.