github.com/martinohmann/rfoutlet@v1.2.1-0.20220707195255-8a66aa411105/internal/timeswitch/timeswitch_test.go (about) 1 package timeswitch 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/jonboulle/clockwork" 9 "github.com/martinohmann/rfoutlet/internal/command" 10 "github.com/martinohmann/rfoutlet/internal/outlet" 11 "github.com/martinohmann/rfoutlet/internal/schedule" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 func TestTimeSwitch(t *testing.T) { 16 now := time.Now() 17 plus1 := now.Add(time.Hour) 18 19 tests := []struct { 20 name string 21 outlets []*outlet.Outlet 22 expectedCommands []command.Command 23 }{ 24 { 25 name: "no outlets", 26 }, 27 { 28 name: "outlet with no schedule", 29 outlets: []*outlet.Outlet{ 30 {Schedule: schedule.New()}, 31 }, 32 }, 33 { 34 name: "outlet with disabled interval", 35 outlets: []*outlet.Outlet{ 36 { 37 Schedule: schedule.NewWithIntervals([]schedule.Interval{ 38 { 39 Enabled: false, 40 Weekdays: []time.Weekday{now.Weekday()}, 41 From: schedule.NewDayTime(now.Hour(), now.Minute()), 42 To: schedule.NewDayTime(plus1.Hour(), plus1.Minute()), 43 }, 44 }), 45 }, 46 }, 47 }, 48 { 49 name: "disabled outlet should be enabled", 50 outlets: []*outlet.Outlet{ 51 { 52 State: outlet.StateOff, 53 Schedule: schedule.NewWithIntervals([]schedule.Interval{ 54 { 55 Enabled: true, 56 Weekdays: []time.Weekday{now.Weekday()}, 57 From: schedule.NewDayTime(now.Hour(), now.Minute()), 58 To: schedule.NewDayTime(plus1.Hour(), plus1.Minute()), 59 }, 60 }), 61 }, 62 }, 63 expectedCommands: []command.Command{ 64 command.StateCorrectionCommand{ 65 DesiredState: outlet.StateOn, 66 Outlet: &outlet.Outlet{ 67 State: outlet.StateOff, 68 Schedule: schedule.NewWithIntervals([]schedule.Interval{ 69 { 70 Enabled: true, 71 Weekdays: []time.Weekday{now.Weekday()}, 72 From: schedule.NewDayTime(now.Hour(), now.Minute()), 73 To: schedule.NewDayTime(plus1.Hour(), plus1.Minute()), 74 }, 75 }), 76 }, 77 }, 78 }, 79 }, 80 { 81 name: "enabled outlet should not be enabled again", 82 outlets: []*outlet.Outlet{ 83 { 84 State: outlet.StateOn, 85 Schedule: schedule.NewWithIntervals([]schedule.Interval{ 86 { 87 Enabled: true, 88 Weekdays: []time.Weekday{now.Weekday()}, 89 From: schedule.NewDayTime(now.Hour(), now.Minute()), 90 To: schedule.NewDayTime(plus1.Hour(), plus1.Minute()), 91 }, 92 }), 93 }, 94 }, 95 }, 96 { 97 name: "enabled outlet should be switched off", 98 outlets: []*outlet.Outlet{ 99 { 100 State: outlet.StateOn, 101 Schedule: schedule.NewWithIntervals([]schedule.Interval{ 102 { 103 Enabled: true, 104 Weekdays: []time.Weekday{now.Weekday()}, 105 From: schedule.NewDayTime(plus1.Hour(), plus1.Minute()), 106 To: schedule.NewDayTime(now.Hour(), now.Minute()), 107 }, 108 }), 109 }, 110 }, 111 expectedCommands: []command.Command{ 112 command.StateCorrectionCommand{ 113 DesiredState: outlet.StateOff, 114 Outlet: &outlet.Outlet{ 115 State: outlet.StateOn, 116 Schedule: schedule.NewWithIntervals([]schedule.Interval{ 117 { 118 Enabled: true, 119 Weekdays: []time.Weekday{now.Weekday()}, 120 From: schedule.NewDayTime(plus1.Hour(), plus1.Minute()), 121 To: schedule.NewDayTime(now.Hour(), now.Minute()), 122 }, 123 }), 124 }, 125 }, 126 }, 127 }, 128 { 129 name: "disabled outlet should not be switched off again", 130 outlets: []*outlet.Outlet{ 131 { 132 State: outlet.StateOff, 133 Schedule: schedule.NewWithIntervals([]schedule.Interval{ 134 { 135 Enabled: true, 136 Weekdays: []time.Weekday{now.Weekday()}, 137 From: schedule.NewDayTime(plus1.Hour(), plus1.Minute()), 138 To: schedule.NewDayTime(now.Hour(), now.Minute()), 139 }, 140 }), 141 }, 142 }, 143 }, 144 } 145 146 for _, test := range tests { 147 t.Run(test.name, func(t *testing.T) { 148 reg := outlet.NewRegistry() 149 reg.RegisterOutlets(test.outlets...) 150 151 queue := make(chan command.Command) 152 defer close(queue) 153 154 fakeClock := clockwork.NewFakeClockAt(now) 155 156 timeSwitch := &TimeSwitch{ 157 Registry: reg, 158 CommandQueue: queue, 159 Clock: fakeClock, 160 } 161 162 ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) 163 defer cancel() 164 165 var commands []command.Command 166 doneCh := make(chan struct{}) 167 168 go timeSwitch.Run(doneCh) 169 170 fakeClock.BlockUntil(1) 171 fakeClock.Advance(1 * time.Minute) 172 173 go func() { 174 defer close(doneCh) 175 for { 176 select { 177 case <-ctx.Done(): 178 return 179 case cmd, ok := <-queue: 180 if !ok { 181 return 182 } 183 commands = append(commands, cmd) 184 } 185 } 186 }() 187 188 <-doneCh 189 190 assert.Equal(t, test.expectedCommands, commands) 191 }) 192 } 193 }