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  }