blob: 041c15dde26b23376ec9eb61612c2177a3ad6b1e [file] [log] [blame]
// Copyright 2021 The Fuchsia 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 "src/ui/scenic/lib/screen_capture/screen_capture.h"
#include <fuchsia/ui/composition/cpp/fidl.h>
#include <lib/syslog/cpp/macros.h>
#include <utility>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
#include "src/ui/scenic/lib/allocation/allocator.h"
#include "src/ui/scenic/lib/allocation/buffer_collection_import_export_tokens.h"
#include "src/ui/scenic/lib/allocation/mock_buffer_collection_importer.h"
#include "src/ui/scenic/lib/flatland/renderer/mock_renderer.h"
#include "src/ui/scenic/lib/utils/helpers.h"
using allocation::BufferCollectionImporter;
using fuchsia::ui::composition::FrameInfo;
using fuchsia::ui::composition::GetNextFrameArgs;
using fuchsia::ui::composition::ScreenCaptureConfig;
using fuchsia::ui::composition::ScreenCaptureError;
using testing::_;
namespace screen_capture::test {
class ScreenCaptureTest : public gtest::TestLoopFixture {
public:
ScreenCaptureTest() = default;
void SetUp() override {
sysmem_allocator_ = utils::CreateSysmemAllocatorSyncPtr();
mock_buffer_collection_importer_ = new allocation::MockBufferCollectionImporter();
buffer_collection_importer_ =
std::shared_ptr<allocation::BufferCollectionImporter>(mock_buffer_collection_importer_);
renderer_ = std::make_shared<flatland::MockRenderer>();
renderables_ =
std::make_pair<std::vector<Rectangle2D>, std::vector<allocation::ImageMetadata>>({}, {});
// Capture uninteresting cleanup calls from Allocator dtor.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferCollection(_))
.Times(::testing::AtLeast(0));
}
void TearDown() override { RunLoopUntilIdle(); }
fpromise::result<FrameInfo, ScreenCaptureError> CaptureScreen(screen_capture::ScreenCapture& sc) {
GetNextFrameArgs frame_args;
zx::event event;
zx::event::create(0, &event);
frame_args.set_event(std::move(event));
fpromise::result<FrameInfo, ScreenCaptureError> response;
bool alloc_result = false;
sc.GetNextFrame(
std::move(frame_args),
[&response, &alloc_result](fpromise::result<FrameInfo, ScreenCaptureError> result) {
response = std::move(result);
alloc_result = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(alloc_result);
return response;
}
std::pair<std::vector<Rectangle2D>, std::vector<allocation::ImageMetadata>> GetRenderables() {
return renderables_;
}
protected:
allocation::MockBufferCollectionImporter* mock_buffer_collection_importer_;
std::shared_ptr<allocation::BufferCollectionImporter> buffer_collection_importer_;
std::shared_ptr<flatland::MockRenderer> renderer_;
private:
fuchsia::sysmem::AllocatorSyncPtr sysmem_allocator_;
std::pair<std::vector<Rectangle2D>, std::vector<allocation::ImageMetadata>> renderables_;
};
TEST_F(ScreenCaptureTest, ConfigureSingleImporterSuccess) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, nullptr,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(1);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
bool configure = false;
sc.Configure(std::move(args), [&configure](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
configure = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(configure);
// Ensure that the buffer image is released.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(1);
}
TEST_F(ScreenCaptureTest, ConfigureSingleImporterFailure) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, nullptr,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(1);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(false));
ScreenCaptureError error;
sc.Configure(std::move(args), [&error](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_TRUE(result.is_error());
error = result.error();
});
RunLoopUntilIdle();
EXPECT_EQ(error, ScreenCaptureError::BAD_OPERATION);
}
TEST_F(ScreenCaptureTest, ConfigureMultipleImportersSuccess) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
auto mock_buffer_collection_importer2 = new allocation::MockBufferCollectionImporter();
auto buffer_collection_importer2 =
std::shared_ptr<allocation::BufferCollectionImporter>(mock_buffer_collection_importer2);
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture_importers.push_back(buffer_collection_importer2);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, nullptr,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(1);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_buffer_collection_importer2, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
bool configure = false;
sc.Configure(std::move(args), [&configure](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
configure = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(configure);
// We expect that for each buffer importer the buffer image will be released.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(1);
EXPECT_CALL(*mock_buffer_collection_importer2, ReleaseBufferImage(_)).Times(1);
}
TEST_F(ScreenCaptureTest, ConfigureMultipleImportersImportFailure) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
auto mock_buffer_collection_importer2 = new allocation::MockBufferCollectionImporter();
auto buffer_collection_importer2 =
std::shared_ptr<allocation::BufferCollectionImporter>(mock_buffer_collection_importer2);
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture_importers.push_back(buffer_collection_importer2);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, nullptr,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(1);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_buffer_collection_importer2, ImportBufferImage(_))
.WillRepeatedly(testing::Return(false));
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_));
sc.Configure(std::move(args), [](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.error(), ScreenCaptureError::BAD_OPERATION);
});
RunLoopUntilIdle();
}
TEST_F(ScreenCaptureTest, ConfigureWithMissingArguments) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), {}, nullptr,
[this]() { return this->GetRenderables(); });
sc.Configure({}, [](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.error(), ScreenCaptureError::MISSING_ARGS);
});
RunLoopUntilIdle();
}
TEST_F(ScreenCaptureTest, ConfigureNoBuffers) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, nullptr,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(0);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
sc.Configure(std::move(args), [](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.error(), ScreenCaptureError::INVALID_ARGS);
});
RunLoopUntilIdle();
}
TEST_F(ScreenCaptureTest, ConfigureTwice) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, renderer_,
[this]() { return this->GetRenderables(); });
// Configure a buffer collection with two (2) VMOs to render into for GetNextFrame().
allocation::BufferCollectionImportExportTokens ref_pair1 =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args1;
args1.set_import_token(std::move(ref_pair1.import_token));
args1.set_size({1, 1});
args1.set_buffer_count(2);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
bool alloc_result = false;
sc.Configure(std::move(args1),
[&alloc_result](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
alloc_result = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(alloc_result);
// Capture call to renderer.
EXPECT_CALL(*renderer_, Render(_, _, _, _, _)).Times(1);
// Request a frame in the buffer.
const auto& response_buffer1 = CaptureScreen(sc);
EXPECT_TRUE(response_buffer1.is_ok());
// Create another buffer collection to render into for GetNextFrame(). This collection only has
// one (1) VMO.
allocation::BufferCollectionImportExportTokens ref_pair2 =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args2;
args2.set_import_token(std::move(ref_pair2.import_token));
args2.set_size({1, 1});
args2.set_buffer_count(1);
// We expect that the two images created for the buffer collection will be released when we
// configure our new buffer collection.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(2);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
alloc_result = false;
sc.Configure(std::move(args2),
[&alloc_result](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
alloc_result = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(alloc_result);
// Capture call to renderer.
EXPECT_CALL(*renderer_, Render(_, _, _, _, _)).Times(1);
// Request a frame into new buffer.
const auto& response1_buffer2 = CaptureScreen(sc);
EXPECT_TRUE(response1_buffer2.is_ok());
// As the new buffer we configured only contained 1 VMO we expect a buffer full error
// when we request another frame without freeing the first one we got.
const auto& response2_buffer2 = CaptureScreen(sc);
EXPECT_TRUE(response2_buffer2.is_error());
EXPECT_EQ(response2_buffer2.error(), ScreenCaptureError::BUFFER_FULL);
// Ensure that the buffer image is released.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(1);
}
TEST_F(ScreenCaptureTest, GetNextFrameNoBuffers) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, nullptr,
[this]() { return this->GetRenderables(); });
// Request a frame without configuring the buffers.
const auto& result = CaptureScreen(sc);
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.error(), ScreenCaptureError::BAD_OPERATION);
}
TEST_F(ScreenCaptureTest, GetNextFrameSuccess) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, renderer_,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(1);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
bool configure = false;
sc.Configure(std::move(args), [&configure](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
configure = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(configure);
// Capture call to renderer.
EXPECT_CALL(*renderer_, Render(_, _, _, _, _)).Times(1);
const auto& result = CaptureScreen(sc);
EXPECT_TRUE(result.is_ok());
// Ensure that the buffer image is released.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(1);
}
TEST_F(ScreenCaptureTest, GetNextFrameBufferFullError) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, renderer_,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(1);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
bool configure = false;
sc.Configure(std::move(args), [&configure](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
configure = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(configure);
// Capture call to renderer.
EXPECT_CALL(*renderer_, Render(_, _, _, _, _)).Times(1);
// Request frame. This will use up the only buffer in the collection.
const auto& result1 = CaptureScreen(sc);
EXPECT_TRUE(result1.is_ok());
// Request another frame. This should cause a buffer full error.
const auto& result2 = CaptureScreen(sc);
EXPECT_TRUE(result2.is_error());
EXPECT_EQ(result2.error(), ScreenCaptureError::BUFFER_FULL);
// Ensure that the buffer image is released.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(1);
}
TEST_F(ScreenCaptureTest, GetNextFrameMultipleBuffers) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, renderer_,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(2);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
bool configure = false;
sc.Configure(std::move(args), [&configure](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
configure = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(configure);
// Capture call to renderer.
EXPECT_CALL(*renderer_, Render(_, _, _, _, _)).Times(1);
// Request frame.
const auto& result1 = CaptureScreen(sc);
EXPECT_TRUE(result1.is_ok());
// Capture call to renderer.
EXPECT_CALL(*renderer_, Render(_, _, _, _, _)).Times(1);
// Request another frame. This should fill the second buffer.
const auto& result2 = CaptureScreen(sc);
EXPECT_TRUE(result2.is_ok());
EXPECT_NE(result1.value().buffer_id(), result2.value().buffer_id());
// Ensure that the buffer images are released.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(2);
}
TEST_F(ScreenCaptureTest, GetNextFrameMissingArgs) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, nullptr,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(1);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
bool configure = false;
sc.Configure(std::move(args), [&configure](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
configure = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(configure);
GetNextFrameArgs frame_args;
// Request a frame. We have not set the frame args so we expect an error.
bool alloc_result = false;
sc.GetNextFrame(std::move(frame_args),
[&alloc_result](fpromise::result<FrameInfo, ScreenCaptureError> result) {
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.error(), ScreenCaptureError::MISSING_ARGS);
alloc_result = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(alloc_result);
// Ensure that the buffer image is released.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(1);
}
TEST_F(ScreenCaptureTest, ReleaseAvailableFrame) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, renderer_,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(1);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
bool alloc_result = false;
sc.Configure(std::move(args), [&alloc_result](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
alloc_result = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(alloc_result);
// Attempt to release a frame that is not used.
alloc_result = false;
sc.ReleaseFrame(/*buffer_id=*/0,
[&alloc_result](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.error(), ScreenCaptureError::INVALID_ARGS);
alloc_result = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(alloc_result);
// Ensure that the buffer image is released.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(1);
}
TEST_F(ScreenCaptureTest, ReleaseOutOfRangeFrame) {
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, renderer_,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(1);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
bool alloc_result = false;
sc.Configure(std::move(args), [&alloc_result](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
alloc_result = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(alloc_result);
// Attempt to release an index that is not within the range of valid indices for the buffer
// collection.
alloc_result = false;
sc.ReleaseFrame(/*buffer_id=*/1,
[&alloc_result](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_TRUE(result.is_error());
EXPECT_EQ(result.error(), ScreenCaptureError::INVALID_ARGS);
alloc_result = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(alloc_result);
// Ensure that the buffer image is released.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(1);
}
TEST_F(ScreenCaptureTest, ReleaseFrameFromFullBuffer) {
const uint32_t kNumBuffers = 3;
const uint32_t kFreedBufferId = 1;
fuchsia::ui::composition::ScreenCapturePtr screencapturer;
std::vector<std::shared_ptr<BufferCollectionImporter>> screen_capture_importers;
screen_capture_importers.push_back(buffer_collection_importer_);
screen_capture::ScreenCapture sc(screencapturer.NewRequest(), screen_capture_importers, renderer_,
[this]() { return this->GetRenderables(); });
allocation::BufferCollectionImportExportTokens ref_pair =
allocation::BufferCollectionImportExportTokens::New();
ScreenCaptureConfig args;
args.set_import_token(std::move(ref_pair.import_token));
args.set_size({1, 1});
args.set_buffer_count(kNumBuffers);
EXPECT_CALL(*mock_buffer_collection_importer_, ImportBufferImage(_))
.WillRepeatedly(testing::Return(true));
bool configure = false;
sc.Configure(std::move(args), [&configure](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_FALSE(result.is_error());
configure = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(configure);
// Fill buffers.
for (uint32_t i = 0; i < kNumBuffers; i++) {
// Capture call to renderer.
EXPECT_CALL(*renderer_, Render(_, _, _, _, _)).Times(1);
const auto& result = CaptureScreen(sc);
EXPECT_TRUE(result.is_ok());
}
// Capture screen again without freeing a buffer. This should cause an error.
const auto& result_full = CaptureScreen(sc);
EXPECT_TRUE(result_full.is_error());
EXPECT_EQ(result_full.error(), ScreenCaptureError::BUFFER_FULL);
// Release a buffer. After this we can call GetNextFrame() successfully again, and it should
// fill the buffer we released.
bool alloc_result = false;
sc.ReleaseFrame(/*buffer_id=*/kFreedBufferId,
[&alloc_result](fpromise::result<void, ScreenCaptureError> result) {
EXPECT_TRUE(result.is_ok());
alloc_result = true;
});
RunLoopUntilIdle();
EXPECT_TRUE(alloc_result);
// Capture call to renderer.
EXPECT_CALL(*renderer_, Render(_, _, _, _, _)).Times(1);
// Now we request another frame. This should be rendered to the VMO at the index we just released.
const auto& result_after_release = CaptureScreen(sc);
EXPECT_TRUE(result_after_release.is_ok());
EXPECT_EQ(kFreedBufferId, result_after_release.value().buffer_id());
// Ensure that the buffer images are released.
EXPECT_CALL(*mock_buffer_collection_importer_, ReleaseBufferImage(_)).Times(3);
}
} // namespace screen_capture::test