Commit 4a7edd72d270f1d1ec0a0c8c4772209742792d78
Committed by
Moritz Wirger
1 parent
cd30bc74
Fix APICache refresh timing, add tests for APICache.
Showing
5 changed files
with
151 additions
and
9 deletions
src/APICache.cpp
| ... | ... | @@ -25,22 +25,35 @@ |
| 25 | 25 | |
| 26 | 26 | hueplusplus::APICache::APICache( |
| 27 | 27 | const std::string& path, const HueCommandAPI& commands, std::chrono::steady_clock::duration refresh) |
| 28 | - : path(path), | |
| 29 | - commands(commands), | |
| 30 | - refreshDuration(refresh), | |
| 31 | - lastRefresh(std::chrono::steady_clock::duration::zero()) | |
| 28 | + : path(path), commands(commands), refreshDuration(refresh), lastRefresh(std::chrono::steady_clock::duration::zero()) | |
| 32 | 29 | {} |
| 33 | 30 | |
| 34 | -void hueplusplus::APICache::Refresh() { | |
| 31 | +void hueplusplus::APICache::Refresh() | |
| 32 | +{ | |
| 35 | 33 | value = commands.GETRequest(path, nlohmann::json::object(), CURRENT_FILE_INFO); |
| 34 | + lastRefresh = std::chrono::steady_clock::now(); | |
| 36 | 35 | } |
| 37 | 36 | |
| 38 | 37 | nlohmann::json& hueplusplus::APICache::GetValue() |
| 39 | 38 | { |
| 40 | - if (std::chrono::steady_clock::now() >= lastRefresh + refreshDuration) | |
| 39 | + using clock = std::chrono::steady_clock; | |
| 40 | + // Explicitly check for zero in case refreshDuration is duration::max() | |
| 41 | + // Negative duration causes overflow check to overflow itself | |
| 42 | + if (lastRefresh.time_since_epoch().count() == 0 || refreshDuration.count() < 0) | |
| 41 | 43 | { |
| 44 | + // No value set yet | |
| 42 | 45 | Refresh(); |
| 43 | 46 | } |
| 47 | + // Check if nextRefresh would overflow (assumes lastRefresh is not negative, which it should not be). | |
| 48 | + // If addition would overflow, do not refresh | |
| 49 | + else if (clock::duration::max() - refreshDuration > lastRefresh.time_since_epoch()) | |
| 50 | + { | |
| 51 | + clock::time_point nextRefresh = lastRefresh + refreshDuration; | |
| 52 | + if (clock::now() >= nextRefresh) | |
| 53 | + { | |
| 54 | + Refresh(); | |
| 55 | + } | |
| 56 | + } | |
| 44 | 57 | return value; |
| 45 | 58 | } |
| 46 | 59 | ... | ... |
test/CMakeLists.txt
test/test_APICache.cpp
0 → 100644
| 1 | +/** | |
| 2 | + \file test_Hue.cpp | |
| 3 | + Copyright Notice\n | |
| 4 | + Copyright (C) 2017 Jan Rogall - developer\n | |
| 5 | + Copyright (C) 2017 Moritz Wirger - developer\n | |
| 6 | + | |
| 7 | + This file is part of hueplusplus. | |
| 8 | + | |
| 9 | + hueplusplus is free software: you can redistribute it and/or modify | |
| 10 | + it under the terms of the GNU Lesser General Public License as published by | |
| 11 | + the Free Software Foundation, either version 3 of the License, or | |
| 12 | + (at your option) any later version. | |
| 13 | + | |
| 14 | + hueplusplus is distributed in the hope that it will be useful, | |
| 15 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 16 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 17 | + GNU Lesser General Public License for more details. | |
| 18 | + | |
| 19 | + You should have received a copy of the GNU Lesser General Public License | |
| 20 | + along with hueplusplus. If not, see <http://www.gnu.org/licenses/>. | |
| 21 | +**/ | |
| 22 | + | |
| 23 | +#include <gtest/gtest.h> | |
| 24 | + | |
| 25 | +#include "testhelper.h" | |
| 26 | + | |
| 27 | +#include "hueplusplus/APICache.h" | |
| 28 | +#include "mocks/mock_HttpHandler.h" | |
| 29 | + | |
| 30 | +using namespace hueplusplus; | |
| 31 | + | |
| 32 | +TEST(APICache, GetRefreshDuration) | |
| 33 | +{ | |
| 34 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 35 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 36 | + { | |
| 37 | + std::chrono::steady_clock::duration refresh = std::chrono::seconds(20); | |
| 38 | + APICache cache("", commands, refresh); | |
| 39 | + EXPECT_EQ(refresh, cache.GetRefreshDuration()); | |
| 40 | + } | |
| 41 | + { | |
| 42 | + std::chrono::steady_clock::duration refresh = std::chrono::seconds(0); | |
| 43 | + APICache cache("", commands, refresh); | |
| 44 | + EXPECT_EQ(refresh, cache.GetRefreshDuration()); | |
| 45 | + } | |
| 46 | + { | |
| 47 | + std::chrono::steady_clock::duration refresh = std::chrono::steady_clock::duration::max(); | |
| 48 | + APICache cache("", commands, refresh); | |
| 49 | + EXPECT_EQ(refresh, cache.GetRefreshDuration()); | |
| 50 | + } | |
| 51 | +} | |
| 52 | + | |
| 53 | +TEST(APICache, Refresh) | |
| 54 | +{ | |
| 55 | + using namespace ::testing; | |
| 56 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 57 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 58 | + | |
| 59 | + { | |
| 60 | + std::string path = "/test/abc"; | |
| 61 | + APICache cache(path, commands, std::chrono::seconds(10)); | |
| 62 | + EXPECT_CALL(*handler, | |
| 63 | + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 64 | + .WillOnce(Return(nlohmann::json::object())); | |
| 65 | + cache.Refresh(); | |
| 66 | + Mock::VerifyAndClearExpectations(handler.get()); | |
| 67 | + } | |
| 68 | + { | |
| 69 | + std::string path = ""; | |
| 70 | + APICache cache(path, commands, std::chrono::seconds(10)); | |
| 71 | + EXPECT_CALL(*handler, | |
| 72 | + GETJson( | |
| 73 | + "/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 74 | + .Times(2) | |
| 75 | + .WillRepeatedly(Return(nlohmann::json::object())); | |
| 76 | + cache.Refresh(); | |
| 77 | + cache.Refresh(); | |
| 78 | + Mock::VerifyAndClearExpectations(handler.get()); | |
| 79 | + } | |
| 80 | +} | |
| 81 | + | |
| 82 | +TEST(APICache, GetValue) | |
| 83 | +{ | |
| 84 | + using namespace ::testing; | |
| 85 | + auto handler = std::make_shared<MockHttpHandler>(); | |
| 86 | + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 87 | + | |
| 88 | + // Always refresh | |
| 89 | + { | |
| 90 | + std::string path = "/test/abc"; | |
| 91 | + APICache cache(path, commands, std::chrono::seconds(0)); | |
| 92 | + nlohmann::json value = { {"a", "b"} }; | |
| 93 | + EXPECT_CALL(*handler, | |
| 94 | + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 95 | + .Times(2) | |
| 96 | + .WillRepeatedly(Return(value)); | |
| 97 | + EXPECT_EQ(value, cache.GetValue()); | |
| 98 | + EXPECT_EQ(value, cache.GetValue()); | |
| 99 | + Mock::VerifyAndClearExpectations(handler.get()); | |
| 100 | + } | |
| 101 | + // Only refresh once | |
| 102 | + { | |
| 103 | + std::string path = "/test/abc"; | |
| 104 | + APICache cache(path, commands, std::chrono::steady_clock::duration::max()); | |
| 105 | + nlohmann::json value = { {"a", "b"} }; | |
| 106 | + EXPECT_CALL(*handler, | |
| 107 | + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 108 | + .WillOnce(Return(value)); | |
| 109 | + EXPECT_EQ(value, cache.GetValue()); | |
| 110 | + EXPECT_EQ(value, cache.GetValue()); | |
| 111 | + Mock::VerifyAndClearExpectations(handler.get()); | |
| 112 | + } | |
| 113 | + // No refresh with const | |
| 114 | + { | |
| 115 | + std::string path = "/test/abc"; | |
| 116 | + const APICache cache(path, commands, std::chrono::steady_clock::duration::max()); | |
| 117 | + nlohmann::json value = { {"a", "b"} }; | |
| 118 | + EXPECT_CALL(*handler, | |
| 119 | + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) | |
| 120 | + .Times(0); | |
| 121 | + EXPECT_EQ(nullptr, cache.GetValue()); | |
| 122 | + Mock::VerifyAndClearExpectations(handler.get()); | |
| 123 | + } | |
| 124 | +} | |
| 0 | 125 | \ No newline at end of file | ... | ... |
test/test_Hue.cpp
| ... | ... | @@ -20,8 +20,6 @@ |
| 20 | 20 | along with hueplusplus. If not, see <http://www.gnu.org/licenses/>. |
| 21 | 21 | **/ |
| 22 | 22 | |
| 23 | -#include <atomic> | |
| 24 | -#include <iostream> | |
| 25 | 23 | #include <memory> |
| 26 | 24 | #include <string> |
| 27 | 25 | |
| ... | ... | @@ -285,6 +283,9 @@ TEST(Hue, getLight) |
| 285 | 283 | .Times(AtLeast(1)) |
| 286 | 284 | .WillRepeatedly(Return(hue_bridge_state["lights"]["1"])); |
| 287 | 285 | |
| 286 | + // Refresh cache | |
| 287 | + test_bridge = Hue(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); | |
| 288 | + | |
| 288 | 289 | // Test when correct data is sent |
| 289 | 290 | HueLight test_light_1 = test_bridge.getLight(1); |
| 290 | 291 | EXPECT_EQ(test_light_1.getName(), "Hue ambiance lamp 1"); |
| ... | ... | @@ -479,7 +480,7 @@ TEST(Hue, lightExists) |
| 479 | 480 | {"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}}}}}; |
| 480 | 481 | EXPECT_CALL( |
| 481 | 482 | *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) |
| 482 | - .Times(AtLeast(2)) | |
| 483 | + .Times(AtLeast(1)) | |
| 483 | 484 | .WillRepeatedly(Return(hue_bridge_state)); |
| 484 | 485 | EXPECT_CALL(*handler, |
| 485 | 486 | GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort())) | ... | ... |
test/test_HueLight.cpp
| ... | ... | @@ -54,6 +54,7 @@ protected: |
| 54 | 54 | hue_bridge_state["lights"]["1"]["state"]["alert"] = "none"; |
| 55 | 55 | hue_bridge_state["lights"]["1"]["state"]["colormode"] = "ct"; |
| 56 | 56 | hue_bridge_state["lights"]["1"]["state"]["reachable"] = true; |
| 57 | + hue_bridge_state["lights"]["1"]["state"]["effect"] = "none"; | |
| 57 | 58 | hue_bridge_state["lights"]["1"]["swupdate"] = nlohmann::json::object(); |
| 58 | 59 | hue_bridge_state["lights"]["1"]["swupdate"]["state"] = "noupdates"; |
| 59 | 60 | hue_bridge_state["lights"]["1"]["swupdate"]["lastinstall"] = nullptr; |
| ... | ... | @@ -77,6 +78,7 @@ protected: |
| 77 | 78 | hue_bridge_state["lights"]["2"]["state"]["alert"] = "none"; |
| 78 | 79 | hue_bridge_state["lights"]["2"]["state"]["colormode"] = "ct"; |
| 79 | 80 | hue_bridge_state["lights"]["2"]["state"]["reachable"] = true; |
| 81 | + hue_bridge_state["lights"]["2"]["state"]["effect"] = "none"; | |
| 80 | 82 | hue_bridge_state["lights"]["2"]["swupdate"] = nlohmann::json::object(); |
| 81 | 83 | hue_bridge_state["lights"]["2"]["swupdate"]["state"] = "noupdates"; |
| 82 | 84 | hue_bridge_state["lights"]["2"]["swupdate"]["lastinstall"] = nullptr; |
| ... | ... | @@ -97,6 +99,7 @@ protected: |
| 97 | 99 | hue_bridge_state["lights"]["3"]["state"]["alert"] = "none"; |
| 98 | 100 | hue_bridge_state["lights"]["3"]["state"]["colormode"] = "ct"; |
| 99 | 101 | hue_bridge_state["lights"]["3"]["state"]["reachable"] = true; |
| 102 | + hue_bridge_state["lights"]["3"]["state"]["effect"] = "none"; | |
| 100 | 103 | hue_bridge_state["lights"]["3"]["swupdate"] = nlohmann::json::object(); |
| 101 | 104 | hue_bridge_state["lights"]["3"]["swupdate"]["state"] = "noupdates"; |
| 102 | 105 | hue_bridge_state["lights"]["3"]["swupdate"]["lastinstall"] = nullptr; | ... | ... |