Skip to content

Commit bdf324a

Browse files
committed
Add example test for a debounce function
1 parent adcc8b8 commit bdf324a

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#include <ArduinoUnitTests.h>
2+
#include <Arduino.h>
3+
4+
// the setup for the debouncing function that we will test
5+
// via https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce
6+
// ... condensed for brevity
7+
//
8+
// pretend that sketch is a library function that is run on every loop
9+
// e.g.
10+
// void loop() { onLoop(); }
11+
//
12+
const int buttonPin = 2; // the number of the pushbutton pin
13+
const int ledPin = 13; // the number of the LED pin
14+
const long debounceDelay = 50; // debounce time; increase if the output flickers
15+
int ledState; // current state of the output pin
16+
int buttonState; // current reading from the input pin
17+
int lastButtonState; // previous reading from the input pin
18+
unsigned long lastDebounceTime; // last time the output pin was toggled
19+
20+
void onLoop() {
21+
// read state, record time if the input flipped
22+
int reading = digitalRead(buttonPin);
23+
if (reading != lastButtonState) lastDebounceTime = millis();
24+
25+
if ((millis() - lastDebounceTime) > debounceDelay) {
26+
if (reading != buttonState) {
27+
buttonState = reading;
28+
if (buttonState == HIGH) ledState = !ledState;
29+
digitalWrite(ledPin, ledState);
30+
}
31+
}
32+
33+
lastButtonState = reading;
34+
}
35+
36+
37+
/////////// Unit tests
38+
//
39+
// This isn't an exhaustive test of states and transitions. Consider permutations of the following variables:
40+
// - ledState
41+
// - buttonState
42+
// - lastButtonState
43+
// - the actual digital value on the input
44+
// - the current time relative to the last debounce time
45+
//
46+
// But we will test a few bounces: 0 transitions, 1 transition, and 3 transitions.
47+
// The general pattern is
48+
// 0. set the initial software state
49+
// 1. set the hardware state (including clock)
50+
// 2. call the library function
51+
// 3. validate software state and hardware output state against expectations
52+
// repeat steps 1-3 as necessary for changing inputs
53+
54+
// Declare state and reset it for each test
55+
GodmodeState* state = GODMODE();
56+
unittest_setup() {
57+
state->reset();
58+
}
59+
60+
unittest(nothing_happens_if_button_isnt_pressed) {
61+
// initial library state
62+
ledState = LOW;
63+
buttonState = LOW;
64+
lastButtonState = LOW;
65+
lastDebounceTime = 0;
66+
67+
state->micros = 0;
68+
assertEqual(LOW, state->digitalPin[buttonPin]); // initial input low (default)
69+
assertEqual(1, state->digitalPin[ledPin].historySize()); // initial output history has 1 entry so far (low)
70+
71+
onLoop();
72+
assertEqual(LOW, state->digitalPin[ledPin]); // nothing has changed on the hardware end
73+
assertEqual(LOW, lastButtonState);
74+
assertEqual(0, state->micros); // remember, only we can advance the clock
75+
assertEqual(0, lastDebounceTime);
76+
77+
state->micros = 50001; // advance the clock
78+
onLoop();
79+
assertEqual(LOW, state->digitalPin[ledPin]); // still no change
80+
assertEqual(LOW, lastButtonState);
81+
assertEqual(0, lastDebounceTime);
82+
}
83+
84+
unittest(perfectly_clean_low_to_high) {
85+
ledState = LOW;
86+
buttonState = LOW;
87+
lastButtonState = LOW;
88+
lastDebounceTime = 0;
89+
state->micros = 25000;
90+
state->digitalPin[buttonPin] = HIGH; // set initial button entry to HIGH
91+
92+
onLoop();
93+
assertEqual(LOW, state->digitalPin[ledPin]);
94+
assertEqual(HIGH, lastButtonState);
95+
assertEqual(LOW, ledState);
96+
assertEqual(25, lastDebounceTime);
97+
assertEqual(1, state->digitalPin[ledPin].historySize()); // no change in output
98+
99+
// actual boundary case
100+
state->micros = 75999;
101+
onLoop();
102+
assertEqual(LOW, state->digitalPin[ledPin]);
103+
assertEqual(HIGH, lastButtonState);
104+
assertEqual(LOW, ledState);
105+
assertEqual(25, lastDebounceTime);
106+
assertEqual(1, state->digitalPin[ledPin].historySize()); // no change in output
107+
108+
// actual boundary case for exact timing
109+
state->micros = 76000;
110+
onLoop();
111+
assertEqual(HIGH, state->digitalPin[ledPin]);
112+
assertEqual(HIGH, lastButtonState);
113+
assertEqual(HIGH, ledState);
114+
assertEqual(25, lastDebounceTime);
115+
assertEqual(2, state->digitalPin[ledPin].historySize()); // output was written
116+
}
117+
118+
unittest(bounce_low_to_high) {
119+
ledState = LOW;
120+
buttonState = LOW;
121+
lastButtonState = LOW;
122+
lastDebounceTime = 0;
123+
124+
state->micros = 25000;
125+
state->digitalPin[buttonPin] = HIGH; // set initial button entry to HIGH
126+
onLoop();
127+
assertEqual(25, lastDebounceTime); // debounce time has reset
128+
assertEqual(LOW, state->digitalPin[ledPin]); // no change in output
129+
assertEqual(HIGH, lastButtonState);
130+
131+
state->micros = 50000;
132+
state->digitalPin[buttonPin] = LOW; // bounce button LOW
133+
onLoop();
134+
assertEqual(50, lastDebounceTime); // debounce time has reset
135+
assertEqual(LOW, state->digitalPin[ledPin]); // no change in LED output
136+
assertEqual(LOW, lastButtonState);
137+
138+
state->micros = 75000;
139+
state->digitalPin[buttonPin] = HIGH; // bounce button HIGH
140+
onLoop();
141+
assertEqual(75, lastDebounceTime); // debounce time is again reset
142+
assertEqual(LOW, state->digitalPin[ledPin]); // still no change in LED output
143+
assertEqual(HIGH, lastButtonState);
144+
145+
state->micros = 126000; // actual boundary case, time elapsed
146+
state->digitalPin[buttonPin] = HIGH;
147+
onLoop();
148+
assertEqual(75, lastDebounceTime); // no additional bounce happened
149+
assertEqual(HIGH, state->digitalPin[ledPin]); // therefore the LED turns on
150+
assertEqual(2, state->digitalPin[ledPin].historySize()); // digital output was written only once
151+
}
152+
153+
unittest_main()

0 commit comments

Comments
 (0)