@padosoft/react-native-ecr17 0.0.0 → 2.0.0
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.
- package/Ecr17.podspec +39 -39
- package/README.md +348 -348
- package/android/CMakeLists.txt +41 -41
- package/android/build.gradle +148 -148
- package/android/fix-prefab.gradle +50 -50
- package/android/gradle.properties +5 -5
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/cpp/cpp-adapter.cpp +8 -8
- package/android/src/main/java/com/margelo/nitro/ecr17/HybridEcr17Transport.kt +233 -233
- package/android/src/main/java/com/padosoft/ecr17/Ecr17Package.kt +30 -30
- package/cpp/Ecr17.hpp +1 -1
- package/cpp/Ecr17Client/HybridEcr17Client.cpp +598 -598
- package/cpp/Ecr17Client/HybridEcr17Client.hpp +85 -85
- package/cpp/Ecr17Protocol/Ecr17Protocol.cpp +277 -277
- package/cpp/Ecr17Protocol/Ecr17Protocol.hpp +103 -103
- package/cpp/Ecr17Response/Ecr17Response.cpp +155 -155
- package/cpp/Ecr17Response/Ecr17Response.hpp +113 -113
- package/cpp/Lcr/Lcr.cpp +42 -42
- package/cpp/Lcr/Lcr.hpp +21 -21
- package/cpp/PacketCodec/PacketCodec.cpp +145 -145
- package/cpp/PacketCodec/PacketCodec.hpp +47 -47
- package/cpp/Session/Ecr17Session.cpp +260 -260
- package/cpp/Session/Ecr17Session.hpp +97 -97
- package/cpp/Session/RetryPolicy.hpp +23 -23
- package/cpp/Transport/FakeTransport.hpp +95 -95
- package/cpp/Transport/NativeTransportAdapter.cpp +42 -42
- package/cpp/Transport/NativeTransportAdapter.hpp +32 -32
- package/cpp/Transport/Transport.hpp +30 -30
- package/cpp/tests/CMakeLists.txt +55 -55
- package/cpp/tests/PosixTcpTransport.hpp +105 -105
- package/cpp/tests/stubs/LrcMode.hpp +25 -25
- package/cpp/tests/test_flows.cpp +148 -148
- package/cpp/tests/test_integration_terminal.cpp +72 -72
- package/cpp/tests/test_lrc.cpp +66 -66
- package/cpp/tests/test_packet_codec.cpp +164 -164
- package/cpp/tests/test_protocol.cpp +102 -102
- package/cpp/tests/test_protocol_commands.cpp +190 -190
- package/cpp/tests/test_response.cpp +164 -164
- package/cpp/tests/test_retry_policy.cpp +28 -28
- package/cpp/tests/test_session.cpp +262 -262
- package/ios/HybridEcr17Transport.swift +103 -103
- package/lib/commonjs/index.js +50 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/client.nitro.js +17 -0
- package/lib/commonjs/specs/client.nitro.js.map +1 -0
- package/lib/commonjs/specs/transport.nitro.js +6 -0
- package/lib/commonjs/specs/transport.nitro.js.map +1 -0
- package/lib/commonjs/types/client.js +2 -0
- package/lib/commonjs/types/client.js.map +1 -0
- package/lib/commonjs/utils/client.js +13 -0
- package/lib/commonjs/utils/client.js.map +1 -0
- package/lib/module/index.js +7 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/specs/client.nitro.js +13 -0
- package/lib/module/specs/client.nitro.js.map +1 -0
- package/lib/module/specs/transport.nitro.js +4 -0
- package/lib/module/specs/transport.nitro.js.map +1 -0
- package/lib/module/types/client.js +2 -0
- package/lib/module/types/client.js.map +1 -0
- package/lib/module/utils/client.js +9 -0
- package/lib/module/utils/client.js.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/specs/client.nitro.d.ts +63 -0
- package/lib/typescript/src/specs/client.nitro.d.ts.map +1 -0
- package/lib/typescript/src/specs/transport.nitro.d.ts +13 -0
- package/lib/typescript/src/specs/transport.nitro.d.ts.map +1 -0
- package/lib/typescript/src/types/client.d.ts +138 -0
- package/lib/typescript/src/types/client.d.ts.map +1 -0
- package/lib/typescript/src/utils/client.d.ts +3 -0
- package/lib/typescript/src/utils/client.d.ts.map +1 -0
- package/nitro.json +30 -30
- package/package.json +4 -4
- package/react-native.config.js +18 -18
- package/src/index.ts +4 -4
- package/src/specs/client.nitro.ts +102 -102
- package/src/specs/transport.nitro.ts +25 -25
- package/src/types/client.ts +196 -196
- 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
|
package/cpp/tests/test_lrc.cpp
CHANGED
|
@@ -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
|
+
}
|