@padosoft/react-native-ecr17 0.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/Ecr17.podspec +39 -39
  2. package/README.md +348 -348
  3. package/android/CMakeLists.txt +41 -41
  4. package/android/build.gradle +148 -148
  5. package/android/fix-prefab.gradle +50 -50
  6. package/android/gradle.properties +5 -5
  7. package/android/src/main/AndroidManifest.xml +2 -2
  8. package/android/src/main/cpp/cpp-adapter.cpp +8 -8
  9. package/android/src/main/java/com/margelo/nitro/ecr17/HybridEcr17Transport.kt +233 -233
  10. package/android/src/main/java/com/padosoft/ecr17/Ecr17Package.kt +30 -30
  11. package/cpp/Ecr17.hpp +1 -1
  12. package/cpp/Ecr17Client/HybridEcr17Client.cpp +598 -598
  13. package/cpp/Ecr17Client/HybridEcr17Client.hpp +85 -85
  14. package/cpp/Ecr17Protocol/Ecr17Protocol.cpp +277 -277
  15. package/cpp/Ecr17Protocol/Ecr17Protocol.hpp +103 -103
  16. package/cpp/Ecr17Response/Ecr17Response.cpp +155 -155
  17. package/cpp/Ecr17Response/Ecr17Response.hpp +113 -113
  18. package/cpp/Lcr/Lcr.cpp +42 -42
  19. package/cpp/Lcr/Lcr.hpp +21 -21
  20. package/cpp/PacketCodec/PacketCodec.cpp +145 -145
  21. package/cpp/PacketCodec/PacketCodec.hpp +47 -47
  22. package/cpp/Session/Ecr17Session.cpp +260 -260
  23. package/cpp/Session/Ecr17Session.hpp +97 -97
  24. package/cpp/Session/RetryPolicy.hpp +23 -23
  25. package/cpp/Transport/FakeTransport.hpp +95 -95
  26. package/cpp/Transport/NativeTransportAdapter.cpp +42 -42
  27. package/cpp/Transport/NativeTransportAdapter.hpp +32 -32
  28. package/cpp/Transport/Transport.hpp +30 -30
  29. package/cpp/tests/CMakeLists.txt +55 -55
  30. package/cpp/tests/PosixTcpTransport.hpp +105 -105
  31. package/cpp/tests/stubs/LrcMode.hpp +25 -25
  32. package/cpp/tests/test_flows.cpp +148 -148
  33. package/cpp/tests/test_integration_terminal.cpp +72 -72
  34. package/cpp/tests/test_lrc.cpp +66 -66
  35. package/cpp/tests/test_packet_codec.cpp +164 -164
  36. package/cpp/tests/test_protocol.cpp +102 -102
  37. package/cpp/tests/test_protocol_commands.cpp +190 -190
  38. package/cpp/tests/test_response.cpp +164 -164
  39. package/cpp/tests/test_retry_policy.cpp +28 -28
  40. package/cpp/tests/test_session.cpp +262 -262
  41. package/ios/HybridEcr17Transport.swift +103 -103
  42. package/lib/commonjs/index.js +50 -0
  43. package/lib/commonjs/index.js.map +1 -0
  44. package/lib/commonjs/package.json +1 -0
  45. package/lib/commonjs/specs/client.nitro.js +17 -0
  46. package/lib/commonjs/specs/client.nitro.js.map +1 -0
  47. package/lib/commonjs/specs/transport.nitro.js +6 -0
  48. package/lib/commonjs/specs/transport.nitro.js.map +1 -0
  49. package/lib/commonjs/types/client.js +2 -0
  50. package/lib/commonjs/types/client.js.map +1 -0
  51. package/lib/commonjs/utils/client.js +13 -0
  52. package/lib/commonjs/utils/client.js.map +1 -0
  53. package/lib/module/index.js +7 -0
  54. package/lib/module/index.js.map +1 -0
  55. package/lib/module/specs/client.nitro.js +13 -0
  56. package/lib/module/specs/client.nitro.js.map +1 -0
  57. package/lib/module/specs/transport.nitro.js +4 -0
  58. package/lib/module/specs/transport.nitro.js.map +1 -0
  59. package/lib/module/types/client.js +2 -0
  60. package/lib/module/types/client.js.map +1 -0
  61. package/lib/module/utils/client.js +9 -0
  62. package/lib/module/utils/client.js.map +1 -0
  63. package/lib/typescript/src/index.d.ts +5 -0
  64. package/lib/typescript/src/index.d.ts.map +1 -0
  65. package/lib/typescript/src/specs/client.nitro.d.ts +63 -0
  66. package/lib/typescript/src/specs/client.nitro.d.ts.map +1 -0
  67. package/lib/typescript/src/specs/transport.nitro.d.ts +13 -0
  68. package/lib/typescript/src/specs/transport.nitro.d.ts.map +1 -0
  69. package/lib/typescript/src/types/client.d.ts +138 -0
  70. package/lib/typescript/src/types/client.d.ts.map +1 -0
  71. package/lib/typescript/src/utils/client.d.ts +3 -0
  72. package/lib/typescript/src/utils/client.d.ts.map +1 -0
  73. package/nitro.json +30 -30
  74. package/package.json +4 -4
  75. package/react-native.config.js +18 -18
  76. package/src/index.ts +4 -4
  77. package/src/specs/client.nitro.ts +102 -102
  78. package/src/specs/transport.nitro.ts +25 -25
  79. package/src/types/client.ts +196 -196
  80. package/src/utils/client.ts +10 -10
