github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/tests/plugins/common/common_test.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2016-2023, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  package sensor
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/e154/smart-home/adaptors"
    29  	"github.com/e154/smart-home/common"
    30  	"github.com/e154/smart-home/common/events"
    31  	m "github.com/e154/smart-home/models"
    32  	"github.com/e154/smart-home/system/bus"
    33  	"github.com/e154/smart-home/system/scripts"
    34  	super "github.com/e154/smart-home/system/supervisor"
    35  	. "github.com/e154/smart-home/tests/plugins"
    36  	. "github.com/smartystreets/goconvey/convey"
    37  )
    38  
    39  func TestCommon(t *testing.T) {
    40  
    41  	Convey("sensor", t, func(ctx C) {
    42  		_ = container.Invoke(func(adaptors *adaptors.Adaptors,
    43  			scriptService scripts.ScriptService,
    44  			supervisor super.Supervisor,
    45  			eventBus bus.Bus) {
    46  
    47  			// register plugins
    48  			AddPlugin(adaptors, "sensor")
    49  
    50  			serviceCh := WaitService(eventBus, time.Second*5, "Supervisor")
    51  			pluginsCh := WaitPlugins(eventBus, time.Second*5, "sensor")
    52  			supervisor.Start(context.Background())
    53  			defer supervisor.Shutdown(context.Background())
    54  			So(<-serviceCh, ShouldBeTrue)
    55  			So(<-pluginsCh, ShouldBeTrue)
    56  
    57  			// bind convey
    58  			RegisterConvey(scriptService, ctx)
    59  
    60  			// add entity
    61  			// ------------------------------------------------
    62  
    63  			sensorEnt := GetNewSensor("device1")
    64  			sensorEnt.Actions = []*m.EntityAction{
    65  				{
    66  					Name:        "ACTION1",
    67  					Description: "action description",
    68  				},
    69  			}
    70  			sensorEnt.States = []*m.EntityState{
    71  				{
    72  					Name:        "STATE1",
    73  					Description: "state description",
    74  				},
    75  				{
    76  					Name:        "STATE2",
    77  					Description: "state description",
    78  				},
    79  			}
    80  			sensorEnt.Attributes = m.Attributes{
    81  				"v": {
    82  					Name: "v",
    83  					Type: common.AttributeString,
    84  				},
    85  			}
    86  			sensorEnt.Settings = m.Attributes{
    87  				"v": {
    88  					Name: "v",
    89  					Type: common.AttributeString,
    90  				},
    91  			}
    92  
    93  			err := adaptors.Entity.Add(context.Background(), sensorEnt)
    94  			ctx.So(err, ShouldBeNil)
    95  
    96  			eventBus.Publish("system/models/entities/"+sensorEnt.Id.String(), events.EventCreatedEntityModel{
    97  				EntityId: sensorEnt.Id,
    98  			})
    99  
   100  			//--
   101  
   102  			sensor2Ent := GetNewSensor("device2")
   103  			sensor2Ent.Attributes = m.Attributes{
   104  				"v": {
   105  					Name: "v",
   106  					Type: common.AttributeString,
   107  				},
   108  			}
   109  
   110  			err = adaptors.Entity.Add(context.Background(), sensor2Ent)
   111  			ctx.So(err, ShouldBeNil)
   112  
   113  			eventBus.Publish("system/models/entities/"+sensor2Ent.Id.String(), events.EventCreatedEntityModel{
   114  				EntityId: sensor2Ent.Id,
   115  			})
   116  			//--
   117  
   118  			sensor3Ent := GetNewSensor("device3")
   119  			sensor3Ent.Attributes = m.Attributes{
   120  				"v": {
   121  					Name: "v",
   122  					Type: common.AttributeString,
   123  				},
   124  			}
   125  
   126  			err = adaptors.Entity.Add(context.Background(), sensor3Ent)
   127  			ctx.So(err, ShouldBeNil)
   128  
   129  			eventBus.Publish("system/models/entities/"+sensor3Ent.Id.String(), events.EventCreatedEntityModel{
   130  				EntityId: sensor3Ent.Id,
   131  			})
   132  			//--
   133  
   134  			time.Sleep(time.Second)
   135  
   136  			t.Run("sensor states", func(t *testing.T) {
   137  				Convey("states", t, func(ctx C) {
   138  
   139  					// common
   140  					// ------------------------------------------------
   141  					ch := make(chan events.EventStateChanged, 99)
   142  					defer close(ch)
   143  					fn := func(_ string, msg interface{}) {
   144  						switch v := msg.(type) {
   145  						case events.EventStateChanged:
   146  							ch <- v
   147  						}
   148  					}
   149  					eventBus.Subscribe("system/entities/+", fn, false)
   150  					defer eventBus.Unsubscribe("system/entities/+", fn)
   151  
   152  					// 1
   153  					// ------------------------------------------------
   154  					ctx.Println("v1")
   155  					supervisor.SetState(sensorEnt.Id, super.EntityStateParams{
   156  						NewState: nil,
   157  						AttributeValues: m.AttributeValue{
   158  							"v": "V1",
   159  						},
   160  						StorageSave: false,
   161  					})
   162  
   163  					// wait message
   164  					msg, ok := WaitT[events.EventStateChanged](time.Second*2, ch)
   165  					ctx.So(ok, ShouldBeTrue)
   166  
   167  					ctx.So(msg.StorageSave, ShouldBeFalse)
   168  					ctx.So(msg.DoNotSaveMetric, ShouldBeFalse)
   169  					ctx.So(msg.PluginName, ShouldEqual, "sensor")
   170  					ctx.So(msg.EntityId, ShouldEqual, "sensor.device1")
   171  					ctx.So(msg.OldState.EntityId, ShouldBeZeroValue)
   172  					ctx.So(msg.OldState.Value, ShouldBeNil)
   173  					ctx.So(msg.OldState.State, ShouldBeNil)
   174  					ctx.So(msg.OldState.Attributes, ShouldBeNil)
   175  					ctx.So(msg.OldState.Settings, ShouldBeNil)
   176  					ctx.So(msg.OldState.LastChanged, ShouldBeNil)
   177  					ctx.So(msg.OldState.LastUpdated, ShouldBeNil)
   178  					ctx.So(msg.NewState.EntityId, ShouldEqual, "sensor.device1")
   179  					ctx.So(msg.NewState.Value, ShouldBeNil)
   180  					ctx.So(msg.NewState.State, ShouldBeNil)
   181  					ctx.So(msg.NewState.Attributes, ShouldNotBeNil)
   182  					ctx.So(msg.NewState.Attributes["v"].String(), ShouldEqual, "V1")
   183  					ctx.So(msg.NewState.Settings, ShouldNotBeNil)
   184  					ctx.So(msg.NewState.Settings["v"].String(), ShouldBeZeroValue)
   185  					ctx.So(msg.NewState.LastChanged, ShouldBeNil)
   186  					ctx.So(msg.NewState.LastUpdated, ShouldNotBeNil)
   187  
   188  					v1NewStateLastChange := msg.NewState.LastChanged // null
   189  					v1NewStateLastUpdate := msg.NewState.LastUpdated
   190  
   191  					time.Sleep(time.Millisecond * 500)
   192  
   193  					// 2
   194  					// ------------------------------------------------
   195  					ctx.Println("\nv2")
   196  					supervisor.SetState(sensorEnt.Id, super.EntityStateParams{
   197  						NewState: nil,
   198  						AttributeValues: m.AttributeValue{
   199  							"v": "V2",
   200  						},
   201  						StorageSave: true,
   202  					})
   203  
   204  					// wait message
   205  					msg, ok = WaitT[events.EventStateChanged](time.Second*2, ch)
   206  					ctx.So(ok, ShouldBeTrue)
   207  
   208  					ctx.So(msg.StorageSave, ShouldBeTrue)
   209  					ctx.So(msg.DoNotSaveMetric, ShouldBeFalse)
   210  					ctx.So(msg.PluginName, ShouldEqual, "sensor")
   211  					ctx.So(msg.EntityId, ShouldEqual, "sensor.device1")
   212  					ctx.So(msg.OldState.EntityId, ShouldEqual, "sensor.device1")
   213  					ctx.So(msg.OldState.Value, ShouldBeNil)
   214  					ctx.So(msg.OldState.State, ShouldBeNil)
   215  					ctx.So(msg.OldState.Attributes, ShouldNotBeNil)
   216  					ctx.So(msg.OldState.Attributes["v"].String(), ShouldEqual, "V1")
   217  					ctx.So(msg.OldState.Settings, ShouldNotBeNil)
   218  					ctx.So(msg.OldState.Settings["v"].String(), ShouldBeZeroValue)
   219  					ctx.So(msg.OldState.LastChanged, ShouldBeNil)
   220  					ctx.So(msg.OldState.LastUpdated, ShouldNotBeNil)
   221  					ctx.So(msg.NewState.EntityId, ShouldEqual, "sensor.device1")
   222  					ctx.So(msg.NewState.Value, ShouldBeNil)
   223  					ctx.So(msg.NewState.State, ShouldBeNil)
   224  					ctx.So(msg.NewState.Attributes, ShouldNotBeNil)
   225  					ctx.So(msg.NewState.Attributes["v"].String(), ShouldEqual, "V2")
   226  					ctx.So(msg.NewState.Settings, ShouldNotBeNil)
   227  					ctx.So(msg.NewState.Settings["v"].String(), ShouldBeZeroValue)
   228  					ctx.So(msg.NewState.LastChanged, ShouldNotBeNil)
   229  					ctx.So(msg.NewState.LastUpdated, ShouldNotBeNil)
   230  
   231  					v2NewStateLastChange := msg.NewState.LastChanged
   232  					v2NewStateLastUpdate := msg.NewState.LastUpdated
   233  
   234  					v2NewStateLastChanged := msg.NewState.LastChanged
   235  					ctx.So(v2NewStateLastChanged.Sub(*msg.OldState.LastUpdated), ShouldEqual, 0)
   236  					ctx.So(v2NewStateLastChanged.Sub(*v1NewStateLastUpdate), ShouldEqual, 0)
   237  
   238  					v2OldStateLastChanged := msg.OldState.LastChanged // null
   239  					v2OldStateLastUpdate := msg.OldState.LastUpdated
   240  					ctx.So(v2OldStateLastChanged, ShouldBeNil)
   241  					ctx.So(v1NewStateLastChange, ShouldBeNil)
   242  					ctx.So(fmt.Sprintf("%v", v2OldStateLastUpdate), ShouldEqual, fmt.Sprintf("%v", v1NewStateLastUpdate))
   243  
   244  					time.Sleep(time.Millisecond * 500)
   245  
   246  					// 3
   247  					// ------------------------------------------------
   248  					ctx.Println("\nv3")
   249  					supervisor.SetState(sensorEnt.Id, super.EntityStateParams{
   250  						NewState: common.String("STATE1"),
   251  						AttributeValues: m.AttributeValue{
   252  							"v": "V3",
   253  						},
   254  						StorageSave: true,
   255  					})
   256  
   257  					// wait message
   258  					msg, ok = WaitT[events.EventStateChanged](time.Second*2, ch)
   259  					ctx.So(ok, ShouldBeTrue)
   260  
   261  					ctx.So(msg.StorageSave, ShouldBeTrue)
   262  					ctx.So(msg.DoNotSaveMetric, ShouldBeFalse)
   263  					ctx.So(msg.PluginName, ShouldEqual, "sensor")
   264  					ctx.So(msg.EntityId, ShouldEqual, "sensor.device1")
   265  					ctx.So(msg.OldState.EntityId, ShouldEqual, "sensor.device1")
   266  					ctx.So(msg.OldState.Value, ShouldBeNil)
   267  					ctx.So(msg.OldState.State, ShouldBeNil)
   268  					ctx.So(msg.OldState.Attributes, ShouldNotBeNil)
   269  					ctx.So(msg.OldState.Attributes["v"].String(), ShouldEqual, "V2")
   270  					ctx.So(msg.OldState.Settings, ShouldNotBeNil)
   271  					ctx.So(msg.OldState.Settings["v"].String(), ShouldBeZeroValue)
   272  					ctx.So(msg.OldState.LastChanged, ShouldNotBeNil)
   273  					ctx.So(msg.OldState.LastUpdated, ShouldNotBeNil)
   274  					ctx.So(msg.NewState.EntityId, ShouldEqual, "sensor.device1")
   275  					ctx.So(msg.NewState.Value, ShouldBeNil)
   276  					ctx.So(msg.NewState.State, ShouldNotBeNil)
   277  					ctx.So(msg.NewState.State.Name, ShouldEqual, "STATE1")
   278  					ctx.So(msg.NewState.State.Description, ShouldEqual, "state description")
   279  					ctx.So(msg.NewState.State.ImageUrl, ShouldBeNil)
   280  					ctx.So(msg.NewState.State.Icon, ShouldBeNil)
   281  					ctx.So(msg.NewState.Attributes, ShouldNotBeNil)
   282  					ctx.So(msg.NewState.Attributes["v"].String(), ShouldEqual, "V3")
   283  					ctx.So(msg.NewState.Settings, ShouldNotBeNil)
   284  					ctx.So(msg.NewState.Settings["v"].String(), ShouldBeZeroValue)
   285  					ctx.So(msg.NewState.LastChanged, ShouldNotBeNil)
   286  					ctx.So(msg.NewState.LastUpdated, ShouldNotBeNil)
   287  
   288  					v3NewStateLastChange := msg.NewState.LastChanged
   289  					v3NewStateLastUpdate := msg.NewState.LastUpdated
   290  
   291  					v3NewStateLastChanged := msg.NewState.LastChanged
   292  					ctx.So(v3NewStateLastChanged.Sub(*msg.OldState.LastUpdated), ShouldEqual, 0)
   293  					ctx.So(v3NewStateLastChanged.Sub(*v2NewStateLastUpdate), ShouldEqual, 0)
   294  
   295  					v3OldStateLastChanged := msg.OldState.LastChanged
   296  					v3OldStateLastUpdate := msg.OldState.LastUpdated
   297  					ctx.So(v3OldStateLastChanged.Sub(*v2NewStateLastChange), ShouldEqual, 0)
   298  					ctx.So(v3OldStateLastUpdate.Sub(*v2NewStateLastUpdate), ShouldEqual, 0)
   299  
   300  					time.Sleep(time.Millisecond * 500)
   301  
   302  					// 4 (skip settings)
   303  					// ------------------------------------------------
   304  					ctx.Println("\nv4")
   305  					supervisor.SetState(sensorEnt.Id, super.EntityStateParams{
   306  						SettingsValue: m.AttributeValue{
   307  							"v": "V4",
   308  						},
   309  						StorageSave: true,
   310  					})
   311  
   312  					// wait message
   313  					msg, ok = WaitT[events.EventStateChanged](time.Second*2, ch)
   314  					ctx.So(ok, ShouldBeFalse)
   315  
   316  					// 5 (skip unknown fields)
   317  					// ------------------------------------------------
   318  					ctx.Println("\nv5")
   319  					supervisor.SetState(sensorEnt.Id, super.EntityStateParams{
   320  						SettingsValue: m.AttributeValue{
   321  							"foo": "bar",
   322  						},
   323  						StorageSave: true,
   324  					})
   325  
   326  					// wait message
   327  					msg, ok = WaitT[events.EventStateChanged](time.Second*2, ch)
   328  					ctx.So(ok, ShouldBeFalse)
   329  
   330  					time.Sleep(time.Millisecond * 500)
   331  
   332  					// 6
   333  					// ------------------------------------------------
   334  					ctx.Println("\nv6")
   335  					supervisor.SetState(sensorEnt.Id, super.EntityStateParams{
   336  						NewState: common.String("FOO"),
   337  						AttributeValues: m.AttributeValue{
   338  							"v": "V6",
   339  						},
   340  						StorageSave: true,
   341  					})
   342  
   343  					// wait message
   344  					msg, ok = WaitT[events.EventStateChanged](time.Second*2, ch)
   345  					ctx.So(ok, ShouldBeTrue)
   346  
   347  					ctx.So(msg.StorageSave, ShouldBeTrue)
   348  					ctx.So(msg.DoNotSaveMetric, ShouldBeFalse)
   349  					ctx.So(msg.PluginName, ShouldEqual, "sensor")
   350  					ctx.So(msg.EntityId, ShouldEqual, "sensor.device1")
   351  					ctx.So(msg.OldState.EntityId, ShouldEqual, "sensor.device1")
   352  					ctx.So(msg.OldState.Value, ShouldBeNil)
   353  					ctx.So(msg.OldState.State, ShouldNotBeNil)
   354  					ctx.So(msg.OldState.State.Name, ShouldEqual, "STATE1")
   355  					ctx.So(msg.OldState.Attributes, ShouldNotBeNil)
   356  					ctx.So(msg.OldState.Attributes["v"].String(), ShouldEqual, "V3")
   357  					ctx.So(msg.OldState.Settings, ShouldNotBeNil)
   358  					ctx.So(msg.OldState.Settings["v"].String(), ShouldBeZeroValue)
   359  					ctx.So(msg.OldState.LastChanged, ShouldNotBeNil)
   360  					ctx.So(msg.OldState.LastUpdated, ShouldNotBeNil)
   361  					ctx.So(msg.NewState.EntityId, ShouldEqual, "sensor.device1")
   362  					ctx.So(msg.NewState.Value, ShouldBeNil)
   363  					ctx.So(msg.NewState.State, ShouldNotBeNil)
   364  					ctx.So(msg.NewState.State.Name, ShouldEqual, "STATE1")
   365  					ctx.So(msg.NewState.State.Description, ShouldEqual, "state description")
   366  					ctx.So(msg.NewState.State.Icon, ShouldBeNil)
   367  					ctx.So(msg.NewState.Attributes, ShouldNotBeNil)
   368  					ctx.So(msg.NewState.Attributes["v"].String(), ShouldEqual, "V6")
   369  					ctx.So(msg.NewState.Settings, ShouldNotBeNil)
   370  					ctx.So(msg.NewState.Settings["v"].String(), ShouldBeZeroValue)
   371  					ctx.So(msg.NewState.LastChanged, ShouldNotBeNil)
   372  					ctx.So(msg.NewState.LastUpdated, ShouldNotBeNil)
   373  
   374  					v6NewStateLastChanged := msg.NewState.LastChanged
   375  					ctx.So(v6NewStateLastChanged.Sub(*msg.OldState.LastUpdated), ShouldEqual, 0)
   376  					ctx.So(v6NewStateLastChanged.Sub(*v3NewStateLastUpdate), ShouldEqual, 0)
   377  
   378  					v6OldStateLastChanged := msg.OldState.LastChanged
   379  					v6OldStateLastUpdate := msg.OldState.LastUpdated
   380  					ctx.So(v6OldStateLastChanged.Sub(*v3NewStateLastChange), ShouldEqual, 0)
   381  					ctx.So(v6OldStateLastUpdate.Sub(*v3NewStateLastUpdate), ShouldEqual, 0)
   382  
   383  					v6NewStateLastUpdate := msg.NewState.LastUpdated
   384  
   385  					time.Sleep(time.Second)
   386  
   387  					list, total, err := adaptors.EntityStorage.List(context.Background(), 10, 0, "asc", "id", []common.EntityId{}, nil, nil)
   388  					ctx.So(err, ShouldBeNil)
   389  					ctx.So(total, ShouldEqual, 3)
   390  
   391  					ctx.So(list[0].EntityId, ShouldEqual, "sensor.device1")
   392  					ctx.So(list[0].Attributes["v"], ShouldEqual, "V2")
   393  					ctx.So(list[0].State, ShouldBeZeroValue)
   394  					ctx.So(v2NewStateLastUpdate.Sub(list[0].CreatedAt), ShouldEqual, 0)
   395  
   396  					ctx.So(list[1].EntityId, ShouldEqual, "sensor.device1")
   397  					ctx.So(list[1].Attributes["v"], ShouldEqual, "V3")
   398  					ctx.So(list[1].State, ShouldEqual, "STATE1")
   399  					ctx.So(v3NewStateLastUpdate.Sub(list[1].CreatedAt), ShouldEqual, 0)
   400  
   401  					ctx.So(list[2].EntityId, ShouldEqual, "sensor.device1")
   402  					ctx.So(list[2].Attributes["v"], ShouldEqual, "V6")
   403  					ctx.So(list[2].State, ShouldEqual, "STATE1")
   404  					ctx.So(v6NewStateLastUpdate.Sub(list[2].CreatedAt), ShouldEqual, 0)
   405  				})
   406  			})
   407  
   408  			t.Run("sensor states2", func(t *testing.T) {
   409  				Convey("states", t, func(ctx C) {
   410  
   411  					// common
   412  					// ------------------------------------------------
   413  					wg := &sync.WaitGroup{}
   414  					wg.Add(100)
   415  					fn := func(_ string, msg interface{}) {
   416  						switch msg.(type) {
   417  						case events.EventStateChanged:
   418  							wg.Done()
   419  						}
   420  					}
   421  					eventBus.Subscribe("system/entities/sensor.device2", fn, false)
   422  					defer eventBus.Unsubscribe("system/entities/sensor.device2", fn)
   423  
   424  					// 7
   425  					// ------------------------------------------------
   426  					ctx.Println("\nv7")
   427  					for i := 0; i < 100; i++ {
   428  						supervisor.SetState(sensor2Ent.Id, super.EntityStateParams{
   429  							AttributeValues: m.AttributeValue{
   430  								"v": fmt.Sprintf("V%d", i),
   431  							},
   432  							StorageSave: true,
   433  						})
   434  					}
   435  
   436  					ok := WaitGroupTimeout(wg, time.Second*3)
   437  					ctx.So(ok, ShouldBeTrue)
   438  
   439  					time.Sleep(time.Second)
   440  
   441  					_, total, err := adaptors.EntityStorage.List(context.Background(), 500, 0, "asc", "id", []common.EntityId{sensor2Ent.Id}, nil, nil)
   442  					ctx.So(err, ShouldBeNil)
   443  					ctx.So(total, ShouldEqual, 100)
   444  				})
   445  			})
   446  
   447  			t.Run("sensor states3", func(t *testing.T) {
   448  				Convey("states", t, func(ctx C) {
   449  
   450  					// common
   451  					// ------------------------------------------------
   452  					wg := &sync.WaitGroup{}
   453  					wg.Add(1000000)
   454  					fn := func(_ string, msg interface{}) {
   455  						switch msg.(type) {
   456  						case events.EventStateChanged:
   457  							wg.Done()
   458  						}
   459  					}
   460  					eventBus.Subscribe("system/entities/sensor.device3", fn, false)
   461  					defer eventBus.Unsubscribe("system/entities/sensor.device3", fn)
   462  
   463  					// 8
   464  					// ------------------------------------------------
   465  					ctx.Println("\nv8")
   466  					for i := 0; i < 1000000; i++ {
   467  						supervisor.SetState(sensor3Ent.Id, super.EntityStateParams{
   468  							AttributeValues: m.AttributeValue{
   469  								"v": fmt.Sprintf("V%d", i),
   470  							},
   471  						})
   472  					}
   473  
   474  					ok := WaitGroupTimeout(wg, time.Second*3)
   475  					ctx.So(ok, ShouldBeTrue)
   476  
   477  					time.Sleep(time.Second)
   478  
   479  					_, total, err := adaptors.EntityStorage.List(context.Background(), 25, 0, "asc", "id", []common.EntityId{sensor3Ent.Id}, nil, nil)
   480  					ctx.So(err, ShouldBeNil)
   481  					ctx.So(total, ShouldEqual, 0)
   482  				})
   483  			})
   484  		})
   485  	})
   486  }