Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions automations/builders_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package halautomations_test

import (
"context"
"testing"

"github.com/dansimau/hal"
halautomations "github.com/dansimau/hal/automations"
"gotest.tools/v3/assert"
)

func TestPrintDebug(t *testing.T) {
t.Parallel()

light := hal.NewLight("light.a")
pd := halautomations.NewPrintDebug("debug", light)

assert.Equal(t, pd.Name(), "debug")
assert.Equal(t, len(pd.Entities()), 1)
assert.Equal(t, pd.Entities()[0].GetID(), "light.a")

// Action just logs the current state; call it to cover the loop.
pd.Action(context.Background(), light)
}

func TestSensorLightsBuilders(t *testing.T) {
t.Parallel()

sensor := hal.NewBinarySensor("binary_sensor.a")
onLight := hal.NewLight("light.on")
offLight := hal.NewLight("light.off")

a := halautomations.NewSensorsTriggerLights().
WithName("test").
WithSensors(sensor).
TurnsOnLights(onLight).
TurnsOffLights(offLight).
WithCondition(func() bool { return true }).
WithConditionScene(func() bool { return false }, map[string]any{"brightness": 10}).
SetScene(map[string]any{"brightness": 200})

assert.Equal(t, a.Name(), "test")

ids := make(map[string]bool)
for _, e := range a.Entities() {
ids[e.GetID()] = true
}

assert.Assert(t, ids["binary_sensor.a"])
assert.Assert(t, ids["light.on"])
}
108 changes: 108 additions & 0 deletions automations/timer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package halautomations_test

import (
"context"
"sync/atomic"
"testing"
"time"

"github.com/dansimau/hal"
halautomations "github.com/dansimau/hal/automations"
"gotest.tools/v3/assert"
)

func TestTimerBuilder(t *testing.T) {
t.Parallel()

entity := hal.NewEntity("test.entity")

timer := halautomations.NewTimer("test timer").
Duration(time.Millisecond).
WithEntities(entity).
Run(func(_ context.Context) {})

assert.Equal(t, timer.Name(), "test timer")
assert.Equal(t, len(timer.Entities()), 1)
assert.Equal(t, timer.Entities()[0].GetID(), "test.entity")
}

func TestTimerRunsActionAfterDelay(t *testing.T) {
t.Parallel()

var ran atomic.Bool

timer := halautomations.NewTimer("test timer").
Duration(10 * time.Millisecond).
Run(func(_ context.Context) {
ran.Store(true)
})

// Action with a passing (nil) condition set starts the timer.
timer.Action(context.Background(), hal.NewEntity("test.entity"))

assert.NilError(t, waitForBool(&ran, time.Second))
}

func TestTimerConditionBlocksStart(t *testing.T) {
t.Parallel()

var ran atomic.Bool

timer := halautomations.NewTimer("test timer").
Duration(10 * time.Millisecond).
Condition(func() bool { return false }).
Run(func(_ context.Context) {
ran.Store(true)
})

// Condition is false, so Action should stop (never start) the timer.
timer.Action(context.Background(), hal.NewEntity("test.entity"))

time.Sleep(50 * time.Millisecond)
assert.Equal(t, ran.Load(), false)
}

func TestTimerConditionRecheckedBeforeAction(t *testing.T) {
t.Parallel()

var (
ran atomic.Bool
allowed atomic.Bool
)

allowed.Store(true)

timer := halautomations.NewTimer("test timer").
Duration(10 * time.Millisecond).
Condition(func() bool { return allowed.Load() }).
Run(func(_ context.Context) {
ran.Store(true)
})

// Condition passes, so the timer starts...
timer.Action(context.Background(), hal.NewEntity("test.entity"))

// ...but flips to false before it fires, so runAction must bail out.
allowed.Store(false)

time.Sleep(50 * time.Millisecond)
assert.Equal(t, ran.Load(), false)
}

// waitForBool polls the flag until it is true or the timeout elapses.
func waitForBool(flag *atomic.Bool, timeout time.Duration) error {
deadline := time.After(timeout)

for {
select {
case <-deadline:
return context.DeadlineExceeded
default:
if flag.Load() {
return nil
}

time.Sleep(time.Millisecond)
}
}
}
Loading
Loading