10#include "gtest/gtest.h"
23TEST(TypedPortsTest, UnwrapTypeAlias) {
28 TypeAliasType channelAlias(
"channelAlias",
"ChannelAlias", &channel);
36TEST(TypedPortsTest, GetUnwrappedAs) {
38 StructType payload(
"payload", {{
"data", &bits}});
40 TypeAliasType nestedAlias(
"nestedAlias",
"NestedAlias", &alias);
42 TypeAliasType channelAlias(
"channelAlias",
"ChannelAlias", &channel);
43 ChannelType outerChannel(
"outerChannel", &channelAlias);
61TEST(TypedPortsTest, VoidTypeCompatibility) {
74TEST(TypedPortsTest, BoolTypeCompatibility) {
87TEST(TypedPortsTest, SignedIntTypeCompatibility) {
137TEST(TypedPortsTest, UnsignedIntTypeCompatibility) {
203 static constexpr std::string_view _ESI_ID =
"MyModule.TestStruct";
208struct DeserializerWithESIID {
209 static constexpr std::string_view _ESI_ID =
"MyModule.DeserializedStruct";
211 class TypeDeserializer
212 :
public QueuedDecodeTypeDeserializer<DeserializerWithESIID> {
214 using Base = QueuedDecodeTypeDeserializer<DeserializerWithESIID>;
218 explicit TypeDeserializer(OutputCallback output)
219 : Base(std::move(output)) {}
222 DecodedOutputs decode(std::unique_ptr<SegmentedMessageData> &msg)
override {
229TEST(TypedPortsTest, ESIIDTypeCompatibility) {
231 StructType matchType(
"MyModule.TestStruct", {});
235 StructType mismatchType(
"OtherModule.OtherStruct", {});
240 UIntType uintWithMatchingID(
"MyModule.TestStruct", 32);
244TEST(TypedPortsTest, NullPortTypeThrows) {
254struct UnknownCppType {
258TEST(TypedPortsTest, FallbackThrows) {
272 MockWritePort(
const Type *type) : WriteChannelPort(type) {}
274 void connect(
const ConnectOptions &opts = {})
override {
278 void disconnect()
override { connected =
false; }
279 bool isConnected()
const override {
return connected; }
281 MessageData lastWritten;
283 bool acceptTryWrites =
true;
286 void writeImpl(
const MessageData &data)
override { lastWritten =
data; }
287 bool tryWriteImpl(
const MessageData &data)
override {
288 if (!acceptTryWrites)
295 bool connected =
false;
302 using WriteChannelPort::WriteChannelPort;
305 std::vector<MessageData> written;
308 void writeImpl(
const MessageData &data)
override { written.push_back(data); }
309 bool tryWriteImpl(
const MessageData &data)
override {
313 written.push_back(data);
318TEST(TypedPortsTest, TranslatedTryWriteKeepsNothingWhenRefused) {
320 StructType into(
"into", {{
"a", &u8}, {
"b", &u8}});
323 {{
"f0", {{
"a"}}}, {
"f1", {{
"b"}}}});
324 CreditedWritePort port(&window);
329 EXPECT_FALSE(port.tryWrite(data));
330 EXPECT_TRUE(port.flush());
331 EXPECT_TRUE(port.written.empty());
335 EXPECT_TRUE(port.tryWrite(data));
336 EXPECT_EQ(port.written.size(), 1u);
337 EXPECT_FALSE(port.flush());
339 EXPECT_TRUE(port.flush());
340 EXPECT_EQ(port.written.size(), 2u);
343TEST(TypedPortsTest, TypedWritePortConnectThrowsOnMismatch) {
345 MockWritePort mock(&uint32);
350TEST(TypedPortsTest, TypedWritePortConnectSucceeds) {
352 MockWritePort mock(&sint31);
354 EXPECT_NO_THROW(typed.connect());
355 EXPECT_TRUE(typed.isConnected());
358TEST(TypedPortsTest, TypedWritePortRoundTrip) {
360 MockWritePort mock(&sint15);
368 ASSERT_EQ(mock.lastWritten.getSize(), 2u);
371TEST(TypedPortsTest, SignExtensionNonByteAligned) {
377 EXPECT_EQ(wi.
bytes, 1u);
398 EXPECT_EQ(wi.
bytes, 3u);
399 uint8_t wire[3] = {0xFF, 0xFF, 0x3F};
408 uint8_t wire[3] = {0xFF, 0xFF, 0x1F};
411 EXPECT_EQ(val, 0x1FFFFF);
415TEST(TypedPortsTest, TypedWritePortVoid) {
417 MockWritePort mock(&voidType);
419 EXPECT_NO_THROW(typed.connect());
422 ASSERT_EQ(mock.lastWritten.getSize(), 1u);
423 EXPECT_EQ(mock.lastWritten.getData()[0], 0);
433 MockReadPort(
const Type *type) : ReadChannelPort(type) {}
435 void connect(std::function<
bool(MessageData)>,
436 const ConnectOptions & = {})
override {
437 mode = Mode::Callback;
439 void connect(
const ConnectOptions & = {})
override { mode = Mode::Polling; }
441 void read(MessageData &outData)
override { outData = nextResponse; }
442 std::future<MessageData> readAsync()
override {
443 std::promise<MessageData> p;
444 p.set_value(nextResponse);
445 return p.get_future();
448 MessageData nextResponse;
451TEST(TypedPortsTest, TypedReadPortCustomDeserializerVerifiesESIID) {
452 StructType matchType(
"MyModule.DeserializedStruct", {});
453 MockReadPort matching(&matchType);
455 EXPECT_NO_THROW(ok.connect());
457 StructType mismatchType(
"OtherModule.OtherStruct", {});
458 MockReadPort mismatch(&mismatchType);
465 CallbackDrivenMockReadPort(
const Type *type) : ReadChannelPort(type) {}
467 bool deliver(std::unique_ptr<SegmentedMessageData> msg) {
469 pending = std::move(msg);
470 return retryPending();
473 bool retryPending() {
475 throw std::runtime_error(
476 "CallbackDrivenMockReadPort::retryPending with no message");
477 if (!invokeCallback(pending))
483 bool hasPending()
const {
return static_cast<bool>(pending); }
484 size_t numActiveCallbacks()
const {
return activeCallbacks; }
486 size_t deliveryCount = 0;
489 std::unique_ptr<SegmentedMessageData> pending;
492class ThrowOnCopyReadCallback {
494 explicit ThrowOnCopyReadCallback(std::shared_ptr<bool> shouldThrow)
495 : shouldThrow(std::move(shouldThrow)) {}
497 ThrowOnCopyReadCallback(
const ThrowOnCopyReadCallback &other)
498 : shouldThrow(other.shouldThrow) {
500 throw std::runtime_error(
"ThrowOnCopyReadCallback copy failure");
503 ThrowOnCopyReadCallback(ThrowOnCopyReadCallback &&) =
default;
504 ThrowOnCopyReadCallback &operator=(
const ThrowOnCopyReadCallback &) =
default;
505 ThrowOnCopyReadCallback &operator=(ThrowOnCopyReadCallback &&) =
default;
507 bool operator()(std::unique_ptr<SegmentedMessageData> &)
const {
512 std::shared_ptr<bool> shouldThrow;
515static MessageData packUint32Words(std::initializer_list<uint32_t> values) {
516 std::vector<uint8_t> bytes(values.size() *
sizeof(uint32_t));
518 for (uint32_t value : values) {
519 std::memcpy(bytes.data() + offset, &value,
sizeof(value));
520 offset +=
sizeof(value);
525struct BufferedSequence {
526 std::vector<uint32_t> values;
528 class TypeDeserializer
529 :
public QueuedDecodeTypeDeserializer<BufferedSequence> {
531 using Base = QueuedDecodeTypeDeserializer<BufferedSequence>;
535 explicit TypeDeserializer(OutputCallback output)
536 : Base(std::move(output)) {}
539 DecodedOutputs decode(std::unique_ptr<SegmentedMessageData> &msg)
override {
541 const MessageData &flat =
542 detail::getMessageDataRef<BufferedSequence>(*msg, scratch);
543 if (flat.
getSize() %
sizeof(uint32_t) != 0)
544 throw std::runtime_error(
545 "BufferedSequence::TypeDeserializer: truncated word payload");
547 DecodedOutputs decoded;
548 for (
size_t offset = 0; offset < flat.
getSize();
549 offset +=
sizeof(uint32_t)) {
551 std::memcpy(&value, flat.
getBytes() + offset,
sizeof(value));
552 auto sequence = std::make_unique<BufferedSequence>();
553 sequence->values.push_back(value);
554 decoded.push_back(std::move(sequence));
562TEST(TypedPortsTest, TypedReadPortPODBackpressuresAfterOneBufferedOutput) {
564 CallbackDrivenMockReadPort mock(&uint32);
568 typed.setMaxDataQueueMsgs(1);
571 mock.deliver(std::make_unique<MessageData>(packUint32Words({11}))));
576 mock.deliver(std::make_unique<MessageData>(packUint32Words({22}))));
577 EXPECT_FALSE(mock.hasPending());
579 mock.deliver(std::make_unique<MessageData>(packUint32Words({33}))));
580 EXPECT_TRUE(mock.hasPending());
582 std::unique_ptr<uint32_t> first = typed.read();
584 EXPECT_EQ(*first, 11u);
585 EXPECT_TRUE(mock.retryPending());
586 EXPECT_FALSE(mock.hasPending());
587 std::unique_ptr<uint32_t> second = typed.read();
589 EXPECT_EQ(*second, 22u);
590 std::unique_ptr<uint32_t> third = typed.read();
592 EXPECT_EQ(*third, 33u);
595TEST(TypedPortsTest, TypedReadPortPODRetriesSameOwnedObjectOnLaterPush) {
597 CallbackDrivenMockReadPort mock(&uint32);
600 const uint32_t *firstObject =
nullptr;
601 size_t callbackAttempts = 0;
603 typed.connect([&](std::unique_ptr<uint32_t> &value) {
606 if (callbackAttempts == 1) {
607 EXPECT_EQ(*value, 11u);
608 firstObject = value.get();
611 if (callbackAttempts == 2) {
612 EXPECT_EQ(*value, 11u);
613 EXPECT_EQ(value.get(), firstObject);
616 EXPECT_EQ(*value, 22u);
621 mock.deliver(std::make_unique<MessageData>(packUint32Words({11}))));
622 EXPECT_FALSE(mock.hasPending());
626 mock.deliver(std::make_unique<MessageData>(packUint32Words({22}))));
627 EXPECT_FALSE(mock.hasPending());
628 EXPECT_EQ(callbackAttempts, 3u);
631TEST(TypedPortsTest, TypedReadPortCustomDeserializerPokesBlockedOutput) {
633 CallbackDrivenMockReadPort mock(&uint32);
637 typed.setMaxDataQueueMsgs(1);
640 mock.deliver(std::make_unique<MessageData>(packUint32Words({10, 20}))));
641 EXPECT_EQ(mock.deliveryCount, 1u);
643 std::unique_ptr<BufferedSequence> first = typed.read();
645 ASSERT_EQ(first->values.size(), 1u);
646 EXPECT_EQ(first->values[0], 10u);
648 std::unique_ptr<BufferedSequence> second = typed.read();
650 ASSERT_EQ(second->values.size(), 1u);
651 EXPECT_EQ(second->values[0], 20u);
652 EXPECT_EQ(mock.deliveryCount, 1u);
656 TypedReadPortCustomDeserializerConsumesMultipleFramesPerRawMessage) {
658 CallbackDrivenMockReadPort mock(&uint32);
663 std::future<std::unique_ptr<BufferedSequence>> first = typed.readAsync();
664 std::future<std::unique_ptr<BufferedSequence>> second = typed.readAsync();
665 std::future<std::unique_ptr<BufferedSequence>> third = typed.readAsync();
668 mock.deliver(std::make_unique<MessageData>(packUint32Words({1, 2, 3}))));
670 std::unique_ptr<BufferedSequence> firstValue = first.get();
671 ASSERT_TRUE(firstValue);
672 EXPECT_EQ(firstValue->values[0], 1u);
674 std::unique_ptr<BufferedSequence> secondValue = second.get();
675 ASSERT_TRUE(secondValue);
676 EXPECT_EQ(secondValue->values[0], 2u);
678 std::unique_ptr<BufferedSequence> thirdValue = third.get();
679 ASSERT_TRUE(thirdValue);
680 EXPECT_EQ(thirdValue->values[0], 3u);
684 TypedReadPortCustomDeserializerQueuesMultiplePendingOutputs) {
686 CallbackDrivenMockReadPort mock(&uint32);
690 typed.setMaxDataQueueMsgs(1);
693 mock.deliver(std::make_unique<MessageData>(packUint32Words({7, 8, 9}))));
694 EXPECT_EQ(mock.deliveryCount, 1u);
696 std::unique_ptr<BufferedSequence> first = typed.read();
698 EXPECT_EQ(first->values[0], 7u);
700 std::unique_ptr<BufferedSequence> second = typed.read();
702 EXPECT_EQ(second->values[0], 8u);
704 std::unique_ptr<BufferedSequence> third = typed.read();
706 EXPECT_EQ(third->values[0], 9u);
707 EXPECT_EQ(mock.deliveryCount, 1u);
710struct FragmentedCoord {
714static_assert(
sizeof(FragmentedCoord) == 8,
"Size mismatch");
716static std::array<uint8_t,
sizeof(FragmentedCoord)> packCoordBytes(uint32_t y,
718 FragmentedCoord coord{y, x};
719 std::array<uint8_t,
sizeof(FragmentedCoord)> bytes{};
720 std::memcpy(bytes.data(), &coord,
sizeof(coord));
724struct FragmentedCoordBatch {
725 std::vector<FragmentedCoord> coords;
727 class TypeDeserializer
728 :
public QueuedDecodeTypeDeserializer<FragmentedCoordBatch> {
730 using Base = QueuedDecodeTypeDeserializer<FragmentedCoordBatch>;
734 explicit TypeDeserializer(OutputCallback output)
735 : Base(std::move(output)) {}
738 DecodedOutputs decode(std::unique_ptr<SegmentedMessageData> &msg)
override {
740 const MessageData &flat =
741 detail::getMessageDataRef<FragmentedCoordBatch>(*msg, scratch);
743 DecodedOutputs decoded;
744 const uint8_t *bytes = flat.
getBytes();
746 while (offset < flat.
getSize()) {
747 size_t needed =
sizeof(FragmentedCoord) - partialFrameBytes.size();
748 size_t chunkSize = std::min(needed, flat.
getSize() - offset);
749 partialFrameBytes.insert(partialFrameBytes.end(), bytes + offset,
750 bytes + offset + chunkSize);
753 if (partialFrameBytes.size() !=
sizeof(FragmentedCoord))
756 FragmentedCoord coord;
757 std::memcpy(&coord, partialFrameBytes.data(),
sizeof(coord));
758 partialFrameBytes.clear();
760 auto batch = std::make_unique<FragmentedCoordBatch>();
761 batch->coords.push_back(coord);
762 decoded.push_back(std::move(batch));
769 std::vector<uint8_t> partialFrameBytes;
774 TypedReadPortCustomDeserializerConsumesSplitFramesAcrossRawMessages) {
776 CallbackDrivenMockReadPort mock(&uint32);
781 std::future<std::unique_ptr<FragmentedCoordBatch>> first = typed.readAsync();
782 std::future<std::unique_ptr<FragmentedCoordBatch>> second = typed.readAsync();
784 std::array<uint8_t,
sizeof(FragmentedCoord)> coordA = packCoordBytes(10, 20);
785 std::array<uint8_t,
sizeof(FragmentedCoord)> coordB = packCoordBytes(30, 40);
787 std::vector<uint8_t> firstChunk(coordA.begin(), coordA.begin() + 6);
788 EXPECT_TRUE(mock.deliver(
789 std::make_unique<MessageData>(
MessageData(std::move(firstChunk)))));
791 std::vector<uint8_t> secondChunk;
792 secondChunk.insert(secondChunk.end(), coordA.begin() + 6, coordA.end());
793 secondChunk.insert(secondChunk.end(), coordB.begin(), coordB.end());
794 EXPECT_TRUE(mock.deliver(
795 std::make_unique<MessageData>(
MessageData(std::move(secondChunk)))));
797 std::unique_ptr<FragmentedCoordBatch> firstBatch = first.get();
798 ASSERT_TRUE(firstBatch);
799 ASSERT_EQ(firstBatch->coords.size(), 1u);
800 EXPECT_EQ(firstBatch->coords[0].y, 10u);
801 EXPECT_EQ(firstBatch->coords[0].x, 20u);
803 std::unique_ptr<FragmentedCoordBatch> secondBatch = second.get();
804 ASSERT_TRUE(secondBatch);
805 ASSERT_EQ(secondBatch->coords.size(), 1u);
806 EXPECT_EQ(secondBatch->coords[0].y, 30u);
807 EXPECT_EQ(secondBatch->coords[0].x, 40u);
813TEST(TypedPortsTest, TypedFunctionNullThrowsAtConnect) {
819TEST(TypedPortsTest, TypedFunctionCallBeforeConnectThrows) {
822 EXPECT_THROW(typed.call(0u).get(), std::runtime_error);
825TEST(TypedPortsTest, TypedFunctionDoubleConnectThrows) {
829 ChannelType argChanType(
"channel<si24>", &argInner);
831 ChannelType resultChanType(
"channel<ui15>", &resultInner);
837 BundleType bundleType(
"func_bundle", channels);
839 MockWritePort mockWrite(&argInner);
840 CallbackDrivenMockReadPort mockRead(&resultInner);
843 mockWrite, mockRead);
847 EXPECT_THROW(typed.connect(), std::runtime_error);
851TEST(TypedPortsTest, TypedFunctionConnectVerifiesTypes) {
854 ChannelType argChanType(
"channel<si24>", &argInner);
856 ChannelType resultChanType(
"channel<ui15>", &resultInner);
862 BundleType bundleType(
"func_bundle", channels);
864 MockWritePort mockWrite(&argInner);
865 MockReadPort mockRead(&resultInner);
868 mockWrite, mockRead);
873 EXPECT_NO_THROW(typed.connect());
877TEST(TypedPortsTest, TypedFunctionConnectRejectsArgMismatch) {
879 ChannelType argChanType(
"channel<ui24>", &argInner);
881 ChannelType resultChanType(
"channel<ui15>", &resultInner);
887 BundleType bundleType(
"func_bundle", channels);
889 MockWritePort mockWrite(&argInner);
890 MockReadPort mockRead(&resultInner);
893 mockWrite, mockRead);
901TEST(TypedPortsTest, TypedFunctionCallRoundTrip) {
903 ChannelType argChanType(
"channel<si24>", &argInner);
905 ChannelType resultChanType(
"channel<ui15>", &resultInner);
911 BundleType bundleType(
"func_bundle", channels);
913 MockWritePort mockWrite(&argInner);
914 CallbackDrivenMockReadPort mockRead(&resultInner);
917 mockWrite, mockRead);
923 std::future<uint16_t> f = typed.call(arg);
926 uint16_t expected = 42;
927 EXPECT_TRUE(mockRead.deliver(std::make_unique<MessageData>(
MessageData(
928 reinterpret_cast<const uint8_t *
>(&expected),
sizeof(expected)))));
929 uint16_t result = f.get();
930 EXPECT_EQ(result, 42);
933 ASSERT_EQ(mockWrite.lastWritten.getSize(), 3u);
951 std::vector<uint32_t> items;
953 size_t numSegments()
const override {
return 2; }
954 Segment segment(
size_t idx)
const override {
956 return {
reinterpret_cast<const uint8_t *
>(&header),
sizeof(Header)};
957 return {
reinterpret_cast<const uint8_t *
>(items.data()),
958 items.size() *
sizeof(uint32_t)};
962TEST(TypedPortsTest, ReadChannelPortSegmentedCallbackRetriesSameMessageObject) {
964 CallbackDrivenMockReadPort mock(&uint32);
966 uint32_t expected = 0x12345678;
967 const uint8_t *firstBytes =
nullptr;
968 size_t callbackCalls = 0;
970 mock.connect([&](std::unique_ptr<SegmentedMessageData> &msg) ->
bool {
972 EXPECT_EQ(msg->numSegments(), 1u);
974 auto *flat =
dynamic_cast<MessageData *
>(msg.get());
975 EXPECT_NE(flat,
nullptr);
978 EXPECT_EQ(*flat->
as<uint32_t>(), expected);
980 if (callbackCalls == 1)
983 EXPECT_EQ(flat->
getBytes(), firstBytes);
985 return callbackCalls == 2;
990 EXPECT_TRUE(mock.hasPending());
991 EXPECT_TRUE(mock.retryPending());
992 EXPECT_FALSE(mock.hasPending());
993 EXPECT_EQ(callbackCalls, 2u);
996TEST(TypedPortsTest, ReadChannelPortFlatCallbackFlattensSegmentedMessageRetry) {
998 CallbackDrivenMockReadPort mock(&uint32);
1000 TestSegmented input;
1001 input.header.a = 0x12345678;
1002 input.header.b = 0xABCD;
1003 input.items = {1, 2, 3};
1006 size_t callbackCalls = 0;
1010 return callbackCalls == 2;
1013 EXPECT_FALSE(mock.deliver(std::make_unique<TestSegmented>(input)));
1014 EXPECT_TRUE(mock.hasPending());
1015 EXPECT_TRUE(mock.retryPending());
1016 EXPECT_FALSE(mock.hasPending());
1017 EXPECT_EQ(callbackCalls, 2u);
1020TEST(TypedPortsTest, ReadChannelPortPollingRetriesFlattenedSegmentedMessage) {
1022 CallbackDrivenMockReadPort mock(&uint32);
1024 mock.setMaxDataQueueMsgs(1);
1026 TestSegmented firstInput;
1027 firstInput.header.a = 0xAAAA5555;
1028 firstInput.header.b = 0x1357;
1029 firstInput.items = {10};
1030 MessageData firstExpected = firstInput.toMessageData();
1032 TestSegmented secondInput;
1033 secondInput.header.a = 0xDEADBEEF;
1034 secondInput.header.b = 0x2468;
1035 secondInput.items = {20, 30};
1036 MessageData secondExpected = secondInput.toMessageData();
1038 EXPECT_TRUE(mock.deliver(std::make_unique<TestSegmented>(firstInput)));
1039 EXPECT_FALSE(mock.deliver(std::make_unique<TestSegmented>(secondInput)));
1040 EXPECT_TRUE(mock.hasPending());
1043 mock.read(firstOut);
1046 EXPECT_TRUE(mock.retryPending());
1047 EXPECT_FALSE(mock.hasPending());
1050 mock.read(secondOut);
1054TEST(TypedPortsTest, ReadChannelPortPollingReadAsyncThrowsWhenDisconnected) {
1056 CallbackDrivenMockReadPort mock(&uint32);
1058 EXPECT_THROW(mock.readAsync(), std::runtime_error);
1061 std::future<MessageData> pending = mock.readAsync();
1064 EXPECT_EQ(pending.wait_for(std::chrono::milliseconds(0)),
1065 std::future_status::ready);
1066 EXPECT_THROW(pending.get(), std::future_error);
1067 EXPECT_THROW(mock.readAsync(), std::runtime_error);
1071 mock.deliver(std::make_unique<MessageData>(packUint32Words({11}))));
1074 EXPECT_EQ(*out.as<uint32_t>(), 11u);
1077TEST(TypedPortsTest, ReadChannelPortPollingConnectRejectsReconnect) {
1079 CallbackDrivenMockReadPort mock(&uint32);
1082 EXPECT_THROW(mock.connect(), std::runtime_error);
1085TEST(TypedPortsTest, ReadChannelPortDisconnectRevokesCallback) {
1087 CallbackDrivenMockReadPort mock(&uint32);
1093 mock.deliver(std::make_unique<MessageData>(packUint32Words({11}))));
1094 EXPECT_TRUE(mock.hasPending());
1097 EXPECT_TRUE(mock.retryPending());
1098 EXPECT_FALSE(mock.hasPending());
1102 EXPECT_EQ(*out.
as<uint32_t>(), 11u);
1105TEST(TypedPortsTest, TypedReadPortDestructorDisconnectsRawPort) {
1107 CallbackDrivenMockReadPort mock(&uint32);
1112 EXPECT_TRUE(mock.isConnected());
1115 EXPECT_FALSE(mock.isConnected());
1117 mock.deliver(std::make_unique<MessageData>(packUint32Words({11}))));
1118 EXPECT_TRUE(mock.hasPending());
1121 reconnected.connect();
1122 EXPECT_TRUE(mock.retryPending());
1123 EXPECT_FALSE(mock.hasPending());
1125 std::unique_ptr<uint32_t> out = reconnected.read();
1127 EXPECT_EQ(*out, 11u);
1131 ReadChannelPortInvokeCallbackMaintainsCountOnCallbackCopyFailure) {
1133 CallbackDrivenMockReadPort mock(&uint32);
1134 auto shouldThrow = std::make_shared<bool>(
false);
1136 mock.connect(ThrowOnCopyReadCallback(shouldThrow));
1137 *shouldThrow =
true;
1140 mock.deliver(std::make_unique<MessageData>(packUint32Words({11}))),
1141 std::runtime_error);
1142 EXPECT_EQ(mock.numActiveCallbacks(), 0u);
1145TEST(TypedPortsTest, TypedWritePortSegmentedMessageData) {
1148 MockWritePort mock(&uint32);
1154 msg.header.a = 0x12345678;
1155 msg.header.b = 0xABCD;
1156 msg.items = {1, 2, 3};
1162 EXPECT_EQ(mock.lastWritten.getSize(), 18u);
1165 const uint8_t *bytes = mock.lastWritten.getBytes();
1168 std::memcpy(&gotA, bytes, 4);
1169 std::memcpy(&gotB, bytes + 4, 2);
1170 EXPECT_EQ(gotA, 0x12345678u);
1171 EXPECT_EQ(gotB, 0xABCDu);
1174 uint32_t gotItems[3];
1175 std::memcpy(gotItems, bytes + 6, 12);
1176 EXPECT_EQ(gotItems[0], 1u);
1177 EXPECT_EQ(gotItems[1], 2u);
1178 EXPECT_EQ(gotItems[2], 3u);
1181TEST(TypedPortsTest, WriteChannelPortTryWriteSegmented) {
1183 MockWritePort mock(&uint32);
1186 auto input = std::make_unique<TestSegmented>();
1187 input->header.a = 0x12345678;
1188 input->header.b = 0xABCD;
1189 input->items = {1, 2, 3};
1192 std::unique_ptr<SegmentedMessageData> msg = std::move(input);
1195 mock.acceptTryWrites =
false;
1196 EXPECT_FALSE(mock.tryWrite(msg));
1197 ASSERT_NE(msg,
nullptr);
1198 EXPECT_EQ(msg.get(), raw);
1199 EXPECT_EQ(mock.lastWritten.getSize(), 0u);
1202 mock.acceptTryWrites =
true;
1203 EXPECT_TRUE(mock.tryWrite(msg));
1204 EXPECT_EQ(msg,
nullptr);
1205 EXPECT_EQ(mock.lastWritten.getData(), expected.
getData());
1207 std::unique_ptr<SegmentedMessageData> null;
1208 EXPECT_THROW(mock.tryWrite(null), std::runtime_error);
1211TEST(TypedPortsTest, TypedWritePortTryWriteSegmentedUniquePtr) {
1213 MockWritePort mock(&uint32);
1218 auto msg = std::make_unique<TestSegmented>();
1221 msg->items = {100, 200};
1223 TestSegmented *raw = msg.get();
1225 mock.acceptTryWrites =
false;
1226 EXPECT_FALSE(typed.tryWrite(msg));
1227 ASSERT_NE(msg,
nullptr);
1228 EXPECT_EQ(msg.get(), raw);
1229 EXPECT_EQ(msg->items.size(), 2u);
1231 mock.acceptTryWrites =
true;
1232 EXPECT_TRUE(typed.tryWrite(msg));
1233 EXPECT_EQ(msg,
nullptr);
1234 EXPECT_EQ(mock.lastWritten.getData(), expected.
getData());
1236 std::unique_ptr<TestSegmented> null;
1237 EXPECT_THROW(typed.tryWrite(null), std::runtime_error);
1240TEST(TypedPortsTest, TypedWritePortTryWriteNonSegmentedUniquePtr) {
1242 MockWritePort mock(&uint32);
1247 auto value = std::make_unique<uint32_t>(0xDEADBEEF);
1249 mock.acceptTryWrites =
false;
1250 EXPECT_FALSE(typed.tryWrite(value));
1251 ASSERT_NE(value,
nullptr);
1252 EXPECT_EQ(*value, 0xDEADBEEFu);
1254 mock.acceptTryWrites =
true;
1255 EXPECT_TRUE(typed.tryWrite(value));
1256 EXPECT_EQ(value,
nullptr);
1257 ASSERT_EQ(mock.lastWritten.getSize(), 4u);
1258 EXPECT_EQ(*mock.lastWritten.as<uint32_t>(), 0xDEADBEEFu);
1261TEST(TypedPortsTest, TypedWritePortSegmentedNoTypeCheck) {
1265 MockWritePort mock(&sint8);
1268 EXPECT_NO_THROW(typed.connect());
1277 EXPECT_EQ(mock.lastWritten.getSize(), 10u);
1280TEST(TypedPortsTest, TypedFunctionSegmentedArg) {
1284 ChannelType argChanType(
"channel<ui32>", &argInner);
1286 ChannelType resultChanType(
"channel<ui16>", &resultInner);
1292 BundleType bundleType(
"func_bundle", channels);
1294 MockWritePort mockWrite(&argInner);
1295 CallbackDrivenMockReadPort mockRead(&resultInner);
1298 mockWrite, mockRead);
1304 arg.header.a = 0xDEAD;
1305 arg.header.b = 0xBE;
1306 arg.items = {10, 20};
1308 std::future<uint16_t> f = typed.call(arg);
1310 uint16_t expected = 99;
1311 EXPECT_TRUE(mockRead.deliver(std::make_unique<MessageData>(
MessageData(
1312 reinterpret_cast<const uint8_t *
>(&expected),
sizeof(expected)))));
1313 uint16_t result = f.get();
1314 EXPECT_EQ(result, 99u);
1317 EXPECT_EQ(mockWrite.lastWritten.getSize(), 14u);
1319 const uint8_t *bytes = mockWrite.lastWritten.getBytes();
1321 std::memcpy(&gotA, bytes, 4);
1322 EXPECT_EQ(gotA, 0xDEADu);
1324 uint32_t gotItem0, gotItem1;
1325 std::memcpy(&gotItem0, bytes + 6, 4);
1326 std::memcpy(&gotItem1, bytes + 10, 4);
1327 EXPECT_EQ(gotItem0, 10u);
1328 EXPECT_EQ(gotItem1, 20u);
1334struct UnrecognizedCppType {
1339TEST(TypedPortsTest, VerifyTypeCompatibilityThrowsForUnsupportedType) {
1365 class TypeDeserializer {
1367 using OutputCallback = detail::TypedReadOwnedCallback<OneWord>;
1369 explicit TypeDeserializer(OutputCallback output)
1370 : output(std::move(output)) {}
1372 bool push(std::unique_ptr<SegmentedMessageData> &msg) {
1375 if (!output(pending))
1380 MessageData scratch;
1381 const MessageData &flat =
1382 detail::getMessageDataRef<OneWord>(*msg, scratch);
1383 if (flat.
getSize() !=
sizeof(uint32_t))
1384 throw std::runtime_error(
"OneWord: bad size");
1385 pending = std::make_unique<OneWord>();
1386 std::memcpy(&pending->value, flat.
getBytes(),
sizeof(uint32_t));
1390 if (output(pending))
1396 if (pending && output(pending)) {
1404 OutputCallback output;
1405 std::unique_ptr<OneWord> pending;
1409TEST(TypedPortsTest, TypedFunctionCustomDeserializerSingleFrame) {
1411 ChannelType argChanType(
"channel<ui32>", &argInner);
1413 ChannelType resultChanType(
"channel<ui32>", &resultInner);
1419 BundleType bundleType(
"func_bundle", channels);
1421 MockWritePort mockWrite(&argInner);
1422 CallbackDrivenMockReadPort mockRead(&resultInner);
1425 mockWrite, mockRead);
1430 std::future<OneWord> f = typed.call(0u);
1433 uint32_t expected = 0xCAFE;
1434 EXPECT_TRUE(mockRead.deliver(std::make_unique<MessageData>(
MessageData(
1435 reinterpret_cast<const uint8_t *
>(&expected),
sizeof(expected)))));
1437 OneWord result = f.get();
1438 EXPECT_EQ(result.value, expected);
1443TEST(TypedPortsTest, TypedFunctionCustomDeserializerReadsMultipleFrames) {
1449 ChannelType argChanType(
"channel<ui32>", &argInner);
1451 ChannelType resultChanType(
"channel<ui32>", &resultInner);
1457 BundleType bundleType(
"func_bundle", channels);
1459 MockWritePort mockWrite(&argInner);
1460 CallbackDrivenMockReadPort mockRead(&resultInner);
1463 mockWrite, mockRead);
1468 std::future<FragmentedCoordBatch> f = typed.call(0u);
1471 std::array<uint8_t,
sizeof(FragmentedCoord)> coordBytes =
1472 packCoordBytes(55, 77);
1473 std::vector<uint8_t> firstChunk(coordBytes.begin(), coordBytes.begin() + 6);
1474 std::vector<uint8_t> secondChunk(coordBytes.begin() + 6, coordBytes.end());
1476 mockRead.deliver(std::make_unique<MessageData>(std::move(firstChunk))));
1478 mockRead.deliver(std::make_unique<MessageData>(std::move(secondChunk))));
1480 FragmentedCoordBatch batch = f.get();
1481 ASSERT_EQ(batch.coords.size(), 1u);
1482 EXPECT_EQ(batch.coords[0].y, 55u);
1483 EXPECT_EQ(batch.coords[0].x, 77u);
1488TEST(TypedPortsTest, TypedFunctionCustomDeserializerSkipsResultTypeCheck) {
1493 ChannelType argChanType(
"channel<ui32>", &argInner);
1496 ChannelType resultChanType(
"channel<Anything>", &resultInner);
1502 BundleType bundleType(
"func_bundle", channels);
1504 MockWritePort mockWrite(&argInner);
1505 CallbackDrivenMockReadPort mockRead(&resultInner);
1508 mockWrite, mockRead);
1511 EXPECT_NO_THROW(typed.connect());
1516TEST(TypedPortsTest, TypedFunctionPipelinedCallsGetOutOfOrder) {
1523 ChannelType argChanType(
"channel<ui32>", &argInner);
1525 ChannelType resultChanType(
"channel<ui32>", &resultInner);
1531 BundleType bundleType(
"func_bundle", channels);
1533 MockWritePort mockWrite(&argInner);
1534 CallbackDrivenMockReadPort mockRead(&resultInner);
1537 mockWrite, mockRead);
1544 std::future<FragmentedCoordBatch> f1 = typed.call(0u);
1545 std::future<FragmentedCoordBatch> f2 = typed.call(0u);
1547 auto deliverCoord = [&](uint32_t y, uint32_t x) {
1548 auto bytes = packCoordBytes(y, x);
1549 std::vector<uint8_t> first(bytes.begin(), bytes.begin() + 6);
1550 std::vector<uint8_t> second(bytes.begin() + 6, bytes.end());
1552 mockRead.deliver(std::make_unique<MessageData>(std::move(first))));
1554 mockRead.deliver(std::make_unique<MessageData>(std::move(second))));
1556 deliverCoord(11, 22);
1557 deliverCoord(33, 44);
1560 FragmentedCoordBatch r2 = f2.get();
1561 ASSERT_EQ(r2.coords.size(), 1u);
1562 EXPECT_EQ(r2.coords[0].y, 33u);
1563 EXPECT_EQ(r2.coords[0].x, 44u);
1565 FragmentedCoordBatch r1 = f1.get();
1566 ASSERT_EQ(r1.coords.size(), 1u);
1567 EXPECT_EQ(r1.coords[0].y, 11u);
1568 EXPECT_EQ(r1.coords[0].x, 22u);
Arrays have a compile time specified (static) size and an element type.
Bits are just an array of bits.
Bundles represent a collection of channels.
std::vector< std::tuple< std::string, Direction, const Type * > > ChannelVector
Channels are the basic communication primitives.
A concrete flat message backed by a single vector of bytes.
const std::vector< uint8_t > & getData() const
Get the data as a vector of bytes.
const uint8_t * getBytes() const
const T * as() const
Cast to a type.
size_t getSize() const
Get the size of the data in bytes.
static MessageData from(T &t)
Cast from a type to its raw bytes.
detail::TypedReadOwnedCallback< DeserializerWithESIID > OutputCallback
std::vector< std::unique_ptr< DeserializerWithESIID > > DecodedOutputs
A ChannelPort which reads data from the accelerator.
Abstract multi-segment message.
Structs are an ordered collection of fields, each with a name and a type.
Type aliases provide a named type which forwards to an inner type.
Root class of the ESI type system.
Strongly typed wrapper around a raw read channel.
The "void" type is a special type which can be used to represent no type.
Windows represent a fixed-size sliding window over a stream of data.
A ChannelPort which sends data to the accelerator.
static Function * get(AppID id, BundleType *type, WriteChannelPort &arg, ReadChannelPort &result)
connect(destination, source)
void verifyTypeCompatibility(const Type *portType)
WireInfo getWireInfo(const Type *portType)
const T * getUnwrappedAs(const Type *type)
Unwrap aliases and channels, then cast the payload to T.
T fromMessageData(const MessageData &msg, WireInfo wi)
Unpack a MessageData into a C++ integral value with the given wire info.
const Type * unwrapTypeAlias(const Type *type)
Unwrap TypeAliasType (possibly recursively) to get the underlying type.
Compute the wire byte count for a port type.