diff --git a/src/Scene.cpp b/src/Scene.cpp
index 261f3f5..a6d596e 100644
--- a/src/Scene.cpp
+++ b/src/Scene.cpp
@@ -319,6 +319,7 @@ void Scene::sendPutRequest(const std::string& path, const nlohmann::json& reques
CreateScene& CreateScene::setName(const std::string& name)
{
+ request["name"] = name;
return *this;
}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index abf9716..497309e 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -42,6 +42,7 @@ set(TEST_SOURCES
test_Main.cpp
test_UPnP.cpp
test_ResourceList.cpp
+ test_Scene.cpp
test_Schedule.cpp
test_SimpleBrightnessStrategy.cpp
test_SimpleColorHueStrategy.cpp
diff --git a/test/test_Scene.cpp b/test/test_Scene.cpp
new file mode 100644
index 0000000..d0ec9cc
--- /dev/null
+++ b/test/test_Scene.cpp
@@ -0,0 +1,500 @@
+/**
+ \file test_Scene.cpp
+ Copyright Notice\n
+ Copyright (C) 2020 Jan Rogall - developer\n
+
+ This file is part of hueplusplus.
+
+ hueplusplus is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ hueplusplus is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with hueplusplus. If not, see .
+**/
+
+#include
+
+#include
+
+#include "testhelper.h"
+
+#include "mocks/mock_HttpHandler.h"
+
+using namespace hueplusplus;
+using namespace testing;
+
+TEST(LightState, On)
+{
+ EXPECT_FALSE(LightState(nlohmann::json::object()).isOn());
+ EXPECT_TRUE(LightState(nlohmann::json {{"on", true}}).isOn());
+ EXPECT_FALSE(LightState(nlohmann::json {{"on", false}}).isOn());
+}
+
+TEST(LightState, Brightness)
+{
+ EXPECT_FALSE(LightState(nlohmann::json::object()).hasBrightness());
+ const int bri = 125;
+ nlohmann::json json {{"bri", bri}};
+ const LightState state {json};
+ EXPECT_TRUE(state.hasBrightness());
+ EXPECT_EQ(bri, state.getBrightness());
+}
+
+TEST(LightState, HueSat)
+{
+ EXPECT_FALSE(LightState(nlohmann::json::object()).hasHueSat());
+ EXPECT_FALSE(LightState(nlohmann::json {{"hue", 0}}).hasHueSat());
+ EXPECT_FALSE(LightState(nlohmann::json {{"sat", 0}}).hasHueSat());
+ const int hue = 12553;
+ const int sat = 240;
+ nlohmann::json json {{"hue", hue}, {"sat", sat}};
+ const LightState state {json};
+ EXPECT_TRUE(state.hasHueSat());
+ EXPECT_EQ(hue, state.getHueSat().hue);
+ EXPECT_EQ(sat, state.getHueSat().saturation);
+}
+
+TEST(LightState, XY)
+{
+ EXPECT_FALSE(LightState(nlohmann::json::object()).hasXY());
+ const float x = 0.6f;
+ const float y = 0.3f;
+ nlohmann::json json {{"xy", {x, y}}};
+ const LightState state {json};
+ EXPECT_TRUE(state.hasXY());
+ EXPECT_FLOAT_EQ(x, state.getXY().x);
+ EXPECT_FLOAT_EQ(y, state.getXY().y);
+}
+
+TEST(LightState, Ct)
+{
+ EXPECT_FALSE(LightState(nlohmann::json::object()).hasCt());
+ const int ct = 260;
+ nlohmann::json json {{"ct", ct}};
+ const LightState state {json};
+ EXPECT_TRUE(state.hasCt());
+ EXPECT_EQ(ct, state.getCt());
+}
+
+TEST(LightState, Effect)
+{
+ EXPECT_FALSE(LightState(nlohmann::json::object()).hasEffect());
+ nlohmann::json json {{"effect", "colorloop"}};
+ const LightState state {json};
+ EXPECT_TRUE(state.hasEffect());
+ EXPECT_TRUE(state.getColorloop());
+ EXPECT_FALSE(LightState(nlohmann::json {{"effect", "none"}}).getColorloop());
+}
+
+TEST(LightState, TransitionTime)
+{
+ EXPECT_EQ(4, LightState(nlohmann::json::object()).getTransitionTime());
+ EXPECT_EQ(0, LightState(nlohmann::json {{"transitiontime", 0}}).getTransitionTime());
+}
+
+TEST(LightState, toJson)
+{
+ const nlohmann::json json {{"on", false}, {"bri", 254}, {"ct", 400}};
+ EXPECT_EQ(json, LightState(json).toJson());
+}
+
+TEST(LightStateBuilder, create)
+{
+ {
+ const nlohmann::json json {{"on", false}, {"bri", 254}, {"ct", 400}, {"effect", "colorloop"}};
+ EXPECT_EQ(
+ json, LightStateBuilder().setOn(false).setBrightness(254).setCt(400).setColorloop(true).create().toJson());
+ }
+ {
+ const nlohmann::json json {{"xy", {0.5f, 0.5f}}, {"effect", "none"}};
+ EXPECT_EQ(json, LightStateBuilder().setXY({0.5f, 0.5f}).setColorloop(false).create().toJson());
+ }
+ {
+ const nlohmann::json json {{"hue", 360}, {"sat", 230}, {"transitiontime", 4}};
+ EXPECT_EQ(json, LightStateBuilder().setHueSat({360, 230}).setTransitionTime(4).create().toJson());
+ }
+}
+
+class SceneTest : public Test
+{
+public:
+ std::shared_ptr handler;
+ HueCommandAPI commands;
+ nlohmann::json sceneState;
+
+ SceneTest()
+ : handler(std::make_shared()),
+ commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler),
+ sceneState({{"name", "Test scene"}, {"type", "GroupScene"}, {"group", "4"}, {"lights", {"3", "4", "5"}},
+ {"owner", "testowner"}, {"recycle", false}, {"locked", false},
+ {"appdata", {{"data", "test-data"}, {"version", 2}}}, {"picture", ""},
+ {"lastupdated", "2020-04-23T12:00:04"}, {"version", 2},
+ {"lightstates",
+ {{"3", {{"on", false}, {"bri", 100}, {"xy", {0.3, 0.2}}}},
+ {"4", {{"on", true}, {"bri", 200}, {"xy", {0.3, 0.2}}, {"effect", "colorloop"}}},
+ {"5", {{"on", true}, {"bri", 100}, {"xy", {0.3, 0.2}}}}}}})
+ {}
+
+ void expectGetState(const std::string& id)
+ {
+ EXPECT_CALL(*handler,
+ GETJson("/api/" + getBridgeUsername() + "/scenes/" + id, nlohmann::json::object(), getBridgeIp(),
+ getBridgePort()))
+ .WillOnce(Return(sceneState));
+ }
+};
+
+TEST_F(SceneTest, Constructor)
+{
+ const std::string id = "asd89263";
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(id, scene.getId());
+}
+
+TEST_F(SceneTest, getName)
+{
+ const std::string id = "125abets8912";
+ const std::string name = "Scene name";
+ sceneState["name"] = name;
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(name, scene.getName());
+}
+
+TEST_F(SceneTest, getType)
+{
+ const std::string id = "125abets8912";
+ {
+ sceneState["type"] = "GroupScene";
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(Scene::Type::groupScene, scene.getType());
+ }
+ {
+ sceneState["type"] = "LightScene";
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(Scene::Type::lightScene, scene.getType());
+ }
+}
+
+TEST_F(SceneTest, getGroupId)
+{
+ const std::string id = "125abets8912";
+ {
+ sceneState["group"] = "3";
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(3, scene.getGroupId());
+ }
+ {
+ sceneState["type"] = "LightScene";
+ sceneState.erase("group");
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(0, scene.getGroupId());
+ }
+}
+
+TEST_F(SceneTest, getLightIds)
+{
+ const std::string id = "125asav3";
+ sceneState["lights"] = {"3", "4", "5"};
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_THAT(scene.getLightIds(), UnorderedElementsAre(3, 4, 5));
+}
+
+TEST_F(SceneTest, getOwner)
+{
+ const std::string id = "125asav3";
+ const std::string owner = "testowner";
+ sceneState["owner"] = owner;
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(owner, scene.getOwner());
+}
+
+TEST_F(SceneTest, getRecycle)
+{
+ const std::string id = "125asav3";
+ const bool recycle = true;
+ sceneState["recycle"] = recycle;
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(recycle, scene.getRecycle());
+}
+
+TEST_F(SceneTest, isLocked)
+{
+ const std::string id = "125asav3";
+ const bool locked = true;
+ sceneState["locked"] = locked;
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(locked, scene.isLocked());
+}
+
+TEST_F(SceneTest, getAppdata)
+{
+ const std::string id = "125asav3";
+ const std::string appdata = "some data";
+ const int version = 10;
+ sceneState["appdata"] = {{"version", version}, {"data", appdata}};
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(version, scene.getAppdataVersion());
+ EXPECT_EQ(appdata, scene.getAppdata());
+}
+
+TEST_F(SceneTest, getPicture)
+{
+ const std::string id = "125asav3";
+ const std::string picture = "abcpicture";
+ sceneState["picture"] = picture;
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(picture, scene.getPicture());
+}
+
+TEST_F(SceneTest, getLastUpdated)
+{
+ const std::string id = "125asav3";
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ const time::AbsoluteTime lastUpdated = scene.getLastUpdated();
+ EXPECT_EQ(std::chrono::seconds(0), lastUpdated.getRandomVariation());
+}
+
+TEST_F(SceneTest, getVersion)
+{
+ const std::string id = "125asav3";
+ const int version = 2;
+ sceneState["version"] = version;
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_EQ(version, scene.getVersion());
+}
+
+TEST_F(SceneTest, getLightstates)
+{
+ const std::string id = "125asav3";
+ {
+ const std::map lightstates{
+ {3, LightStateBuilder().setOn(false).setBrightness(100).setXY({0.3, 0.2}).create()},
+ {4, LightStateBuilder().setOn(false).setBrightness(200).setXY({0.3, 0.2}).setColorloop(true).create()},
+ {5, LightStateBuilder().setOn(true).setBrightness(100).setXY({0.3, 0.2}).create()} };
+ nlohmann::json lightstatesJson;
+ for (const auto& entry : lightstates)
+ {
+ lightstatesJson[std::to_string(entry.first)] = entry.second.toJson();
+ }
+ sceneState["lightstates"] = lightstatesJson;
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ const std::map result = scene.getLightStates();
+ EXPECT_EQ(lightstates, result);
+ }
+ // No lightstates (old scene)
+ {
+ sceneState.erase("lightstates");
+ expectGetState(id);
+ const Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_TRUE(scene.getLightStates().empty());
+ }
+}
+
+TEST_F(SceneTest, refresh)
+{
+ const std::string id = "125asav3";
+ expectGetState(id);
+ Scene scene(id, commands, std::chrono::seconds(0));
+ expectGetState(id);
+ scene.refresh();
+}
+
+TEST_F(SceneTest, setName)
+{
+ const std::string id = "125asav3";
+ expectGetState(id);
+ Scene scene(id, commands, std::chrono::seconds(0));
+ const std::string name = "Scene name";
+ nlohmann::json request = {{"name", name}};
+ nlohmann::json response = {{"success", {"/scenes/" + id + "/name", name}}};
+ EXPECT_CALL(
+ *handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
+ .WillOnce(Return(response));
+ expectGetState(id);
+ scene.setName(name);
+}
+
+TEST_F(SceneTest, setLightIds)
+{
+ const std::string id = "125asav3";
+ expectGetState(id);
+ Scene scene(id, commands, std::chrono::seconds(0));
+ const std::vector lightIds = {3, 4, 6, 8};
+ nlohmann::json request = {{"lights", {"3", "4", "6", "8"}}};
+ nlohmann::json response = {{"success", {"/scenes/" + id + "/lights", {"3", "4", "6", "8"}}}};
+ EXPECT_CALL(
+ *handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
+ .WillOnce(Return(response));
+ expectGetState(id);
+ scene.setLightIds(lightIds);
+}
+
+TEST_F(SceneTest, setAppdata)
+{
+ const std::string id = "125asav3";
+ expectGetState(id);
+ Scene scene(id, commands, std::chrono::seconds(0));
+ const std::string appdata = "New appdata";
+ const int version = 3;
+ nlohmann::json request = {{"appdata", {{"version", version}, {"data", appdata}}}};
+ nlohmann::json response = {{"success", {"/scenes/" + id + "/appdata/version", version}},
+ {"success", {"/scenes/" + id + "/appdata/data", appdata}}};
+ EXPECT_CALL(
+ *handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
+ .WillOnce(Return(response));
+ expectGetState(id);
+ scene.setAppdata(appdata, version);
+}
+
+TEST_F(SceneTest, setLightStates)
+{
+ const std::string id = "125asav3";
+ const std::map lightstates {
+ {3, LightStateBuilder().setOn(false).setBrightness(100).setCt(200).create()},
+ {5, LightStateBuilder().setOn(true).setBrightness(200).setXY({0.3, 0.2}).create()}};
+ nlohmann::json lightstatesJson;
+ for (const auto& entry : lightstates)
+ {
+ lightstatesJson[std::to_string(entry.first)] = entry.second.toJson();
+ }
+ expectGetState(id);
+ Scene scene(id, commands, std::chrono::seconds(0));
+ nlohmann::json request = {{"lightstates", lightstatesJson}};
+ nlohmann::json response = {{"success", {"/scenes/" + id + "/lights/3/state/on", false}},
+ {"success", {"/scenes/" + id + "/lights/3/state/bri", 100}},
+ {"success", {"/scenes/" + id + "/lights/3/state/ct", 200}},
+ {"success", {"/scenes/" + id + "/lights/5/state/on", true}},
+ {"success", {"/scenes/" + id + "/lights/5/state/bri", 200}},
+ {"success", {"/scenes/" + id + "/lights/5/state/xy", {0.3, 0.2}}}};
+ EXPECT_CALL(
+ *handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
+ .WillOnce(Return(response));
+ expectGetState(id);
+ scene.setLightStates(lightstates);
+}
+
+TEST_F(SceneTest, storeCurrentLightState)
+{
+ const std::string id = "125asav3";
+ expectGetState(id);
+ Scene scene(id, commands, std::chrono::seconds(0));
+ {
+ nlohmann::json request = {{"storelightstate", true}};
+ nlohmann::json response = {{"success", {"/scenes/" + id + "/storelightstate", true}}};
+ EXPECT_CALL(
+ *handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
+ .WillOnce(Return(response));
+ expectGetState(id);
+ scene.storeCurrentLightState();
+ }
+ {
+ const int transitiontime = 3;
+ nlohmann::json request = {{"storelightstate", true}, {"transitiontime", 3}};
+ nlohmann::json response = {{"success", {"/scenes/" + id + "/storelightstate", true}}};
+ EXPECT_CALL(
+ *handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
+ .WillOnce(Return(response));
+ expectGetState(id);
+ scene.storeCurrentLightState(transitiontime);
+ }
+}
+
+TEST_F(SceneTest, recall)
+{
+ const std::string id = "125asav3";
+ // LightScene
+ {
+ sceneState["type"] = "LightScene";
+ expectGetState(id);
+ nlohmann::json request = {{"scene", id}};
+ nlohmann::json response = {{"success", {"/groups/0/action/scene", id}}};
+ Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_CALL(*handler,
+ PUTJson("/api/" + getBridgeUsername() + "/groups/0/action", request, getBridgeIp(), getBridgePort()))
+ .WillOnce(Return(response));
+ scene.recall();
+ Mock::VerifyAndClearExpectations(handler.get());
+ }
+ // GroupScene
+ {
+ sceneState["type"] = "GroupScene";
+ std::string groupId = "3";
+ sceneState["group"] = groupId;
+ expectGetState(id);
+ nlohmann::json request = {{"scene", id}};
+ nlohmann::json response = {{"success", {"/groups/" + groupId + "/action/scene", id}}};
+ Scene scene(id, commands, std::chrono::seconds(0));
+ EXPECT_CALL(*handler,
+ PUTJson("/api/" + getBridgeUsername() + "/groups/" + groupId + "/action", request, getBridgeIp(),
+ getBridgePort()))
+ .WillOnce(Return(response));
+ scene.recall();
+ }
+}
+
+TEST(CreateScene, setName)
+{
+ const std::string name = "New scene";
+ const nlohmann::json request = {{"name", name}};
+ EXPECT_EQ(request, CreateScene().setName(name).getRequest());
+}
+
+TEST(CreateScene, setGroupId)
+{
+ const int groupId = 23;
+ const nlohmann::json request = {{"group", "23"}, {"type", "GroupScene"}};
+ EXPECT_EQ(request, CreateScene().setGroupId(groupId).getRequest());
+ EXPECT_THROW(CreateScene().setGroupId(2).setLightIds({1}), HueException);
+}
+
+TEST(CreateScene, setLightIds)
+{
+ const std::vector lightIds = {3, 4, 5, 9};
+ const nlohmann::json request = {{"lights", {"3", "4", "5", "9"}}, {"type", "LightScene"}};
+ EXPECT_EQ(request, CreateScene().setLightIds(lightIds).getRequest());
+ EXPECT_THROW(CreateScene().setLightIds(lightIds).setGroupId(1), HueException);
+}
+
+TEST(CreateScene, setRecycle)
+{
+ const nlohmann::json request = {{"recycle", true}};
+ EXPECT_EQ(request, CreateScene().setRecycle(true).getRequest());
+}
+
+TEST(CreateScene, setAppdata)
+{
+ const std::string data = "testdata";
+ const int version = 3;
+ const nlohmann::json request = {{"appdata", {{"data", data}, {"version", version}}}};
+ EXPECT_EQ(request, CreateScene().setAppdata(data, version).getRequest());
+}
+
+TEST(CreateScene, setLightStates)
+{
+ const std::map lightStates
+ = {{1, LightStateBuilder().setOn(true).create()}, {5, LightStateBuilder().setCt(300).create()}};
+ const nlohmann::json request = {{"lightstates", {{"1", {{"on", true}}}, {"5", {{"ct", 300}}}}}};
+ EXPECT_EQ(request, CreateScene().setLightStates(lightStates).getRequest());
+}
\ No newline at end of file