blob: e857d2741c8789f2001cafda9e8ae145321fc23e [file] [log] [blame]
// Copyright 2019 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/display/display_coordinator_listener.h"
#include <fuchsia/hardware/display/cpp/fidl.h>
#include <fuchsia/hardware/display/types/cpp/fidl.h>
#include <fuchsia/images2/cpp/fidl.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/channel.h>
#include <gtest/gtest.h>
#include "lib/fidl/cpp/comparison.h"
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
#include "src/ui/scenic/lib/display/tests/mock_display_coordinator.h"
namespace scenic_impl {
namespace display {
namespace test {
namespace {
struct ChannelPair {
zx::channel server;
zx::channel client;
};
ChannelPair CreateChannelPair() {
ChannelPair c;
FX_CHECK(ZX_OK == zx::channel::create(0, &c.server, &c.client));
return c;
}
} // namespace
class DisplayCoordinatorListenerTest : public gtest::TestLoopFixture {
public:
void SetUp() {
ChannelPair coordinator_channel = CreateChannelPair();
mock_display_coordinator_ =
std::make_unique<MockDisplayCoordinator>(fuchsia::hardware::display::Info{});
mock_display_coordinator_->Bind(std::move(coordinator_channel.server));
auto coordinator = std::make_shared<fuchsia::hardware::display::CoordinatorSyncPtr>();
coordinator->Bind(std::move(coordinator_channel.client));
display_coordinator_listener_ = std::make_unique<DisplayCoordinatorListener>(coordinator);
}
DisplayCoordinatorListener* display_coordinator_listener() {
return display_coordinator_listener_.get();
}
void ResetMockDisplayCoordinator() { mock_display_coordinator_.reset(); }
void ResetDisplayCoordinatorListener() { display_coordinator_listener_.reset(); }
MockDisplayCoordinator* mock_display_coordinator() { return mock_display_coordinator_.get(); }
private:
std::unique_ptr<MockDisplayCoordinator> mock_display_coordinator_;
std::unique_ptr<DisplayCoordinatorListener> display_coordinator_listener_;
};
using DisplayCoordinatorListenerBasicTest = gtest::TestLoopFixture;
// Verify the documented constructor behavior doesn't cause any crash.
TEST_F(DisplayCoordinatorListenerBasicTest, ConstructorArgs) {
// Valid arguments.
ChannelPair coordinator_channel = CreateChannelPair();
auto coordinator = std::make_shared<fuchsia::hardware::display::CoordinatorSyncPtr>();
coordinator->Bind(std::move(coordinator_channel.client));
DisplayCoordinatorListener listener(coordinator);
}
// Verify that DisplayCoordinator connects to the FIDL service and the
// connection can be torn down when the coordinator server channel is closed.
TEST_F(DisplayCoordinatorListenerTest, ConnectAndDisconnect) {
display_coordinator_listener()->InitializeCallbacks(/*displays_changed_cb=*/nullptr,
/*client_ownership_change_cb=*/nullptr);
EXPECT_TRUE(mock_display_coordinator()->binding().is_bound());
RunLoopUntilIdle();
EXPECT_TRUE(mock_display_coordinator()->binding().is_bound());
mock_display_coordinator()->ResetCoordinatorBinding();
RunLoopUntilIdle();
// Expect no crashes on teardown.
ResetDisplayCoordinatorListener();
RunLoopUntilIdle();
}
TEST_F(DisplayCoordinatorListenerTest, OnDisplaysChanged) {
std::vector<fuchsia::hardware::display::Info> displays_added;
std::vector<fuchsia::hardware::display::types::DisplayId> displays_removed;
auto displays_changed_cb =
[&displays_added, &displays_removed](
std::vector<fuchsia::hardware::display::Info> added,
std::vector<fuchsia::hardware::display::types::DisplayId> removed) {
displays_added = added;
displays_removed = removed;
};
display_coordinator_listener()->InitializeCallbacks(std::move(displays_changed_cb),
/*client_ownership_change_cb=*/nullptr);
fuchsia::hardware::display::Mode test_mode;
test_mode.horizontal_resolution = 1024;
test_mode.vertical_resolution = 800;
test_mode.refresh_rate_e2 = 60;
test_mode.flags = 0;
fuchsia::hardware::display::Info test_display;
test_display.id = {.value = 1};
test_display.modes = {test_mode};
test_display.pixel_format = {fuchsia::images2::PixelFormat::B8G8R8A8};
test_display.manufacturer_name = "fake_manufacturer_name";
test_display.monitor_name = "fake_monitor_name";
test_display.monitor_serial = "fake_monitor_serial";
mock_display_coordinator()->events().OnDisplaysChanged(/*added=*/{test_display},
/*removed=*/{{.value = 2u}});
ASSERT_EQ(0u, displays_added.size());
ASSERT_EQ(0u, displays_removed.size());
RunLoopUntilIdle();
ASSERT_EQ(1u, displays_added.size());
ASSERT_EQ(1u, displays_removed.size());
EXPECT_TRUE(fidl::Equals(displays_added[0], test_display));
EXPECT_EQ(displays_removed[0].value, 2u);
// Verify we stop getting callbacks after ClearCallbacks().
display_coordinator_listener()->ClearCallbacks();
mock_display_coordinator()->events().OnDisplaysChanged(/*added=*/{},
/*removed=*/{{.value = 3u}});
RunLoopUntilIdle();
// Expect that nothing changed.
ASSERT_EQ(1u, displays_added.size());
ASSERT_EQ(1u, displays_removed.size());
EXPECT_EQ(displays_removed[0].value, 2u);
// Expect no crashes on teardown.
ResetDisplayCoordinatorListener();
RunLoopUntilIdle();
}
TEST_F(DisplayCoordinatorListenerTest, OnClientOwnershipChangeCallback) {
bool has_ownership = false;
auto client_ownership_change_cb = [&has_ownership](bool ownership) { has_ownership = ownership; };
display_coordinator_listener()->InitializeCallbacks(
/*displays_changed_cb=*/nullptr, std::move(client_ownership_change_cb));
mock_display_coordinator()->events().OnClientOwnershipChange(true);
EXPECT_FALSE(has_ownership);
RunLoopUntilIdle();
EXPECT_TRUE(has_ownership);
// Verify we stop getting callbacks after ClearCallbacks().
display_coordinator_listener()->ClearCallbacks();
mock_display_coordinator()->events().OnClientOwnershipChange(false);
RunLoopUntilIdle();
// Expect that nothing changed.
EXPECT_TRUE(has_ownership);
// Expect no crashes on teardown.
ResetDisplayCoordinatorListener();
RunLoopUntilIdle();
}
TEST_F(DisplayCoordinatorListenerTest, OnVsyncCallback) {
fuchsia::hardware::display::types::DisplayId last_display_id = {
.value = fuchsia::hardware::display::types::INVALID_DISP_ID};
uint64_t last_timestamp = 0u;
fuchsia::hardware::display::types::ConfigStamp last_config_stamp = {
.value = fuchsia::hardware::display::types::INVALID_CONFIG_STAMP_VALUE};
auto vsync_cb = [&](fuchsia::hardware::display::types::DisplayId display_id, uint64_t timestamp,
fuchsia::hardware::display::types::ConfigStamp stamp, uint64_t cookie) {
last_display_id = display_id;
last_timestamp = timestamp;
last_config_stamp = std::move(stamp);
};
display_coordinator_listener()->InitializeCallbacks(
/*displays_changed_cb=*/nullptr,
/*client_ownership_change_cb=*/nullptr);
display_coordinator_listener()->SetOnVsyncCallback(std::move(vsync_cb));
constexpr fuchsia::hardware::display::types::DisplayId kTestDisplayId = {.value = 1};
constexpr fuchsia::hardware::display::types::DisplayId kInvalidDisplayId = {.value = 2};
const uint64_t kTestTimestamp = 111111u;
const fuchsia::hardware::display::types::ConfigStamp kConfigStamp = {.value = 2u};
mock_display_coordinator()->events().OnVsync(kTestDisplayId, kTestTimestamp, kConfigStamp, 0);
ASSERT_EQ(fuchsia::hardware::display::types::INVALID_CONFIG_STAMP_VALUE, last_config_stamp.value);
RunLoopUntilIdle();
EXPECT_EQ(kTestDisplayId.value, last_display_id.value);
EXPECT_EQ(kTestTimestamp, last_timestamp);
EXPECT_EQ(last_config_stamp.value, kConfigStamp.value);
// Verify we stop getting callbacks after ClearCallbacks().
display_coordinator_listener()->ClearCallbacks();
mock_display_coordinator()->events().OnVsync(kInvalidDisplayId, kTestTimestamp, kConfigStamp, 0);
// Expect that nothing changed.
RunLoopUntilIdle();
EXPECT_EQ(kTestDisplayId.value, last_display_id.value);
// Expect no crashes on teardown.
ResetDisplayCoordinatorListener();
RunLoopUntilIdle();
}
} // namespace test
} // namespace display
} // namespace scenic_impl