diff --git a/src/BaseDevice.cpp b/src/BaseDevice.cpp index 492325f..70ab841 100644 --- a/src/BaseDevice.cpp +++ b/src/BaseDevice.cpp @@ -93,7 +93,7 @@ bool BaseDevice::setName(const std::string& name) BaseDevice::BaseDevice( int id, const HueCommandAPI& commands, const std::string& path, std::chrono::steady_clock::duration refreshDuration) - : id(id), state(path + std::to_string(id), commands, refreshDuration), path(path) + : id(id), path(path), state(path + std::to_string(id), commands, refreshDuration) { state.refresh(); } diff --git a/src/CLIPSensors.cpp b/src/CLIPSensors.cpp index f132ade..27f4446 100644 --- a/src/CLIPSensors.cpp +++ b/src/CLIPSensors.cpp @@ -188,12 +188,12 @@ constexpr const char* CLIPGenericStatus::typeStr; int CLIPGenericStatus::getStatus() const { - return state.getValue().at("config").at("status").get(); + return state.getValue().at("state").at("status").get(); } void CLIPGenericStatus::setStatus(int status) { - sendPutRequest("/config", nlohmann::json {{"status", status}}, CURRENT_FILE_INFO); + sendPutRequest("/state", nlohmann::json {{"status", status}}, CURRENT_FILE_INFO); } } // namespace sensors } // namespace hueplusplus \ No newline at end of file diff --git a/src/Sensor.cpp b/src/Sensor.cpp index 26e65af..4cda9a8 100644 --- a/src/Sensor.cpp +++ b/src/Sensor.cpp @@ -270,7 +270,7 @@ bool DaylightSensor::isOn() const void DaylightSensor::setOn(bool on) { - sendPutRequest("/config", {"on", on}, CURRENT_FILE_INFO); + sendPutRequest("/config", { {"on", on} }, CURRENT_FILE_INFO); } bool DaylightSensor::hasBatteryState() const @@ -324,7 +324,7 @@ void DaylightSensor::setSunsetOffset(int minutes) bool DaylightSensor::isDaylight() const { - return state.getValue().at("config").at("daylight").get(); + return state.getValue().at("state").at("daylight").get(); } } // namespace sensors } // namespace hueplusplus diff --git a/src/ZLLSensors.cpp b/src/ZLLSensors.cpp index bdff4e6..2361054 100644 --- a/src/ZLLSensors.cpp +++ b/src/ZLLSensors.cpp @@ -92,7 +92,7 @@ Alert ZLLSwitch::getLastAlert() const } void ZLLSwitch::sendAlert(Alert type) { - sendPutRequest("/state", nlohmann::json {{"alert", alertToString(type)}}, CURRENT_FILE_INFO); + sendPutRequest("/config", nlohmann::json {{"alert", alertToString(type)}}, CURRENT_FILE_INFO); } bool ZLLSwitch::isReachable() const { @@ -141,7 +141,7 @@ Alert ZLLPresence::getLastAlert() const } void ZLLPresence::sendAlert(Alert type) { - sendPutRequest("/state", nlohmann::json {{"alert", alertToString(type)}}, CURRENT_FILE_INFO); + sendPutRequest("/config", nlohmann::json {{"alert", alertToString(type)}}, CURRENT_FILE_INFO); } bool ZLLPresence::isReachable() const { @@ -158,7 +158,7 @@ int ZLLPresence::getMaxSensitivity() const } void ZLLPresence::setSensitivity(int sensitivity) { - sendPutRequest("/state", nlohmann::json {{"sensitivity", sensitivity}}, CURRENT_FILE_INFO); + sendPutRequest("/config", nlohmann::json {{"sensitivity", sensitivity}}, CURRENT_FILE_INFO); } bool ZLLPresence::getPresence() const { @@ -185,7 +185,7 @@ bool ZLLTemperature::isOn() const void ZLLTemperature::setOn(bool on) { - sendPutRequest("/config", {"on", on}, CURRENT_FILE_INFO); + sendPutRequest("/config", {{"on", on}}, CURRENT_FILE_INFO); } bool ZLLTemperature::hasBatteryState() const { @@ -203,7 +203,7 @@ Alert ZLLTemperature::getLastAlert() const } void ZLLTemperature::sendAlert(Alert type) { - sendPutRequest("/state", nlohmann::json {{"alert", alertToString(type)}}, CURRENT_FILE_INFO); + sendPutRequest("/config", nlohmann::json {{"alert", alertToString(type)}}, CURRENT_FILE_INFO); } bool ZLLTemperature::isReachable() const { @@ -224,7 +224,7 @@ bool ZLLLightLevel::isOn() const void ZLLLightLevel::setOn(bool on) { - sendPutRequest("/config", {"on", on}, CURRENT_FILE_INFO); + sendPutRequest("/config", {{"on", on}}, CURRENT_FILE_INFO); } bool ZLLLightLevel::hasBatteryState() const { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 245dd4f..ff95aa4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -35,6 +35,7 @@ set(TEST_SOURCES test_BaseHttpHandler.cpp test_Bridge.cpp test_BridgeConfig.cpp + test_SensorImpls.cpp test_ColorUnits.cpp test_ExtendedColorHueStrategy.cpp test_ExtendedColorTemperatureStrategy.cpp diff --git a/test/test_SensorImpls.cpp b/test/test_SensorImpls.cpp new file mode 100644 index 0000000..c99435a --- /dev/null +++ b/test/test_SensorImpls.cpp @@ -0,0 +1,392 @@ +/** + \file test_SensorImpls.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 + +#include "testhelper.h" + +#include "mocks/mock_HttpHandler.h" + +using namespace testing; +using namespace hueplusplus; +using namespace hueplusplus::sensors; + +// Many sensor classes contain duplicate methods, with the type parameterized tests at least the test cases +// do not have to be duplicated +template +class SensorImplTest : public Test +{ +protected: + std::shared_ptr handler; + HueCommandAPI commands; + nlohmann::json state; + +protected: + SensorImplTest() + : handler(std::make_shared()), + commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler), + state({{"type", T::typeStr}, {"config", nlohmann::json::object()}, {"state", nlohmann::json::object()}}) + { } + + void expectConfigSet(const std::string& key, const nlohmann::json& value) + { + EXPECT_CALL(*handler, + PUTJson("/api/" + getBridgeUsername() + "/sensors/1/config", nlohmann::json({{key, value}}), getBridgeIp(), + getBridgePort())) + .WillOnce(Return(nlohmann::json {{{"success", {{"/sensors/1/config/" + key, value}}}}})); + } + void expectStateSet(const std::string& key, const nlohmann::json& value) + { + EXPECT_CALL(*handler, + PUTJson("/api/" + getBridgeUsername() + "/sensors/1/state", nlohmann::json({{key, value}}), getBridgeIp(), + getBridgePort())) + .WillOnce(Return(nlohmann::json {{{"success", {{"/sensors/1/state/" + key, value}}}}})); + } + + T getSensor() + { + EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername() + "/sensors/1", _, getBridgeIp(), getBridgePort())) + .WillOnce(Return(state)); + return T(Sensor(1, commands, std::chrono::steady_clock::duration::max())); + } +}; + +// Sensors with shared methods + +template +class SensorOnTest : public SensorImplTest +{ }; +// Only need to test one CLIP type, because they share the basic methods +using SensorOnTypes + = Types; +TYPED_TEST_SUITE(SensorOnTest, SensorOnTypes); + +template +class SensorBatteryTest : public SensorImplTest +{ }; +using SensorBatteryTypes = Types; +TYPED_TEST_SUITE(SensorBatteryTest, SensorBatteryTypes); + +template +class SensorReachableTest : public SensorImplTest +{ }; +using SensorReachableTypes = Types; +TYPED_TEST_SUITE(SensorReachableTest, SensorReachableTypes); + +template +class SensorUpdateTest : public SensorImplTest +{ }; +using SensorUpdateTypes = Types; +TYPED_TEST_SUITE(SensorUpdateTest, SensorUpdateTypes); + +template +class SensorAlertTest : public SensorImplTest +{ }; +using SensorAlertTypes = Types; +TYPED_TEST_SUITE(SensorAlertTest, SensorAlertTypes); + +template +class SensorButtonTest : public SensorImplTest +{ }; +using SensorButtonTypes = Types; +TYPED_TEST_SUITE(SensorButtonTest, SensorButtonTypes); + +template +class SensorTemperatureTest : public SensorImplTest +{ }; +using SensorTemperatureTypes = Types; +TYPED_TEST_SUITE(SensorTemperatureTest, SensorTemperatureTypes); + +template +class SensorLightLevelTest : public SensorImplTest +{ }; +using SensorLightLevelTypes = Types; +TYPED_TEST_SUITE(SensorLightLevelTest, SensorLightLevelTypes); + +template +class SensorPresenceTest : public SensorImplTest +{ }; +using SensorPresenceTypes = Types; +TYPED_TEST_SUITE(SensorPresenceTest, SensorPresenceTypes); + +// Sensors with unique methods + +class DaylightSensorTest : public SensorImplTest +{ }; + +class ZLLPresenceTest : public SensorImplTest +{ }; + +class CLIPSwitchTest : public SensorImplTest +{ }; + +class CLIPOpenCloseTest : public SensorImplTest +{ }; + +class CLIPPresenceTest : public SensorImplTest +{ }; + +class CLIPTemperatureTest : public SensorImplTest +{ }; + +class CLIPHumidityTest : public SensorImplTest +{ }; + +class CLIPGenericFlagTest : public SensorImplTest +{ }; + +class CLIPGenericStatusTest : public SensorImplTest +{ }; + +TYPED_TEST(SensorOnTest, On) +{ + this->state["config"]["on"] = false; + EXPECT_FALSE(this->getSensor().isOn()); + this->state["config"]["on"] = true; + EXPECT_TRUE(this->getSensor().isOn()); + + this->expectConfigSet("on", false); + this->getSensor().setOn(false); +} + +TYPED_TEST(SensorBatteryTest, BatteryState) +{ + EXPECT_FALSE(this->getSensor().hasBatteryState()); + this->state["config"]["battery"] = 90; + EXPECT_TRUE(this->getSensor().hasBatteryState()); + EXPECT_EQ(90, this->getSensor().getBatteryState()); +} + +TYPED_TEST(SensorReachableTest, Reachable) +{ + this->state["config"]["reachable"] = true; + EXPECT_TRUE(this->getSensor().isReachable()); +} + +TYPED_TEST(SensorUpdateTest, getLastUpdated) +{ + time::AbsoluteTime none = this->getSensor().getLastUpdated(); + EXPECT_EQ(std::chrono::seconds(0), none.getBaseTime().time_since_epoch()); + + const std::string timestamp = "2020-05-02T12:00:01"; + this->state["state"]["lastupdated"] = timestamp; + time::AbsoluteTime time = time::AbsoluteTime::parseUTC(timestamp); + EXPECT_EQ(time.getBaseTime(), this->getSensor().getLastUpdated().getBaseTime()); +} + +TYPED_TEST(SensorAlertTest, Alert) +{ + this->state["config"]["alert"] = "none"; + EXPECT_EQ(Alert::none, this->getSensor().getLastAlert()); + + this->expectConfigSet("alert", "lselect"); + this->getSensor().sendAlert(Alert::lselect); +} + +TYPED_TEST(SensorButtonTest, ButtonEvent) +{ + int code = 12; + this->state["state"]["buttonevent"] = code; + EXPECT_EQ(code, this->getSensor().getButtonEvent()); +} + +TYPED_TEST(SensorTemperatureTest, Temperature) +{ + int temperature = 1200; + this->state["state"]["temperature"] = temperature; + EXPECT_EQ(temperature, this->getSensor().getTemperature()); +} + +TYPED_TEST(SensorLightLevelTest, LightLevel) +{ + int lightLevel = 1200; + this->state["state"] = {{"lightlevel", lightLevel}, {"dark", true}, {"daylight", false}}; + EXPECT_EQ(lightLevel, this->getSensor().getLightLevel()); + EXPECT_TRUE(this->getSensor().isDark()); + EXPECT_FALSE(this->getSensor().isDaylight()); +} + +TYPED_TEST(SensorLightLevelTest, DarkThreshold) +{ + int darkThreshold = 12000; + this->state["config"]["tholddark"] = darkThreshold; + EXPECT_EQ(darkThreshold, this->getSensor().getDarkThreshold()); + + int newThreshold = 10; + this->expectConfigSet("tholddark", newThreshold); + this->getSensor().setDarkThreshold(newThreshold); +} + +TYPED_TEST(SensorLightLevelTest, ThresholdOffset) +{ + int offset = 12000; + this->state["config"]["tholdoffset"] = offset; + EXPECT_EQ(offset, this->getSensor().getThresholdOffset()); + + int newOffset = 10; + this->expectConfigSet("tholdoffset", newOffset); + this->getSensor().setThresholdOffset(newOffset); +} + +TYPED_TEST(SensorPresenceTest, Presence) +{ + this->state["state"]["presence"] = true; + EXPECT_TRUE(this->getSensor().getPresence()); +} + +TEST_F(DaylightSensorTest, Coordinates) +{ + const std::string lat = "000.0000N"; + const std::string lon = "000.0000E"; + EXPECT_CALL(*handler, + PUTJson("/api/" + getBridgeUsername() + "/sensors/1/config", nlohmann::json({{"lat", lat}, {"long", lon}}), + getBridgeIp(), getBridgePort())) + .WillOnce(Return(nlohmann::json { + {{"success", {{"/sensors/1/config/lat", lat}}}}, {{"success", {{"/sensors/1/config/long", lon}}}}})); + getSensor().setCoordinates(lat, lon); + state["config"]["configured"] = true; + EXPECT_TRUE(getSensor().isConfigured()); +} + +TEST_F(DaylightSensorTest, SunriseOffset) +{ + int offset = 10; + state["config"]["sunriseoffset"] = offset; + EXPECT_EQ(offset, getSensor().getSunriseOffset()); + + int newOffset = 20; + expectConfigSet("sunriseoffset", newOffset); + getSensor().setSunriseOffset(newOffset); +} + +TEST_F(DaylightSensorTest, SunsetOffset) +{ + int offset = 10; + state["config"]["sunsetoffset"] = offset; + EXPECT_EQ(offset, getSensor().getSunsetOffset()); + + int newOffset = 20; + expectConfigSet("sunsetoffset", newOffset); + getSensor().setSunsetOffset(newOffset); +} + +TEST_F(DaylightSensorTest, isDaylight) +{ + state["state"]["daylight"] = true; + EXPECT_TRUE(getSensor().isDaylight()); +} + +TEST_F(ZLLPresenceTest, Sensitivity) +{ + int sensitivity = 1000; + state["config"]["sensitivity"] = sensitivity; + int maxSensitivity = 10000; + state["config"]["sensitivitymax"] = maxSensitivity; + EXPECT_EQ(sensitivity, getSensor().getSensitivity()); + EXPECT_EQ(maxSensitivity, getSensor().getMaxSensitivity()); + + int newSensitivity = 10; + expectConfigSet("sensitivity", newSensitivity); + this->getSensor().setSensitivity(newSensitivity); +} + +TEST_F(CLIPSwitchTest, setBatteryState) +{ + int percent = 10; + expectConfigSet("battery", percent); + this->getSensor().setBatteryState(percent); +} + +TEST_F(CLIPSwitchTest, URL) +{ + EXPECT_FALSE(getSensor().hasURL()); + const std::string url = "https://abc"; + state["config"]["url"] = url; + EXPECT_TRUE(getSensor().hasURL()); + EXPECT_EQ(url, getSensor().getURL()); + + std::string newUrl = "https://cde"; + expectConfigSet("url", newUrl); + getSensor().setURL(newUrl); +} + +TEST_F(CLIPSwitchTest, setButtonEvent) +{ + int code = 10; + expectStateSet("buttonevent", code); + this->getSensor().setButtonEvent(code); +} + +TEST_F(CLIPOpenCloseTest, Open) +{ + state["state"]["open"] = true; + EXPECT_TRUE(getSensor().isOpen()); + + bool open = false; + expectStateSet("open", open); + getSensor().setOpen(open); +} + +TEST_F(CLIPPresenceTest, setPresence) +{ + bool presence = false; + expectStateSet("presence", presence); + getSensor().setPresence(presence); +} + +TEST_F(CLIPTemperatureTest, setPresence) +{ + int temperature = 1100; + expectStateSet("temperature", temperature); + getSensor().setTemperature(temperature); +} + +TEST_F(CLIPHumidityTest, Humidity) +{ + int humidity = 100; + state["state"]["humidity"] = humidity; + EXPECT_EQ(humidity, getSensor().getHumidity()); + + int newHumidity = 1100; + expectStateSet("humidity", newHumidity); + getSensor().setHumidity(newHumidity); +} + +TEST_F(CLIPGenericFlagTest, Flag) +{ + state["state"]["flag"] = true; + EXPECT_TRUE(getSensor().getFlag()); + expectStateSet("flag", false); + getSensor().setFlag(false); +} + +TEST_F(CLIPGenericStatusTest, Status) +{ + int status = 32; + state["state"]["status"] = status; + EXPECT_EQ(status, getSensor().getStatus()); + int newStatus = 52; + expectStateSet("status", newStatus); + getSensor().setStatus(newStatus); +}