-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathTauDomainPropertyTest.cpp
More file actions
208 lines (171 loc) · 6.91 KB
/
TauDomainPropertyTest.cpp
File metadata and controls
208 lines (171 loc) · 6.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// Tests the Tau IDL -> Agent/Proxy workflow across two named domains.
// Domain A hosts an ISensorAgent that exposes a single integer property.
// Domain B holds an ISensorProxy and fetches that property value over the network.
#include <gtest/gtest.h>
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include "KAI/Core/BuiltinTypes/All.h"
#include "KAI/Core/Registry.h"
#include "KAI/Core/StringStreamTraits.h"
#include "KAI/Core/Tree.h"
#include "KAI/Language/Tau/Generate/GenerateAgent.h"
#include "KAI/Language/Tau/Generate/GenerateProxy.h"
#include "KAI/Network/Agent.h"
#include "KAI/Network/ConnectionEvent.h"
#include "KAI/Network/Domain.h"
#include "KAI/Network/Future.h"
#include "KAI/Network/Node.h"
#include "KAI/Network/ProxyBase.h"
using namespace kai;
using namespace kai::net;
using namespace std::chrono_literals;
// The .tau IDL defining a sensor interface with a single read/write property.
static const std::string kSensorTau = R"(
namespace Sensor {
interface ISensor {
int Value;
}
}
)";
namespace {
// Servant class - lives in Domain A and holds the actual data.
struct SensorImpl {
int value = 42;
};
// Agent in Domain A: registers SensorImpl.value as a network-accessible property.
class ISensorAgent : public Agent<SensorImpl> {
public:
explicit ISensorAgent(Node &node,
std::shared_ptr<SensorImpl> impl = std::make_shared<SensorImpl>())
: Agent<SensorImpl>(node, std::move(impl)) {
BindMemberProperty("Value", &SensorImpl::value);
}
};
// Proxy in Domain B: provides typed access to the remote ISensorAgent.
class ISensorProxy : public ProxyBase {
public:
ISensorProxy(Node &node, NetHandle handle) : ProxyBase(node, handle) {}
Future<int> Value() { return Fetch<int>("Value"); }
Future<void> SetValue(int v) { return Store("Value", std::move(v)); }
};
// Pump both nodes until pred() becomes true or the timeout expires.
static bool PollUntil(Node &a, Node &b, std::function<bool()> pred,
std::chrono::milliseconds timeout = 3000ms) {
const auto deadline = std::chrono::steady_clock::now() + timeout;
while (!pred()) {
a.Update();
b.Update();
if (std::chrono::steady_clock::now() >= deadline) return false;
std::this_thread::sleep_for(1ms);
}
return true;
}
} // namespace
// Verify that the .tau IDL is parsed and generates the expected class names.
TEST(TauDomainPropertyTest, IdlGeneratesExpectedClassNames) {
std::string proxyOut;
tau::Generate::GenerateProxy proxyGen(kSensorTau.c_str(), proxyOut);
ASSERT_FALSE(proxyGen.Failed) << "Proxy generation failed: " << proxyGen.Error;
EXPECT_NE(proxyOut.find("ISensorProxy"), std::string::npos)
<< "Generated proxy should contain ISensorProxy:\n" << proxyOut;
std::string agentOut;
tau::Generate::GenerateAgent agentGen(kSensorTau.c_str(), agentOut);
ASSERT_FALSE(agentGen.Failed) << "Agent generation failed: " << agentGen.Error;
EXPECT_NE(agentOut.find("ISensorAgent"), std::string::npos)
<< "Generated agent should contain ISensorAgent:\n" << agentOut;
}
// Domain A hosts the agent; Domain B connects and fetches the property value.
TEST(TauDomainPropertyTest, DomainBProxyFetchesPropertyFromDomainA) {
Registry registry;
registry.AddClass<void>();
registry.AddClass<bool>();
registry.AddClass<int>();
registry.AddClass<float>();
registry.AddClass<String>();
registry.AddClass<StringStream>();
registry.AddClass<BinaryStream>();
registry.AddClass<Array>();
registry.AddClass<Map>();
// Domain A: the agent lives here.
Node nodeA;
nodeA.SetRegistry(®istry);
int port = 0;
for (int candidate = 16100; candidate < 16200; ++candidate) {
nodeA.Listen(IpAddress("127.0.0.1"), candidate);
if (nodeA.IsRunning()) {
port = candidate;
break;
}
}
if (port == 0) GTEST_SKIP() << "Local networking is unavailable in this environment";
// Domain B: the proxy lives here. Pump Domain A whenever Domain B ticks.
Node nodeB;
nodeB.SetRegistry(®istry);
nodeB.SetUpdatePump([&]() { nodeA.Update(); });
bool connected = false;
nodeB.SetConnectionEventCallback([&](ConnectionEvent ev, const NetAddress &) {
if (ev == ConnectionEvent::Connected) connected = true;
});
nodeB.Connect(IpAddress("127.0.0.1"), port);
bool ok = PollUntil(nodeA, nodeB, [&] { return connected; });
if (!ok) GTEST_SKIP() << "Domain B did not connect to Domain A within timeout";
// Create the agent in Domain A. Initial property value is 42.
ISensorAgent agent(nodeA);
// Tell Domain B where the agent lives so it can route requests.
NetAddress agentAddr("127.0.0.1", static_cast<unsigned short>(port));
nodeB.BindProxyAddress(agent.Handle(), agentAddr);
ISensorProxy proxy(nodeB, agent.Handle());
// Domain B fetches the property from Domain A.
auto future = proxy.Value();
int result = nodeB.WaitFor(future, 2000ms);
EXPECT_TRUE(future.Succeeded());
EXPECT_EQ(result, 42);
}
// Domain B can also write a property back to Domain A.
TEST(TauDomainPropertyTest, DomainBProxySetsPropertyOnDomainA) {
Registry registry;
registry.AddClass<void>();
registry.AddClass<bool>();
registry.AddClass<int>();
registry.AddClass<float>();
registry.AddClass<String>();
registry.AddClass<StringStream>();
registry.AddClass<BinaryStream>();
registry.AddClass<Array>();
registry.AddClass<Map>();
Node nodeA;
nodeA.SetRegistry(®istry);
int port = 0;
for (int candidate = 16200; candidate < 16300; ++candidate) {
nodeA.Listen(IpAddress("127.0.0.1"), candidate);
if (nodeA.IsRunning()) {
port = candidate;
break;
}
}
if (port == 0) GTEST_SKIP() << "Local networking is unavailable in this environment";
Node nodeB;
nodeB.SetRegistry(®istry);
nodeB.SetUpdatePump([&]() { nodeA.Update(); });
bool connected = false;
nodeB.SetConnectionEventCallback([&](ConnectionEvent ev, const NetAddress &) {
if (ev == ConnectionEvent::Connected) connected = true;
});
nodeB.Connect(IpAddress("127.0.0.1"), port);
bool ok = PollUntil(nodeA, nodeB, [&] { return connected; });
if (!ok) GTEST_SKIP() << "Domain B did not connect to Domain A within timeout";
// Create agent with default value 42, then Domain B overwrites it.
ISensorAgent agent(nodeA);
NetAddress agentAddr("127.0.0.1", static_cast<unsigned short>(port));
nodeB.BindProxyAddress(agent.Handle(), agentAddr);
ISensorProxy proxy(nodeB, agent.Handle());
// Set the property from Domain B to 99.
auto setFuture = proxy.SetValue(99);
nodeB.WaitFor(setFuture, 2000ms);
EXPECT_TRUE(setFuture.Succeeded());
// Verify Domain A sees the updated value.
EXPECT_EQ(agent.Instance().value, 99);
}