Checkpoint for pass 3 of Peripheral integration.

This commit is contained in:
Kevin Dewald
2025-08-02 23:01:34 -07:00
parent 870ae77cfd
commit 465fee45fc
19 changed files with 266 additions and 47 deletions

View File

@@ -6,11 +6,14 @@
#include "utils.hpp"
#include "simpleble/Config.h"
#include "simpleble/SimpleBLE.h"
using namespace std::chrono_literals;
int main() {
SimpleBLE::Config::SimpleBluez::use_legacy_bluez_backend = false;
auto adapter_optional = Utils::getAdapter();
if (!adapter_optional.has_value()) {

View File

@@ -5,6 +5,8 @@
#include <simplebluez/Device.h>
#include <simplebluez/interfaces/Adapter1.h>
#include <simpledbus/advanced/ProxyRegistry.h>
#include <kvn/kvn_safe_callback.hpp>
#include <functional>
@@ -41,6 +43,8 @@ class Adapter : public SimpleDBus::Proxy {
std::shared_ptr<Adapter1> adapter1();
kvn::safe_callback<void(std::shared_ptr<Device> device)> _on_device_updated;
static const SimpleDBus::AutoRegisterProxy<Adapter> registry;
};
} // namespace SimpleBluez

View File

@@ -6,6 +6,8 @@
#include <simplebluez/Types.h>
#include <simplebluez/interfaces/GattCharacteristic1.h>
#include <simpledbus/advanced/ProxyRegistry.h>
#include <cstdlib>
namespace SimpleBluez {
@@ -41,6 +43,8 @@ class Characteristic : public SimpleDBus::Proxy {
std::shared_ptr<SimpleDBus::Proxy> path_create(const std::string& path) override;
std::shared_ptr<GattCharacteristic1> gattcharacteristic1();
static const SimpleDBus::AutoRegisterProxy<Characteristic> registry;
};
} // namespace SimpleBluez

View File

@@ -5,6 +5,8 @@
#include <simplebluez/Types.h>
#include <simplebluez/interfaces/GattDescriptor1.h>
#include <simpledbus/advanced/ProxyRegistry.h>
#include <cstdlib>
namespace SimpleBluez {
@@ -28,6 +30,8 @@ class Descriptor : public SimpleDBus::Proxy {
private:
std::shared_ptr<GattDescriptor1> gattdescriptor1();
static const SimpleDBus::AutoRegisterProxy<Descriptor> registry;
};
} // namespace SimpleBluez

View File

@@ -7,6 +7,8 @@
#include <simplebluez/interfaces/Battery1.h>
#include <simplebluez/interfaces/Device1.h>
#include <simpledbus/advanced/ProxyRegistry.h>
namespace SimpleBluez {
class Device : public SimpleDBus::Proxy {
@@ -59,6 +61,8 @@ class Device : public SimpleDBus::Proxy {
std::shared_ptr<Device1> device1();
std::shared_ptr<Battery1> battery1();
static const SimpleDBus::AutoRegisterProxy<Device> registry;
};
} // namespace SimpleBluez

View File

@@ -5,6 +5,8 @@
#include <simplebluez/Characteristic.h>
#include <simplebluez/interfaces/GattService1.h>
#include <simpledbus/advanced/ProxyRegistry.h>
namespace SimpleBluez {
class Service : public SimpleDBus::Proxy {
@@ -22,6 +24,8 @@ class Service : public SimpleDBus::Proxy {
private:
std::shared_ptr<SimpleDBus::Proxy> path_create(const std::string& path) override;
std::shared_ptr<GattService1> gattservice1();
static const SimpleDBus::AutoRegisterProxy<Service> registry;
};
} // namespace SimpleBluez

View File

@@ -5,6 +5,13 @@
using namespace SimpleBluez;
const SimpleDBus::AutoRegisterProxy<Adapter> Adapter::registry{
"hci",
[](std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name, const std::string& path) -> std::shared_ptr<SimpleDBus::Proxy> {
return std::static_pointer_cast<SimpleDBus::Proxy>(std::make_shared<Adapter>(conn, bus_name, path));
}
};
Adapter::Adapter(std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name, const std::string& path)
: Proxy(conn, bus_name, path) {}

View File

@@ -6,8 +6,13 @@ using namespace SimpleBluez;
Agent::Agent(std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name, const std::string& path)
: Proxy(conn, bus_name, path) {
_interfaces.emplace(std::make_pair(
"org.bluez.Agent1", std::static_pointer_cast<SimpleDBus::Interface>(std::make_shared<Agent1>(_conn, _path))));
{
std::scoped_lock lock(_global_interfaces_mutex);
auto& path_interfaces = _global_interfaces[SimpleDBus::Path(_path)];
path_interfaces.emplace(std::make_pair(
"org.bluez.Agent1", std::static_pointer_cast<SimpleDBus::Interface>(std::make_shared<Agent1>(_conn, _path))));
}
}
std::string Agent::capabilities() const {

View File

@@ -7,7 +7,10 @@ using namespace SimpleBluez;
BluezRoot::BluezRoot(std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name, const std::string& path)
: Proxy(conn, bus_name, path) {
_interfaces.emplace(std::make_pair("org.freedesktop.DBus.ObjectManager", std::make_shared<SimpleDBus::ObjectManager>(conn, _bus_name, _path)));
{
std::scoped_lock lock(_global_interfaces_mutex);
_global_interfaces[SimpleDBus::Path(_path)].emplace(std::make_pair("org.freedesktop.DBus.ObjectManager", std::make_shared<SimpleDBus::ObjectManager>(conn, _bus_name, _path)));
}
object_manager()->InterfacesAdded = [&](std::string path, SimpleDBus::Holder options) { path_add(path, options); };
object_manager()->InterfacesRemoved = [&](std::string path, SimpleDBus::Holder options) {

View File

@@ -4,6 +4,13 @@
using namespace SimpleBluez;
const SimpleDBus::AutoRegisterProxy<Characteristic> Characteristic::registry{
"char",
[](std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name, const std::string& path) -> std::shared_ptr<SimpleDBus::Proxy> {
return std::static_pointer_cast<SimpleDBus::Proxy>(std::make_shared<Characteristic>(conn, bus_name, path));
}
};
Characteristic::Characteristic(std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name,
const std::string& path)
: Proxy(conn, bus_name, path) {}

View File

@@ -2,6 +2,13 @@
using namespace SimpleBluez;
const SimpleDBus::AutoRegisterProxy<Descriptor> Descriptor::registry{
"descriptor",
[](std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name, const std::string& path) -> std::shared_ptr<SimpleDBus::Proxy> {
return std::static_pointer_cast<SimpleDBus::Proxy>(std::make_shared<Descriptor>(conn, bus_name, path));
}
};
Descriptor::Descriptor(std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name,
const std::string& path)
: Proxy(conn, bus_name, path) {}

View File

@@ -6,6 +6,13 @@
using namespace SimpleBluez;
const SimpleDBus::AutoRegisterProxy<Device> Device::registry{
"dev",
[](std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name, const std::string& path) -> std::shared_ptr<SimpleDBus::Proxy> {
return std::static_pointer_cast<SimpleDBus::Proxy>(std::make_shared<Device>(conn, bus_name, path));
}
};
Device::Device(std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name, const std::string& path)
: Proxy(conn, bus_name, path) {}

View File

@@ -4,6 +4,13 @@
using namespace SimpleBluez;
const SimpleDBus::AutoRegisterProxy<Service> Service::registry{
"service",
[](std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name, const std::string& path) -> std::shared_ptr<SimpleDBus::Proxy> {
return std::static_pointer_cast<SimpleDBus::Proxy>(std::make_shared<Service>(conn, bus_name, path));
}
};
Service::Service(std::shared_ptr<SimpleDBus::Connection> conn, const std::string& bus_name, const std::string& path)
: Proxy(conn, bus_name, path) {}

View File

@@ -44,7 +44,8 @@ class InterfaceRegistry {
iface->load(options);
return iface;
}
return nullptr;
return std::make_shared<Interface>(conn, bus_name, path, key);
}
private:

View File

@@ -99,6 +99,17 @@ class Proxy {
return result;
}
// NOTE: This method is used only for testing purposes.
static void purge_global_interfaces() {
std::scoped_lock lock(_global_interfaces_mutex);
_global_interfaces.clear();
}
static void purge_global_proxies() {
std::scoped_lock lock(_global_proxies_mutex);
_global_proxies.clear();
}
protected:
bool _valid;
bool _registered;
@@ -107,11 +118,16 @@ class Proxy {
std::shared_ptr<Connection> _conn;
std::map<std::string, std::shared_ptr<Interface>> _interfaces;
std::map<std::string, std::shared_ptr<Proxy>> _children;
std::recursive_mutex _interface_access_mutex;
std::recursive_mutex _child_access_mutex;
std::map<std::string, std::shared_ptr<Proxy>> children() const;
static std::map<Path, std::unordered_map<std::string, std::shared_ptr<Interface>>> _global_interfaces;
static std::recursive_mutex _global_interfaces_mutex;
static std::map<Path, std::shared_ptr<Proxy>> _global_proxies;
static std::recursive_mutex _global_proxies_mutex;
};
} // namespace SimpleDBus

View File

@@ -0,0 +1,60 @@
#pragma once
#include <functional>
#include <memory>
#include <stdexcept>
#include <string>
#include <typeindex>
#include <simpledbus/base/Connection.h>
#include <simpledbus/base/Holder.h>
#include <simpledbus/base/Logging.h>
namespace SimpleDBus {
class Proxy;
using ProxyCreatorFunction = std::shared_ptr<Proxy> (*)(std::shared_ptr<Connection>, const std::string&, const std::string&);
class ProxyRegistry {
public:
static ProxyRegistry& getInstance() {
static ProxyRegistry instance;
return instance;
}
template <typename T>
void registerClass(const std::string& prefix, ProxyCreatorFunction creator) {
static_assert(std::is_base_of<Proxy, T>::value, "T must inherit from Proxy");
creators[prefix] = creator;
}
bool isRegistered(const std::string& prefix) const {
return creators.find(prefix) != creators.end();
}
std::shared_ptr<Proxy> create(const std::string& prefix, std::shared_ptr<Connection> conn, const std::string& bus_name, const std::string& path) const {
auto it = creators.find(prefix);
if (it != creators.end()) {
auto proxy = it->second(conn, bus_name, path);
return proxy;
}
return std::make_shared<Proxy>(conn, bus_name, path);
}
private:
std::unordered_map<std::string, ProxyCreatorFunction> creators;
ProxyRegistry() = default;
};
template <typename T>
struct AutoRegisterProxy {
AutoRegisterProxy(const std::string& prefix, ProxyCreatorFunction creator) {
static_assert(std::is_base_of<Proxy, T>::value, "T must inherit from Proxy");
ProxyRegistry::getInstance().registerClass<T>(prefix, creator);
LOG_DEBUG("Registered class with prefix {}", prefix);
}
};
} // namespace SimpleDBus

View File

@@ -6,10 +6,18 @@
#include <simpledbus/base/Path.h>
#include <algorithm>
#include <fmt/core.h>
// #include <simpledbus/interfaces/Properties.h>
using namespace SimpleDBus;
std::map<Path, std::unordered_map<std::string, std::shared_ptr<Interface>>> Proxy::_global_interfaces;
std::recursive_mutex Proxy::_global_interfaces_mutex;
std::map<Path, std::shared_ptr<Proxy>> Proxy::_global_proxies;
std::recursive_mutex Proxy::_global_proxies_mutex;
Proxy::Proxy(std::shared_ptr<Connection> conn, const std::string& bus_name, const std::string& path)
: _conn(conn), _bus_name(bus_name), _path(path), _valid(true), _registered(false) {
// TODO: UNCOMMENT THIS WHEN MIGRATING TO NEW PROXY FORWARDING LOGIC
@@ -42,9 +50,34 @@ std::string Proxy::path() const { return _path; }
std::string Proxy::bus_name() const { return _bus_name; }
const std::map<std::string, std::shared_ptr<Proxy>>& Proxy::children() { return _children; }
std::map<std::string, std::shared_ptr<Proxy>> Proxy::children() const {
std::map<std::string, std::shared_ptr<Proxy>> direct_children;
std::scoped_lock lock(_global_proxies_mutex);
const std::map<std::string, std::shared_ptr<Interface>>& Proxy::interfaces() { return _interfaces; }
// Find the first potential child path (current path + "/")
std::string child_prefix = _path + "/";
auto it = _global_proxies.lower_bound(Path(child_prefix));
// Iterate through potential children
while (it != _global_proxies.end()) {
const std::string& path_str = static_cast<std::string>(it->first);
// If we've moved beyond children of this path, stop
if (path_str.find(child_prefix) != 0) {
break;
}
// Check if this is a direct child (not a grandchild)
if (PathUtils::is_child(_path, path_str)) {
std::string child_path_name = PathUtils::next_child_strip(_path, path_str);
direct_children.emplace(child_path_name, it->second);
}
++it;
}
return direct_children;
}
// ----- PATH HANDLING -----
@@ -72,22 +105,27 @@ std::string Proxy::introspect() {
// ----- INTERFACE HANDLING -----
bool Proxy::interface_exists(const std::string& name) {
std::scoped_lock lock(_interface_access_mutex);
return _interfaces.find(name) != _interfaces.end();
std::scoped_lock lock(_global_interfaces_mutex);
return _global_interfaces[Path(_path)].find(name) != _global_interfaces[Path(_path)].end();
}
std::shared_ptr<Interface> Proxy::interface_get(const std::string& name) {
std::scoped_lock lock(_interface_access_mutex);
if (!interface_exists(name)) {
throw Exception::InterfaceNotFoundException(_path, name);
std::scoped_lock lock(_global_interfaces_mutex);
auto& path_interfaces = _global_interfaces[Path(_path)];
auto iface_it = path_interfaces.find(name);
if (iface_it != path_interfaces.end()) {
return iface_it->second;
}
return _interfaces[name];
throw Exception::InterfaceNotFoundException(_path, name);
}
size_t Proxy::interfaces_count() {
std::scoped_lock lock(_global_interfaces_mutex);
auto& path_interfaces = _global_interfaces[Path(_path)];
size_t count = 0;
std::scoped_lock lock(_interface_access_mutex);
for (auto& [iface_name, interface] : _interfaces) {
for (auto& [iface_name, interface] : path_interfaces) {
if (interface->is_loaded()) {
count++;
}
@@ -98,25 +136,30 @@ size_t Proxy::interfaces_count() {
void Proxy::interfaces_load(Holder managed_interfaces) {
auto managed_interface = managed_interfaces.get_dict_string();
std::scoped_lock lock(_interface_access_mutex);
std::scoped_lock lock(_global_interfaces_mutex);
auto& path_interfaces = _global_interfaces[Path(_path)];
for (auto& [iface_name, options] : managed_interface) {
// If the interface has not been loaded, load it
if (!interface_exists(iface_name)) {
auto iface_it = path_interfaces.find(iface_name);
if (iface_it == path_interfaces.end()) {
// Interface doesn't exist, create it
if (InterfaceRegistry::getInstance().isRegistered(iface_name)) {
_interfaces.emplace(std::make_pair(
iface_name, InterfaceRegistry::getInstance().create(iface_name, _conn, _bus_name, _path, options)));
auto interface = InterfaceRegistry::getInstance().create(iface_name, _conn, _bus_name, _path, options);
path_interfaces[iface_name] = interface;
} else {
LOG_WARN("Interface {} not registered within SimpleDBus", iface_name);
}
} else {
_interfaces[iface_name]->load(options);
// Interface exists, reload it
iface_it->second->load(options);
}
}
}
void Proxy::interfaces_reload(Holder managed_interfaces) {
std::scoped_lock lock(_interface_access_mutex);
for (auto& [iface_name, interface] : _interfaces) {
std::scoped_lock lock(_global_interfaces_mutex);
auto& path_interfaces = _global_interfaces[Path(_path)];
for (auto& [iface_name, interface] : path_interfaces) {
interface->unload();
}
@@ -124,18 +167,22 @@ void Proxy::interfaces_reload(Holder managed_interfaces) {
}
void Proxy::interfaces_unload(SimpleDBus::Holder removed_interfaces) {
std::scoped_lock lock(_interface_access_mutex);
std::scoped_lock lock(_global_interfaces_mutex);
auto& path_interfaces = _global_interfaces[Path(_path)];
for (auto& option : removed_interfaces.get_array()) {
std::string iface_name = option.get_string();
if (interface_exists(iface_name)) {
_interfaces[iface_name]->unload();
auto iface_it = path_interfaces.find(iface_name);
if (iface_it != path_interfaces.end()) {
iface_it->second->unload();
}
}
}
bool Proxy::interfaces_loaded() {
std::scoped_lock lock(_interface_access_mutex);
for (auto& [iface_name, interface] : _interfaces) {
std::scoped_lock lock(_global_interfaces_mutex);
auto& path_interfaces = _global_interfaces[Path(_path)];
for (auto& [iface_name, interface] : path_interfaces) {
if (interface->is_loaded()) {
return true;
}
@@ -146,16 +193,17 @@ bool Proxy::interfaces_loaded() {
// ----- CHILD HANDLING -----
bool Proxy::path_exists(const std::string& path) {
std::scoped_lock lock(_child_access_mutex);
return _children.find(path) != _children.end();
std::scoped_lock lock(_global_proxies_mutex);
return _global_proxies.find(Path(path)) != _global_proxies.end();
}
std::shared_ptr<Proxy> Proxy::path_get(const std::string& path) {
std::scoped_lock lock(_child_access_mutex);
if (!path_exists(path)) {
std::scoped_lock lock(_global_proxies_mutex);
auto it = _global_proxies.find(Path(path));
if (it == _global_proxies.end()) {
throw Exception::PathNotFoundException(_path, path);
}
return _children[path];
return it->second;
}
void Proxy::path_add(const std::string& path, SimpleDBus::Holder managed_interfaces) {
@@ -203,12 +251,12 @@ void Proxy::path_add(const std::string& path, SimpleDBus::Holder managed_interfa
}
}
bool Proxy::path_remove(const std::string& path, SimpleDBus::Holder options) {
bool Proxy::path_remove(const std::string& path, SimpleDBus::Holder removed_interfaces) {
// `options` contains an array of strings of the interfaces that need to be removed.
if (path == _path) {
invalidate();
interfaces_unload(options);
interfaces_unload(removed_interfaces);
return path_prune();
}
@@ -223,7 +271,7 @@ bool Proxy::path_remove(const std::string& path, SimpleDBus::Holder options) {
// If the path is a direct child of the proxy path, forward the request to the child proxy.
std::string child_path = PathUtils::next_child(_path, path);
if (path_exists(child_path)) {
bool must_erase = _children.at(child_path)->path_remove(path, options);
bool must_erase = _children.at(child_path)->path_remove(path, removed_interfaces);
// if the child proxy is no longer needed and there is only one active instance of the child proxy,
// then remove it.
@@ -253,6 +301,10 @@ bool Proxy::path_prune() {
// For self to be pruned, the following conditions must be met:
// 1. The proxy has no children
// 2. The proxy has no interfaces or all interfaces are disabled.
fmt::print("Prune: {} children, {} interfaces\n", _children.size(), interfaces_count());
fmt::print("Interfaces: {}\n", interfaces_loaded());
if (_children.empty() && !interfaces_loaded()) {
return true;
}
@@ -264,9 +316,14 @@ Holder Proxy::path_collect() {
SimpleDBus::Holder result = SimpleDBus::Holder::create_dict();
SimpleDBus::Holder interfaces = SimpleDBus::Holder::create_dict();
for (const auto& [interface_name, interface_ptr] : _interfaces) {
SimpleDBus::Holder properties = interface_ptr->property_collect();
interfaces.dict_append(SimpleDBus::Holder::Type::STRING, interface_name, std::move(properties));
// Use global interfaces instead of local _interfaces
std::scoped_lock lock(_global_interfaces_mutex);
auto path_it = _global_interfaces.find(Path(_path));
if (path_it != _global_interfaces.end()) {
for (const auto& [interface_name, interface_ptr] : path_it->second) {
SimpleDBus::Holder properties = interface_ptr->property_collect();
interfaces.dict_append(SimpleDBus::Holder::Type::STRING, interface_name, std::move(properties));
}
}
if (!interfaces.get_dict_string().empty()) {
@@ -373,3 +430,5 @@ void Proxy::message_handle(Message& msg) {
LOG_ERROR("Unhandled message: {}", msg.to_string());
}
}

View File

@@ -5,6 +5,9 @@
using namespace SimpleDBus;
TEST(ProxyChildren, AppendChild) {
Proxy::purge_global_interfaces();
Proxy::purge_global_proxies();
Proxy p = Proxy(nullptr, "", "/a/b");
p.path_add("/a/b/c", Holder());
@@ -14,6 +17,9 @@ TEST(ProxyChildren, AppendChild) {
}
TEST(ProxyChildren, AppendRepeatedChild) {
Proxy::purge_global_interfaces();
Proxy::purge_global_proxies();
Proxy p = Proxy(nullptr, "", "/a/b");
p.path_add("/a/b/c", Holder());
@@ -23,6 +29,9 @@ TEST(ProxyChildren, AppendRepeatedChild) {
}
TEST(ProxyChildren, AppendExtendedChild) {
Proxy::purge_global_interfaces();
Proxy::purge_global_proxies();
Proxy p = Proxy(nullptr, "", "/");
p.path_add("/a/b/c/d", Holder());
@@ -43,6 +52,9 @@ TEST(ProxyChildren, AppendExtendedChild) {
}
TEST(ProxyChildren, RemoveSelf) {
Proxy::purge_global_interfaces();
Proxy::purge_global_proxies();
Proxy p = Proxy(nullptr, "", "/");
// Should notify that the proxy can be safely deleted, as nothing worth keeping is left
@@ -65,6 +77,9 @@ TEST(ProxyChildren, RemoveSelf) {
}
TEST(ProxyChildren, RemoveChildNoInterfaces) {
Proxy::purge_global_interfaces();
Proxy::purge_global_proxies();
Proxy p = Proxy(nullptr, "", "/");
p.path_add("/a", Holder());
@@ -82,6 +97,9 @@ TEST(ProxyChildren, RemoveChildNoInterfaces) {
}
TEST(ProxyChildren, RemoveChildWithInterfaces) {
Proxy::purge_global_interfaces();
Proxy::purge_global_proxies();
Proxy p = Proxy(nullptr, "", "/");
Holder managed_interfaces = Holder::create_dict();
@@ -98,7 +116,6 @@ TEST(ProxyChildren, RemoveChildWithInterfaces) {
{
std::shared_ptr<Proxy> p_a = std::dynamic_pointer_cast<Proxy>(p.children().at("/a"));
ASSERT_EQ(1, p_a->interfaces_count());
ASSERT_EQ(1, p_a->interfaces().count("i.1"));
}
// Remove the second interface

View File

@@ -44,8 +44,8 @@ TEST(ProxyInterfaces, LoadInterfaces) {
h.interfaces_load(managed_interfaces);
EXPECT_TRUE(h.interfaces_loaded());
EXPECT_EQ(1, h.interfaces().size());
EXPECT_EQ(1, h.interfaces().count("i.1"));
EXPECT_EQ(1, h.interfaces_count());
EXPECT_TRUE(h.interface_exists("i.1"));
}
TEST(ProxyInterfaces, UnloadInterfaces) {
@@ -64,7 +64,7 @@ TEST(ProxyInterfaces, UnloadInterfaces) {
EXPECT_EQ(2, h.interfaces_count());
EXPECT_TRUE(h.interfaces_loaded());
EXPECT_FALSE(h.interfaces().at("i.3")->is_loaded());
EXPECT_FALSE(h.interface_get("i.3")->is_loaded());
removed_interfaces = Holder::create_array();
removed_interfaces.array_append(Holder::create_string("i.2"));
@@ -72,7 +72,7 @@ TEST(ProxyInterfaces, UnloadInterfaces) {
EXPECT_EQ(1, h.interfaces_count());
EXPECT_TRUE(h.interfaces_loaded());
EXPECT_FALSE(h.interfaces().at("i.2")->is_loaded());
EXPECT_FALSE(h.interface_get("i.2")->is_loaded());
removed_interfaces = Holder::create_array();
removed_interfaces.array_append(Holder::create_string("i.1"));
@@ -80,7 +80,7 @@ TEST(ProxyInterfaces, UnloadInterfaces) {
EXPECT_EQ(0, h.interfaces_count());
EXPECT_FALSE(h.interfaces_loaded());
EXPECT_FALSE(h.interfaces().at("i.1")->is_loaded());
EXPECT_FALSE(h.interface_get("i.1")->is_loaded());
}
TEST(ProxyInterfaces, ReloadInterfaces) {