Add debouncer

This commit is contained in:
2024-10-10 22:40:29 +02:00
parent a465fa3adc
commit 760d293044
7 changed files with 254 additions and 38 deletions

53
debouncer/debouncer.go Normal file
View File

@@ -0,0 +1,53 @@
package debouncer
import "time"
type Debouncer struct {
input <-chan bool
C <-chan bool
}
func New(ch <-chan bool) *Debouncer {
output := make(chan bool)
var currentState bool
var lastState bool
falseTimer := time.NewTimer(0)
falseTimer.Stop()
go func() {
for {
select {
case <-falseTimer.C:
if !currentState && lastState {
output <- false
lastState = false
} else {
falseTimer.Reset(200 * time.Millisecond)
}
case v, ok := <-ch:
if !ok {
falseTimer.Reset(200 * time.Millisecond)
<-falseTimer.C
output <- false
close(output)
return
}
if v {
if !lastState {
output <- true
lastState = true
falseTimer.Reset(200 * time.Millisecond)
}
currentState = true
} else {
falseTimer.Reset(200 * time.Millisecond)
currentState = false
}
}
}
}()
d := &Debouncer{input: ch, C: output}
return d
}

View File

@@ -0,0 +1,89 @@
package debouncer_test
import (
"testing"
"time"
"git.t-juice.club/torjus/ghettoptt/debouncer"
)
func TestDebouncer(t *testing.T) {
t.Run("Simple", func(t *testing.T) {
ch := make(chan bool)
d := debouncer.New(ch)
go func() {
ch <- true
close(ch)
}()
if v := <-d.C; v != true {
t.Errorf("Expected true, got %v", v)
}
})
t.Run("TrueTrueTrueFalse", func(t *testing.T) {
ch := make(chan bool)
d := debouncer.New(ch)
go func() {
ch <- true
ch <- true
ch <- true
ch <- false
close(ch)
}()
if v := <-d.C; v != true {
t.Errorf("Expected true, got %v", v)
}
if v := <-d.C; v != false {
t.Errorf("Expected false, got %v", v)
}
})
t.Run("Debounce", func(t *testing.T) {
ch := make(chan bool)
d := debouncer.New(ch)
go func() {
ch <- true
time.Sleep(10 * time.Millisecond)
ch <- false
time.Sleep(1 * time.Millisecond)
ch <- true
time.Sleep(1 * time.Millisecond)
ch <- false
time.Sleep(10 * time.Millisecond)
close(ch)
}()
if v := <-d.C; v != true {
t.Errorf("Expected first value to be true, got %v", v)
}
if v := <-d.C; v != false {
t.Errorf("Expected second value to be false, got %v", v)
}
if v, ok := <-d.C; ok {
t.Errorf("Expected closed channel, got %v", v)
}
})
t.Run("DebounceDelay", func(t *testing.T) {
ch := make(chan bool)
d := debouncer.New(ch)
go func() {
ch <- true
ch <- false
close(ch)
}()
start := time.Now()
if v := <-d.C; v != true {
t.Errorf("Expected first value to be true, got %v", v)
}
if v, ok := <-d.C; v != false || !ok {
if ok {
t.Errorf("Expected second value to be false, got %v", v)
} else {
t.Error("Unexpected closed channel")
}
}
if duration := time.Since(start); duration < 200*time.Millisecond {
t.Errorf("Got false too soon: %s", duration)
}
})
}