@@ -1,72 +1,72 @@
1
- // OPT-IN real-terminal integration test.
2
- //
3
- // Skipped unless ECR17_TERMINAL_HOST is set, so it never affects CI. To run it
4
- // against a real Nexi terminal on your LAN:
5
- //
6
- // ECR17_TERMINAL_HOST=192.168.1.50 \
7
- // ECR17_TERMINAL_PORT=10000 \
8
- // ECR17_TERMINAL_ID=00000000 \
9
- // ECR17_LRC_MODE=std \
10
- // ctest --test-dir build --output-on-failure -R Integration
11
- //
12
- // It connects over real TCP, sends a Terminal Status ('s') request through the
13
- // full C++ core (builder -> session/ACK-NAK -> parser) and prints the result.
14
-
15
- #if !defined(_WIN32)
16
-
17
- #include <gtest/gtest.h>
18
-
19
- #include <cstdlib>
20
- #include <iostream>
21
- #include <string>
22
-
23
- #include "Ecr17Protocol/Ecr17Protocol.hpp"
24
- #include "Ecr17Response/Ecr17Response.hpp"
25
- #include "PosixTcpTransport.hpp"
26
- #include "Session/Ecr17Session.hpp"
27
-
28
- using namespace margelo::nitro::ecr17;
29
-
30
- namespace {
31
- LrcMode lrcModeFromEnv() {
32
- const char* m = std::getenv("ECR17_LRC_MODE");
33
- const std::string mode = m ? m : "std";
34
- if (mode == "stx") return LrcMode::STX;
35
- if (mode == "noext") return LrcMode::NOEXT;
36
- if (mode == "stx_noext") return LrcMode::STX_NOEXT;
37
- return LrcMode::STD;
38
- }
39
- } // namespace
40
-
41
- TEST(Integration, RealTerminalStatus) {
42
- const char* host = std::getenv("ECR17_TERMINAL_HOST");
43
- if (host == nullptr) {
44
- GTEST_SKIP() << "set ECR17_TERMINAL_HOST to run against a real terminal";
45
- }
46
- const char* portEnv = std::getenv("ECR17_TERMINAL_PORT");
47
- const char* idEnv = std::getenv("ECR17_TERMINAL_ID");
48
- const int port = portEnv ? std::atoi(portEnv) : 10000;
49
- const std::string terminalId = idEnv ? idEnv : "00000000";
50
-
51
- PosixTcpTransport transport(host, port);
52
- transport.connect();
53
-
54
- SessionConfig cfg;
55
- cfg.lrcMode = lrcModeFromEnv();
56
- cfg.ackTimeoutMs = 3000;
57
- cfg.responseTimeoutMs = 15000;
58
- Ecr17Session session(transport, cfg);
59
-
60
- DecodedPacket pkt = session.exchange(Ecr17Protocol::buildStatusMessage(terminalId));
61
- StatusResponse status = Ecr17Response::parseStatus(pkt.payload);
62
-
63
- std::cout << "[ECR17] terminalId=" << status.terminalId << " status=" << status.status
64
- << " dateTime=" << status.dateTimeRaw << " sw=" << status.softwareRelease << std::endl;
65
-
66
- EXPECT_TRUE(pkt.validLrc);
67
- EXPECT_FALSE(status.terminalId.empty());
68
-
69
- transport.disconnect();
70
- }
71
-
72
- #endif // !_WIN32
1
+ // OPT-IN real-terminal integration test.
2
+ //
3
+ // Skipped unless ECR17_TERMINAL_HOST is set, so it never affects CI. To run it
4
+ // against a real Nexi terminal on your LAN:
5
+ //
6
+ // ECR17_TERMINAL_HOST=192.168.1.50 \
7
+ // ECR17_TERMINAL_PORT=10000 \
8
+ // ECR17_TERMINAL_ID=00000000 \
9
+ // ECR17_LRC_MODE=std \
10
+ // ctest --test-dir build --output-on-failure -R Integration
11
+ //
12
+ // It connects over real TCP, sends a Terminal Status ('s') request through the
13
+ // full C++ core (builder -> session/ACK-NAK -> parser) and prints the result.
14
+
15
+ #if !defined(_WIN32)
16
+
17
+ #include <gtest/gtest.h>
18
+
19
+ #include <cstdlib>
20
+ #include <iostream>
21
+ #include <string>
22
+
23
+ #include "Ecr17Protocol/Ecr17Protocol.hpp"
24
+ #include "Ecr17Response/Ecr17Response.hpp"
25
+ #include "PosixTcpTransport.hpp"
26
+ #include "Session/Ecr17Session.hpp"
27
+
28
+ using namespace margelo::nitro::ecr17;
29
+
30
+ namespace {
31
+ LrcMode lrcModeFromEnv() {
32
+ const char* m = std::getenv("ECR17_LRC_MODE");
33
+ const std::string mode = m ? m : "std";
34
+ if (mode == "stx") return LrcMode::STX;
35
+ if (mode == "noext") return LrcMode::NOEXT;
36
+ if (mode == "stx_noext") return LrcMode::STX_NOEXT;
37
+ return LrcMode::STD;
38
+ }
39
+ } // namespace
40
+
41
+ TEST(Integration, RealTerminalStatus) {
42
+ const char* host = std::getenv("ECR17_TERMINAL_HOST");
43
+ if (host == nullptr) {
44
+ GTEST_SKIP() << "set ECR17_TERMINAL_HOST to run against a real terminal";
45
+ }
46
+ const char* portEnv = std::getenv("ECR17_TERMINAL_PORT");
47
+ const char* idEnv = std::getenv("ECR17_TERMINAL_ID");
48
+ const int port = portEnv ? std::atoi(portEnv) : 10000;
49
+ const std::string terminalId = idEnv ? idEnv : "00000000";
50
+
51
+ PosixTcpTransport transport(host, port);
52
+ transport.connect();
53
+
54
+ SessionConfig cfg;
55
+ cfg.lrcMode = lrcModeFromEnv();
56
+ cfg.ackTimeoutMs = 3000;
57
+ cfg.responseTimeoutMs = 15000;
58
+ Ecr17Session session(transport, cfg);
59
+
60
+ DecodedPacket pkt = session.exchange(Ecr17Protocol::buildStatusMessage(terminalId));
61
+ StatusResponse status = Ecr17Response::parseStatus(pkt.payload);
62
+
63
+ std::cout << "[ECR17] terminalId=" << status.terminalId << " status=" << status.status
64
+ << " dateTime=" << status.dateTimeRaw << " sw=" << status.softwareRelease << std::endl;
65
+
66
+ EXPECT_TRUE(pkt.validLrc);
67
+ EXPECT_FALSE(status.terminalId.empty());
68
+
69
+ transport.disconnect();
70
+ }
71
+
72
+ #endif // !_WIN32
@@ -1,66 +1,66 @@
1
- #include <gtest/gtest.h>
2
-
3
- #include <cstdint>
4
- #include <string>
5
- #include <vector>
6
-
7
- #include "Lcr/Lcr.hpp"
8
-
9
- using margelo::nitro::ecr17::Lrc;
10
- using margelo::nitro::ecr17::LrcMode;
11
-
12
- namespace {
13
-
14
- constexpr uint8_t kBase = 0x7F;
15
- constexpr uint8_t kStx = 0x02;
16
- constexpr uint8_t kEtx = 0x03;
17
-
18
- // Reference implementation kept intentionally independent from the production
19
- // code, so the tests assert against first principles rather than a copy.
20
- uint8_t reference(const std::vector<uint8_t>& payload, LrcMode mode) {
21
- uint8_t lrc = kBase;
22
- if (mode == LrcMode::STX || mode == LrcMode::STX_NOEXT) {
23
- lrc ^= kStx;
24
- }
25
- for (uint8_t b : payload) {
26
- lrc ^= b;
27
- }
28
- if (mode == LrcMode::STX || mode == LrcMode::NOEXT) {
29
- lrc ^= kEtx;
30
- }
31
- return lrc;
32
- }
33
-
34
- } // namespace
35
-
36
- TEST(Lrc, EmptyPayloadStdIsBase) {
37
- EXPECT_EQ(Lrc::compute(std::vector<uint8_t>{}, LrcMode::STD), kBase);
38
- }
39
-
40
- TEST(Lrc, EmptyPayloadStxFoldsStxAndEtx) {
41
- // 0x7F ^ 0x02 ^ 0x03 == 0x7E
42
- EXPECT_EQ(Lrc::compute(std::vector<uint8_t>{}, LrcMode::STX), 0x7E);
43
- }
44
-
45
- TEST(Lrc, KnownVectorAllModes) {
46
- const std::vector<uint8_t> payload{'A'}; // 0x41
47
- EXPECT_EQ(Lrc::compute(payload, LrcMode::STD), 0x3E);
48
- EXPECT_EQ(Lrc::compute(payload, LrcMode::STX), 0x3F);
49
- EXPECT_EQ(Lrc::compute(payload, LrcMode::NOEXT), 0x3D);
50
- EXPECT_EQ(Lrc::compute(payload, LrcMode::STX_NOEXT), 0x3C);
51
- }
52
-
53
- TEST(Lrc, MatchesReferenceForEveryMode) {
54
- const std::vector<uint8_t> payload{0x00, 0x7F, 0x55, 0xAA, 'Z', 0x10};
55
- for (LrcMode mode : {LrcMode::STX, LrcMode::STD, LrcMode::NOEXT, LrcMode::STX_NOEXT}) {
56
- EXPECT_EQ(Lrc::compute(payload, mode), reference(payload, mode));
57
- }
58
- }
59
-
60
- TEST(Lrc, StringAndVectorOverloadsAgree) {
61
- const std::string payload = "12345678P0";
62
- const std::vector<uint8_t> bytes(payload.begin(), payload.end());
63
- for (LrcMode mode : {LrcMode::STX, LrcMode::STD, LrcMode::NOEXT, LrcMode::STX_NOEXT}) {
64
- EXPECT_EQ(Lrc::compute(payload, mode), Lrc::compute(bytes, mode));
65
- }
66
- }
1
+ #include <gtest/gtest.h>
2
+
3
+ #include <cstdint>
4
+ #include <string>
5
+ #include <vector>
6
+
7
+ #include "Lcr/Lcr.hpp"
8
+
9
+ using margelo::nitro::ecr17::Lrc;
10
+ using margelo::nitro::ecr17::LrcMode;
11
+
12
+ namespace {
13
+
14
+ constexpr uint8_t kBase = 0x7F;
15
+ constexpr uint8_t kStx = 0x02;
16
+ constexpr uint8_t kEtx = 0x03;
17
+
18
+ // Reference implementation kept intentionally independent from the production
19
+ // code, so the tests assert against first principles rather than a copy.
20
+ uint8_t reference(const std::vector<uint8_t>& payload, LrcMode mode) {
21
+ uint8_t lrc = kBase;
22
+ if (mode == LrcMode::STX || mode == LrcMode::STX_NOEXT) {
23
+ lrc ^= kStx;
24
+ }
25
+ for (uint8_t b : payload) {
26
+ lrc ^= b;
27
+ }
28
+ if (mode == LrcMode::STX || mode == LrcMode::NOEXT) {
29
+ lrc ^= kEtx;
30
+ }
31
+ return lrc;
32
+ }
33
+
34
+ } // namespace
35
+
36
+ TEST(Lrc, EmptyPayloadStdIsBase) {
37
+ EXPECT_EQ(Lrc::compute(std::vector<uint8_t>{}, LrcMode::STD), kBase);
38
+ }
39
+
40
+ TEST(Lrc, EmptyPayloadStxFoldsStxAndEtx) {
41
+ // 0x7F ^ 0x02 ^ 0x03 == 0x7E
42
+ EXPECT_EQ(Lrc::compute(std::vector<uint8_t>{}, LrcMode::STX), 0x7E);
43
+ }
44
+
45
+ TEST(Lrc, KnownVectorAllModes) {
46
+ const std::vector<uint8_t> payload{'A'}; // 0x41
47
+ EXPECT_EQ(Lrc::compute(payload, LrcMode::STD), 0x3E);
48
+ EXPECT_EQ(Lrc::compute(payload, LrcMode::STX), 0x3F);
49
+ EXPECT_EQ(Lrc::compute(payload, LrcMode::NOEXT), 0x3D);
50
+ EXPECT_EQ(Lrc::compute(payload, LrcMode::STX_NOEXT), 0x3C);
51
+ }
52
+
53
+ TEST(Lrc, MatchesReferenceForEveryMode) {
54
+ const std::vector<uint8_t> payload{0x00, 0x7F, 0x55, 0xAA, 'Z', 0x10};
55
+ for (LrcMode mode : {LrcMode::STX, LrcMode::STD, LrcMode::NOEXT, LrcMode::STX_NOEXT}) {
56
+ EXPECT_EQ(Lrc::compute(payload, mode), reference(payload, mode));
57
+ }
58
+ }
59
+
60
+ TEST(Lrc, StringAndVectorOverloadsAgree) {
61
+ const std::string payload = "12345678P0";
62
+ const std::vector<uint8_t> bytes(payload.begin(), payload.end());
63
+ for (LrcMode mode : {LrcMode::STX, LrcMode::STD, LrcMode::NOEXT, LrcMode::STX_NOEXT}) {
64
+ EXPECT_EQ(Lrc::compute(payload, mode), Lrc::compute(bytes, mode));
65
+ }
66
+ }
@@ -1,164 +1,164 @@
1
- #include <gtest/gtest.h>
2
-
3
- #include <cstdint>
4
- #include <string>
5
- #include <vector>
6
-
7
- #include "PacketCodec/PacketCodec.hpp"
8
-
9
- using margelo::nitro::ecr17::DecodedPacket;
10
- using margelo::nitro::ecr17::LrcMode;
11
- using margelo::nitro::ecr17::PacketCodec;
12
- using margelo::nitro::ecr17::PacketType;
13
-
14
- namespace {
15
- constexpr uint8_t kSoh = 0x01;
16
- constexpr uint8_t kStx = 0x02;
17
- constexpr uint8_t kEtx = 0x03;
18
- constexpr uint8_t kEot = 0x04;
19
- constexpr uint8_t kAck = 0x06;
20
- constexpr uint8_t kNak = 0x15;
21
- } // namespace
22
-
23
- TEST(PacketCodec, EncodeApplicationFramesStxPayloadEtxLrc) {
24
- PacketCodec codec(LrcMode::STD);
25
- auto frame = codec.encodeApplication("AB");
26
- ASSERT_EQ(frame.size(), 5u);
27
- EXPECT_EQ(frame[0], kStx);
28
- EXPECT_EQ(frame[1], 'A');
29
- EXPECT_EQ(frame[2], 'B');
30
- EXPECT_EQ(frame[3], kEtx);
31
- EXPECT_EQ(frame[4], 0x7C); // 0x7F ^ 'A' ^ 'B'
32
- }
33
-
34
- TEST(PacketCodec, ApplicationRoundTrip) {
35
- for (LrcMode mode : {LrcMode::STX, LrcMode::STD, LrcMode::NOEXT, LrcMode::STX_NOEXT}) {
36
- PacketCodec codec(mode);
37
- const std::string payload = "123456780P0000065000";
38
- DecodedPacket decoded = codec.decode(codec.encodeApplication(payload));
39
- EXPECT_EQ(decoded.type, PacketType::APPLICATION);
40
- EXPECT_EQ(decoded.payload, payload);
41
- EXPECT_TRUE(decoded.validLrc);
42
- }
43
- }
44
-
45
- TEST(PacketCodec, ApplicationDetectsCorruptedLrc) {
46
- PacketCodec codec(LrcMode::STD);
47
- auto frame = codec.encodeApplication("HELLO");
48
- frame.back() ^= 0xFF; // corrupt the LRC byte
49
- DecodedPacket decoded = codec.decode(frame);
50
- EXPECT_EQ(decoded.type, PacketType::APPLICATION);
51
- EXPECT_EQ(decoded.payload, "HELLO");
52
- EXPECT_FALSE(decoded.validLrc);
53
- }
54
-
55
- TEST(PacketCodec, EncodeControlFramesCtrlEtxLrc) {
56
- PacketCodec codec(LrcMode::STD);
57
- auto frame = codec.encodeControl(kAck);
58
- ASSERT_EQ(frame.size(), 3u);
59
- EXPECT_EQ(frame[0], kAck);
60
- EXPECT_EQ(frame[1], kEtx);
61
- }
62
-
63
- TEST(PacketCodec, DecodeAck) {
64
- PacketCodec codec(LrcMode::STD);
65
- DecodedPacket decoded = codec.decode({kAck});
66
- EXPECT_EQ(decoded.type, PacketType::ACK);
67
- EXPECT_TRUE(decoded.validLrc);
68
- }
69
-
70
- TEST(PacketCodec, DecodeNak) {
71
- PacketCodec codec(LrcMode::STD);
72
- DecodedPacket decoded = codec.decode({kNak});
73
- EXPECT_EQ(decoded.type, PacketType::NAK);
74
- EXPECT_TRUE(decoded.validLrc);
75
- }
76
-
77
- TEST(PacketCodec, DecodeEmptyIsUnknown) {
78
- PacketCodec codec(LrcMode::STD);
79
- DecodedPacket decoded = codec.decode({});
80
- EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
81
- EXPECT_FALSE(decoded.validLrc);
82
- }
83
-
84
- // Regression: a lone SOH byte previously built a string from an inverted
85
- // iterator range [begin()+1, end()-1) == [end(), begin()) -> UB/crash.
86
- TEST(PacketCodec, DecodeLoneSohIsUnknownNotCrash) {
87
- PacketCodec codec(LrcMode::STD);
88
- DecodedPacket decoded = codec.decode({kSoh});
89
- EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
90
- EXPECT_FALSE(decoded.validLrc);
91
- }
92
-
93
- TEST(PacketCodec, DecodeProgressUpdate) {
94
- PacketCodec codec(LrcMode::STD);
95
- std::vector<uint8_t> frame{kSoh};
96
- const std::string msg = "ELABORAZIONE... "; // 20 chars per spec
97
- frame.insert(frame.end(), msg.begin(), msg.end());
98
- frame.push_back(kEot);
99
-
100
- DecodedPacket decoded = codec.decode(frame);
101
- EXPECT_EQ(decoded.type, PacketType::PROGRESS);
102
- EXPECT_EQ(decoded.payload, msg);
103
- }
104
-
105
- TEST(PacketCodec, DecodeStxWithoutEtxIsUnknown) {
106
- PacketCodec codec(LrcMode::STD);
107
- DecodedPacket decoded = codec.decode({kStx, 'A', 'B'});
108
- EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
109
- EXPECT_FALSE(decoded.validLrc);
110
- }
111
-
112
- // Regression: ETX present but no trailing LRC byte must not read past the end
113
- // nor mistake ETX for the LRC.
114
- TEST(PacketCodec, DecodeStxWithEtxButNoLrcIsUnknown) {
115
- PacketCodec codec(LrcMode::STD);
116
- DecodedPacket decoded = codec.decode({kStx, 'A', kEtx});
117
- EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
118
- EXPECT_FALSE(decoded.validLrc);
119
- }
120
-
121
- TEST(PacketCodec, DecodeUnknownLeadByte) {
122
- PacketCodec codec(LrcMode::STD);
123
- DecodedPacket decoded = codec.decode({0x99, 0x00});
124
- EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
125
- EXPECT_FALSE(decoded.validLrc);
126
- }
127
-
128
- // Regression: trailing bytes after a complete frame's LRC must not be silently
129
- // accepted (the LRC must be the final byte).
130
- TEST(PacketCodec, DecodeStxWithTrailingBytesAfterLrcIsUnknown) {
131
- PacketCodec codec(LrcMode::STD);
132
- auto frame = codec.encodeApplication("AB"); // STX A B ETX LRC
133
- frame.push_back(0x00); // stray trailing byte
134
- DecodedPacket decoded = codec.decode(frame);
135
- EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
136
- EXPECT_FALSE(decoded.validLrc);
137
- }
138
-
139
- // Regression: a coalesced read holding two frames must not be reported as one
140
- // valid APPLICATION packet (which would silently drop the second frame).
141
- // Framing/splitting a byte stream is the transport layer's job.
142
- TEST(PacketCodec, DecodeCoalescedFramesIsUnknown) {
143
- PacketCodec codec(LrcMode::STD);
144
- auto first = codec.encodeApplication("AB");
145
- auto second = codec.encodeApplication("CD");
146
- first.insert(first.end(), second.begin(), second.end());
147
- DecodedPacket decoded = codec.decode(first);
148
- EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
149
- EXPECT_FALSE(decoded.validLrc);
150
- }
151
-
152
- // Regression: SOH frame whose last byte is not EOT must not be accepted as a
153
- // valid PROGRESS packet. Only SOH + payload + EOT is well-formed per spec.
154
- TEST(PacketCodec, DecodeSohWithoutEotIsUnknown) {
155
- PacketCodec codec(LrcMode::STD);
156
- // SOH + 20-char message, but terminated with 0xFF instead of EOT.
157
- std::vector<uint8_t> frame{kSoh};
158
- const std::string msg = "ELABORAZIONE... ";
159
- frame.insert(frame.end(), msg.begin(), msg.end());
160
- frame.push_back(0xFF); // wrong terminator
161
- DecodedPacket decoded = codec.decode(frame);
162
- EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
163
- EXPECT_FALSE(decoded.validLrc);
164
- }
1
+ #include <gtest/gtest.h>
2
+
3
+ #include <cstdint>
4
+ #include <string>
5
+ #include <vector>
6
+
7
+ #include "PacketCodec/PacketCodec.hpp"
8
+
9
+ using margelo::nitro::ecr17::DecodedPacket;
10
+ using margelo::nitro::ecr17::LrcMode;
11
+ using margelo::nitro::ecr17::PacketCodec;
12
+ using margelo::nitro::ecr17::PacketType;
13
+
14
+ namespace {
15
+ constexpr uint8_t kSoh = 0x01;
16
+ constexpr uint8_t kStx = 0x02;
17
+ constexpr uint8_t kEtx = 0x03;
18
+ constexpr uint8_t kEot = 0x04;
19
+ constexpr uint8_t kAck = 0x06;
20
+ constexpr uint8_t kNak = 0x15;
21
+ } // namespace
22
+
23
+ TEST(PacketCodec, EncodeApplicationFramesStxPayloadEtxLrc) {
24
+ PacketCodec codec(LrcMode::STD);
25
+ auto frame = codec.encodeApplication("AB");
26
+ ASSERT_EQ(frame.size(), 5u);
27
+ EXPECT_EQ(frame[0], kStx);
28
+ EXPECT_EQ(frame[1], 'A');
29
+ EXPECT_EQ(frame[2], 'B');
30
+ EXPECT_EQ(frame[3], kEtx);
31
+ EXPECT_EQ(frame[4], 0x7C); // 0x7F ^ 'A' ^ 'B'
32
+ }
33
+
34
+ TEST(PacketCodec, ApplicationRoundTrip) {
35
+ for (LrcMode mode : {LrcMode::STX, LrcMode::STD, LrcMode::NOEXT, LrcMode::STX_NOEXT}) {
36
+ PacketCodec codec(mode);
37
+ const std::string payload = "123456780P0000065000";
38
+ DecodedPacket decoded = codec.decode(codec.encodeApplication(payload));
39
+ EXPECT_EQ(decoded.type, PacketType::APPLICATION);
40
+ EXPECT_EQ(decoded.payload, payload);
41
+ EXPECT_TRUE(decoded.validLrc);
42
+ }
43
+ }
44
+
45
+ TEST(PacketCodec, ApplicationDetectsCorruptedLrc) {
46
+ PacketCodec codec(LrcMode::STD);
47
+ auto frame = codec.encodeApplication("HELLO");
48
+ frame.back() ^= 0xFF; // corrupt the LRC byte
49
+ DecodedPacket decoded = codec.decode(frame);
50
+ EXPECT_EQ(decoded.type, PacketType::APPLICATION);
51
+ EXPECT_EQ(decoded.payload, "HELLO");
52
+ EXPECT_FALSE(decoded.validLrc);
53
+ }
54
+
55
+ TEST(PacketCodec, EncodeControlFramesCtrlEtxLrc) {
56
+ PacketCodec codec(LrcMode::STD);
57
+ auto frame = codec.encodeControl(kAck);
58
+ ASSERT_EQ(frame.size(), 3u);
59
+ EXPECT_EQ(frame[0], kAck);
60
+ EXPECT_EQ(frame[1], kEtx);
61
+ }
62
+
63
+ TEST(PacketCodec, DecodeAck) {
64
+ PacketCodec codec(LrcMode::STD);
65
+ DecodedPacket decoded = codec.decode({kAck});
66
+ EXPECT_EQ(decoded.type, PacketType::ACK);
67
+ EXPECT_TRUE(decoded.validLrc);
68
+ }
69
+
70
+ TEST(PacketCodec, DecodeNak) {
71
+ PacketCodec codec(LrcMode::STD);
72
+ DecodedPacket decoded = codec.decode({kNak});
73
+ EXPECT_EQ(decoded.type, PacketType::NAK);
74
+ EXPECT_TRUE(decoded.validLrc);
75
+ }
76
+
77
+ TEST(PacketCodec, DecodeEmptyIsUnknown) {
78
+ PacketCodec codec(LrcMode::STD);
79
+ DecodedPacket decoded = codec.decode({});
80
+ EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
81
+ EXPECT_FALSE(decoded.validLrc);
82
+ }
83
+
84
+ // Regression: a lone SOH byte previously built a string from an inverted
85
+ // iterator range [begin()+1, end()-1) == [end(), begin()) -> UB/crash.
86
+ TEST(PacketCodec, DecodeLoneSohIsUnknownNotCrash) {
87
+ PacketCodec codec(LrcMode::STD);
88
+ DecodedPacket decoded = codec.decode({kSoh});
89
+ EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
90
+ EXPECT_FALSE(decoded.validLrc);
91
+ }
92
+
93
+ TEST(PacketCodec, DecodeProgressUpdate) {
94
+ PacketCodec codec(LrcMode::STD);
95
+ std::vector<uint8_t> frame{kSoh};
96
+ const std::string msg = "ELABORAZIONE... "; // 20 chars per spec
97
+ frame.insert(frame.end(), msg.begin(), msg.end());
98
+ frame.push_back(kEot);
99
+
100
+ DecodedPacket decoded = codec.decode(frame);
101
+ EXPECT_EQ(decoded.type, PacketType::PROGRESS);
102
+ EXPECT_EQ(decoded.payload, msg);
103
+ }
104
+
105
+ TEST(PacketCodec, DecodeStxWithoutEtxIsUnknown) {
106
+ PacketCodec codec(LrcMode::STD);
107
+ DecodedPacket decoded = codec.decode({kStx, 'A', 'B'});
108
+ EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
109
+ EXPECT_FALSE(decoded.validLrc);
110
+ }
111
+
112
+ // Regression: ETX present but no trailing LRC byte must not read past the end
113
+ // nor mistake ETX for the LRC.
114
+ TEST(PacketCodec, DecodeStxWithEtxButNoLrcIsUnknown) {
115
+ PacketCodec codec(LrcMode::STD);
116
+ DecodedPacket decoded = codec.decode({kStx, 'A', kEtx});
117
+ EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
118
+ EXPECT_FALSE(decoded.validLrc);
119
+ }
120
+
121
+ TEST(PacketCodec, DecodeUnknownLeadByte) {
122
+ PacketCodec codec(LrcMode::STD);
123
+ DecodedPacket decoded = codec.decode({0x99, 0x00});
124
+ EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
125
+ EXPECT_FALSE(decoded.validLrc);
126
+ }
127
+
128
+ // Regression: trailing bytes after a complete frame's LRC must not be silently
129
+ // accepted (the LRC must be the final byte).
130
+ TEST(PacketCodec, DecodeStxWithTrailingBytesAfterLrcIsUnknown) {
131
+ PacketCodec codec(LrcMode::STD);
132
+ auto frame = codec.encodeApplication("AB"); // STX A B ETX LRC
133
+ frame.push_back(0x00); // stray trailing byte
134
+ DecodedPacket decoded = codec.decode(frame);
135
+ EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
136
+ EXPECT_FALSE(decoded.validLrc);
137
+ }
138
+
139
+ // Regression: a coalesced read holding two frames must not be reported as one
140
+ // valid APPLICATION packet (which would silently drop the second frame).
141
+ // Framing/splitting a byte stream is the transport layer's job.
142
+ TEST(PacketCodec, DecodeCoalescedFramesIsUnknown) {
143
+ PacketCodec codec(LrcMode::STD);
144
+ auto first = codec.encodeApplication("AB");
145
+ auto second = codec.encodeApplication("CD");
146
+ first.insert(first.end(), second.begin(), second.end());
147
+ DecodedPacket decoded = codec.decode(first);
148
+ EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
149
+ EXPECT_FALSE(decoded.validLrc);
150
+ }
151
+
152
+ // Regression: SOH frame whose last byte is not EOT must not be accepted as a
153
+ // valid PROGRESS packet. Only SOH + payload + EOT is well-formed per spec.
154
+ TEST(PacketCodec, DecodeSohWithoutEotIsUnknown) {
155
+ PacketCodec codec(LrcMode::STD);
156
+ // SOH + 20-char message, but terminated with 0xFF instead of EOT.
157
+ std::vector<uint8_t> frame{kSoh};
158
+ const std::string msg = "ELABORAZIONE... ";
159
+ frame.insert(frame.end(), msg.begin(), msg.end());
160
+ frame.push_back(0xFF); // wrong terminator
161
+ DecodedPacket decoded = codec.decode(frame);
162
+ EXPECT_EQ(decoded.type, PacketType::UNKNOWN);
163
+ EXPECT_FALSE(decoded.validLrc);
164
+ }