From 4a7edd72d270f1d1ec0a0c8c4772209742792d78 Mon Sep 17 00:00:00 2001
From: Jojo-1000 <33495614+Jojo-1000@users.noreply.github.com>
Date: Sun, 12 Apr 2020 23:09:59 +0200
Subject: [PATCH] Fix APICache refresh timing, add tests for APICache.
---
src/APICache.cpp | 25 +++++++++++++++++++------
test/CMakeLists.txt | 1 +
test/test_APICache.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
test/test_Hue.cpp | 7 ++++---
test/test_HueLight.cpp | 3 +++
5 files changed, 151 insertions(+), 9 deletions(-)
create mode 100644 test/test_APICache.cpp
diff --git a/src/APICache.cpp b/src/APICache.cpp
index ebd8653..74d627f 100644
--- a/src/APICache.cpp
+++ b/src/APICache.cpp
@@ -25,22 +25,35 @@
hueplusplus::APICache::APICache(
const std::string& path, const HueCommandAPI& commands, std::chrono::steady_clock::duration refresh)
- : path(path),
- commands(commands),
- refreshDuration(refresh),
- lastRefresh(std::chrono::steady_clock::duration::zero())
+ : path(path), commands(commands), refreshDuration(refresh), lastRefresh(std::chrono::steady_clock::duration::zero())
{}
-void hueplusplus::APICache::Refresh() {
+void hueplusplus::APICache::Refresh()
+{
value = commands.GETRequest(path, nlohmann::json::object(), CURRENT_FILE_INFO);
+ lastRefresh = std::chrono::steady_clock::now();
}
nlohmann::json& hueplusplus::APICache::GetValue()
{
- if (std::chrono::steady_clock::now() >= lastRefresh + refreshDuration)
+ using clock = std::chrono::steady_clock;
+ // Explicitly check for zero in case refreshDuration is duration::max()
+ // Negative duration causes overflow check to overflow itself
+ if (lastRefresh.time_since_epoch().count() == 0 || refreshDuration.count() < 0)
{
+ // No value set yet
Refresh();
}
+ // Check if nextRefresh would overflow (assumes lastRefresh is not negative, which it should not be).
+ // If addition would overflow, do not refresh
+ else if (clock::duration::max() - refreshDuration > lastRefresh.time_since_epoch())
+ {
+ clock::time_point nextRefresh = lastRefresh + refreshDuration;
+ if (clock::now() >= nextRefresh)
+ {
+ Refresh();
+ }
+ }
return value;
}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 1cedabc..d077247 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -30,6 +30,7 @@ target_compile_features(gtest PUBLIC cxx_std_14)
# define all test sources
set(TEST_SOURCES
+ test_APICache.cpp
test_BaseHttpHandler.cpp
test_ExtendedColorHueStrategy.cpp
test_ExtendedColorTemperatureStrategy.cpp
diff --git a/test/test_APICache.cpp b/test/test_APICache.cpp
new file mode 100644
index 0000000..c673cc8
--- /dev/null
+++ b/test/test_APICache.cpp
@@ -0,0 +1,124 @@
+/**
+ \file test_Hue.cpp
+ Copyright Notice\n
+ Copyright (C) 2017 Jan Rogall - developer\n
+ Copyright (C) 2017 Moritz Wirger - 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 "testhelper.h"
+
+#include "hueplusplus/APICache.h"
+#include "mocks/mock_HttpHandler.h"
+
+using namespace hueplusplus;
+
+TEST(APICache, GetRefreshDuration)
+{
+ auto handler = std::make_shared();
+ HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
+ {
+ std::chrono::steady_clock::duration refresh = std::chrono::seconds(20);
+ APICache cache("", commands, refresh);
+ EXPECT_EQ(refresh, cache.GetRefreshDuration());
+ }
+ {
+ std::chrono::steady_clock::duration refresh = std::chrono::seconds(0);
+ APICache cache("", commands, refresh);
+ EXPECT_EQ(refresh, cache.GetRefreshDuration());
+ }
+ {
+ std::chrono::steady_clock::duration refresh = std::chrono::steady_clock::duration::max();
+ APICache cache("", commands, refresh);
+ EXPECT_EQ(refresh, cache.GetRefreshDuration());
+ }
+}
+
+TEST(APICache, Refresh)
+{
+ using namespace ::testing;
+ auto handler = std::make_shared();
+ HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
+
+ {
+ std::string path = "/test/abc";
+ APICache cache(path, commands, std::chrono::seconds(10));
+ EXPECT_CALL(*handler,
+ GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
+ .WillOnce(Return(nlohmann::json::object()));
+ cache.Refresh();
+ Mock::VerifyAndClearExpectations(handler.get());
+ }
+ {
+ std::string path = "";
+ APICache cache(path, commands, std::chrono::seconds(10));
+ EXPECT_CALL(*handler,
+ GETJson(
+ "/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
+ .Times(2)
+ .WillRepeatedly(Return(nlohmann::json::object()));
+ cache.Refresh();
+ cache.Refresh();
+ Mock::VerifyAndClearExpectations(handler.get());
+ }
+}
+
+TEST(APICache, GetValue)
+{
+ using namespace ::testing;
+ auto handler = std::make_shared();
+ HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
+
+ // Always refresh
+ {
+ std::string path = "/test/abc";
+ APICache cache(path, commands, std::chrono::seconds(0));
+ nlohmann::json value = { {"a", "b"} };
+ EXPECT_CALL(*handler,
+ GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
+ .Times(2)
+ .WillRepeatedly(Return(value));
+ EXPECT_EQ(value, cache.GetValue());
+ EXPECT_EQ(value, cache.GetValue());
+ Mock::VerifyAndClearExpectations(handler.get());
+ }
+ // Only refresh once
+ {
+ std::string path = "/test/abc";
+ APICache cache(path, commands, std::chrono::steady_clock::duration::max());
+ nlohmann::json value = { {"a", "b"} };
+ EXPECT_CALL(*handler,
+ GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
+ .WillOnce(Return(value));
+ EXPECT_EQ(value, cache.GetValue());
+ EXPECT_EQ(value, cache.GetValue());
+ Mock::VerifyAndClearExpectations(handler.get());
+ }
+ // No refresh with const
+ {
+ std::string path = "/test/abc";
+ const APICache cache(path, commands, std::chrono::steady_clock::duration::max());
+ nlohmann::json value = { {"a", "b"} };
+ EXPECT_CALL(*handler,
+ GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
+ .Times(0);
+ EXPECT_EQ(nullptr, cache.GetValue());
+ Mock::VerifyAndClearExpectations(handler.get());
+ }
+}
\ No newline at end of file
diff --git a/test/test_Hue.cpp b/test/test_Hue.cpp
index f7976fd..8312b0f 100644
--- a/test/test_Hue.cpp
+++ b/test/test_Hue.cpp
@@ -20,8 +20,6 @@
along with hueplusplus. If not, see .
**/
-#include
-#include
#include
#include
@@ -285,6 +283,9 @@ TEST(Hue, getLight)
.Times(AtLeast(1))
.WillRepeatedly(Return(hue_bridge_state["lights"]["1"]));
+ // Refresh cache
+ test_bridge = Hue(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
+
// Test when correct data is sent
HueLight test_light_1 = test_bridge.getLight(1);
EXPECT_EQ(test_light_1.getName(), "Hue ambiance lamp 1");
@@ -479,7 +480,7 @@ TEST(Hue, lightExists)
{"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}}}}}};
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
- .Times(AtLeast(2))
+ .Times(AtLeast(1))
.WillRepeatedly(Return(hue_bridge_state));
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
diff --git a/test/test_HueLight.cpp b/test/test_HueLight.cpp
index ea23894..a8b3edd 100644
--- a/test/test_HueLight.cpp
+++ b/test/test_HueLight.cpp
@@ -54,6 +54,7 @@ protected:
hue_bridge_state["lights"]["1"]["state"]["alert"] = "none";
hue_bridge_state["lights"]["1"]["state"]["colormode"] = "ct";
hue_bridge_state["lights"]["1"]["state"]["reachable"] = true;
+ hue_bridge_state["lights"]["1"]["state"]["effect"] = "none";
hue_bridge_state["lights"]["1"]["swupdate"] = nlohmann::json::object();
hue_bridge_state["lights"]["1"]["swupdate"]["state"] = "noupdates";
hue_bridge_state["lights"]["1"]["swupdate"]["lastinstall"] = nullptr;
@@ -77,6 +78,7 @@ protected:
hue_bridge_state["lights"]["2"]["state"]["alert"] = "none";
hue_bridge_state["lights"]["2"]["state"]["colormode"] = "ct";
hue_bridge_state["lights"]["2"]["state"]["reachable"] = true;
+ hue_bridge_state["lights"]["2"]["state"]["effect"] = "none";
hue_bridge_state["lights"]["2"]["swupdate"] = nlohmann::json::object();
hue_bridge_state["lights"]["2"]["swupdate"]["state"] = "noupdates";
hue_bridge_state["lights"]["2"]["swupdate"]["lastinstall"] = nullptr;
@@ -97,6 +99,7 @@ protected:
hue_bridge_state["lights"]["3"]["state"]["alert"] = "none";
hue_bridge_state["lights"]["3"]["state"]["colormode"] = "ct";
hue_bridge_state["lights"]["3"]["state"]["reachable"] = true;
+ hue_bridge_state["lights"]["3"]["state"]["effect"] = "none";
hue_bridge_state["lights"]["3"]["swupdate"] = nlohmann::json::object();
hue_bridge_state["lights"]["3"]["swupdate"]["state"] = "noupdates";
hue_bridge_state["lights"]["3"]["swupdate"]["lastinstall"] = nullptr;
--
libgit2 0.21.4