blob: 4ffd7416a4a50f8c4810a48dd4b82c139567c018 [file] [log] [blame]
Lang Hames617fc352017-09-05 03:34:09 +00001//===---------------------- RemoteObjectLayerTest.cpp ---------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
11#include "llvm/ExecutionEngine/Orc/NullResolver.h"
12#include "llvm/ExecutionEngine/Orc/RemoteObjectLayer.h"
13#include "OrcTestCommon.h"
14#include "QueueChannel.h"
15#include "gtest/gtest.h"
16
17using namespace llvm;
18using namespace llvm::orc;
19
20namespace {
21
22class MockObjectLayer {
23public:
24
25 using ObjHandleT = uint64_t;
26
Lang Hames589eece2018-02-21 21:55:49 +000027 using ObjectPtr = std::unique_ptr<MemoryBuffer>;
Lang Hames617fc352017-09-05 03:34:09 +000028
29 using LookupFn = std::function<JITSymbol(StringRef, bool)>;
30 using SymbolLookupTable = std::map<ObjHandleT, LookupFn>;
31
32 using AddObjectFtor =
33 std::function<Expected<ObjHandleT>(ObjectPtr, SymbolLookupTable&)>;
34
35 class ObjectNotFound : public remote::ResourceNotFound<ObjHandleT> {
36 public:
37 ObjectNotFound(ObjHandleT H) : ResourceNotFound(H, "Object handle") {}
38 };
39
40 MockObjectLayer(AddObjectFtor AddObject)
41 : AddObject(std::move(AddObject)) {}
42
43 Expected<ObjHandleT> addObject(ObjectPtr Obj,
44 std::shared_ptr<JITSymbolResolver> Resolver) {
Lang Hames589eece2018-02-21 21:55:49 +000045 return AddObject(std::move(Obj), SymTab);
Lang Hames617fc352017-09-05 03:34:09 +000046 }
47
48 Error removeObject(ObjHandleT H) {
49 if (SymTab.count(H))
50 return Error::success();
51 else
52 return make_error<ObjectNotFound>(H);
53 }
54
55 JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) {
56 for (auto KV : SymTab) {
57 if (auto Sym = KV.second(Name, ExportedSymbolsOnly))
58 return Sym;
59 else if (auto Err = Sym.takeError())
60 return std::move(Err);
61 }
62 return JITSymbol(nullptr);
63 }
64
65 JITSymbol findSymbolIn(ObjHandleT H, StringRef Name,
66 bool ExportedSymbolsOnly) {
67 auto LI = SymTab.find(H);
68 if (LI != SymTab.end())
69 return LI->second(Name, ExportedSymbolsOnly);
70 else
71 return make_error<ObjectNotFound>(H);
72 }
73
74 Error emitAndFinalize(ObjHandleT H) {
75 if (SymTab.count(H))
76 return Error::success();
77 else
78 return make_error<ObjectNotFound>(H);
79 }
80
81private:
82 AddObjectFtor AddObject;
83 SymbolLookupTable SymTab;
84};
85
86using RPCEndpoint = rpc::SingleThreadedRPCEndpoint<rpc::RawByteChannel>;
87
88MockObjectLayer::ObjectPtr createTestObject() {
89 OrcNativeTarget::initialize();
90 auto TM = std::unique_ptr<TargetMachine>(EngineBuilder().selectTarget());
91
92 if (!TM)
93 return nullptr;
94
95 LLVMContext Ctx;
96 ModuleBuilder MB(Ctx, TM->getTargetTriple().str(), "TestModule");
97 MB.getModule()->setDataLayout(TM->createDataLayout());
James Y Knightc0044112019-01-13 16:09:28 +000098 auto *Main = MB.createFunctionDecl(
99 FunctionType::get(Type::getInt32Ty(Ctx),
100 {Type::getInt32Ty(Ctx),
101 Type::getInt8PtrTy(Ctx)->getPointerTo()},
102 false),
103 "main");
Lang Hames617fc352017-09-05 03:34:09 +0000104 Main->getBasicBlockList().push_back(BasicBlock::Create(Ctx));
105 IRBuilder<> B(&Main->back());
106 B.CreateRet(ConstantInt::getSigned(Type::getInt32Ty(Ctx), 42));
107
108 SimpleCompiler IRCompiler(*TM);
Lang Hames589eece2018-02-21 21:55:49 +0000109 return IRCompiler(*MB.getModule());
Lang Hames617fc352017-09-05 03:34:09 +0000110}
111
112TEST(RemoteObjectLayer, AddObject) {
113 llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
114 auto TestObject = createTestObject();
115 if (!TestObject)
116 return;
117
118 auto Channels = createPairedQueueChannels();
119
Jonas Devlieghere45eb84f2018-11-11 01:46:03 +0000120 auto ReportError = [](Error Err) {
121 logAllUnhandledErrors(std::move(Err), llvm::errs());
122 };
Lang Hames617fc352017-09-05 03:34:09 +0000123
124 // Copy the bytes out of the test object: the copy will be used to verify
125 // that the original is correctly transmitted over RPC to the mock layer.
Lang Hames589eece2018-02-21 21:55:49 +0000126 StringRef ObjBytes = TestObject->getBuffer();
Lang Hames617fc352017-09-05 03:34:09 +0000127 std::vector<char> ObjContents(ObjBytes.size());
128 std::copy(ObjBytes.begin(), ObjBytes.end(), ObjContents.begin());
129
130 RPCEndpoint ClientEP(*Channels.first, true);
131 RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
132
133 RPCEndpoint ServerEP(*Channels.second, true);
134 MockObjectLayer BaseLayer(
135 [&ObjContents](MockObjectLayer::ObjectPtr Obj,
136 MockObjectLayer::SymbolLookupTable &SymTab) {
137
138 // Check that the received object file content matches the original.
Lang Hames589eece2018-02-21 21:55:49 +0000139 StringRef RPCObjContents = Obj->getBuffer();
Lang Hames617fc352017-09-05 03:34:09 +0000140 EXPECT_EQ(RPCObjContents.size(), ObjContents.size())
141 << "RPC'd object file has incorrect size";
142 EXPECT_TRUE(std::equal(RPCObjContents.begin(), RPCObjContents.end(),
143 ObjContents.begin()))
144 << "RPC'd object file content does not match original content";
145
146 return 1;
147 });
148 RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
149 ServerEP,
150 ReportError);
151
152 bool Finished = false;
153 ServerEP.addHandler<remote::utils::TerminateSession>(
154 [&]() { Finished = true; }
155 );
156
157 auto ServerThread =
158 std::thread([&]() {
159 while (!Finished)
160 cantFail(ServerEP.handleOne());
161 });
162
163 cantFail(Client.addObject(std::move(TestObject),
Lang Hames371412b2018-02-03 16:52:48 +0000164 std::make_shared<NullLegacyResolver>()));
Lang Hames617fc352017-09-05 03:34:09 +0000165 cantFail(ClientEP.callB<remote::utils::TerminateSession>());
166 ServerThread.join();
167}
168
169TEST(RemoteObjectLayer, AddObjectFailure) {
170 llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
171 auto TestObject = createTestObject();
172 if (!TestObject)
173 return;
174
175 auto Channels = createPairedQueueChannels();
176
177 auto ReportError =
178 [](Error Err) {
179 auto ErrMsg = toString(std::move(Err));
180 EXPECT_EQ(ErrMsg, "AddObjectFailure - Test Message")
181 << "Expected error string to be \"AddObjectFailure - Test Message\"";
182 };
183
184 RPCEndpoint ClientEP(*Channels.first, true);
185 RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
186
187 RPCEndpoint ServerEP(*Channels.second, true);
188 MockObjectLayer BaseLayer(
189 [](MockObjectLayer::ObjectPtr Obj,
190 MockObjectLayer::SymbolLookupTable &SymTab)
191 -> Expected<MockObjectLayer::ObjHandleT> {
192 return make_error<StringError>("AddObjectFailure - Test Message",
193 inconvertibleErrorCode());
194 });
195 RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
196 ServerEP,
197 ReportError);
198
199 bool Finished = false;
200 ServerEP.addHandler<remote::utils::TerminateSession>(
201 [&]() { Finished = true; }
202 );
203
204 auto ServerThread =
205 std::thread([&]() {
206 while (!Finished)
207 cantFail(ServerEP.handleOne());
208 });
209
Lang Hames371412b2018-02-03 16:52:48 +0000210 auto HandleOrErr = Client.addObject(std::move(TestObject),
211 std::make_shared<NullLegacyResolver>());
Lang Hames617fc352017-09-05 03:34:09 +0000212
213 EXPECT_FALSE(HandleOrErr) << "Expected error from addObject";
214
215 auto ErrMsg = toString(HandleOrErr.takeError());
216 EXPECT_EQ(ErrMsg, "AddObjectFailure - Test Message")
217 << "Expected error string to be \"AddObjectFailure - Test Message\"";
218
219 cantFail(ClientEP.callB<remote::utils::TerminateSession>());
220 ServerThread.join();
221}
222
223
224TEST(RemoteObjectLayer, RemoveObject) {
225 llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
226 auto TestObject = createTestObject();
227 if (!TestObject)
228 return;
229
230 auto Channels = createPairedQueueChannels();
231
Jonas Devlieghere45eb84f2018-11-11 01:46:03 +0000232 auto ReportError = [](Error Err) {
233 logAllUnhandledErrors(std::move(Err), llvm::errs());
234 };
Lang Hames617fc352017-09-05 03:34:09 +0000235
236 RPCEndpoint ClientEP(*Channels.first, true);
237 RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
238
239 RPCEndpoint ServerEP(*Channels.second, true);
240
241 MockObjectLayer BaseLayer(
242 [](MockObjectLayer::ObjectPtr Obj,
243 MockObjectLayer::SymbolLookupTable &SymTab) {
244 SymTab[1] = MockObjectLayer::LookupFn();
245 return 1;
246 });
247 RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
248 ServerEP,
249 ReportError);
250
251 bool Finished = false;
252 ServerEP.addHandler<remote::utils::TerminateSession>(
253 [&]() { Finished = true; }
254 );
255
256 auto ServerThread =
257 std::thread([&]() {
258 while (!Finished)
259 cantFail(ServerEP.handleOne());
260 });
261
Lang Hames371412b2018-02-03 16:52:48 +0000262 auto H = cantFail(Client.addObject(std::move(TestObject),
263 std::make_shared<NullLegacyResolver>()));
Lang Hames617fc352017-09-05 03:34:09 +0000264
265 cantFail(Client.removeObject(H));
266
267 cantFail(ClientEP.callB<remote::utils::TerminateSession>());
268 ServerThread.join();
269}
270
271TEST(RemoteObjectLayer, RemoveObjectFailure) {
272 llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
273 auto TestObject = createTestObject();
274 if (!TestObject)
275 return;
276
277 auto Channels = createPairedQueueChannels();
278
279 auto ReportError =
280 [](Error Err) {
281 auto ErrMsg = toString(std::move(Err));
282 EXPECT_EQ(ErrMsg, "Object handle 42 not found")
283 << "Expected error string to be \"Object handle 42 not found\"";
284 };
285
286 RPCEndpoint ClientEP(*Channels.first, true);
287 RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
288
289 RPCEndpoint ServerEP(*Channels.second, true);
290
291 // AddObject lambda does not update symbol table, so removeObject will treat
292 // this as a bad object handle.
293 MockObjectLayer BaseLayer(
294 [](MockObjectLayer::ObjectPtr Obj,
295 MockObjectLayer::SymbolLookupTable &SymTab) {
296 return 42;
297 });
298 RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
299 ServerEP,
300 ReportError);
301
302 bool Finished = false;
303 ServerEP.addHandler<remote::utils::TerminateSession>(
304 [&]() { Finished = true; }
305 );
306
307 auto ServerThread =
308 std::thread([&]() {
309 while (!Finished)
310 cantFail(ServerEP.handleOne());
311 });
312
Lang Hames371412b2018-02-03 16:52:48 +0000313 auto H = cantFail(Client.addObject(std::move(TestObject),
314 std::make_shared<NullLegacyResolver>()));
Lang Hames617fc352017-09-05 03:34:09 +0000315
316 auto Err = Client.removeObject(H);
317 EXPECT_TRUE(!!Err) << "Expected error from removeObject";
318
319 auto ErrMsg = toString(std::move(Err));
320 EXPECT_EQ(ErrMsg, "Object handle 42 not found")
321 << "Expected error string to be \"Object handle 42 not found\"";
322
323 cantFail(ClientEP.callB<remote::utils::TerminateSession>());
324 ServerThread.join();
325}
326
327TEST(RemoteObjectLayer, FindSymbol) {
328 llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
329 auto TestObject = createTestObject();
330 if (!TestObject)
331 return;
332
333 auto Channels = createPairedQueueChannels();
334
335 auto ReportError =
336 [](Error Err) {
337 auto ErrMsg = toString(std::move(Err));
Lang Hames4c744022017-09-05 22:24:40 +0000338 EXPECT_EQ(ErrMsg, "Could not find symbol 'badsymbol'")
Lang Hames617fc352017-09-05 03:34:09 +0000339 << "Expected error string to be \"Object handle 42 not found\"";
340 };
341
342 RPCEndpoint ClientEP(*Channels.first, true);
343 RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
344
345 RPCEndpoint ServerEP(*Channels.second, true);
346
347 // AddObject lambda does not update symbol table, so removeObject will treat
348 // this as a bad object handle.
349 MockObjectLayer BaseLayer(
350 [](MockObjectLayer::ObjectPtr Obj,
351 MockObjectLayer::SymbolLookupTable &SymTab) {
352 SymTab[42] =
353 [](StringRef Name, bool ExportedSymbolsOnly) -> JITSymbol {
354 if (Name == "foobar")
355 return JITSymbol(0x12348765, JITSymbolFlags::Exported);
Lang Hames4c744022017-09-05 22:24:40 +0000356 if (Name == "badsymbol")
357 return make_error<JITSymbolNotFound>(Name);
358 return nullptr;
Lang Hames617fc352017-09-05 03:34:09 +0000359 };
360 return 42;
361 });
362 RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
363 ServerEP,
364 ReportError);
365
366 bool Finished = false;
367 ServerEP.addHandler<remote::utils::TerminateSession>(
368 [&]() { Finished = true; }
369 );
370
371 auto ServerThread =
372 std::thread([&]() {
373 while (!Finished)
374 cantFail(ServerEP.handleOne());
375 });
376
377 cantFail(Client.addObject(std::move(TestObject),
Lang Hames371412b2018-02-03 16:52:48 +0000378 std::make_shared<NullLegacyResolver>()));
Lang Hames617fc352017-09-05 03:34:09 +0000379
Lang Hames4c744022017-09-05 22:24:40 +0000380 // Check that we can find and materialize a valid symbol.
Lang Hames617fc352017-09-05 03:34:09 +0000381 auto Sym1 = Client.findSymbol("foobar", true);
Lang Hames617fc352017-09-05 03:34:09 +0000382 EXPECT_TRUE(!!Sym1) << "Symbol 'foobar' should be findable";
383 EXPECT_EQ(cantFail(Sym1.getAddress()), 0x12348765ULL)
384 << "Symbol 'foobar' does not return the correct address";
385
Lang Hames4c744022017-09-05 22:24:40 +0000386 {
387 // Check that we can return a symbol containing an error.
388 auto Sym2 = Client.findSymbol("badsymbol", true);
389 EXPECT_FALSE(!!Sym2) << "Symbol 'badsymbol' should not be findable";
390 auto Err = Sym2.takeError();
391 EXPECT_TRUE(!!Err) << "Sym2 should contain an error value";
392 auto ErrMsg = toString(std::move(Err));
393 EXPECT_EQ(ErrMsg, "Could not find symbol 'badsymbol'")
394 << "Expected symbol-not-found error for Sym2";
395 }
396
397 {
398 // Check that we can return a 'null' symbol.
399 auto Sym3 = Client.findSymbol("baz", true);
400 EXPECT_FALSE(!!Sym3) << "Symbol 'baz' should convert to false";
401 auto Err = Sym3.takeError();
402 EXPECT_FALSE(!!Err) << "Symbol 'baz' should not contain an error";
403 }
Lang Hames617fc352017-09-05 03:34:09 +0000404
405 cantFail(ClientEP.callB<remote::utils::TerminateSession>());
406 ServerThread.join();
407}
408
409TEST(RemoteObjectLayer, FindSymbolIn) {
410 llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
411 auto TestObject = createTestObject();
412 if (!TestObject)
413 return;
414
415 auto Channels = createPairedQueueChannels();
416
417 auto ReportError =
418 [](Error Err) {
419 auto ErrMsg = toString(std::move(Err));
420 EXPECT_EQ(ErrMsg, "Could not find symbol 'barbaz'")
421 << "Expected error string to be \"Object handle 42 not found\"";
422 };
423
424 RPCEndpoint ClientEP(*Channels.first, true);
425 RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
426
427 RPCEndpoint ServerEP(*Channels.second, true);
428
429 // AddObject lambda does not update symbol table, so removeObject will treat
430 // this as a bad object handle.
431 MockObjectLayer BaseLayer(
432 [](MockObjectLayer::ObjectPtr Obj,
433 MockObjectLayer::SymbolLookupTable &SymTab) {
434 SymTab[42] =
435 [](StringRef Name, bool ExportedSymbolsOnly) -> JITSymbol {
436 if (Name == "foobar")
437 return JITSymbol(0x12348765, JITSymbolFlags::Exported);
438 return make_error<JITSymbolNotFound>(Name);
439 };
440 // Dummy symbol table entry - this should not be visible to
441 // findSymbolIn.
442 SymTab[43] =
443 [](StringRef Name, bool ExportedSymbolsOnly) -> JITSymbol {
444 if (Name == "barbaz")
445 return JITSymbol(0xdeadbeef, JITSymbolFlags::Exported);
446 return make_error<JITSymbolNotFound>(Name);
447 };
448
449 return 42;
450 });
451 RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
452 ServerEP,
453 ReportError);
454
455 bool Finished = false;
456 ServerEP.addHandler<remote::utils::TerminateSession>(
457 [&]() { Finished = true; }
458 );
459
460 auto ServerThread =
461 std::thread([&]() {
462 while (!Finished)
463 cantFail(ServerEP.handleOne());
464 });
465
466 auto H = cantFail(Client.addObject(std::move(TestObject),
Lang Hames371412b2018-02-03 16:52:48 +0000467 std::make_shared<NullLegacyResolver>()));
Lang Hames617fc352017-09-05 03:34:09 +0000468
469 auto Sym1 = Client.findSymbolIn(H, "foobar", true);
470
471 EXPECT_TRUE(!!Sym1) << "Symbol 'foobar' should be findable";
472 EXPECT_EQ(cantFail(Sym1.getAddress()), 0x12348765ULL)
473 << "Symbol 'foobar' does not return the correct address";
474
475 auto Sym2 = Client.findSymbolIn(H, "barbaz", true);
476 EXPECT_FALSE(!!Sym2) << "Symbol 'barbaz' should not be findable";
477 auto Err = Sym2.takeError();
478 EXPECT_TRUE(!!Err) << "Sym2 should contain an error value";
479 auto ErrMsg = toString(std::move(Err));
480 EXPECT_EQ(ErrMsg, "Could not find symbol 'barbaz'")
481 << "Expected symbol-not-found error for Sym2";
482
483 cantFail(ClientEP.callB<remote::utils::TerminateSession>());
484 ServerThread.join();
485}
486
487TEST(RemoteObjectLayer, EmitAndFinalize) {
488 llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
489 auto TestObject = createTestObject();
490 if (!TestObject)
491 return;
492
493 auto Channels = createPairedQueueChannels();
494
Jonas Devlieghere45eb84f2018-11-11 01:46:03 +0000495 auto ReportError = [](Error Err) {
496 logAllUnhandledErrors(std::move(Err), llvm::errs());
497 };
Lang Hames617fc352017-09-05 03:34:09 +0000498
499 RPCEndpoint ClientEP(*Channels.first, true);
500 RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
501
502 RPCEndpoint ServerEP(*Channels.second, true);
503
504 MockObjectLayer BaseLayer(
505 [](MockObjectLayer::ObjectPtr Obj,
506 MockObjectLayer::SymbolLookupTable &SymTab) {
507 SymTab[1] = MockObjectLayer::LookupFn();
508 return 1;
509 });
510 RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
511 ServerEP,
512 ReportError);
513
514 bool Finished = false;
515 ServerEP.addHandler<remote::utils::TerminateSession>(
516 [&]() { Finished = true; }
517 );
518
519 auto ServerThread =
520 std::thread([&]() {
521 while (!Finished)
522 cantFail(ServerEP.handleOne());
523 });
524
525 auto H = cantFail(Client.addObject(std::move(TestObject),
Lang Hames371412b2018-02-03 16:52:48 +0000526 std::make_shared<NullLegacyResolver>()));
Lang Hames617fc352017-09-05 03:34:09 +0000527
528 auto Err = Client.emitAndFinalize(H);
529 EXPECT_FALSE(!!Err) << "emitAndFinalize should work";
530
531 cantFail(ClientEP.callB<remote::utils::TerminateSession>());
532 ServerThread.join();
533}
534
535TEST(RemoteObjectLayer, EmitAndFinalizeFailure) {
536 llvm::orc::rpc::registerStringError<rpc::RawByteChannel>();
537 auto TestObject = createTestObject();
538 if (!TestObject)
539 return;
540
541 auto Channels = createPairedQueueChannels();
542
543 auto ReportError =
544 [](Error Err) {
545 auto ErrMsg = toString(std::move(Err));
546 EXPECT_EQ(ErrMsg, "Object handle 1 not found")
547 << "Expected bad handle error";
548 };
549
550 RPCEndpoint ClientEP(*Channels.first, true);
551 RemoteObjectClientLayer<RPCEndpoint> Client(ClientEP, ReportError);
552
553 RPCEndpoint ServerEP(*Channels.second, true);
554
555 MockObjectLayer BaseLayer(
556 [](MockObjectLayer::ObjectPtr Obj,
557 MockObjectLayer::SymbolLookupTable &SymTab) {
558 return 1;
559 });
560 RemoteObjectServerLayer<MockObjectLayer, RPCEndpoint> Server(BaseLayer,
561 ServerEP,
562 ReportError);
563
564 bool Finished = false;
565 ServerEP.addHandler<remote::utils::TerminateSession>(
566 [&]() { Finished = true; }
567 );
568
569 auto ServerThread =
570 std::thread([&]() {
571 while (!Finished)
572 cantFail(ServerEP.handleOne());
573 });
574
575 auto H = cantFail(Client.addObject(std::move(TestObject),
Lang Hames371412b2018-02-03 16:52:48 +0000576 std::make_shared<NullLegacyResolver>()));
Lang Hames617fc352017-09-05 03:34:09 +0000577
578 auto Err = Client.emitAndFinalize(H);
579 EXPECT_TRUE(!!Err) << "emitAndFinalize should work";
580
581 auto ErrMsg = toString(std::move(Err));
582 EXPECT_EQ(ErrMsg, "Object handle 1 not found")
583 << "emitAndFinalize returned incorrect error";
584
585 cantFail(ClientEP.callB<remote::utils::TerminateSession>());
586 ServerThread.join();
587}
588
589}