Skip to content

Commit 18e481c

Browse files
misc
1 parent 5a1823d commit 18e481c

File tree

4 files changed

+40
-31
lines changed

4 files changed

+40
-31
lines changed

AGENTS.md

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
# LibCANard instructions for AI agents
22

3-
Please read `README.md` for general information about the library, and `CONTRIBUTING.md` for development-related notes.
3+
Read `README.md` for general information about the library, and `CONTRIBUTING.md` for development-related notes.
44

5-
Keep the code and comments very brief. Be sure every significant code block is preceded with a brief comment.
6-
7-
If you need a build directory, create one in the project root named with a `build` prefix;
8-
you can also use existing build directories if you prefer so,
9-
but avoid using `cmake-build-*` because these are used by CLion.
10-
When building the code, don't hesitate to use multiple jobs to use all CPU cores.
5+
Build directories should be created in the project root named with a `build` prefix. Parallelize compilation.
116

127
Run all tests in debug build to ensure that all assertion checks are enabled.
138

14-
It is best to use Clang-Format to format the code when done editing.
9+
## Style conventions
10+
11+
Language targets: C99 for the library, C99 and C++20 for the test harness. Strict std only, compiler extensions not allowed.
12+
13+
Naming patterns: `canard_*` functions, `canard_*_t` types, `CANARD_*` macros. Internal definitions need no prefixing. Enums and constants are `lower_snake_case`. Uppercase only for macros.
14+
15+
Keep code compact and add brief comments before non-obvious logic.
16+
17+
Treat warnings as errors and keep compatibility with strict warning flags.
18+
19+
For agent-authored commits, set `GIT_AUTHOR_NAME="Agent"` and `GIT_COMMITTER_NAME="Agent"`.
20+
21+
## Adversarial validation and verification
22+
23+
Practice an adversarial approach to testing: the purpose of a test case is not to provide coverage, but to empirically prove correctness of the tested code. Always treat the code as suspect; you will be rewarded for pointing out flaws in it. If the code does not appear to be correct, refuse to test it and provide evidence of its defects instead of proceeding with testing.
24+
25+
When using subagents to implement tests, always instruct them to summarize their findings concerning the correctness of the tested code and its possible limitations at the end of their run. At the end of the turn, provide a summary of the findings.

