11#include <netinet/in.h>
12#include <sys/socket.h>
27using namespace std::chrono_literals;
33 std::cerr << "FAIL " << __LINE__ << ": " #cond << std::endl; \
40 const uint8_t err[] = {5, 0, 0, 0, 0, 0xE0, 0x1D, 7, 4,
'n',
'o',
'p',
'e'};
41 const uint8_t data[] = {16, 0, 0, 0, 1, 0x33, 0x01, 0, 1, 2, 3, 4};
49 const auto t0 = SubscriptionTracker::Clock::now();
50 const uint8_t pt = tr.
assign({0x20, 0x01});
51 const uint8_t bv = tr.
assign({0x33, 0x01});
55 CHECK(t.first == 0x33 && t.second == 0x01);
59 const auto again = tr.
due(t0 + 1000ms);
60 CHECK(again.size() == 1 && again[0].first == 0x33 && again[0].second == 0x01);
68 const auto t0 = SubscriptionTracker::Clock::now();
76 const auto t = tr.
rejected(200, SubscriptionTracker::Clock::now());
77 CHECK(t.first == 0 && t.second == 0);
84 for (
int i = 0; i < 600; i++) {
85 const uint8_t
id = tr.
assign({0x20,
static_cast<uint8_t
>(i)});
86 CHECK(
id != 0 &&
id != 255);
89 CHECK(seen.size() == 254);
94 const auto t0 = SubscriptionTracker::Clock::now();
95 const uint8_t
id = tr.
assign({0x33, 0x02});
105 std::atomic<uint16_t>
port{0};
106 std::vector<uint8_t> got;
107 std::thread db([&]() {
108 int srv = socket(AF_INET, SOCK_STREAM, 0);
110 setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt,
sizeof(opt));
111 struct sockaddr_in
a{};
112 a.sin_family = AF_INET;
113 a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
115 bind(srv,
reinterpret_cast<struct sockaddr*
>(&
a),
sizeof(
a));
116 socklen_t sl =
sizeof(
a);
117 getsockname(srv,
reinterpret_cast<struct sockaddr*
>(&
a), &sl);
119 port = ntohs(
a.sin_port);
120 int c = accept(srv,
nullptr,
nullptr);
122 while (got.size() < 20) {
123 ssize_t
n =
read(c, buf,
sizeof(buf));
126 got.insert(got.end(), buf, buf +
n);
131 for (
int i = 0; i < 500 &&
port.load() == 0; i++)
132 std::this_thread::sleep_for(2ms);
135 CHECK(client.connect(
"127.0.0.1",
port.load()));
136 CHECK(client.subscribe_table(0x33, 0x01, 42));
137 CHECK(client.subscribe_tables({{0x20, 0x05}}));
140 CHECK(got.size() == 20);
141 if (got.size() == 20) {
142 CHECK(got[5] == 0x11 && got[6] == 0x0d);
144 CHECK(got[8] == 0x33 && got[9] == 0x01);
146 CHECK(got[18] == 0x20 && got[19] == 0x05);
159 std::cerr <<
g_failures <<
" failure(s)" << std::endl;
162 std::cout <<
"test_subscription_tracker: all passed" << std::endl;
Standalone Elodin database client.
void reset()
A new connection: every subscription is sent afresh.
Table rejected(uint8_t request_id, Clock::time_point now)
The DB refused the subscription sent under request_id.
std::vector< Table > due(Clock::time_point now)
Refused tables whose wait is over; each is forgotten here until rejected again.
size_t refused() const
Tables currently waiting to be asked for again.
uint8_t assign(Table table)
The request id to subscribe table under.
static const emmc_port_t port
*brief create JSON pointer *sa reference_tokens const string_t & a
bool is_error_response(const uint8_t *pkt, size_t len)
ErrorResponse: header len(4) | ty(1) | msg id [0xE0, 0x1D] | request id(1).
*brief user defined string literal for JSON values *sa std::size_t n
static double read(int32_t code)
static void test_error_response_is_recognised()
static void test_unknown_request_id_names_nothing()
static void test_reset_forgets_everything()
static void test_refused_table_is_asked_for_again()
static void test_accepted_table_is_never_asked_for_twice()
static void test_ids_rotate_and_skip_0_and_255()
static void test_request_id_is_on_the_wire()
subscribe_table puts the request id where the DB echoes it from: header byte 7.