Skip to content

Commit 486fb66

Browse files
andrebraitNebuleon
authored andcommitted
[Enhancement] Improvements for debounce test coverage + bug fixes for sym_defer_g and sym_eager_pr (qmk#21667)
Co-authored-by: Nebuleon <[email protected]>
1 parent 872b247 commit 486fb66

15 files changed

+458
-23
lines changed

platforms/test/timer.c

+36-5
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,65 @@
1717
#include "timer.h"
1818
#include <stdatomic.h>
1919

20-
static atomic_uint_least32_t current_time = 0;
20+
static atomic_uint_least32_t current_time = 0;
21+
static atomic_uint_least32_t async_tick_amount = 0;
22+
static atomic_uint_least32_t access_counter = 0;
23+
24+
void simulate_async_tick(uint32_t t) {
25+
async_tick_amount = t;
26+
}
27+
28+
uint32_t timer_read_internal(void) {
29+
return current_time;
30+
}
31+
32+
uint32_t current_access_counter(void) {
33+
return access_counter;
34+
}
35+
36+
void reset_access_counter(void) {
37+
access_counter = 0;
38+
}
2139

2240
void timer_init(void) {
23-
current_time = 0;
41+
current_time = 0;
42+
async_tick_amount = 0;
43+
access_counter = 0;
2444
}
2545

2646
void timer_clear(void) {
27-
current_time = 0;
47+
current_time = 0;
48+
async_tick_amount = 0;
49+
access_counter = 0;
2850
}
2951

3052
uint16_t timer_read(void) {
31-
return current_time & 0xFFFF;
53+
return (uint16_t)timer_read32();
3254
}
55+
3356
uint32_t timer_read32(void) {
57+
if (access_counter++ > 0) {
58+
current_time += async_tick_amount;
59+
}
3460
return current_time;
3561
}
62+
3663
uint16_t timer_elapsed(uint16_t last) {
3764
return TIMER_DIFF_16(timer_read(), last);
3865
}
66+
3967
uint32_t timer_elapsed32(uint32_t last) {
4068
return TIMER_DIFF_32(timer_read32(), last);
4169
}
4270

4371
void set_time(uint32_t t) {
44-
current_time = t;
72+
current_time = t;
73+
access_counter = 0;
4574
}
75+
4676
void advance_time(uint32_t ms) {
4777
current_time += ms;
78+
access_counter = 0;
4879
}
4980

5081
void wait_ms(uint32_t ms) {

quantum/debounce/none.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,15 @@
2020
void debounce_init(uint8_t num_rows) {}
2121

2222
bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {
23-
bool cooked_changed = memcmp(raw, cooked, sizeof(matrix_row_t) * num_rows) != 0;
23+
bool cooked_changed = false;
2424

25-
memcpy(cooked, raw, sizeof(matrix_row_t) * num_rows);
25+
if (changed) {
26+
size_t matrix_size = num_rows * sizeof(matrix_row_t);
27+
if (memcmp(cooked, raw, matrix_size) != 0) {
28+
memcpy(cooked, raw, matrix_size);
29+
cooked_changed = true;
30+
}
31+
}
2632

2733
return cooked_changed;
2834
}

quantum/debounce/sym_defer_g.c

+10-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
2424
# define DEBOUNCE 5
2525
#endif
2626

27+
// Maximum debounce: 255ms
28+
#if DEBOUNCE > UINT8_MAX
29+
# undef DEBOUNCE
30+
# define DEBOUNCE UINT8_MAX
31+
#endif
32+
2733
#if DEBOUNCE > 0
2834
static bool debouncing = false;
2935
static fast_timer_t debouncing_time;
@@ -36,11 +42,10 @@ bool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
3642
if (changed) {
3743
debouncing = true;
3844
debouncing_time = timer_read_fast();
39-
}
40-
41-
if (debouncing && timer_elapsed_fast(debouncing_time) >= DEBOUNCE) {
42-
if (memcmp(cooked, raw, sizeof(matrix_row_t) * num_rows) != 0) {
43-
memcpy(cooked, raw, sizeof(matrix_row_t) * num_rows);
45+
} else if (debouncing && timer_elapsed_fast(debouncing_time) >= DEBOUNCE) {
46+
size_t matrix_size = num_rows * sizeof(matrix_row_t);
47+
if (memcmp(cooked, raw, matrix_size) != 0) {
48+
memcpy(cooked, raw, matrix_size);
4449
cooked_changed = true;
4550
}
4651
debouncing = false;

quantum/debounce/sym_eager_pr.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui
128128
if (existing_row != raw_row) {
129129
if (*debounce_pointer == DEBOUNCE_ELAPSED) {
130130
*debounce_pointer = DEBOUNCE;
131-
cooked[row] = raw_row;
132-
cooked_changed |= cooked[row] ^ raw[row];
131+
cooked_changed |= cooked[row] ^ raw_row;
132+
cooked[row] = raw_row;
133133
counters_need_update = true;
134134
}
135135
}

quantum/debounce/tests/asym_eager_defer_pk_tests.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -392,3 +392,32 @@ TEST_F(DebounceTest, OneKeyDelayedScan8) {
392392
time_jumps_ = true;
393393
runEvents();
394394
}
395+
396+
TEST_F(DebounceTest, AsyncTickOneKeyShort1) {
397+
addEvents({
398+
/* Time, Inputs, Outputs */
399+
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
400+
/* Release key after 1ms delay */
401+
{1, {{0, 1, UP}}, {}},
402+
403+
/*
404+
* Until the eager timer on DOWN is observed to finish, the defer timer
405+
* on UP can't start. There's no workaround for this because it's not
406+
* possible to debounce an event that isn't being tracked.
407+
*
408+
* sym_defer_pk has the same problem but the test has to track that the
409+
* key changed state so the DOWN timer is always allowed to finish
410+
* before starting the UP timer.
411+
*/
412+
{5, {}, {}},
413+
414+
{10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */
415+
/* Press key again after 1ms delay */
416+
{11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
417+
});
418+
/*
419+
* Debounce implementations should never read the timer more than once per invocation
420+
*/
421+
async_time_jumps_ = DEBOUNCE;
422+
runEvents();
423+
}

quantum/debounce/tests/debounce_test_common.cpp

+18-7
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ extern "C" {
2626
#include "debounce.h"
2727
#include "timer.h"
2828

29-
void set_time(uint32_t t);
30-
void advance_time(uint32_t ms);
29+
void simulate_async_tick(uint32_t t);
30+
void reset_access_counter(void);
31+
uint32_t current_access_counter(void);
32+
uint32_t timer_read_internal(void);
33+
void set_time(uint32_t t);
34+
void advance_time(uint32_t ms);
3135
}
3236

3337
void DebounceTest::addEvents(std::initializer_list<DebounceTestEvent> events) {
@@ -58,6 +62,7 @@ void DebounceTest::runEventsInternal() {
5862
/* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */
5963
debounce_init(MATRIX_ROWS);
6064
set_time(time_offset_);
65+
simulate_async_tick(async_time_jumps_);
6166
std::fill(std::begin(input_matrix_), std::end(input_matrix_), 0);
6267
std::fill(std::begin(output_matrix_), std::end(output_matrix_), 0);
6368

@@ -70,9 +75,9 @@ void DebounceTest::runEventsInternal() {
7075
advance_time(1);
7176
} else {
7277
/* Fast forward to the time for this event, calling debounce() with no changes */
73-
ASSERT_LT((time_offset_ + event.time_) - timer_read_fast(), 60000) << "Test tries to advance more than 1 minute of time";
78+
ASSERT_LT((time_offset_ + event.time_) - timer_read_internal(), 60000) << "Test tries to advance more than 1 minute of time";
7479

75-
while (timer_read_fast() != time_offset_ + event.time_) {
80+
while (timer_read_internal() != time_offset_ + event.time_) {
7681
runDebounce(false);
7782
checkCookedMatrix(false, "debounce() modified cooked matrix");
7883
advance_time(1);
@@ -124,14 +129,20 @@ void DebounceTest::runDebounce(bool changed) {
124129
std::copy(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_));
125130
std::copy(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_));
126131

132+
reset_access_counter();
133+
127134
bool cooked_changed = debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed);
128135

129136
if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) {
130137
FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime() << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) << "\nraw_matrix:\n" << strMatrix(raw_matrix_);
131138
}
132139

133-
if (std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_)) && cooked_changed) {
134-
FAIL() << "Fatal error: debounce() did detect a wrong cooked matrix change at " << strTime() << "\noutput_matrix: cooked_changed=" << cooked_changed << "\n" << strMatrix(output_matrix_) << "\ncooked_matrix:\n" << strMatrix(cooked_matrix_);
140+
if (std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_)) == cooked_changed) {
141+
FAIL() << "Fatal error: debounce() reported a wrong cooked matrix change result at " << strTime() << "\noutput_matrix: cooked_changed=" << cooked_changed << "\n" << strMatrix(output_matrix_) << "\ncooked_matrix:\n" << strMatrix(cooked_matrix_);
142+
}
143+
144+
if (current_access_counter() > 1) {
145+
FAIL() << "Fatal error: debounce() read the timer multiple times, which is not allowed, at " << strTime() << "\ntimer: access_count=" << current_access_counter() << "\noutput_matrix: cooked_changed=" << cooked_changed << "\n" << strMatrix(output_matrix_) << "\ncooked_matrix:\n" << strMatrix(cooked_matrix_);
135146
}
136147
}
137148

@@ -144,7 +155,7 @@ void DebounceTest::checkCookedMatrix(bool changed, const std::string &error_mess
144155
std::string DebounceTest::strTime() {
145156
std::stringstream text;
146157

147-
text << "time " << (timer_read_fast() - time_offset_) << " (extra_iterations=" << extra_iterations_ << ", auto_advance_time=" << auto_advance_time_ << ")";
158+
text << "time " << (timer_read_internal() - time_offset_) << " (extra_iterations=" << extra_iterations_ << ", auto_advance_time=" << auto_advance_time_ << ")";
148159

149160
return text.str();
150161
}

quantum/debounce/tests/debounce_test_common.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ class DebounceTest : public ::testing::Test {
5454
void addEvents(std::initializer_list<DebounceTestEvent> events);
5555
void runEvents();
5656

57-
fast_timer_t time_offset_ = 7777;
58-
bool time_jumps_ = false;
57+
fast_timer_t time_offset_ = 7777;
58+
bool time_jumps_ = false;
59+
fast_timer_t async_time_jumps_ = 0;
5960

6061
private:
6162
static bool directionValue(Direction direction);

0 commit comments

Comments
 (0)