libcanard/canard.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -538,11 +538,11 @@ static tx_frame_t* tx_spool(canard_t* const self,
538538
const uint16_t crc_seed,
539539
const size_t mtu,
540540
const byte_t transfer_id,
541+
const size_t size,
541542
const canard_bytes_chain_t payload)
542543
{
543544
bytes_chain_reader_t reader = { .cursor = &payload, .position = 0U };
544545
tx_frame_t* head = NULL;
545-
const size_t size = bytes_chain_size(payload);
546546
bool toggle = true; // Cyphal transfers start with toggle==1, unlike legacy
547547
if (size < mtu) { // Single-frame transfer; no CRC required -- easy case.
548548
const size_t frame_size = tx_ceil_frame_payload_size(size + 1U);
@@ -623,10 +623,10 @@ static tx_frame_t* tx_spool(canard_t* const self,
623623
static tx_frame_t* tx_spool_v0(canard_t* const self,
624624
const uint16_t crc_seed,
625625
const byte_t transfer_id,
626+
const size_t size,
626627
const canard_bytes_chain_t payload)
627628
{
628-
bool toggle = false; // in v0, toggle starts with zero; that's how v0/v1 can be distinguished
629-
const size_t size = bytes_chain_size(payload);
629+
bool toggle = false; // in v0, toggle starts with zero; that's how v0/v1 can be distinguished
630630
if (size < CANARD_MTU_CAN_CLASSIC) { // single-frame transfer
631631
tx_frame_t* const item = tx_frame_new(self, size + 1U);
632632
if (item != NULL) {
@@ -723,10 +723,11 @@ static bool tx_push(canard_t* const self,
723723
{
724724
CANARD_ASSERT(tr != NULL);
725725
CANARD_ASSERT((!tr->fd) || !v0); // The caller must ensure this.
726+
CANARD_ASSERT(iface_bitmap != 0);
726727

727728
// Ensure the queue has enough space. v0 transfers always use Classic CAN regardless of tr->fd.
728729
const size_t mtu = tr->fd ? CANARD_MTU_CAN_FD : CANARD_MTU_CAN_CLASSIC;
729-
const size_t size = bytes_chain_size(payload); // TODO: pass the precomputed size into spool functions
730+
const size_t size = bytes_chain_size(payload);
730731
const size_t n_frames = tx_predict_frame_count(size, mtu);
731732
CANARD_ASSERT(n_frames > 0);
732733
if (!tx_ensure_queue_space(self, n_frames)) {
@@ -737,8 +738,8 @@ static bool tx_push(canard_t* const self,
737738

738739
// Make a shared frame spool. Unlike the Cyphal/UDP implementation, we require all ifaces to use the same MTU.
739740
const size_t queue_size_before = self->tx.queue_size;
740-
tx_frame_t* const spool = v0 ? tx_spool_v0(self, crc_seed, tr->transfer_id, payload)
741-
: tx_spool(self, crc_seed, mtu, tr->transfer_id, payload);
741+
tx_frame_t* const spool = v0 ? tx_spool_v0(self, crc_seed, tr->transfer_id, size, payload)
742+
: tx_spool(self, crc_seed, mtu, tr->transfer_id, size, payload);
742743
if (spool == NULL) {
743744
self->err.oom++;
744745
mem_free(self->mem.tx_transfer, sizeof(canard_txfer_t), tr);

tests/src/test_api_tx.cpp

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,12 @@ static bool mock_tx(canard_t* const,
3434
static bool mock_filter(canard_t* const, const size_t, const canard_filter_t*) { return true; }
3535

3636
// Shared vtable and memory resources used by canard_new() tests.
37-
static const canard_vtable_t kTestVtable = { .now = mock_now,
37+
static const canard_vtable_t test_vtable = { .now = mock_now,
3838
.on_unicast = nullptr,
3939
.tx = mock_tx,
4040
.filter = mock_filter };
4141

42-
static const canard_mem_vtable_t kStdMemVtable = {
43-
.free = std_free_mem,
44-
.alloc = std_alloc_mem,
45-
};
42+
static const canard_mem_vtable_t std_mem_vtable = { .free = std_free_mem, .alloc = std_alloc_mem };
4643

4744
// Captures outgoing TX callback invocations for API-level poll/unicast checks.
4845
struct tx_record_t
@@ -93,7 +90,7 @@ static bool capture_tx(canard_t* const self,
9390
return cap->accept_tx;
9491
}
9592

96-
static const canard_vtable_t kCaptureVtable = {
93+
static const canard_vtable_t capture_vtable = {
9794
.now = capture_now,
9895
.on_unicast = nullptr,
9996
.tx = capture_tx,
@@ -102,7 +99,7 @@ static const canard_vtable_t kCaptureVtable = {
10299

103100
static canard_mem_set_t make_std_memory()
104101
{
105-
const canard_mem_t r = { .vtable = &kStdMemVtable, .context = nullptr };
102+
const canard_mem_t r = { .vtable = &std_mem_vtable, .context = nullptr };
106103
return canard_mem_set_t{
107104
.tx_transfer = r,
108105
.tx_frame = r,
@@ -117,7 +114,7 @@ static void init_with_capture_node_id(canard_t* const self, tx_capture_t* const
117114
capture->now = 0;
118115
capture->accept_tx = true;
119116
capture->count = 0;
120-
TEST_ASSERT_TRUE(canard_new(self, &kCaptureVtable, make_std_memory(), 16U, node_id, 1234U, 0U, nullptr));
117+
TEST_ASSERT_TRUE(canard_new(self, &capture_vtable, make_std_memory(), 16U, node_id, 1234U, 0U, nullptr));
121118
self->user_context = capture;
122119
}
123120

@@ -141,13 +138,13 @@ static void test_canard_new_validation()
141138
.rx_payload = bad_mr,
142139
};
143140

144-
TEST_ASSERT_FALSE(canard_new(nullptr, &kTestVtable, mem, 16, 1, 1234, 0, nullptr)); // Invalid self.
141+
TEST_ASSERT_FALSE(canard_new(nullptr, &test_vtable, mem, 16, 1, 1234, 0, nullptr)); // Invalid self.
145142
TEST_ASSERT_FALSE(canard_new(&self, nullptr, mem, 16, 1, 1234, 0, nullptr)); // Invalid vtable.
146143
TEST_ASSERT_FALSE(
147-
canard_new(&self, &kTestVtable, mem, 16, CANARD_NODE_ID_MAX + 1U, 1234, 0, nullptr)); // Invalid node-ID.
148-
TEST_ASSERT_FALSE(canard_new(&self, &kTestVtable, mem, 16, 1, 1234, 1, nullptr)); // Missing filter storage.
149-
TEST_ASSERT_FALSE(canard_new(&self, &kTestVtable, bad_mem, 16, 1, 1234, 0, nullptr)); // Invalid memory callbacks.
150-
TEST_ASSERT_TRUE(canard_new(&self, &kTestVtable, mem, 16, 1, 1234, 1, &filters)); // Valid constructor call.
144+
canard_new(&self, &test_vtable, mem, 16, CANARD_NODE_ID_MAX + 1U, 1234, 0, nullptr)); // Invalid node-ID.
145+
TEST_ASSERT_FALSE(canard_new(&self, &test_vtable, mem, 16, 1, 1234, 1, nullptr)); // Missing filter storage.
146+
TEST_ASSERT_FALSE(canard_new(&self, &test_vtable, bad_mem, 16, 1, 1234, 0, nullptr)); // Invalid memory callbacks.
147+
TEST_ASSERT_TRUE(canard_new(&self, &test_vtable, mem, 16, 1, 1234, 1, &filters)); // Valid constructor call.
151148
canard_destroy(&self);
152149
}
153150

@@ -156,7 +153,7 @@ static void test_canard_new_and_destroy()
156153
{
157154
canard_t self = {};
158155
const canard_mem_set_t mem = make_std_memory();
159-
TEST_ASSERT_TRUE(canard_new(&self, &kTestVtable, mem, 16, 42, 0x0123456789ABCDEFULL, 0, nullptr));
156+
TEST_ASSERT_TRUE(canard_new(&self, &test_vtable, mem, 16, 42, 0x0123456789ABCDEFULL, 0, nullptr));
160157
TEST_ASSERT_EQUAL_UINT8(42U, (uint8_t)self.node_id);
161158
TEST_ASSERT_TRUE(self.tx.fd);
162159
TEST_ASSERT_EQUAL_size_t(16U, self.tx.queue_capacity);
@@ -179,7 +176,7 @@ static void test_canard_pending_ifaces()
179176
{
180177
canard_t self = {};
181178
const canard_mem_set_t mem = make_std_memory();
182-
TEST_ASSERT_TRUE(canard_new(&self, &kTestVtable, mem, 16, 1, 1234, 0, nullptr));
179+
TEST_ASSERT_TRUE(canard_new(&self, &test_vtable, mem, 16, 1, 1234, 0, nullptr));
183180

184181
const canard_bytes_chain_t payload = { .bytes = { .size = 0, .data = nullptr }, .next = nullptr };
185182
TEST_ASSERT_EQUAL_UINT8(0U, canard_pending_ifaces(&self));

tests/src/test_intrusive_tx.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ static void test_tx_spool_single_frame(void)
7373
const byte_t data[] = { 1U, 2U, 3U, 4U };
7474
const canard_bytes_chain_t payload = { .bytes = { .size = sizeof(data), .data = data }, .next = NULL };
7575

76-
tx_frame_t* const head = tx_spool(&self, CRC_INITIAL, CANARD_MTU_CAN_CLASSIC, 7U, payload);
76+
tx_frame_t* const head = tx_spool(&self, CRC_INITIAL, CANARD_MTU_CAN_CLASSIC, 7U, sizeof(data), payload);
7777
TEST_ASSERT_NOT_NULL(head);
7878
TEST_ASSERT_EQUAL_size_t(1U, count_frames(head));
7979
TEST_ASSERT_EQUAL_size_t(5U, canard_dlc_to_len[head->dlc]);
@@ -97,7 +97,7 @@ static void test_tx_spool_multi_frame(void)
9797
}
9898
const canard_bytes_chain_t payload = { .bytes = { .size = sizeof(data), .data = data }, .next = NULL };
9999

100-
tx_frame_t* const head = tx_spool(&self, CRC_INITIAL, CANARD_MTU_CAN_CLASSIC, 3U, payload);
100+
tx_frame_t* const head = tx_spool(&self, CRC_INITIAL, CANARD_MTU_CAN_CLASSIC, 3U, sizeof(data), payload);
101101
TEST_ASSERT_NOT_NULL(head);
102102
TEST_ASSERT_EQUAL_size_t(2U, count_frames(head));
103103
TEST_ASSERT_EQUAL_HEX8(0xA3, head->data[7]);

0 commit comments

Comments
 (0)