Commit 4a7edd72d270f1d1ec0a0c8c4772209742792d78

Authored by Jojo-1000
Committed by Moritz Wirger
1 parent cd30bc74

Fix APICache refresh timing, add tests for APICache.

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
... ... @@ -30,6 +30,7 @@ target_compile_features(gtest PUBLIC cxx_std_14)
30 30  
31 31 # define all test sources
32 32 set(TEST_SOURCES
  33 + test_APICache.cpp
33 34 test_BaseHttpHandler.cpp
34 35 test_ExtendedColorHueStrategy.cpp
35 36 test_ExtendedColorTemperatureStrategy.cpp
... ...
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;
... ...