mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-18 09:14:01 -04:00
197 lines
5.2 KiB
Go
197 lines
5.2 KiB
Go
package wellbeing
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type clock struct{ now time.Time }
|
|
|
|
func (c *clock) advance(d time.Duration) { c.now = c.now.Add(d) }
|
|
|
|
func newTestManager(t *testing.T, start time.Time) (*Manager, *clock) {
|
|
t.Helper()
|
|
c := &clock{now: start}
|
|
m, err := newManager(filepath.Join(t.TempDir(), "db"), func() time.Time { return c.now })
|
|
require.NoError(t, err)
|
|
t.Cleanup(m.Close)
|
|
return m, c
|
|
}
|
|
|
|
func drain(ch chan State) []State {
|
|
var out []State
|
|
for {
|
|
select {
|
|
case s := <-ch:
|
|
out = append(out, s)
|
|
default:
|
|
return out
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFocusTimeSplitsAtMidnightPerApp(t *testing.T) {
|
|
start := time.Date(2026, 10, 1, 23, 59, 30, 0, time.UTC)
|
|
m, c := newTestManager(t, start)
|
|
|
|
m.SetState("firefox", true, 1)
|
|
c.advance(50 * time.Second)
|
|
m.SetState("kitty", true, 2)
|
|
c.advance(10 * time.Second)
|
|
m.SetState("", false, 3)
|
|
|
|
days := m.Summary(2)
|
|
assert.Equal(t, "2026-10-01", days[0].Date)
|
|
assert.Equal(t, int64(30), days[0].Active)
|
|
assert.Equal(t, map[string]int64{"firefox": 30}, days[0].Apps)
|
|
assert.Equal(t, "2026-10-02", days[1].Date)
|
|
assert.Equal(t, int64(30), days[1].Active)
|
|
assert.Equal(t, map[string]int64{"firefox": 20, "kitty": 10}, days[1].Apps)
|
|
}
|
|
|
|
func TestInactiveAndFrozenGapsCountNothing(t *testing.T) {
|
|
start := time.Date(2026, 10, 1, 12, 0, 0, 0, time.UTC)
|
|
m, c := newTestManager(t, start)
|
|
|
|
m.SetState("firefox", false, 1)
|
|
c.advance(40 * time.Second)
|
|
m.SetState("firefox", true, 2)
|
|
c.advance(maxCreditGap + time.Second)
|
|
m.Flush()
|
|
c.advance(20 * time.Second)
|
|
m.SetState("", false, 3)
|
|
|
|
today := m.Summary(1)[0]
|
|
assert.Equal(t, int64(20), today.Active)
|
|
assert.Equal(t, map[string]int64{"firefox": 20}, today.Apps)
|
|
}
|
|
|
|
func TestStaleSequenceIsIgnored(t *testing.T) {
|
|
m, c := newTestManager(t, time.Date(2026, 10, 1, 12, 0, 0, 0, time.UTC))
|
|
|
|
m.SetState("kitty", true, 10)
|
|
m.SetState("firefox", true, 9)
|
|
c.advance(15 * time.Second)
|
|
m.SetState("", false, 11)
|
|
|
|
assert.Equal(t, map[string]int64{"kitty": 15}, m.Summary(1)[0].Apps)
|
|
}
|
|
|
|
func TestOpenSegmentShowsInSummaryWithoutBeingStored(t *testing.T) {
|
|
m, c := newTestManager(t, time.Date(2026, 10, 1, 12, 0, 0, 0, time.UTC))
|
|
|
|
m.SetState("firefox", true, 1)
|
|
c.advance(25 * time.Second)
|
|
|
|
assert.Equal(t, int64(25), m.Summary(1)[0].Apps["firefox"])
|
|
assert.Equal(t, int64(25), m.GetState().Today.Active)
|
|
assert.Equal(t, int64(25), m.Summary(1)[0].Apps["firefox"], "a read must not credit twice")
|
|
}
|
|
|
|
func TestLimitsAlertOncePerDayAndAgainAfterRaise(t *testing.T) {
|
|
m, c := newTestManager(t, time.Date(2026, 10, 1, 12, 0, 0, 0, time.UTC))
|
|
ch := m.Subscribe("test")
|
|
m.SetLimits(Limits{Daily: 60, Apps: map[string]int64{"firefox": 30}})
|
|
|
|
m.SetState("firefox", true, 1)
|
|
c.advance(30 * time.Second)
|
|
m.Flush()
|
|
c.advance(30 * time.Second)
|
|
m.Flush()
|
|
c.advance(30 * time.Second)
|
|
m.Flush()
|
|
|
|
var kinds []string
|
|
for _, s := range drain(ch) {
|
|
if s.Limit != nil {
|
|
kinds = append(kinds, s.Limit.Kind+":"+s.Limit.AppID)
|
|
}
|
|
}
|
|
assert.Equal(t, []string{"app:firefox", "daily:"}, kinds)
|
|
|
|
m.SetLimits(Limits{Daily: 60, Apps: map[string]int64{"firefox": 80}})
|
|
c.advance(30 * time.Second)
|
|
m.Flush()
|
|
limits := 0
|
|
for _, s := range drain(ch) {
|
|
if s.Limit != nil {
|
|
limits++
|
|
assert.Equal(t, int64(120), s.Limit.Used)
|
|
}
|
|
}
|
|
assert.Equal(t, 1, limits, "a raised app limit alerts again, the unchanged daily limit does not")
|
|
|
|
c.advance(12 * time.Hour)
|
|
m.Flush()
|
|
c.advance(30 * time.Second)
|
|
m.Flush()
|
|
for _, s := range drain(ch) {
|
|
assert.Nil(t, s.Limit, "nothing crosses on a frozen gap or a fresh day under the limits")
|
|
}
|
|
assert.Equal(t, int64(30), m.Summary(1)[0].Active)
|
|
}
|
|
|
|
func TestLastUnsubscribeEndsTheOpenSegment(t *testing.T) {
|
|
m, c := newTestManager(t, time.Date(2026, 10, 1, 12, 0, 0, 0, time.UTC))
|
|
m.Subscribe("shell")
|
|
m.SetState("firefox", true, 1)
|
|
c.advance(10 * time.Second)
|
|
m.Unsubscribe("shell")
|
|
c.advance(50 * time.Second)
|
|
|
|
assert.Equal(t, int64(10), m.Summary(1)[0].Active)
|
|
assert.True(t, m.Snapshot().Resync)
|
|
}
|
|
|
|
func TestClearDropsHistoryAndRearmsAlerts(t *testing.T) {
|
|
m, c := newTestManager(t, time.Date(2026, 10, 1, 12, 0, 0, 0, time.UTC))
|
|
ch := m.Subscribe("test")
|
|
m.SetLimits(Limits{Daily: 10})
|
|
m.SetState("firefox", true, 1)
|
|
c.advance(10 * time.Second)
|
|
m.Flush()
|
|
require.NotNil(t, drain(ch)[0].Limit)
|
|
|
|
m.Clear()
|
|
assert.Equal(t, int64(0), m.Summary(1)[0].Active)
|
|
c.advance(10 * time.Second)
|
|
m.Flush()
|
|
alerts := 0
|
|
for _, s := range drain(ch) {
|
|
if s.Limit != nil {
|
|
alerts++
|
|
}
|
|
}
|
|
assert.Equal(t, 1, alerts)
|
|
}
|
|
|
|
func TestAlertRecordSurvivesRestart(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "db")
|
|
c := &clock{now: time.Date(2026, 10, 1, 12, 0, 0, 0, time.UTC)}
|
|
now := func() time.Time { return c.now }
|
|
|
|
m, err := newManager(path, now)
|
|
require.NoError(t, err)
|
|
m.SetLimits(Limits{Daily: 10})
|
|
m.SetState("firefox", true, 1)
|
|
c.advance(10 * time.Second)
|
|
m.Close()
|
|
|
|
m, err = newManager(path, now)
|
|
require.NoError(t, err)
|
|
defer m.Close()
|
|
ch := m.Subscribe("test")
|
|
m.SetLimits(Limits{Daily: 10})
|
|
m.SetState("firefox", true, 2)
|
|
c.advance(10 * time.Second)
|
|
m.Flush()
|
|
assert.Equal(t, int64(20), m.Summary(1)[0].Active)
|
|
for _, s := range drain(ch) {
|
|
assert.Nil(t, s.Limit, "the alert already fired before the restart")
|
|
}
|
|
}
|