blob: 63197b2e5ed0a7af2754f05d05b4567a75420609 [file] [log] [blame]
// Copyright 2018 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/gfx/engine/object_linker.h"
#include <lib/fit/function.h>
#include <lib/zx/eventpair.h>
#include <zircon/types.h>
#include "gtest/gtest.h"
#include "src/lib/fsl/handles/object_info.h"
#include "src/ui/scenic/lib/gfx/tests/error_reporting_test.h"
namespace scenic_impl {
namespace gfx {
namespace test {
constexpr int kExportValue = 57;
constexpr int kImportValue = 42;
#define ERROR_IF_CALLED(str) \
std::bind([]() { EXPECT_TRUE(false) << "Delegate called unexpectedly: " << str; })
class ObjectLinkerTest : public ErrorReportingTest {
public:
void TearDown() override {
ErrorReportingTest::TearDown();
EXPECT_EQ(0u, object_linker_.ExportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedExportCount());
EXPECT_EQ(0u, object_linker_.ImportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedImportCount());
}
protected:
struct TestExportObj {
int value = 0;
explicit TestExportObj(int value) : value(value) {}
};
struct TestImportObj {
int value = 0;
explicit TestImportObj(int value) : value(value) {}
};
using TestObjectLinker = ObjectLinker<TestExportObj, TestImportObj>;
TestObjectLinker object_linker_;
};
TEST_F(ObjectLinkerTest, InitialState) {
EXPECT_EQ(0u, object_linker_.ExportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedExportCount());
EXPECT_EQ(0u, object_linker_.ImportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedImportCount());
TestObjectLinker::ExportLink export_link;
TestObjectLinker::ImportLink import_link;
EXPECT_FALSE(export_link.valid());
EXPECT_FALSE(import_link.valid());
EXPECT_FALSE(export_link.initialized());
EXPECT_FALSE(import_link.initialized());
}
TEST_F(ObjectLinkerTest, AllowsExport) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestExportObj export_obj(kExportValue);
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_link.valid());
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
}
TEST_F(ObjectLinkerTest, CannotExportInvalidToken) {
TestExportObj export_obj(kExportValue);
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), zx::eventpair(), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(1); // CreateExport throws an error.
EXPECT_FALSE(export_link.valid());
EXPECT_EQ(0u, object_linker_.ExportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedExportCount());
}
TEST_F(ObjectLinkerTest, CannotExportWithDeadExportToken) {
zx::eventpair export_token, import_token;
{
zx::eventpair export_token2;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token2, &import_token));
export_token = zx::eventpair{export_token2.get()};
// |export_token2| dies now, |export_token| is an invalid copy.
}
TestExportObj export_obj(kExportValue);
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(1); // CreateExport throws an error.
EXPECT_FALSE(export_link.valid());
EXPECT_EQ(0u, object_linker_.ExportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedExportCount());
}
TEST_F(ObjectLinkerTest, CanExportWithDeadImportToken) {
zx::eventpair export_token, import_token;
{
zx::eventpair import_token2;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token2));
import_token = zx::eventpair{import_token2.get()};
// |import_token2| dies now, |import_token| is an invalid copy.
}
TestExportObj export_obj(kExportValue);
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_link.valid());
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
}
TEST_F(ObjectLinkerTest, CannotExportSameTokenTwice) {
zx::eventpair export_token, export_token2, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
EXPECT_EQ(ZX_OK, export_token.duplicate(ZX_RIGHT_SAME_RIGHTS, &export_token2));
TestExportObj export_obj(kExportValue);
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_link.valid());
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
TestExportObj export_obj2(kExportValue);
TestObjectLinker::ExportLink export_link2 = object_linker_.CreateExport(
std::move(export_obj2), std::move(export_token2), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(1); // CreateExport throws an error.
EXPECT_FALSE(export_link2.valid());
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
}
TEST_F(ObjectLinkerTest, LinkDeathRemovesExport) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
{
TestExportObj export_obj(kExportValue);
TestObjectLinker::ExportLink export_link = object_linker_.CreateExport(
std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_link.valid());
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
// | export_link dies now. |
}
EXPECT_EQ(0u, object_linker_.ExportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedExportCount());
}
TEST_F(ObjectLinkerTest, AllowsImport) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestImportObj import_obj(kImportValue);
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(import_link.valid());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
}
TEST_F(ObjectLinkerTest, CannotImportInvalidToken) {
zx::eventpair import_token{ZX_HANDLE_INVALID};
TestImportObj import_obj(kImportValue);
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(1); // CreateImport throws an error.
EXPECT_FALSE(import_link.valid());
EXPECT_EQ(0u, object_linker_.ImportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedImportCount());
}
TEST_F(ObjectLinkerTest, CannotImportWithDeadImportToken) {
zx::eventpair export_token, import_token;
{
zx::eventpair import_token2;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token2));
import_token = zx::eventpair{import_token2.get()};
// |import_token2| dies now, |import_token| is an invalid copy.
}
TestImportObj import_obj(kImportValue);
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(1); // CreateImport throws an error.
EXPECT_FALSE(import_link.valid());
EXPECT_EQ(0u, object_linker_.ImportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedImportCount());
}
TEST_F(ObjectLinkerTest, CanImportWithDeadExportToken) {
zx::eventpair export_token, import_token;
{
zx::eventpair export_token2;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token2, &import_token));
export_token = zx::eventpair{export_token2.get()};
// |export_token2| dies now, |export_token| is an invalid copy.
}
TestImportObj import_obj(kImportValue);
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(import_link.valid());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
}
TEST_F(ObjectLinkerTest, CannotImportSameTokenTwice) {
zx::eventpair export_token, import_token, import_token2;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
EXPECT_EQ(ZX_OK, import_token.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_token2));
TestImportObj import_obj(kImportValue);
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(import_link.valid());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
TestObjectLinker::ImportLink import_link2 = object_linker_.CreateImport(
std::move(import_obj), std::move(import_token2), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(1); // CreateImport throws an error.
EXPECT_FALSE(import_link2.valid());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
}
TEST_F(ObjectLinkerTest, LinkDeathRemovesImport) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
{
TestImportObj import_obj(kImportValue);
TestObjectLinker::ImportLink import_link = object_linker_.CreateImport(
std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(import_link.valid());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
// | import_link dies now. |
}
EXPECT_EQ(0u, object_linker_.ImportCount());
}
// TODO(ES-179): Only fails in debug builds.
TEST_F(ObjectLinkerTest, DISABLED_InitializingLinkTwiceCausesDeath) {
TestExportObj export_obj(kExportValue);
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_link.valid());
export_link.Initialize(ERROR_IF_CALLED("export.link_resolved"),
ERROR_IF_CALLED("export.link_disconnected"));
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
// 2nd Initialize() attempt dies with a DCHECK.
EXPECT_DEATH_IF_SUPPORTED(export_link.Initialize(ERROR_IF_CALLED("export.link_resolved"),
ERROR_IF_CALLED("export.link_disconnected")),
"");
}
TEST_F(ObjectLinkerTest, InitializeLinksMatchingPeersExportBeforeImport) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestExportObj export_obj(kExportValue);
TestImportObj import_obj(kImportValue);
bool export_linked = false, import_linked = false;
bool export_disconnected = false, import_disconnected = false;
bool export_fail_on_disconnect_called = false, import_fail_on_disconnect_called = false;
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_link.valid());
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
{
TestObjectLinker::ImportLink import_link = object_linker_.CreateImport(
std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(import_link.valid());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
export_link.Initialize(
[&import_linked](TestImportObj obj) {
EXPECT_EQ(kImportValue, obj.value);
EXPECT_FALSE(import_linked);
import_linked = true;
},
[&export_fail_on_disconnect_called, &import_disconnected](bool on_link_destruction) {
EXPECT_FALSE(on_link_destruction);
EXPECT_FALSE(export_fail_on_disconnect_called);
EXPECT_FALSE(import_disconnected);
import_disconnected = true;
});
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_FALSE(export_linked);
EXPECT_FALSE(import_linked);
EXPECT_FALSE(export_disconnected);
EXPECT_FALSE(import_disconnected);
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
import_link.Initialize(
[&export_linked](TestExportObj obj) {
EXPECT_EQ(kExportValue, obj.value);
EXPECT_FALSE(export_linked);
export_linked = true;
},
[&import_fail_on_disconnect_called, &export_disconnected](bool on_link_destruction) {
EXPECT_TRUE(on_link_destruction);
EXPECT_FALSE(import_fail_on_disconnect_called);
EXPECT_FALSE(export_disconnected);
export_disconnected = true;
});
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_linked);
EXPECT_TRUE(import_linked);
EXPECT_FALSE(export_disconnected);
EXPECT_FALSE(import_disconnected);
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedExportCount());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedImportCount());
// |import_link| dies now, which also invalidates |export_link|.
}
EXPECT_TRUE(import_disconnected);
EXPECT_TRUE(export_disconnected);
// |export_link| dies now. No additional disconnect callbacks should be called.
export_fail_on_disconnect_called = true;
import_fail_on_disconnect_called = true;
}
TEST_F(ObjectLinkerTest, InitializeLinksMatchingPeersImportBeforeExport) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestExportObj export_obj(kExportValue);
TestImportObj import_obj(kImportValue);
bool export_linked = false, import_linked = false;
bool export_disconnected = false, import_disconnected = false;
bool export_fail_on_disconnect_called = false, import_fail_on_disconnect_called = false;
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(import_link.valid());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
{
import_link.Initialize(
[&export_linked](TestExportObj obj) {
EXPECT_EQ(kExportValue, obj.value);
EXPECT_FALSE(export_linked);
export_linked = true;
},
[&import_fail_on_disconnect_called, &export_disconnected](bool on_link_destruction) {
EXPECT_FALSE(on_link_destruction);
EXPECT_FALSE(import_fail_on_disconnect_called);
EXPECT_FALSE(export_disconnected);
export_disconnected = true;
});
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_FALSE(export_linked);
EXPECT_FALSE(import_linked);
EXPECT_FALSE(export_disconnected);
EXPECT_FALSE(import_disconnected);
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
TestObjectLinker::ExportLink export_link = object_linker_.CreateExport(
std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_link.valid());
EXPECT_FALSE(export_linked);
EXPECT_FALSE(import_linked);
EXPECT_FALSE(export_disconnected);
EXPECT_FALSE(import_disconnected);
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
export_link.Initialize(
[&import_linked](TestImportObj obj) {
EXPECT_EQ(kImportValue, obj.value);
EXPECT_FALSE(import_linked);
import_linked = true;
},
[&export_fail_on_disconnect_called, &import_disconnected](bool on_link_destruction) {
EXPECT_TRUE(on_link_destruction);
EXPECT_FALSE(export_fail_on_disconnect_called);
EXPECT_FALSE(import_disconnected);
import_disconnected = true;
});
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_linked);
EXPECT_TRUE(import_linked);
EXPECT_FALSE(export_disconnected);
EXPECT_FALSE(import_disconnected);
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedExportCount());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedImportCount());
// |export_link| dies now, which also invalidates |import_link|.
}
EXPECT_TRUE(export_disconnected);
EXPECT_TRUE(import_disconnected);
// |import_link| dies now. No additional disconnect callbacks should be called.
export_fail_on_disconnect_called = true;
import_fail_on_disconnect_called = true;
}
TEST_F(ObjectLinkerTest, InitializeDoesNotLinkNonMatchingPeers) {
bool export_disconnected = false, import_disconnected = false;
bool export_fail_on_disconnect_called = false, import_fail_on_disconnect_called = false;
{
zx::eventpair export_token, import_token;
zx::eventpair export_token2, import_token2;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token2, &import_token2));
TestExportObj export_obj(kExportValue);
TestImportObj import_obj(kImportValue);
TestObjectLinker::ImportLink import_link = object_linker_.CreateImport(
std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(import_link.valid());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
{
TestObjectLinker::ExportLink export_link = object_linker_.CreateExport(
std::move(export_obj), std::move(export_token2), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_link.valid());
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
import_link.Initialize(
ERROR_IF_CALLED("import.link_resolved"),
[&import_fail_on_disconnect_called, &export_disconnected](bool on_link_destruction) {
EXPECT_TRUE(on_link_destruction);
EXPECT_FALSE(import_fail_on_disconnect_called);
EXPECT_FALSE(export_disconnected);
export_disconnected = true;
});
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
export_link.Initialize(
ERROR_IF_CALLED("export.link_resolved"),
[&export_fail_on_disconnect_called, &import_disconnected](bool on_link_destruction) {
EXPECT_TRUE(on_link_destruction);
EXPECT_FALSE(export_fail_on_disconnect_called);
EXPECT_FALSE(import_disconnected);
import_disconnected = true;
});
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
// |export_link| dies now. Only the |export_link| disconnected callback should be called.
}
EXPECT_TRUE(import_disconnected);
EXPECT_FALSE(export_disconnected);
// |import_link| dies now. Only the |import_link| disconnected callback should be called.
export_fail_on_disconnect_called = true;
}
// Both links have died. No more disconnected callbacks should be called.
EXPECT_TRUE(import_disconnected);
EXPECT_TRUE(export_disconnected);
import_fail_on_disconnect_called = true;
}
TEST_F(ObjectLinkerTest, EarlyImportTokenDeathCausesExportDisconnection) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestExportObj export_obj(kExportValue);
bool import_disconnected = false;
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_link.valid());
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
// This should cause the export to get a link_disconnected event when it is
// initialized.
import_token.reset();
EXPECT_TRUE(RunLoopUntilIdle());
EXPECT_FALSE(import_disconnected);
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
export_link.Initialize(ERROR_IF_CALLED("export.link_resolved"),
[&import_disconnected](bool on_link_destruction) {
EXPECT_FALSE(on_link_destruction);
EXPECT_FALSE(import_disconnected);
import_disconnected = true;
});
EXPECT_FALSE(export_link.valid());
EXPECT_TRUE(import_disconnected);
EXPECT_EQ(0u, object_linker_.ExportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedExportCount());
}
TEST_F(ObjectLinkerTest, ImportTokenDeathCausesExportDisconnection) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestExportObj export_obj(kExportValue);
bool import_disconnected = false;
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(export_link.valid());
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
export_link.Initialize(ERROR_IF_CALLED("export.link_resolved"),
[&import_disconnected](bool on_link_destruction) {
EXPECT_FALSE(on_link_destruction);
EXPECT_FALSE(import_disconnected);
import_disconnected = true;
});
EXPECT_EQ(1u, object_linker_.ExportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedExportCount());
// This should cause the export to get a link_disconnected event when the
// eventloop ticks.
import_token.reset();
EXPECT_TRUE(RunLoopUntilIdle());
EXPECT_FALSE(export_link.valid());
EXPECT_TRUE(import_disconnected);
EXPECT_EQ(0u, object_linker_.ExportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedExportCount());
}
TEST_F(ObjectLinkerTest, EarlyExportTokenDeathCausesImportDisconnection) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestImportObj import_obj(kImportValue);
bool export_disconnected = false;
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(import_link.valid());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
// This should cause the import to get a link_disconnected event when it is
// initialized.
export_token.reset();
EXPECT_TRUE(RunLoopUntilIdle());
EXPECT_FALSE(export_disconnected);
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
import_link.Initialize(ERROR_IF_CALLED("import.link_resolved"),
[&export_disconnected](bool on_link_destruction) {
EXPECT_FALSE(on_link_destruction);
EXPECT_FALSE(export_disconnected);
export_disconnected = true;
});
EXPECT_FALSE(import_link.valid());
EXPECT_TRUE(export_disconnected);
EXPECT_EQ(0u, object_linker_.ImportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedImportCount());
}
TEST_F(ObjectLinkerTest, ExportTokenDeathCausesImportDisconnection) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestImportObj import_obj(kImportValue);
bool export_disconnected = false;
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
EXPECT_TRUE(import_link.valid());
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
import_link.Initialize(ERROR_IF_CALLED("import.link_resolved"),
[&export_disconnected](bool on_link_destruction) {
EXPECT_FALSE(on_link_destruction);
EXPECT_FALSE(export_disconnected);
export_disconnected = true;
});
EXPECT_EQ(1u, object_linker_.ImportCount());
EXPECT_EQ(1u, object_linker_.UnresolvedImportCount());
// This should cause the import to get a link_disconnected event when the
// eventloop ticks.
export_token.reset();
EXPECT_TRUE(RunLoopUntilIdle());
EXPECT_FALSE(import_link.valid());
EXPECT_TRUE(export_disconnected);
EXPECT_EQ(0u, object_linker_.ImportCount());
EXPECT_EQ(0u, object_linker_.UnresolvedImportCount());
}
TEST_F(ObjectLinkerTest, MoveInitializedLink) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestImportObj import_obj(kImportValue);
TestExportObj export_obj(kExportValue);
uint64_t import_linked = 0;
uint64_t export_linked = 0;
uint64_t import_disconnected = 0;
uint64_t export_disconnected = 0;
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
import_link.Initialize([&](TestExportObj obj) { ++export_linked; },
[&](bool on_link_destruction) { ++import_disconnected; });
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
export_link.Initialize([&](TestImportObj obj) { ++import_linked; },
[&](bool on_link_destruction) { ++export_disconnected; });
RunLoopUntilIdle();
EXPECT_EQ(1u, import_linked);
EXPECT_EQ(1u, export_linked);
EXPECT_EQ(0u, import_disconnected);
EXPECT_EQ(0u, export_disconnected);
// Move the successful links into new objects.
TestObjectLinker::ImportLink saved_import = std::move(import_link);
TestObjectLinker::ExportLink saved_export = std::move(export_link);
EXPECT_EQ(1u, import_linked);
EXPECT_EQ(1u, export_linked);
EXPECT_EQ(0u, import_disconnected);
EXPECT_EQ(0u, export_disconnected);
EXPECT_FALSE(import_link.valid());
EXPECT_FALSE(export_link.valid());
// Perform a second linking, re-using the stack variables that have been invalidated.
zx::eventpair export_token2, import_token2;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token2, &import_token2));
TestImportObj import_obj2(kImportValue);
TestExportObj export_obj2(kExportValue);
uint64_t import_linked2 = 0;
uint64_t export_linked2 = 0;
uint64_t import_disconnected2 = 0;
uint64_t export_disconnected2 = 0;
import_link = object_linker_.CreateImport(std::move(import_obj2), std::move(import_token2),
error_reporter());
import_link.Initialize([&](TestExportObj obj) { ++export_linked2; },
[&](bool on_link_destruction) { ++import_disconnected2; });
export_link = object_linker_.CreateExport(std::move(export_obj2), std::move(export_token2),
error_reporter());
export_link.Initialize([&](TestImportObj obj) { ++import_linked2; },
[&](bool on_link_destruction) { ++export_disconnected2; });
RunLoopUntilIdle();
// Confirm that linking has occurred.
EXPECT_EQ(1u, import_linked2);
EXPECT_EQ(1u, export_linked2);
EXPECT_EQ(0u, import_disconnected2);
EXPECT_EQ(0u, export_disconnected2);
// Invalidate one the saved objects.
saved_import = TestObjectLinker::ImportLink();
// Confirm that both of the saved objects have been invalidated and that their disconnect
// callbacks have been called.
EXPECT_FALSE(saved_import.valid());
EXPECT_FALSE(saved_export.valid());
EXPECT_EQ(1u, import_disconnected);
EXPECT_EQ(1u, export_disconnected);
// Confirm that the new links have been untouched.
EXPECT_TRUE(import_link.valid());
EXPECT_TRUE(export_link.valid());
EXPECT_EQ(0u, import_disconnected2);
EXPECT_EQ(0u, export_disconnected2);
// Invalidate the other saved object.
saved_export = TestObjectLinker::ExportLink();
// Confirm that nothing changes in the saved objects and that callbacks aren't called again.
EXPECT_FALSE(saved_import.valid());
EXPECT_FALSE(saved_export.valid());
EXPECT_EQ(1u, import_disconnected);
EXPECT_EQ(1u, export_disconnected);
// Confirm that the new links are still untouched.
EXPECT_TRUE(import_link.valid());
EXPECT_TRUE(export_link.valid());
EXPECT_EQ(0u, import_disconnected2);
EXPECT_EQ(0u, export_disconnected2);
// Invalidate in new links in the opposite order.
export_link = TestObjectLinker::ExportLink();
// Confirm that both new links are still invalidated and that their disconnect callbacks have
// been called.
EXPECT_FALSE(import_link.valid());
EXPECT_FALSE(export_link.valid());
EXPECT_EQ(1u, import_disconnected2);
EXPECT_EQ(1u, export_disconnected2);
// Invalidating the other link doesn't change anything.
import_link = TestObjectLinker::ImportLink();
EXPECT_FALSE(import_link.valid());
EXPECT_FALSE(export_link.valid());
EXPECT_EQ(1u, import_disconnected2);
EXPECT_EQ(1u, export_disconnected2);
}
TEST_F(ObjectLinkerTest, ImportLinkDeathDestroysImport) {
// Use a custom ObjectLinker template.
using SharedTestObjectLinker =
ObjectLinker<std::shared_ptr<TestExportObj>, std::shared_ptr<TestImportObj>>;
SharedTestObjectLinker object_linker;
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
auto import_obj = std::make_shared<TestImportObj>(kImportValue);
// Fetch a weak pointer to the original object so that invalidating the link will destroy the
// object.
std::weak_ptr<TestImportObj> weak_import_obj = import_obj;
EXPECT_FALSE(weak_import_obj.expired());
SharedTestObjectLinker::ImportLink import_link =
object_linker.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
import_link.Initialize(ERROR_IF_CALLED("import.link_resolved"), [](bool on_link_destruction) {});
// This should cause the import to get a link_disconnected event when the eventloop ticks, which
// will delete the only shared pointer to the object, invalidating the weak pointer.
export_token.reset();
EXPECT_TRUE(RunLoopUntilIdle());
EXPECT_TRUE(weak_import_obj.expired());
}
TEST_F(ObjectLinkerTest, ExportLinkDeathDestroysExport) {
// Use a custom ObjectLinker template.
using SharedTestObjectLinker =
ObjectLinker<std::shared_ptr<TestExportObj>, std::shared_ptr<TestImportObj>>;
SharedTestObjectLinker object_linker;
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
auto export_obj = std::make_shared<TestExportObj>(kExportValue);
// Fetch a weak pointer to the original object so that invalidating the link will destroy the
// object.
std::weak_ptr<TestExportObj> weak_export_obj = export_obj;
EXPECT_FALSE(weak_export_obj.expired());
SharedTestObjectLinker::ExportLink export_link =
object_linker.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
EXPECT_SCENIC_SESSION_ERROR_COUNT(0);
export_link.Initialize(ERROR_IF_CALLED("import.link_resolved"), [](bool on_link_destruction) {});
// This should cause the export to get a link_disconnected event when the eventloop ticks, which
// will delete the only shared pointer to the object, invalidating the weak pointer.
import_token.reset();
EXPECT_TRUE(RunLoopUntilIdle());
EXPECT_TRUE(weak_export_obj.expired());
}
TEST_F(ObjectLinkerTest, LinkOnlyReleasesTokenOnce) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
const zx_koid_t import_koid = fsl::GetKoid(import_token.get());
const zx_koid_t export_koid = fsl::GetKoid(export_token.get());
TestImportObj import_obj(kImportValue);
TestExportObj export_obj(kExportValue);
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
auto released_import = import_link.ReleaseToken();
EXPECT_TRUE(released_import.has_value());
EXPECT_EQ(fsl::GetKoid(released_import.value().get()), import_koid);
auto released_import2 = import_link.ReleaseToken();
EXPECT_FALSE(released_import2.has_value());
auto released_export = export_link.ReleaseToken();
EXPECT_TRUE(released_export.has_value());
EXPECT_EQ(fsl::GetKoid(released_export.value().get()), export_koid);
auto released_export2 = export_link.ReleaseToken();
EXPECT_FALSE(released_export2.has_value());
}
TEST_F(ObjectLinkerTest, ReleaseImportTokenBeforeInitialization) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
const zx_koid_t import_koid = fsl::GetKoid(import_token.get());
TestImportObj import_obj(kImportValue);
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
// Releasing the token invalidates the |import_link|.
auto token = import_link.ReleaseToken();
EXPECT_TRUE(token.has_value());
EXPECT_EQ(fsl::GetKoid(token.value().get()), import_koid);
EXPECT_FALSE(import_link.valid());
EXPECT_EQ(object_linker_.ImportCount(), 0u);
EXPECT_EQ(object_linker_.UnresolvedImportCount(), 0u);
}
TEST_F(ObjectLinkerTest, ReleaseExportTokenBeforeInitialization) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
const zx_koid_t export_koid = fsl::GetKoid(export_token.get());
TestExportObj export_obj(kExportValue);
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
// Releasing the token invalidates the |export_link|.
auto token = export_link.ReleaseToken();
EXPECT_TRUE(token.has_value());
EXPECT_EQ(fsl::GetKoid(token.value().get()), export_koid);
EXPECT_FALSE(export_link.valid());
EXPECT_EQ(object_linker_.ExportCount(), 0u);
EXPECT_EQ(object_linker_.UnresolvedExportCount(), 0u);
}
TEST_F(ObjectLinkerTest, ReleaseImportTokenAfterInitialization) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
const zx_koid_t import_koid = fsl::GetKoid(import_token.get());
TestImportObj import_obj(kImportValue);
bool import_disconnected = false;
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
import_link.Initialize(ERROR_IF_CALLED("import.link_resolved"), [&](bool on_link_destruction) {
EXPECT_FALSE(on_link_destruction);
import_disconnected = true;
});
// Releasing the token from the |import_link| causes the invalidation of the link.
auto token = import_link.ReleaseToken();
EXPECT_TRUE(token.has_value());
EXPECT_EQ(fsl::GetKoid(token.value().get()), import_koid);
EXPECT_FALSE(import_link.valid());
EXPECT_TRUE(import_disconnected);
EXPECT_EQ(object_linker_.ImportCount(), 0u);
EXPECT_EQ(object_linker_.UnresolvedImportCount(), 0u);
}
TEST_F(ObjectLinkerTest, ReleaseExportTokenAfterInitialization) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
const zx_koid_t export_koid = fsl::GetKoid(export_token.get());
TestExportObj export_obj(kExportValue);
bool export_disconnected = false;
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
export_link.Initialize(ERROR_IF_CALLED("export.link_resolved"), [&](bool on_link_destruction) {
EXPECT_FALSE(on_link_destruction);
export_disconnected = true;
});
// Releasing the token from the |export_link| causes the invalidation of the link.
auto token = export_link.ReleaseToken();
EXPECT_TRUE(token.has_value());
EXPECT_EQ(fsl::GetKoid(token.value().get()), export_koid);
EXPECT_FALSE(export_link.valid());
EXPECT_TRUE(export_disconnected);
EXPECT_EQ(object_linker_.ExportCount(), 0u);
EXPECT_EQ(object_linker_.UnresolvedExportCount(), 0u);
}
TEST_F(ObjectLinkerTest, ReleaseImportTokenAfterLinkResolution) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestImportObj import_obj(kImportValue);
TestExportObj export_obj(kExportValue);
int import_connected = 0;
int export_connected = 0;
int import_disconnected = 0;
int export_disconnected = 0;
int last_linked_import = 0;
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
import_link.Initialize(
[&import_connected](TestExportObj obj) { import_connected++; },
[&import_disconnected](bool on_link_destruction) { import_disconnected++; });
export_link.Initialize(
[&export_connected, &last_linked_import](TestImportObj obj) {
last_linked_import = obj.value;
export_connected++;
},
[&export_disconnected](bool on_link_destruction) { export_disconnected++; });
EXPECT_EQ(import_connected, 1);
EXPECT_EQ(export_connected, 1);
EXPECT_EQ(import_disconnected, 0);
EXPECT_EQ(export_disconnected, 0);
EXPECT_EQ(last_linked_import, kImportValue);
// Releasing the import token triggers the disconnect callbacks for both links. The export link
// remains valid but unresolved, but the import link becomes invalid.
auto token = import_link.ReleaseToken();
EXPECT_TRUE(token.has_value());
zx::eventpair import_token2 = zx::eventpair(std::move(token.value()));
RunLoopUntilIdle();
EXPECT_FALSE(import_link.valid());
EXPECT_TRUE(export_link.initialized());
EXPECT_EQ(import_disconnected, 1);
EXPECT_EQ(export_disconnected, 1);
EXPECT_EQ(object_linker_.UnresolvedImportCount(), 0u);
EXPECT_EQ(object_linker_.ImportCount(), 0u);
EXPECT_EQ(object_linker_.UnresolvedExportCount(), 1u);
EXPECT_EQ(object_linker_.ExportCount(), 1u);
// The import token can then be used to initialize a different ImportLink.
const int import_value2 = 2 * kImportValue;
TestImportObj import_obj2(import_value2);
int import_connected2 = 0;
int import_disconnected2 = 0;
TestObjectLinker::ImportLink import_link2 = object_linker_.CreateImport(
std::move(import_obj2), std::move(import_token2), error_reporter());
import_link2.Initialize(
[&import_connected2](TestExportObj obj) { import_connected2++; },
[&import_disconnected2](bool on_link_destruction) { import_disconnected2++; });
EXPECT_EQ(import_connected2, 1);
EXPECT_EQ(export_connected, 2);
EXPECT_EQ(import_disconnected2, 0);
EXPECT_EQ(export_disconnected, 1);
EXPECT_EQ(last_linked_import, import_value2);
EXPECT_EQ(object_linker_.UnresolvedImportCount(), 0u);
EXPECT_EQ(object_linker_.ImportCount(), 1u);
EXPECT_EQ(object_linker_.UnresolvedExportCount(), 0u);
EXPECT_EQ(object_linker_.ExportCount(), 1u);
}
TEST_F(ObjectLinkerTest, ReleaseExportTokenAfterLinkResolution) {
zx::eventpair export_token, import_token;
EXPECT_EQ(ZX_OK, zx::eventpair::create(0, &export_token, &import_token));
TestImportObj import_obj(kImportValue);
TestExportObj export_obj(kExportValue);
int import_connected = 0;
int export_connected = 0;
int import_disconnected = 0;
int export_disconnected = 0;
int last_linked_export = 0;
TestObjectLinker::ImportLink import_link =
object_linker_.CreateImport(std::move(import_obj), std::move(import_token), error_reporter());
TestObjectLinker::ExportLink export_link =
object_linker_.CreateExport(std::move(export_obj), std::move(export_token), error_reporter());
import_link.Initialize(
[&import_connected, &last_linked_export](TestExportObj obj) {
last_linked_export = obj.value;
import_connected++;
},
[&import_disconnected](bool on_link_destruction) { import_disconnected++; });
export_link.Initialize(
[&export_connected](TestImportObj obj) { export_connected++; },
[&export_disconnected](bool on_link_destruction) { export_disconnected++; });
EXPECT_EQ(import_connected, 1);
EXPECT_EQ(export_connected, 1);
EXPECT_EQ(import_disconnected, 0);
EXPECT_EQ(export_disconnected, 0);
EXPECT_EQ(last_linked_export, kExportValue);
// Releasing the export token triggers the disconnect callbacks for both links. The import link
// remains valid but unresolved, but the export link becomes invalid.
auto token = export_link.ReleaseToken();
EXPECT_TRUE(token.has_value());
zx::eventpair export_token2 = zx::eventpair(std::move(token.value()));
RunLoopUntilIdle();
EXPECT_FALSE(export_link.valid());
EXPECT_TRUE(import_link.initialized());
EXPECT_EQ(import_disconnected, 1);
EXPECT_EQ(export_disconnected, 1);
EXPECT_EQ(object_linker_.UnresolvedImportCount(), 1u);
EXPECT_EQ(object_linker_.ImportCount(), 1u);
EXPECT_EQ(object_linker_.UnresolvedExportCount(), 0u);
EXPECT_EQ(object_linker_.ExportCount(), 0u);
// The import token can then be used to initialize a different ExportLink.
const int export_value2 = 2 * kExportValue;
TestExportObj export_obj2(export_value2);
int export_connected2 = 0;
int export_disconnected2 = 0;
TestObjectLinker::ExportLink export_link2 = object_linker_.CreateExport(
std::move(export_obj2), std::move(export_token2), error_reporter());
export_link2.Initialize(
[&export_connected2](TestImportObj obj) { export_connected2++; },
[&export_disconnected2](bool on_link_destruction) { export_disconnected2++; });
EXPECT_EQ(import_connected, 2);
EXPECT_EQ(export_connected2, 1);
EXPECT_EQ(import_disconnected, 1);
EXPECT_EQ(export_disconnected2, 0);
EXPECT_EQ(last_linked_export, export_value2);
EXPECT_EQ(object_linker_.UnresolvedImportCount(), 0u);
EXPECT_EQ(object_linker_.ImportCount(), 1u);
EXPECT_EQ(object_linker_.UnresolvedExportCount(), 0u);
EXPECT_EQ(object_linker_.ExportCount(), 1u);
}
} // namespace test
} // namespace gfx
} // namespace scenic_impl