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 }