2021-06-09 08:23:21 +01:00
/* Copyright 2021 Simon Arlott
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtest/gtest.h"
#include "debounce_test_common.h"
#include <algorithm>
#include <iomanip>
#include <sstream>
extern "C" {
#include "quantum.h"
#include "timer.h"
#include "debounce.h"
void set_time ( uint32_t t );
void advance_time ( uint32_t ms );
}
2022-02-12 10:29:31 -08:00
void DebounceTest :: addEvents ( std :: initializer_list < DebounceTestEvent > events ) {
events_ . insert ( events_ . end (), events . begin (), events . end ());
}
2021-06-09 08:23:21 +01:00
void DebounceTest :: runEvents () {
/* Run the test multiple times, from 1kHz to 10kHz scan rate */
for ( extra_iterations_ = 0 ; extra_iterations_ < 10 ; extra_iterations_ ++ ) {
if ( time_jumps_ ) {
/* Don't advance time smoothly, jump to the next event (some tests require this) */
auto_advance_time_ = false ;
runEventsInternal ();
} else {
/* Run the test with both smooth and irregular time; it must produce the same result */
auto_advance_time_ = true ;
runEventsInternal ();
auto_advance_time_ = false ;
runEventsInternal ();
}
}
}
void DebounceTest :: runEventsInternal () {
fast_timer_t previous = 0 ;
2021-11-01 19:18:33 +00:00
bool first = true ;
2021-06-09 08:23:21 +01:00
/* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */
debounce_init ( MATRIX_ROWS );
set_time ( time_offset_ );
std :: fill ( std :: begin ( input_matrix_ ), std :: end ( input_matrix_ ), 0 );
std :: fill ( std :: begin ( output_matrix_ ), std :: end ( output_matrix_ ), 0 );
for ( auto & event : events_ ) {
if ( ! auto_advance_time_ ) {
/* Jump to the next event */
set_time ( time_offset_ + event . time_ );
} else if ( ! first && event . time_ == previous + 1 ) {
/* This event immediately follows the previous one, don't make extra debounce() calls */
advance_time ( 1 );
} else {
/* Fast forward to the time for this event, calling debounce() with no changes */
ASSERT_LT (( time_offset_ + event . time_ ) - timer_read_fast (), 60000 ) << "Test tries to advance more than 1 minute of time" ;
while ( timer_read_fast () != time_offset_ + event . time_ ) {
runDebounce ( false );
checkCookedMatrix ( false , "debounce() modified cooked matrix" );
advance_time ( 1 );
}
}
2021-11-01 19:18:33 +00:00
first = false ;
2021-06-09 08:23:21 +01:00
previous = event . time_ ;
/* Prepare input matrix */
for ( auto & input : event . inputs_ ) {
matrixUpdate ( input_matrix_ , "input" , input );
}
/* Call debounce */
runDebounce ( ! event . inputs_ . empty ());
/* Prepare output matrix */
for ( auto & output : event . outputs_ ) {
matrixUpdate ( output_matrix_ , "output" , output );
}
/* Check output matrix has expected change events */
for ( auto & output : event . outputs_ ) {
2021-11-01 19:18:33 +00:00
EXPECT_EQ ( !! ( cooked_matrix_ [ output . row_ ] & ( 1U << output . col_ )), directionValue ( output . direction_ )) << "Missing event at " << strTime () << " expected key " << output . row_ << "," << output . col_ << " " << directionLabel ( output . direction_ ) << " \n input_matrix: changed=" << ! event . inputs_ . empty () << " \n " << strMatrix ( input_matrix_ ) << " \n expected_matrix: \n " << strMatrix ( output_matrix_ ) << " \n actual_matrix: \n " << strMatrix ( cooked_matrix_ );
2021-06-09 08:23:21 +01:00
}
/* Check output matrix has no other changes */
checkCookedMatrix ( ! event . inputs_ . empty (), "debounce() cooked matrix does not match expected output matrix" );
/* Perform some extra iterations of the matrix scan with no changes */
for ( int i = 0 ; i < extra_iterations_ ; i ++ ) {
runDebounce ( false );
checkCookedMatrix ( false , "debounce() modified cooked matrix" );
}
}
/* Check that no further changes happen for 1 minute */
for ( int i = 0 ; i < 60000 ; i ++ ) {
runDebounce ( false );
checkCookedMatrix ( false , "debounce() modified cooked matrix" );
advance_time ( 1 );
}
debounce_free ();
}
void DebounceTest :: runDebounce ( bool changed ) {
std :: copy ( std :: begin ( input_matrix_ ), std :: end ( input_matrix_ ), std :: begin ( raw_matrix_ ));
std :: copy ( std :: begin ( output_matrix_ ), std :: end ( output_matrix_ ), std :: begin ( cooked_matrix_ ));
2022-07-07 10:00:40 +02:00
bool cooked_changed = debounce ( raw_matrix_ , cooked_matrix_ , MATRIX_ROWS , changed );
2021-06-09 08:23:21 +01:00
if ( ! std :: equal ( std :: begin ( input_matrix_ ), std :: end ( input_matrix_ ), std :: begin ( raw_matrix_ ))) {
2021-11-01 19:18:33 +00:00
FAIL () << "Fatal error: debounce() modified raw matrix at " << strTime () << " \n input_matrix: changed=" << changed << " \n " << strMatrix ( input_matrix_ ) << " \n raw_matrix: \n " << strMatrix ( raw_matrix_ );
2021-06-09 08:23:21 +01:00
}
2022-07-07 10:00:40 +02:00
if ( std :: equal ( std :: begin ( output_matrix_ ), std :: end ( output_matrix_ ), std :: begin ( cooked_matrix_ )) && cooked_changed ) {
FAIL () << "Fatal error: debounce() did detect a wrong cooked matrix change at " << strTime () << " \n output_matrix: cooked_changed=" << cooked_changed << " \n " << strMatrix ( output_matrix_ ) << " \n cooked_matrix: \n " << strMatrix ( cooked_matrix_ );
}
2021-06-09 08:23:21 +01:00
}
void DebounceTest :: checkCookedMatrix ( bool changed , const std :: string & error_message ) {
if ( ! std :: equal ( std :: begin ( output_matrix_ ), std :: end ( output_matrix_ ), std :: begin ( cooked_matrix_ ))) {
2021-11-01 19:18:33 +00:00
FAIL () << "Unexpected event: " << error_message << " at " << strTime () << " \n input_matrix: changed=" << changed << " \n " << strMatrix ( input_matrix_ ) << " \n expected_matrix: \n " << strMatrix ( output_matrix_ ) << " \n actual_matrix: \n " << strMatrix ( cooked_matrix_ );
2021-06-09 08:23:21 +01:00
}
}
std :: string DebounceTest :: strTime () {
std :: stringstream text ;
2021-11-01 19:18:33 +00:00
text << "time " << ( timer_read_fast () - time_offset_ ) << " (extra_iterations=" << extra_iterations_ << ", auto_advance_time=" << auto_advance_time_ << ")" ;
2021-06-09 08:23:21 +01:00
return text . str ();
}
std :: string DebounceTest :: strMatrix ( matrix_row_t matrix []) {
std :: stringstream text ;
text << " \t " << std :: setw ( 3 ) << "" ;
for ( int col = 0 ; col < MATRIX_COLS ; col ++ ) {
text << " " << std :: setw ( 2 ) << col ;
}
text << " \n " ;
for ( int row = 0 ; row < MATRIX_ROWS ; row ++ ) {
text << " \t " << std :: setw ( 2 ) << row << ":" ;
for ( int col = 0 ; col < MATRIX_COLS ; col ++ ) {
text << (( matrix [ row ] & ( 1U << col )) ? " XX" : " __" );
}
text << " \n " ;
}
return text . str ();
}
bool DebounceTest :: directionValue ( Direction direction ) {
switch ( direction ) {
2021-11-01 19:18:33 +00:00
case DOWN :
return true ;
2021-06-09 08:23:21 +01:00
2021-11-01 19:18:33 +00:00
case UP :
return false ;
2021-06-09 08:23:21 +01:00
}
}
std :: string DebounceTest :: directionLabel ( Direction direction ) {
switch ( direction ) {
2021-11-01 19:18:33 +00:00
case DOWN :
return "DOWN" ;
2021-06-09 08:23:21 +01:00
2021-11-01 19:18:33 +00:00
case UP :
return "UP" ;
2021-06-09 08:23:21 +01:00
}
}
/* Modify a matrix and verify that events always specify a change */
void DebounceTest :: matrixUpdate ( matrix_row_t matrix [], const std :: string & name , const MatrixTestEvent & event ) {
2021-11-01 19:18:33 +00:00
ASSERT_NE ( !! ( matrix [ event . row_ ] & ( 1U << event . col_ )), directionValue ( event . direction_ )) << "Test " << name << " at " << strTime () << " sets key " << event . row_ << "," << event . col_ << " " << directionLabel ( event . direction_ ) << " but it is already " << directionLabel ( event . direction_ ) << " \n " << name << "_matrix: \n " << strMatrix ( matrix );
2021-06-09 08:23:21 +01:00
switch ( event . direction_ ) {
2021-11-01 19:18:33 +00:00
case DOWN :
matrix [ event . row_ ] |= ( 1U << event . col_ );
break ;
2021-06-09 08:23:21 +01:00
2021-11-01 19:18:33 +00:00
case UP :
matrix [ event . row_ ] &= ~ ( 1U << event . col_ );
break ;
2021-06-09 08:23:21 +01:00
}
}
2021-11-01 19:18:33 +00:00
DebounceTestEvent :: DebounceTestEvent ( fast_timer_t time , std :: initializer_list < MatrixTestEvent > inputs , std :: initializer_list < MatrixTestEvent > outputs ) : time_ ( time ), inputs_ ( inputs ), outputs_ ( outputs ) {}
2021-06-09 08:23:21 +01:00
2021-11-01 19:18:33 +00:00
MatrixTestEvent :: MatrixTestEvent ( int row , int col , Direction direction ) : row_ ( row ), col_ ( col ), direction_ ( direction ) {}