diff --git a/include/hueplusplus/ResourceList.h b/include/hueplusplus/ResourceList.h index f2a5394..4eebf20 100644 --- a/include/hueplusplus/ResourceList.h +++ b/include/hueplusplus/ResourceList.h @@ -66,7 +66,7 @@ public: //! Necessary if Resource is not constructible as described above. ResourceList(const HueCommandAPI& commands, const std::string& path, std::chrono::steady_clock::duration refreshDuration, - const std::function& factory = nullptr) + const std::function& factory = nullptr) : stateCache(path, commands, refreshDuration), factory(factory), path(path + '/') {} @@ -230,7 +230,7 @@ private: protected: APICache stateCache; - std::function factory; + std::function factory; std::string path; std::map resources; }; diff --git a/src/Schedule.cpp b/src/Schedule.cpp index c259724..7a30de3 100644 --- a/src/Schedule.cpp +++ b/src/Schedule.cpp @@ -199,7 +199,7 @@ CreateSchedule& CreateSchedule::setName(const std::string& name) CreateSchedule& CreateSchedule::setDescription(const std::string& description) { - request["descripton"] = description; + request["description"] = description; return *this; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f2055a9..abf9716 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -40,11 +40,12 @@ set(TEST_SOURCES test_HueLightFactory.cpp test_HueCommandAPI.cpp test_Main.cpp + test_UPnP.cpp + test_ResourceList.cpp + test_Schedule.cpp test_SimpleBrightnessStrategy.cpp test_SimpleColorHueStrategy.cpp test_SimpleColorTemperatureStrategy.cpp - test_UPnP.cpp - test_Schedule.cpp test_StateTransaction.cpp test_TimePattern.cpp) diff --git a/test/test_ResourceList.cpp b/test/test_ResourceList.cpp new file mode 100644 index 0000000..1a5b82f --- /dev/null +++ b/test/test_ResourceList.cpp @@ -0,0 +1,258 @@ +/** + \file test_ResourceList.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 "testhelper.h" + +#include "hueplusplus/ResourceList.h" +#include "mocks/mock_HttpHandler.h" + +using namespace hueplusplus; +using namespace testing; + +class TestResource +{ +public: + TestResource(int id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration) : id(id) {} + + void refresh() {} + +public: + int id; +}; +class TestResourceFactory +{ +public: + void refresh() {} +}; +class TestStringResource +{ +public: + TestStringResource(const std::string& id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration) + : id(id) + {} + void refresh() {} + +public: + std::string id; +}; + +class TestCreateType +{ +public: + MOCK_CONST_METHOD0(getRequest, nlohmann::json()); +}; + +TEST(ResourceList, refresh) +{ + auto handler = std::make_shared(); + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + const std::string path = "/resources"; + { + ResourceList list(commands, path, std::chrono::steady_clock::duration::max()); + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(2) + .WillRepeatedly(Return(nlohmann::json::object())); + list.refresh(); + list.refresh(); + Mock::VerifyAndClearExpectations(handler.get()); + } + { + auto baseCache = std::make_shared("", commands, std::chrono::steady_clock::duration::max()); + ResourceList list(baseCache, "resources", std::chrono::steady_clock::duration::max()); + InSequence s; + EXPECT_CALL( + *handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .WillOnce(Return(nlohmann::json {{"resources", nlohmann::json::object()}})); + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .Times(2) + .WillRepeatedly(Return(nlohmann::json::object())); + list.refresh(); + list.refresh(); + list.refresh(); + Mock::VerifyAndClearExpectations(handler.get()); + } +} + +TEST(ResourceList, get) +{ + auto handler = std::make_shared(); + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + const std::string path = "/resources"; + // No factory + { + const int id = 2; + const nlohmann::json response = {{std::to_string(id), {{"resource", "state"}}}}; + ResourceList list(commands, path, std::chrono::steady_clock::duration::max()); + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)); + + TestResource& r = list.get(id); + EXPECT_EQ(id, r.id); + TestResource& r2 = list.get(id); + EXPECT_EQ(id, r2.id); + } + // With factory + { + const int id = 2; + const nlohmann::json state = {{"resource", "state"}}; + const nlohmann::json response = {{std::to_string(id), state}}; + + MockFunction factory; + EXPECT_CALL(factory, Call(id, state)) + .WillOnce(Return(TestResource(id, commands, std::chrono::steady_clock::duration::max()))); + + ResourceList list( + commands, path, std::chrono::steady_clock::duration::max(), factory.AsStdFunction()); + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)); + + TestResource& r = list.get(id); + EXPECT_EQ(id, r.id); + } + // String id without factory + { + const std::string id = "id-2"; + const nlohmann::json response = {{id, {{"resource", "state"}}}}; + ResourceList list(commands, path, std::chrono::steady_clock::duration::max()); + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)); + + TestStringResource& r = list.get(id); + EXPECT_EQ(id, r.id); + TestStringResource& r2 = list.get(id); + EXPECT_EQ(id, r2.id); + } + + { + ResourceList list(commands, path, std::chrono::steady_clock::duration::max()); + + const int id = 2; + const nlohmann::json response = { {std::to_string(id), {{"resource", "state"}}} }; + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)); + EXPECT_THROW(list.get(id), HueException); + } + { + ResourceList list(commands, path, std::chrono::steady_clock::duration::max(), + [](int, const nlohmann::json&) {return TestResourceFactory(); }); + + const int id = 2; + const nlohmann::json response = { {std::to_string(id), {{"resource", "state"}}} }; + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)); + EXPECT_NO_THROW(list.get(id)); + } +} + +TEST(ResourceList, exists) +{ + auto handler = std::make_shared(); + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + const std::string path = "/resources"; + const int id = 2; + const nlohmann::json response = {{std::to_string(id), {{"resource", "state"}}}}; + ResourceList list(commands, path, std::chrono::steady_clock::duration::max()); + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)); + list.refresh(); + EXPECT_TRUE(list.exists(id)); + EXPECT_FALSE(list.exists(4)); +} + +TEST(ResourceList, getAll) +{ + auto handler = std::make_shared(); + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + const std::string path = "/resources"; + + const int id = 2; + const nlohmann::json response = {{std::to_string(id), {{"resource", "state"}}}}; + ResourceList list(commands, path, std::chrono::steady_clock::duration::max()); + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)); + + auto resources = list.getAll(); + EXPECT_THAT(resources, ElementsAre(testing::Field("id", &TestResource::id, Eq(id)))); + + const int id2 = 3; + const nlohmann::json response2 = {{std::to_string(id), {{"r", "s"}}}, {std::to_string(id2), {{"b", "c"}}}}; + + EXPECT_CALL(*handler, + GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .WillOnce(Return(response2)); + list.refresh(); + auto resources2 = list.getAll(); + EXPECT_THAT(resources2, + ElementsAre(testing::Field("id", &TestResource::id, Eq(id)), testing::Field("id", &TestResource::id, Eq(id2)))); +} + +TEST(ResourceList, remove) +{ + auto handler = std::make_shared(); + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + const std::string path = "/resources"; + const int id = 2; + const std::string requestPath = path + "/" + std::to_string(id); + const nlohmann::json response = {{{"success", requestPath + " deleted"}}}; + ResourceList list(commands, path, std::chrono::steady_clock::duration::max()); + EXPECT_CALL(*handler, + DELETEJson( + "/api/" + getBridgeUsername() + requestPath, nlohmann::json::object(), getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)) + .WillOnce(Return(nlohmann::json())); + EXPECT_TRUE(list.remove(id)); + EXPECT_FALSE(list.remove(id)); +} + +TEST(CreateableResourceList, create) +{ + auto handler = std::make_shared(); + HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler); + + const std::string path = "/resources"; + const nlohmann::json response = { {{"success", {{"id", path + "/2"}}}} }; + const nlohmann::json request = { {"name", "bla"} }; + CreateableResourceList list(commands, path, std::chrono::steady_clock::duration::max()); + EXPECT_CALL(*handler, POSTJson( + "/api/" + getBridgeUsername() + path, request, getBridgeIp(), getBridgePort())) + .WillOnce(Return(response)) + .WillOnce(Return(nlohmann::json())); + TestCreateType params; + EXPECT_CALL(params, getRequest()).Times(2).WillRepeatedly(Return(request)); + EXPECT_EQ(2, list.create(params)); + EXPECT_EQ(0, list.create(params)); +} \ No newline at end of file diff --git a/test/test_Schedule.cpp b/test/test_Schedule.cpp index d8795ce..9d8c440 100644 --- a/test/test_Schedule.cpp +++ b/test/test_Schedule.cpp @@ -287,4 +287,69 @@ TEST_F(ScheduleTest, setAutodelete) .WillOnce(Return(response)); expectGetState(id); schedule.setAutodelete(autodelete); -} \ No newline at end of file +} + +TEST(CreateSchedule, setName) +{ + const std::string name = "New schedule"; + const nlohmann::json request = {{"name", name}}; + EXPECT_EQ(request, CreateSchedule().setName(name).getRequest()); +} + +TEST(CreateSchedule, setDescription) +{ + const std::string description = "New schedule description"; + { + const nlohmann::json request = {{"description", description}}; + EXPECT_EQ(request, CreateSchedule().setDescription(description).getRequest()); + } + { + const std::string name = "New schedule name"; + const nlohmann::json request = {{"name", name}, {"description", description}}; + EXPECT_EQ(request, CreateSchedule().setName(name).setDescription(description).getRequest()); + } +} + +TEST(CreateSchedule, setCommand) +{ + const nlohmann::json commandJson = {{"address", "/api/asdf"}, {"method", "PUT"}, {"body", {}}}; + ScheduleCommand command {commandJson}; + const nlohmann::json request = {{"command", commandJson}}; + EXPECT_EQ(request, CreateSchedule().setCommand(command).getRequest()); +} + +TEST(CreateSchedule, setTime) +{ + const time::AbsoluteTime time(std::chrono::system_clock::now()); + const nlohmann::json request = {{"localtime", time.toString()}}; + EXPECT_EQ(request, CreateSchedule().setTime(time::TimePattern(time)).getRequest()); +} + +TEST(CreateSchedule, setStatus) +{ + { + const nlohmann::json request = {{"status", "enabled"}}; + EXPECT_EQ(request, CreateSchedule().setStatus(Schedule::Status::enabled).getRequest()); + } + { + const nlohmann::json request = {{"status", "disabled"}}; + EXPECT_EQ(request, CreateSchedule().setStatus(Schedule::Status::disabled).getRequest()); + } +} + +TEST(CreateSchedule, setAutodelete) +{ + { + const nlohmann::json request = { {"autodelete", true} }; + EXPECT_EQ(request, CreateSchedule().setAutodelete(true).getRequest()); + } +} + + +TEST(CreateSchedule, setRecycle) +{ + { + const nlohmann::json request = {{"recycle", true}}; + EXPECT_EQ(request, CreateSchedule().setRecycle(true).getRequest()); + } +} diff --git a/test/test_TimePattern.cpp b/test/test_TimePattern.cpp index d60e113..62c33f3 100644 --- a/test/test_TimePattern.cpp +++ b/test/test_TimePattern.cpp @@ -20,6 +20,7 @@ **/ #include +#include #include using namespace hueplusplus::time; @@ -334,6 +335,50 @@ TEST(TimePattern, Undefined) TimePattern pattern = TimePattern::parse("none"); EXPECT_EQ(TimePattern::Type::undefined, pattern.getType()); } + EXPECT_THROW(TimePattern::parse("bla"), hueplusplus::HueException); +} + +TEST(TimePattern, CopyConstructor) +{ + { + TimePattern pattern; + TimePattern copy = pattern; + EXPECT_EQ(TimePattern::Type::undefined, copy.getType()); + } + { + const AbsoluteTime abs(system_clock::now()); + const TimePattern pattern(abs); + const TimePattern copy(pattern); + ASSERT_EQ(TimePattern::Type::absolute, copy.getType()); + EXPECT_EQ(abs.getBaseTime(), copy.asAbsolute().getBaseTime()); + } + { + const RecurringTime rec(12h + 30min, Weekdays::monday(), 1h); + const TimePattern pattern(rec); + const TimePattern copy(pattern); + ASSERT_EQ(TimePattern::Type::recurring, copy.getType()); + EXPECT_EQ(rec.getDaytime(), copy.asRecurring().getDaytime()); + EXPECT_EQ(rec.getWeekdays(), copy.asRecurring().getWeekdays()); + EXPECT_EQ(rec.getRandomVariation(), copy.asRecurring().getRandomVariation()); + } + { + const TimeInterval interval(12h + 30min, 13h + 20min, Weekdays::friday()); + const TimePattern pattern(interval); + const TimePattern copy(pattern); + ASSERT_EQ(TimePattern::Type::interval, copy.getType()); + EXPECT_EQ(interval.getStartTime(), copy.asInterval().getStartTime()); + EXPECT_EQ(interval.getEndTime(), copy.asInterval().getEndTime()); + EXPECT_EQ(interval.getWeekdays(), copy.asInterval().getWeekdays()); + } + { + const Timer timer(1h + 30min, 5, 20s); + const TimePattern pattern(timer); + const TimePattern copy(pattern); + ASSERT_EQ(TimePattern::Type::timer, copy.getType()); + EXPECT_EQ(timer.getExpiryTime(), copy.asTimer().getExpiryTime()); + EXPECT_EQ(timer.getRandomVariation(), copy.asTimer().getRandomVariation()); + EXPECT_EQ(timer.getNumberOfExecutions(), copy.asTimer().getNumberOfExecutions()); + } } TEST(TimePattern, Absolute)