Publish LumaOps source

This commit is contained in:
LumaOps release export
2026-09-03 01:18:36 +02:00
commit 7f1c0e5f71
2363 changed files with 501543 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
if(NOT hueplusplus_NO_EXTERNAL_LIBRARIES)
find_package(GTest)
find_package(GMock)
endif()
if(NOT GTest_FOUND OR NOT GMock_FOUND)
# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G ${CMAKE_GENERATOR} .
RESULT_VARIABLE result
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/googletest-download"
)
if(result)
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
endif()
execute_process(COMMAND "${CMAKE_COMMAND}" --build .
RESULT_VARIABLE result
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/googletest-download"
)
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
# Prevent overriding the parent project's compiler/linker
# settings on Windows
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Add googletest directly to our build. This defines
# the gtest and gtest_main targets.
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src EXCLUDE_FROM_ALL
${CMAKE_CURRENT_BINARY_DIR}/googletest-build EXCLUDE_FROM_ALL
)
target_compile_features(gmock PUBLIC cxx_std_14)
target_compile_features(gtest PUBLIC cxx_std_14)
endif()
# define all test sources
set(TEST_SOURCES
test_Action.cpp
test_APICache.cpp
test_BaseDevice.cpp
test_BaseHttpHandler.cpp
test_Bridge.cpp
test_BridgeConfig.cpp
test_SensorImpls.cpp
test_ColorUnits.cpp
test_ExtendedColorHueStrategy.cpp
test_ExtendedColorTemperatureStrategy.cpp
test_Group.cpp
test_HueCommandAPI.cpp
test_Light.cpp
test_LightFactory.cpp
test_Main.cpp
test_NewDeviceList.cpp
test_UPnP.cpp
test_ResourceList.cpp
test_Rule.cpp
test_Scene.cpp
test_Schedule.cpp
test_Sensor.cpp
test_SensorList.cpp
test_SimpleBrightnessStrategy.cpp
test_SimpleColorHueStrategy.cpp
test_SimpleColorTemperatureStrategy.cpp
test_StateTransaction.cpp
test_TimePattern.cpp)
set(HuePlusPlus_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include")
# test executable
add_executable(test_HuePlusPlus ${TEST_SOURCES})
#if(DO_CLANG_TIDY)
# set_target_properties(test_HuePlusPlus PROPERTIES CXX_CLANG_TIDY ${DO_CLANG_TIDY})
#endif()
target_compile_features(test_HuePlusPlus PUBLIC cxx_std_14)
set_property(TARGET test_HuePlusPlus PROPERTY CXX_EXTENSIONS OFF)
target_link_libraries(test_HuePlusPlus PUBLIC hueplusplusstatic)
target_link_libraries(test_HuePlusPlus PUBLIC gtest gmock)
target_include_directories(test_HuePlusPlus PUBLIC ${GTest_INCLUDE_DIRS})
# add custom target to make it simple to run the tests
add_custom_target("unittest"
# Run the executable
COMMAND test_HuePlusPlus
# Depends on test_HuePlusPlus
DEPENDS test_HuePlusPlus
)
# Check for coverage test prerequisites
find_program( GCOV_PATH gcov )
find_program( LCOV_PATH lcov )
mark_as_advanced(GCOV_PATH)
mark_as_advanced(LCOV_PATH)
if(LCOV_PATH AND GCOV_PATH)
# GCov
include(CodeCoverage.cmake)
add_executable(testcov_HuePlusPlus ${TEST_SOURCES} ${hueplusplus_SOURCES})
target_include_directories(testcov_HuePlusPlus PUBLIC "${PROJECT_SOURCE_DIR}/include")
target_compile_features(testcov_HuePlusPlus PUBLIC cxx_std_14)
set_property(TARGET testcov_HuePlusPlus PROPERTY CXX_EXTENSIONS OFF)
target_link_libraries(testcov_HuePlusPlus PRIVATE mbedtls)
target_link_libraries(testcov_HuePlusPlus PUBLIC nlohmann_json::nlohmann_json gtest gmock)
target_include_directories(testcov_HuePlusPlus PUBLIC ${GTest_INCLUDE_DIRS})
# this will be already done by APPEND_COVERAGE_COMPILER_FLAGS()
#set_target_properties(
# testcov_HuePlusPlus PROPERTIES
# COMPILE_FLAGS "-O0 -g -fprofile-arcs -ftest-coverage"
#)
# Normally this would be -lgcov, but on mac only -Lgcov works
#set_target_properties(
# testcov_HuePlusPlus PROPERTIES
# LINK_FLAGS "-O0 -g -Lgcov -fprofile-arcs -ftest-coverage"
#)
# exclude some special files we do not want to profile
set(COVERAGE_EXCLUDES
'/usr/*' # unix
'*/hueplusplus/build/*'
'*/json*'
'*/test/*'
'*/v1/*' # iOS
)
APPEND_COVERAGE_COMPILER_FLAGS()
SETUP_TARGET_FOR_COVERAGE(
NAME "coveragetest"
EXECUTABLE testcov_HuePlusPlus
DEPENDENCIES testcov_HuePlusPlus
)
endif()
+15
View File
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 2.8.2)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG main
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
+234
View File
@@ -0,0 +1,234 @@
# Copyright (c) 2012 - 2017, Lars Bilke
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# CHANGES:
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# 2016-02-03, Lars Bilke
# - Refactored functions to use named parameters
#
# 2017-06-02, Lars Bilke
# - Merged with modified version from github.com/ufz/ogs
#
#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt:
# include(CodeCoverage)
#
# 3. Append necessary compiler flags:
# APPEND_COVERAGE_COMPILER_FLAGS()
#
# 4. If you need to exclude additional directories from the report, specify them
# using the COVERAGE_EXCLUDES variable before calling SETUP_TARGET_FOR_COVERAGE.
# Example:
# set(COVERAGE_EXCLUDES 'dir1/*' 'dir2/*')
#
# 5. Use the functions described below to create a custom make target which
# runs your test executable and produces a code coverage report.
#
# 6. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
include(CMakeParseArguments)
# Check prereqs
find_program( GCOV_PATH gcov )
find_program( LCOV_PATH lcov )
find_program( GENHTML_PATH genhtml )
find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
find_program( SIMPLE_PYTHON_EXECUTABLE python )
if(NOT GCOV_PATH)
message(FATAL_ERROR "gcov not found! Aborting...")
endif() # NOT GCOV_PATH
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
endif()
elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
endif()
set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage"
CACHE INTERNAL "")
set(CMAKE_CXX_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
set(CMAKE_C_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
mark_as_advanced(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
link_libraries(gcov)
else()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
endif()
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# SETUP_TARGET_FOR_COVERAGE(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# )
function(SETUP_TARGET_FOR_COVERAGE)
set(options NONE)
set(oneValueArgs NAME)
set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif() # NOT LCOV_PATH
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif() # NOT GENHTML_PATH
# Setup target
add_custom_target(${Coverage_NAME}
# Cleanup lcov
COMMAND ${LCOV_PATH} --directory . --zerocounters
# Run tests
COMMAND ${Coverage_EXECUTABLE}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info --ignore-errors mismatch
COMMAND ${LCOV_PATH} --remove ${Coverage_NAME}.info ${COVERAGE_EXCLUDES} --output-file ${Coverage_NAME}.info.cleaned --ignore-errors unused
COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} ${Coverage_NAME}.info.cleaned
COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.info ${Coverage_NAME}.info.cleaned
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # SETUP_TARGET_FOR_COVERAGE
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# SETUP_TARGET_FOR_COVERAGE_COBERTURA(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# )
function(SETUP_TARGET_FOR_COVERAGE_COBERTURA)
set(options NONE)
set(oneValueArgs NAME)
set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT SIMPLE_PYTHON_EXECUTABLE)
message(FATAL_ERROR "python not found! Aborting...")
endif() # NOT SIMPLE_PYTHON_EXECUTABLE
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Combine excludes to several -e arguments
set(COBERTURA_EXCLUDES "")
foreach(EXCLUDE ${COVERAGE_EXCLUDES})
set(COBERTURA_EXCLUDES "-e ${EXCLUDE} ${COBERTURA_EXCLUDES}")
endforeach()
add_custom_target(${Coverage_NAME}
# Run tests
${Coverage_EXECUTABLE}
# Running gcovr
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} ${COBERTURA_EXCLUDES}
-o ${Coverage_NAME}.xml
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
COMMENT "Running gcovr to produce Cobertura code coverage report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
)
endfunction() # SETUP_TARGET_FOR_COVERAGE_COBERTURA
function(APPEND_COVERAGE_COMPILER_FLAGS)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
endfunction() # APPEND_COVERAGE_COMPILER_FLAGS
+63
View File
@@ -0,0 +1,63 @@
/**
\file TestTransaction.h
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 <http://www.gnu.org/licenses/>.
**/
#ifndef INCLUDE_HUEPLUSPLUS_TEST_TRANSACTION_H
#define INCLUDE_HUEPLUSPLUS_TEST_TRANSACTION_H
#include <hueplusplus/StateTransaction.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "mocks/mock_HttpHandler.h"
class TestTransaction : public hueplusplus::StateTransaction
{
public:
TestTransaction(hueplusplus::StateTransaction& t) : hueplusplus::StateTransaction(std::move(t)) {}
nlohmann::json getRequest() const { return request; }
nlohmann::json getResponse() const
{
nlohmann::json response;
const std::string pathPrefix = path + '/';
for (auto it = request.begin(); it != request.end(); ++it)
{
response.push_back({{"success", {{pathPrefix + it.key(), it.value()}}}});
}
return response;
}
std::string getPath() const { return path; }
decltype(auto) expectPut(const std::shared_ptr<MockHttpHandler>& handler) const
{
return EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), getBridgePort()));
}
decltype(auto) expectSuccessfulPut(const std::shared_ptr<MockHttpHandler>& handler,
const testing::Cardinality& cardinality = testing::AtLeast(1)) const
{
return expectPut(handler).Times(cardinality).WillRepeatedly(testing::Return(getResponse()));
}
};
#endif
@@ -0,0 +1,44 @@
/**
\file mock_BaseHttpHandler.h
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 <http://www.gnu.org/licenses/>.
**/
#ifndef _MOCK_BASE_HTTPHANDLER_H
#define _MOCK_BASE_HTTPHANDLER_H
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include "hueplusplus/BaseHttpHandler.h"
#include <nlohmann/json.hpp>
//! Mock Class
class MockBaseHttpHandler : public hueplusplus::BaseHttpHandler
{
public:
MOCK_CONST_METHOD3(send, std::string(const std::string& msg, const std::string& adr, int port));
MOCK_CONST_METHOD4(
sendMulticast, std::vector<std::string>(const std::string& msg, const std::string& adr, int port, std::chrono::steady_clock::duration timeout));
};
#endif
@@ -0,0 +1,79 @@
/**
\file mock_HttpHandler.h
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 <http://www.gnu.org/licenses/>.
**/
#ifndef _MOCK_HTTPHANDLER_H
#define _MOCK_HTTPHANDLER_H
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include "hueplusplus/IHttpHandler.h"
#include <nlohmann/json.hpp>
//! Mock Class
class MockHttpHandler : public hueplusplus::IHttpHandler
{
public:
MOCK_CONST_METHOD3(send, std::string(const std::string& msg, const std::string& adr, int port));
MOCK_CONST_METHOD3(sendGetHTTPBody, std::string(const std::string& msg, const std::string& adr, int port));
MOCK_CONST_METHOD4(sendMulticast,
std::vector<std::string>(
const std::string& msg, const std::string& adr, int port, std::chrono::steady_clock::duration timeout));
MOCK_CONST_METHOD6(sendHTTPRequest,
std::string(const std::string& method, const std::string& uri, const std::string& content_type,
const std::string& body, const std::string& adr, int port));
MOCK_CONST_METHOD5(GETString,
std::string(const std::string& uri, const std::string& content_type, const std::string& body,
const std::string& adr, int port));
MOCK_CONST_METHOD5(POSTString,
std::string(const std::string& uri, const std::string& content_type, const std::string& body,
const std::string& adr, int port));
MOCK_CONST_METHOD5(PUTString,
std::string(const std::string& uri, const std::string& content_type, const std::string& body,
const std::string& adr, int port));
MOCK_CONST_METHOD5(DELETEString,
std::string(const std::string& uri, const std::string& content_type, const std::string& body,
const std::string& adr, int port));
MOCK_CONST_METHOD4(
GETJson, nlohmann::json(const std::string& uri, const nlohmann::json& body, const std::string& adr, int port));
MOCK_CONST_METHOD4(
POSTJson, nlohmann::json(const std::string& uri, const nlohmann::json& body, const std::string& adr, int port));
MOCK_CONST_METHOD4(
PUTJson, nlohmann::json(const std::string& uri, const nlohmann::json& body, const std::string& adr, int port));
MOCK_CONST_METHOD4(DELETEJson,
nlohmann::json(const std::string& uri, const nlohmann::json& body, const std::string& adr, int port));
};
#endif
+130
View File
@@ -0,0 +1,130 @@
/**
\file mock_Light.h
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 <http://www.gnu.org/licenses/>.
**/
#ifndef _MOCK_HUE_LIGHT_H
#define _MOCK_HUE_LIGHT_H
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include "../testhelper.h"
#include "hueplusplus/Light.h"
#include <nlohmann/json.hpp>
//! Mock Class
class MockLight : public hueplusplus::Light
{
public:
MockLight(std::shared_ptr<const hueplusplus::IHttpHandler> handler)
: Light(1, hueplusplus::HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler), nullptr,
nullptr, nullptr, std::chrono::steady_clock::duration::max(), nullptr)
{
// Set refresh duration to max, so random refreshes do not hinder the test setups
}
nlohmann::json& getState() { return state.getValue(); }
MOCK_METHOD1(on, bool(uint8_t transition));
MOCK_METHOD1(off, bool(uint8_t transition));
MOCK_METHOD0(isOn, bool());
MOCK_CONST_METHOD0(isOn, bool());
MOCK_CONST_METHOD0(getId, int());
MOCK_CONST_METHOD0(getType, std::string());
MOCK_METHOD0(getName, std::string());
MOCK_CONST_METHOD0(getName, std::string());
MOCK_CONST_METHOD0(getModelId, std::string());
MOCK_CONST_METHOD0(getUId, std::string());
MOCK_CONST_METHOD0(getManufacturername, std::string());
MOCK_CONST_METHOD0(getLuminaireUId, std::string());
MOCK_METHOD0(getSwVersion, std::string());
MOCK_CONST_METHOD0(getSwVersion, std::string());
MOCK_METHOD1(setName, bool(const std::string& name));
MOCK_CONST_METHOD0(getColorType, hueplusplus::ColorType());
MOCK_CONST_METHOD0(hasBrightnessControl, bool());
MOCK_CONST_METHOD0(hasTemperatureControl, bool());
MOCK_CONST_METHOD0(hasColorControl, bool());
MOCK_METHOD2(setBrightness, bool(unsigned int bri, uint8_t transition));
MOCK_CONST_METHOD0(getBrightness, unsigned int());
MOCK_METHOD0(getBrightness, unsigned int());
MOCK_METHOD2(setColorTemperature, bool(unsigned int mired, uint8_t transition));
MOCK_CONST_METHOD0(getColorTemperature, unsigned int());
MOCK_METHOD0(getColorTemperature, unsigned int());
MOCK_METHOD2(setColorHue, bool(uint16_t hue, uint8_t transition));
MOCK_METHOD2(setColorSaturation, bool(uint8_t sat, uint8_t transition));
MOCK_METHOD2(setColorHueSaturation, bool(const hueplusplus::HueSaturation& hueSat, uint8_t transition));
MOCK_CONST_METHOD0(getColorHueSaturation, hueplusplus::HueSaturation());
MOCK_METHOD0(getColorHueSaturation, hueplusplus::HueSaturation());
MOCK_METHOD2(setColorXY, bool(const hueplusplus::XYBrightness& xy, uint8_t transition));
MOCK_CONST_METHOD0(getColorXY, hueplusplus::XYBrightness());
MOCK_METHOD0(getColorXY, hueplusplus::XYBrightness());
MOCK_METHOD2(setColorRGB, bool(const hueplusplus::RGB& rgb, uint8_t transition));
MOCK_METHOD0(alert, bool());
MOCK_METHOD1(alertTemperature, bool(unsigned int mired));
MOCK_METHOD1(alertHueSaturation, bool(const hueplusplus::HueSaturation& hueSat));
MOCK_METHOD1(alertXY, bool(const hueplusplus::XYBrightness& xy));
MOCK_METHOD1(setColorLoop, bool(bool on));
MOCK_METHOD3(sendPutRequest,
nlohmann::json(const std::string& subPath, const nlohmann::json& request, hueplusplus::FileInfo fileInfo));
};
#endif
+323
View File
@@ -0,0 +1,323 @@
/**
\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 <http://www.gnu.org/licenses/>.
**/
#include <gtest/gtest.h>
#include "testhelper.h"
#include "hueplusplus/APICache.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
TEST(APICache, getRefreshDuration)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
{
std::chrono::steady_clock::duration refresh = std::chrono::seconds(20);
APICache cache("", commands, refresh, nullptr);
EXPECT_EQ(refresh, cache.getRefreshDuration());
}
{
std::chrono::steady_clock::duration refresh = std::chrono::seconds(0);
APICache cache("", commands, refresh, nullptr);
EXPECT_EQ(refresh, cache.getRefreshDuration());
}
{
std::chrono::steady_clock::duration refresh = c_refreshNever;
APICache cache("", commands, refresh, nullptr);
EXPECT_EQ(refresh, cache.getRefreshDuration());
}
// With base cache, still independent duration
{
auto duration = std::chrono::seconds(5);
auto baseCache = std::make_shared<APICache>("/test", commands, std::chrono::seconds(0), nullptr);
APICache c(baseCache, "api", duration);
EXPECT_EQ(duration, c.getRefreshDuration());
}
}
TEST(APICache, refresh)
{
using namespace ::testing;
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
{
std::string path = "/test/abc";
APICache cache(path, commands, std::chrono::seconds(10), nullptr);
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), nullptr);
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, refreshBase)
{
using namespace ::testing;
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string basePath = "/test";
const std::string childPath = "/test/abc";
// Base cache with max duration
{
auto baseCache
= std::make_shared<APICache>(basePath, commands, c_refreshNever, nullptr);
APICache cache(baseCache, "abc", std::chrono::seconds(0));
// First call refreshes base, second call only child
InSequence s;
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + basePath, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json::object()));
EXPECT_CALL(*handler,
GETJson(
"/api/" + getBridgeUsername() + childPath, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json::object()));
cache.refresh();
cache.refresh();
Mock::VerifyAndClearExpectations(handler.get());
}
// Base cache with min duration
{
auto baseCache = std::make_shared<APICache>(basePath, commands, std::chrono::seconds(0), nullptr);
APICache cache(baseCache, "abc", std::chrono::seconds(0));
// Both calls refresh base
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + basePath, 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<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
// Always refresh
{
std::string path = "/test/abc";
APICache cache(path, commands, std::chrono::seconds(0), nullptr);
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, c_refreshNever, nullptr);
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());
}
// Only refresh once
{
std::string path = "/test/abc";
APICache cache(path, commands, std::chrono::seconds(0), nullptr);
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, Const(cache).getValue());
Mock::VerifyAndClearExpectations(handler.get());
}
// No refresh with const throws exception
{
std::string path = "/test/abc";
const APICache cache(path, commands, c_refreshNever, nullptr);
nlohmann::json value = {{"a", "b"}};
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(0);
EXPECT_THROW(cache.getValue(), HueException);
Mock::VerifyAndClearExpectations(handler.get());
}
// No refresh with initial value
{
std::string path = "/test/abc";
nlohmann::json value = {{"a", "b"}};
APICache cache(path, commands, c_refreshNever, value);
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(0);
EXPECT_EQ(value, cache.getValue());
EXPECT_EQ(value, cache.getValue());
Mock::VerifyAndClearExpectations(handler.get());
}
// No refresh with const but initial value
{
std::string path = "/test/abc";
nlohmann::json value = {{"a", "b"}};
const APICache cache(path, commands, c_refreshNever, value);
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(0);
EXPECT_EQ(value, cache.getValue());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(APICache, getValueBase)
{
using namespace ::testing;
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string basePath = "/test";
const std::string childPath = "/test/abc";
const nlohmann::json childValue = {{"test", "value"}};
const nlohmann::json baseValue = {{"abc", childValue}};
// Always refresh base
{
auto baseCache = std::make_shared<APICache>(basePath, commands, std::chrono::seconds(0), nullptr);
APICache cache(baseCache, "abc", std::chrono::seconds(0));
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + basePath, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(2)
.WillRepeatedly(Return(baseValue));
EXPECT_EQ(childValue, cache.getValue());
EXPECT_EQ(childValue, cache.getValue());
Mock::VerifyAndClearExpectations(handler.get());
}
// Child duration > base duration
{
auto baseCache = std::make_shared<APICache>(basePath, commands, std::chrono::seconds(0), nullptr);
APICache cache(baseCache, "abc", c_refreshNever);
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + basePath, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1)
.WillRepeatedly(Return(baseValue));
EXPECT_EQ(childValue, cache.getValue());
EXPECT_EQ(childValue, cache.getValue());
Mock::VerifyAndClearExpectations(handler.get());
}
// Child duration < base duration
{
auto baseCache
= std::make_shared<APICache>(basePath, commands, c_refreshNever, nullptr);
APICache cache(baseCache, "abc", std::chrono::seconds(0));
const nlohmann::json updateChildValue = {{"test", "updated"}};
InSequence s;
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + basePath, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1)
.WillRepeatedly(Return(baseValue));
EXPECT_CALL(*handler,
GETJson(
"/api/" + getBridgeUsername() + childPath, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1)
.WillRepeatedly(Return(updateChildValue));
EXPECT_EQ(childValue, cache.getValue());
EXPECT_EQ(updateChildValue, cache.getValue());
// Base cache is updated
EXPECT_EQ(updateChildValue, baseCache->getValue()["abc"]);
Mock::VerifyAndClearExpectations(handler.get());
}
// Only refresh once
{
auto baseCache
= std::make_shared<APICache>(basePath, commands, c_refreshNever, nullptr);
APICache cache(baseCache, "abc", c_refreshNever);
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + basePath, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1)
.WillRepeatedly(Return(baseValue));
EXPECT_EQ(childValue, cache.getValue());
EXPECT_EQ(childValue, cache.getValue());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(APICache, setRefreshDuration)
{
using namespace ::testing;
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
{
std::string path = "/test/abc";
APICache cache(path, commands, std::chrono::seconds(0), nullptr);
nlohmann::json value = { {"a", "b"} };
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1)
.WillOnce(Return(value));
EXPECT_EQ(value, cache.getValue());
cache.setRefreshDuration(c_refreshNever);
EXPECT_EQ(c_refreshNever, cache.getRefreshDuration());
// Next getValue does not refresh
EXPECT_EQ(value, cache.getValue());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(APICache, getRequestPath)
{
using namespace ::testing;
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
// No base cache
{
std::string path = "/test/api";
APICache c(path, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(path, c.getRequestPath());
}
// With base cache
{
auto baseCache = std::make_shared<APICache>("/test", commands, std::chrono::seconds(0), nullptr);
APICache c(baseCache, "api", std::chrono::seconds(0));
EXPECT_EQ("/test/api", c.getRequestPath());
}
}
+70
View File
@@ -0,0 +1,70 @@
/**
\file test_Action.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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/Action.h>
#include <hueplusplus/HueException.h>
#include <gtest/gtest.h>
using namespace testing;
using hueplusplus::Action;
TEST(Action, Constructor)
{
const std::string address = "/api/abcd/test";
const nlohmann::json body = {{"test", "value"}};
const nlohmann::json json = {{"address", address}, {"method", "PUT"}, {"body", body}};
Action command(json);
EXPECT_EQ(address, command.getAddress());
EXPECT_EQ(Action::Method::put, command.getMethod());
EXPECT_EQ(body, command.getBody());
EXPECT_EQ(json, command.toJson());
}
TEST(Action, getMethod)
{
nlohmann::json json = {{"address", "/test"}, {"method", "PUT"}, {"body", {}}};
EXPECT_EQ(Action::Method::put, Action(json).getMethod());
json["method"] = "POST";
EXPECT_EQ(Action::Method::post, Action(json).getMethod());
json["method"] = "DELETE";
EXPECT_EQ(Action::Method::deleteMethod, Action(json).getMethod());
json["method"] = "unknown";
EXPECT_THROW(Action(json).getMethod(), hueplusplus::HueException);
}
TEST(Action, parseMethod)
{
using M = Action::Method;
EXPECT_EQ(M::put, Action::parseMethod("PUT"));
EXPECT_EQ(M::post, Action::parseMethod("POST"));
EXPECT_EQ(M::deleteMethod, Action::parseMethod("DELETE"));
EXPECT_THROW(Action::parseMethod("unknown"), hueplusplus::HueException);
}
TEST(Action, methodToString)
{
using M = Action::Method;
EXPECT_EQ("POST", Action::methodToString(M::post));
EXPECT_EQ("PUT", Action::methodToString(M::put));
EXPECT_EQ("DELETE", Action::methodToString(M::deleteMethod));
}
+125
View File
@@ -0,0 +1,125 @@
/**
\file test_BaseDevice.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 <http://www.gnu.org/licenses/>.
**/
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "hueplusplus/BaseDevice.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
using namespace testing;
class TestDevice : public BaseDevice
{
public:
TestDevice(int id, const HueCommandAPI& commands, const std::string& path,
std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState)
: BaseDevice(id, commands, path, refreshDuration, currentState)
{ }
};
class BaseDeviceTest : public Test
{
protected:
std::shared_ptr<MockHttpHandler> handler;
HueCommandAPI commands;
nlohmann::json state;
std::string path = "/test/";
protected:
BaseDeviceTest()
: handler(std::make_shared<MockHttpHandler>()),
commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler),
state({{"type", "testType"}, {"name", "Test name"}, {"swversion", "1.2.3.4"}, {"modelid", "TEST"},
{"manufacturername", "testManuf"}, {"uniqueid", "00:00:00:00:00:00:00:00-00"},
{"productname", "Test type"}})
{ }
TestDevice getDevice(int id)
{
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + path + std::to_string(id), _, getBridgeIp(), getBridgePort()))
.WillOnce(Return(state));
return TestDevice(id, commands, path, std::chrono::steady_clock::duration::max(), nullptr);
}
};
TEST_F(BaseDeviceTest, getId)
{
const int id = 1;
EXPECT_EQ(id, getDevice(id).getId());
}
TEST_F(BaseDeviceTest, getName)
{
EXPECT_EQ("Test name", getDevice(1).getName());
}
TEST_F(BaseDeviceTest, getType)
{
EXPECT_EQ("testType", getDevice(1).getType());
}
TEST_F(BaseDeviceTest, getModelId)
{
EXPECT_EQ("TEST", getDevice(1).getModelId());
}
TEST_F(BaseDeviceTest, getUId)
{
EXPECT_EQ("00:00:00:00:00:00:00:00-00", getDevice(1).getUId());
state.erase("uniqueid");
EXPECT_EQ("", getDevice(1).getUId());
}
TEST_F(BaseDeviceTest, getManufacturername)
{
EXPECT_EQ("testManuf", getDevice(1).getManufacturername());
state.erase("manufacturername");
EXPECT_EQ("", getDevice(1).getManufacturername());
}
TEST_F(BaseDeviceTest, getProductname)
{
EXPECT_EQ("Test type", getDevice(1).getProductname());
state.erase("productname");
EXPECT_EQ("", getDevice(1).getProductname());
}
TEST_F(BaseDeviceTest, getSwVersion)
{
EXPECT_EQ("1.2.3.4", getDevice(1).getSwVersion());
}
TEST_F(BaseDeviceTest, setName)
{
const std::string name = "asdbsdakfl";
const nlohmann::json request = {{"name", name}};
const nlohmann::json response = { {{"success", {{"/lights/1/name", name}}}} };
TestDevice device = getDevice(1);
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + path + "1/name", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
EXPECT_TRUE(device.setName(name));
}
@@ -0,0 +1,239 @@
/**
\file test_BaseHttpHandler.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 <http://www.gnu.org/licenses/>.
**/
#include <memory>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "hueplusplus/HueException.h"
#include <nlohmann/json.hpp>
#include "mocks/mock_BaseHttpHandler.h"
using namespace hueplusplus;
TEST(BaseHttpHandler, sendGetHTTPBody)
{
using namespace ::testing;
MockBaseHttpHandler handler;
EXPECT_CALL(handler, send("testmsg", "192.168.2.1", 90))
.Times(AtLeast(2))
.WillOnce(Return(""))
.WillRepeatedly(Return("\r\n\r\ntestreply"));
EXPECT_THROW(handler.sendGetHTTPBody("testmsg", "192.168.2.1", 90), HueException);
EXPECT_EQ("testreply", handler.sendGetHTTPBody("testmsg", "192.168.2.1", 90));
}
TEST(BaseHttpHandler, sendHTTPRequest)
{
using namespace ::testing;
MockBaseHttpHandler handler;
EXPECT_CALL(handler,
send("GET UrI HTTP/1.0\r\nContent-Type: "
"text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n",
"192.168.2.1", 90))
.Times(AtLeast(2))
.WillOnce(Return(""))
.WillRepeatedly(Return("\r\n\r\ntestreply"));
EXPECT_THROW(handler.sendHTTPRequest("GET", "UrI", "text/html", "body", "192.168.2.1", 90), HueException);
EXPECT_EQ("testreply", handler.sendHTTPRequest("GET", "UrI", "text/html", "body", "192.168.2.1", 90));
}
TEST(BaseHttpHandler, GETString)
{
using namespace ::testing;
MockBaseHttpHandler handler;
EXPECT_CALL(handler,
send("GET UrI HTTP/1.0\r\nContent-Type: "
"text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n",
"192.168.2.1", 90))
.Times(AtLeast(2))
.WillOnce(Return(""))
.WillRepeatedly(Return("\r\n\r\ntestreply"));
EXPECT_THROW(handler.GETString("UrI", "text/html", "body", "192.168.2.1", 90), HueException);
EXPECT_EQ("testreply", handler.GETString("UrI", "text/html", "body", "192.168.2.1", 90));
}
TEST(BaseHttpHandler, POSTString)
{
using namespace ::testing;
MockBaseHttpHandler handler;
EXPECT_CALL(handler,
send("POST UrI HTTP/1.0\r\nContent-Type: "
"text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n",
"192.168.2.1", 90))
.Times(AtLeast(2))
.WillOnce(Return(""))
.WillRepeatedly(Return("\r\n\r\ntestreply"));
EXPECT_THROW(handler.POSTString("UrI", "text/html", "body", "192.168.2.1", 90), HueException);
EXPECT_EQ("testreply", handler.POSTString("UrI", "text/html", "body", "192.168.2.1", 90));
}
TEST(BaseHttpHandler, PUTString)
{
using namespace ::testing;
MockBaseHttpHandler handler;
EXPECT_CALL(handler,
send("PUT UrI HTTP/1.0\r\nContent-Type: "
"text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n",
"192.168.2.1", 90))
.Times(AtLeast(2))
.WillOnce(Return(""))
.WillRepeatedly(Return("\r\n\r\ntestreply"));
EXPECT_THROW(handler.PUTString("UrI", "text/html", "body", "192.168.2.1", 90), HueException);
EXPECT_EQ("testreply", handler.PUTString("UrI", "text/html", "body", "192.168.2.1", 90));
}
TEST(BaseHttpHandler, DELETEString)
{
using namespace ::testing;
MockBaseHttpHandler handler;
EXPECT_CALL(handler,
send("DELETE UrI HTTP/1.0\r\nContent-Type: "
"text/html\r\nContent-Length: 4\r\n\r\nbody\r\n\r\n",
"192.168.2.1", 90))
.Times(AtLeast(2))
.WillOnce(Return(""))
.WillRepeatedly(Return("\r\n\r\ntestreply"));
EXPECT_THROW(handler.DELETEString("UrI", "text/html", "body", "192.168.2.1", 90), HueException);
EXPECT_EQ("testreply", handler.DELETEString("UrI", "text/html", "body", "192.168.2.1", 90));
}
TEST(BaseHttpHandler, GETJson)
{
using namespace ::testing;
MockBaseHttpHandler handler;
nlohmann::json testval;
testval["test"] = 100;
std::string expected_call = "GET UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: ";
expected_call.append(std::to_string(testval.dump().size()));
expected_call.append("\r\n\r\n");
expected_call.append(testval.dump());
expected_call.append("\r\n\r\n");
EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90))
.Times(AtLeast(2))
.WillOnce(Return(""))
.WillOnce(Return("\r\n\r\n"))
.WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}"));
nlohmann::json expected;
expected["test"] = "whatever";
EXPECT_THROW(handler.GETJson("UrI", testval, "192.168.2.1", 90), HueException);
EXPECT_THROW(handler.GETJson("UrI", testval, "192.168.2.1", 90), nlohmann::json::parse_error);
EXPECT_EQ(expected, handler.GETJson("UrI", testval, "192.168.2.1", 90));
}
TEST(BaseHttpHandler, POSTJson)
{
using namespace ::testing;
MockBaseHttpHandler handler;
nlohmann::json testval;
testval["test"] = 100;
std::string expected_call = "POST UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: ";
expected_call.append(std::to_string(testval.dump().size()));
expected_call.append("\r\n\r\n");
expected_call.append(testval.dump());
expected_call.append("\r\n\r\n");
EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90))
.Times(AtLeast(2))
.WillOnce(Return(""))
.WillOnce(Return("\r\n\r\n"))
.WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}"));
nlohmann::json expected;
expected["test"] = "whatever";
EXPECT_THROW(handler.POSTJson("UrI", testval, "192.168.2.1", 90), HueException);
EXPECT_THROW(handler.POSTJson("UrI", testval, "192.168.2.1", 90), nlohmann::json::parse_error);
EXPECT_EQ(expected, handler.POSTJson("UrI", testval, "192.168.2.1", 90));
}
TEST(BaseHttpHandler, PUTJson)
{
using namespace ::testing;
MockBaseHttpHandler handler;
nlohmann::json testval;
testval["test"] = 100;
std::string expected_call = "PUT UrI HTTP/1.0\r\nContent-Type: application/json\r\nContent-Length: ";
expected_call.append(std::to_string(testval.dump().size()));
expected_call.append("\r\n\r\n");
expected_call.append(testval.dump());
expected_call.append("\r\n\r\n");
EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90))
.Times(AtLeast(2))
.WillOnce(Return(""))
.WillOnce(Return("\r\n\r\n"))
.WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}"));
nlohmann::json expected;
expected["test"] = "whatever";
EXPECT_THROW(handler.PUTJson("UrI", testval, "192.168.2.1", 90), HueException);
EXPECT_THROW(handler.PUTJson("UrI", testval, "192.168.2.1", 90), nlohmann::json::parse_error);
EXPECT_EQ(expected, handler.PUTJson("UrI", testval, "192.168.2.1", 90));
}
TEST(BaseHttpHandler, DELETEJson)
{
using namespace ::testing;
MockBaseHttpHandler handler;
nlohmann::json testval;
testval["test"] = 100;
std::string expected_call = "DELETE UrI HTTP/1.0\r\nContent-Type: "
"application/json\r\nContent-Length: ";
expected_call.append(std::to_string(testval.dump().size()));
expected_call.append("\r\n\r\n");
expected_call.append(testval.dump());
expected_call.append("\r\n\r\n");
EXPECT_CALL(handler, send(expected_call, "192.168.2.1", 90))
.Times(AtLeast(2))
.WillOnce(Return(""))
.WillOnce(Return("\r\n\r\n"))
.WillRepeatedly(Return("\r\n\r\n{\"test\" : \"whatever\"}"));
nlohmann::json expected;
expected["test"] = "whatever";
EXPECT_THROW(handler.DELETEJson("UrI", testval, "192.168.2.1", 90), HueException);
EXPECT_THROW(handler.DELETEJson("UrI", testval, "192.168.2.1", 90), nlohmann::json::parse_error);
EXPECT_EQ(expected, handler.DELETEJson("UrI", testval, "192.168.2.1", 90));
}
+625
View File
@@ -0,0 +1,625 @@
/**
\file test_Bridge.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 <http://www.gnu.org/licenses/>.
**/
#include <memory>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "hueplusplus/Bridge.h"
#include "hueplusplus/LibConfig.h"
#include <nlohmann/json.hpp>
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
class BridgeFinderTest : public ::testing::Test
{
protected:
std::shared_ptr<MockHttpHandler> handler;
protected:
BridgeFinderTest() : handler(std::make_shared<MockHttpHandler>())
{
using namespace ::testing;
EXPECT_CALL(*handler,
sendMulticast("M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: "
"\"ssdp:discover\"\r\nMX: 5\r\nST: ssdp:all\r\n\r\n",
"239.255.255.250", 1900, Config::instance().getUPnPTimeout()))
.Times(AtLeast(1))
.WillRepeatedly(Return(getMulticastReply()));
EXPECT_CALL(*handler, GETString("/description.xml", "application/xml", "", "192.168.2.1", getBridgePort()))
.Times(0);
EXPECT_CALL(*handler, GETString("/description.xml", "application/xml", "", getBridgeIp(), getBridgePort()))
.Times(AtLeast(1))
.WillRepeatedly(Return(getBridgeXml()));
}
~BridgeFinderTest() {};
};
TEST_F(BridgeFinderTest, findBridges)
{
BridgeFinder finder(handler);
std::vector<BridgeFinder::BridgeIdentification> bridges = finder.findBridges();
BridgeFinder::BridgeIdentification bridge_to_comp;
bridge_to_comp.ip = getBridgeIp();
bridge_to_comp.port = getBridgePort();
bridge_to_comp.mac = getBridgeMac();
EXPECT_EQ(bridges.size(), 1) << "BridgeFinder found more than one Bridge";
EXPECT_EQ(bridges[0].ip, bridge_to_comp.ip) << "BridgeIdentification ip does not match";
EXPECT_EQ(bridges[0].port, bridge_to_comp.port) << "BridgeIdentification port does not match";
EXPECT_EQ(bridges[0].mac, bridge_to_comp.mac) << "BridgeIdentification mac does not match";
// Test invalid description
EXPECT_CALL(*handler, GETString("/description.xml", "application/xml", "", getBridgeIp(), getBridgePort()))
.Times(1)
.WillOnce(::testing::Return("invalid stuff"));
bridges = finder.findBridges();
EXPECT_TRUE(bridges.empty());
}
TEST_F(BridgeFinderTest, getBridge)
{
using namespace ::testing;
nlohmann::json request {{"devicetype", "HuePlusPlus#User"}, {"generateclientkey", true}};
nlohmann::json errorResponse
= {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}};
EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))
.Times(AtLeast(1))
.WillRepeatedly(Return(errorResponse));
BridgeFinder finder(handler);
std::vector<BridgeFinder::BridgeIdentification> bridges = finder.findBridges();
ASSERT_THROW(finder.getBridge(bridges[0]), HueException);
nlohmann::json successResponse = {{{"success", {{"username", getBridgeUsername()}}}}};
EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))
.Times(1)
.WillOnce(Return(successResponse));
finder = BridgeFinder(handler);
bridges = finder.findBridges();
Bridge test_bridge = finder.getBridge(bridges[0]);
EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching";
EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching";
EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching";
Mock::VerifyAndClearExpectations(handler.get());
}
TEST_F(BridgeFinderTest, addUsername)
{
BridgeFinder finder(handler);
std::vector<BridgeFinder::BridgeIdentification> bridges = finder.findBridges();
finder.addUsername(bridges[0].mac, getBridgeUsername());
Bridge test_bridge = finder.getBridge(bridges[0]);
EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching";
EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching";
EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching";
}
TEST_F(BridgeFinderTest, getAllUsernames)
{
BridgeFinder finder(handler);
std::vector<BridgeFinder::BridgeIdentification> bridges = finder.findBridges();
finder.addUsername(bridges[0].mac, getBridgeUsername());
std::map<std::string, std::string> users = finder.getAllUsernames();
EXPECT_EQ(users[getBridgeMac()], getBridgeUsername()) << "Username of MAC:" << getBridgeMac() << "not matching";
}
TEST(Bridge, Constructor)
{
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching";
EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching";
EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching";
}
TEST(Bridge, requestUsername)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
nlohmann::json request {{"devicetype", "HuePlusPlus#User"}, {"generateclientkey", true}};
{
nlohmann::json errorResponse
= {{{"error", {{"type", 101}, {"address", ""}, {"description", "link button not pressed"}}}}};
EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))
.Times(AtLeast(1))
.WillRepeatedly(Return(errorResponse));
Bridge test_bridge(getBridgeIp(), getBridgePort(), "", handler);
std::string username = test_bridge.requestUsername();
EXPECT_EQ(username, "") << "Returned username not matching";
EXPECT_EQ(test_bridge.getUsername(), "") << "Bridge username not matching";
}
{
// Other error code causes exception
int otherError = 1;
nlohmann::json exceptionResponse
= {{{"error", {{"type", otherError}, {"address", ""}, {"description", "some error"}}}}};
Bridge testBridge(getBridgeIp(), getBridgePort(), "", handler);
EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(exceptionResponse));
try
{
testBridge.requestUsername();
FAIL() << "requestUsername did not throw";
}
catch (const HueAPIResponseException& e)
{
EXPECT_EQ(e.GetErrorNumber(), otherError);
}
catch (const std::exception& e)
{
FAIL() << "wrong exception: " << e.what();
}
}
{
nlohmann::json successResponse = {{{"success", {{"username", getBridgeUsername()}}}}};
EXPECT_CALL(*handler, POSTJson("/api", request, getBridgeIp(), getBridgePort()))
.Times(1)
.WillRepeatedly(Return(successResponse));
Bridge test_bridge(getBridgeIp(), getBridgePort(), "", handler);
nlohmann::json hue_bridge_state {{"lights", {}}};
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1)
.WillOnce(Return(hue_bridge_state));
std::string username = test_bridge.requestUsername();
EXPECT_EQ(username, test_bridge.getUsername()) << "Returned username not matching";
EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching";
EXPECT_EQ(test_bridge.getUsername(), getBridgeUsername()) << "Bridge username not matching";
}
}
TEST(Bridge, setIP)
{
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
Bridge test_bridge(getBridgeIp(), getBridgePort(), "", handler);
EXPECT_EQ(test_bridge.getBridgeIP(), getBridgeIp()) << "Bridge IP not matching after initialization";
test_bridge.setIP("192.168.2.112");
EXPECT_EQ(test_bridge.getBridgeIP(), "192.168.2.112") << "Bridge IP not matching after setting it";
}
TEST(Bridge, setPort)
{
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
Bridge test_bridge = Bridge(getBridgeIp(), getBridgePort(), "", handler);
EXPECT_EQ(test_bridge.getBridgePort(), getBridgePort()) << "Bridge Port not matching after initialization";
test_bridge.setPort(81);
EXPECT_EQ(test_bridge.getBridgePort(), 81) << "Bridge Port not matching after setting it";
}
TEST(Bridge, getLight)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1);
Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
// Test exception
ASSERT_THROW(test_bridge.lights().get(1), HueException);
nlohmann::json hue_bridge_state {{"lights",
{{"1",
{{"state",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},
{"reachable", true}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"},
{"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"},
{"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(1)
.WillOnce(Return(hue_bridge_state));
// Refresh cache
test_bridge = Bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
// Test when correct data is sent
Light test_light_1 = test_bridge.lights().get(1);
EXPECT_EQ(test_light_1.getName(), "Hue ambiance lamp 1");
EXPECT_EQ(test_light_1.getColorType(), ColorType::TEMPERATURE);
// Test again to check whether light is returned directly -> interesting for
// code coverage test
test_light_1 = test_bridge.lights().get(1);
EXPECT_EQ(test_light_1.getName(), "Hue ambiance lamp 1");
EXPECT_EQ(test_light_1.getColorType(), ColorType::TEMPERATURE);
}
TEST(Bridge, SharedState)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
nlohmann::json hue_bridge_state {{"lights",
{{"1",
{{"state",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},
{"reachable", true}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"},
{"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"},
{"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(1)
.WillOnce(Return(hue_bridge_state));
Bridge test_bridge(
getBridgeIp(), getBridgePort(), getBridgeUsername(), handler, "", std::chrono::seconds(10), true);
// Test when correct data is sent
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_copy = test_bridge.lights().get(1);
const std::string newName = "New light name";
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/name", _, getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json({{"success", {{{"/lights/1/name", newName}}}}})));
test_light_1.setName(newName);
hue_bridge_state["lights"]["1"]["name"] = newName;
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1)
.WillOnce(Return(hue_bridge_state["lights"]["1"]));
test_light_1.refresh(true);
EXPECT_EQ(newName, test_light_copy.getName());
}
TEST(Bridge, removeLight)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
nlohmann::json hue_bridge_state {{"lights",
{{"1",
{{"state",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},
{"reachable", true}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"},
{"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"},
{"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(1)
.WillOnce(Return(hue_bridge_state));
Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
nlohmann::json return_answer;
return_answer = nlohmann::json::array();
return_answer[0] = nlohmann::json::object();
return_answer[0]["success"] = "/lights/1 deleted";
EXPECT_CALL(*handler,
DELETEJson(
"/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(2)
.WillOnce(Return(return_answer))
.WillOnce(Return(nlohmann::json()));
// Test when correct data is sent
Light test_light_1 = test_bridge.lights().get(1);
EXPECT_EQ(test_bridge.lights().remove(1), true);
EXPECT_EQ(test_bridge.lights().remove(1), false);
}
TEST(Bridge, getAllLights)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
nlohmann::json hue_bridge_state {{"lights",
{{"1",
{{"state",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},
{"reachable", true}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"},
{"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"},
{"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(1))
.WillRepeatedly(Return(hue_bridge_state));
Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
std::vector<Light> test_lights = test_bridge.lights().getAll();
ASSERT_EQ(1, test_lights.size());
EXPECT_EQ(test_lights[0].getName(), "Hue ambiance lamp 1");
EXPECT_EQ(test_lights[0].getColorType(), ColorType::TEMPERATURE);
}
TEST(Bridge, lightExists)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
nlohmann::json hue_bridge_state {{"lights",
{{"1",
{{"state",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},
{"reachable", true}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"},
{"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"},
{"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(1))
.WillRepeatedly(Return(hue_bridge_state));
Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
test_bridge.refresh();
EXPECT_TRUE(Const(test_bridge).lights().exists(1));
EXPECT_FALSE(Const(test_bridge).lights().exists(2));
}
TEST(Bridge, getGroup)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1);
Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
// Test exception
ASSERT_THROW(test_bridge.groups().get(1), HueException);
nlohmann::json hue_bridge_state {{"groups",
{{"1",
{{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}},
{"action",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200},
{"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}},
{"state", {{"any_on", true}, {"all_on", true}}}}}}}};
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1)
.WillOnce(Return(hue_bridge_state));
// Refresh cache
test_bridge = Bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
// Test when correct data is sent
Group test_group_1 = test_bridge.groups().get(1);
EXPECT_EQ(test_group_1.getName(), "Group 1");
EXPECT_EQ(test_group_1.getType(), "LightGroup");
// Test again to check whether group is returned directly
test_group_1 = test_bridge.groups().get(1);
EXPECT_EQ(test_group_1.getName(), "Group 1");
EXPECT_EQ(test_group_1.getType(), "LightGroup");
}
TEST(Bridge, removeGroup)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
nlohmann::json hue_bridge_state {{"groups",
{{"1",
{{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}},
{"action",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200},
{"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}},
{"state", {{"any_on", true}, {"all_on", true}}}}}}}};
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(1)
.WillOnce(Return(hue_bridge_state));
Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
nlohmann::json return_answer;
return_answer = nlohmann::json::array();
return_answer[0] = nlohmann::json::object();
return_answer[0]["success"] = "/groups/1 deleted";
EXPECT_CALL(*handler,
DELETEJson(
"/api/" + getBridgeUsername() + "/groups/1", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(2)
.WillOnce(Return(return_answer))
.WillOnce(Return(nlohmann::json()));
// Test when correct data is sent
Group test_group_1 = test_bridge.groups().get(1);
EXPECT_EQ(test_bridge.groups().remove(1), true);
EXPECT_EQ(test_bridge.groups().remove(1), false);
}
TEST(Bridge, groupExists)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
nlohmann::json hue_bridge_state {{"groups",
{{"1",
{{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}},
{"action",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200},
{"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}},
{"state", {{"any_on", true}, {"all_on", true}}}}}}}};
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(AtLeast(1))
.WillRepeatedly(Return(hue_bridge_state));
Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
test_bridge.refresh();
EXPECT_EQ(true, Const(test_bridge).groups().exists(1));
EXPECT_EQ(false, Const(test_bridge).groups().exists(2));
}
TEST(Bridge, getAllGroups)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
nlohmann::json hue_bridge_state {{"groups",
{{"1",
{{"name", "Group 1"}, {"type", "LightGroup"}, {"lights", {"1", "2", "3"}},
{"action",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"hue", 200},
{"sat", 254}, {"effect", "none"}, {"xy", {0.f, 0.f}}}},
{"state", {{"any_on", true}, {"all_on", true}}}}}}}};
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(AtLeast(1))
.WillRepeatedly(Return(hue_bridge_state));
Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
std::vector<Group> test_groups = test_bridge.groups().getAll();
ASSERT_EQ(1, test_groups.size());
EXPECT_EQ(test_groups[0].getName(), "Group 1");
EXPECT_EQ(test_groups[0].getType(), "LightGroup");
}
TEST(Bridge, createGroup)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(AtLeast(1));
Bridge test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
CreateGroup create = CreateGroup::Room({2, 3}, "Nice room", "LivingRoom");
nlohmann::json request = create.getRequest();
const int id = 4;
nlohmann::json response = {{{"success", {{"id", std::to_string(id)}}}}};
EXPECT_CALL(*handler, POSTJson("/api/" + getBridgeUsername() + "/groups", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
EXPECT_EQ(id, test_bridge.groups().create(create));
response = {};
EXPECT_CALL(*handler, POSTJson("/api/" + getBridgeUsername() + "/groups", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
EXPECT_EQ(0, test_bridge.groups().create(create));
}
#define IGNORE_EXCEPTIONS(statement) \
try \
{ \
statement; \
} \
catch (...) \
{ }
TEST(Bridge, instantiateResourceLists)
{
// Instantiate all methods on the resource lists, so that compile errors become visible
using namespace ::testing;
nlohmann::json bridgeState {{"lights", nlohmann::json::object()}, {"groups", nlohmann::json::object()},
{"schedules", nlohmann::json::object()}, {"scenes", nlohmann::json::object()},
{"sensors", nlohmann::json::object()}, {"rules", nlohmann::json::object()}};
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
EXPECT_CALL(*handler, GETJson(_, _, getBridgeIp(), getBridgePort())).Times(AnyNumber());
EXPECT_CALL(*handler, POSTJson(_, _, getBridgeIp(), getBridgePort())).Times(AnyNumber());
EXPECT_CALL(*handler, DELETEJson(_, _, getBridgeIp(), getBridgePort())).Times(AnyNumber());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.Times(AtLeast(1))
.WillRepeatedly(Return(bridgeState));
Bridge bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
IGNORE_EXCEPTIONS(bridge.lights().getAll());
IGNORE_EXCEPTIONS(bridge.lights().get(1));
IGNORE_EXCEPTIONS(bridge.lights().exists(1));
IGNORE_EXCEPTIONS(bridge.lights().search());
IGNORE_EXCEPTIONS(bridge.lights().getNewDevices());
IGNORE_EXCEPTIONS(bridge.lights().remove(1));
IGNORE_EXCEPTIONS(bridge.groups().getAll());
IGNORE_EXCEPTIONS(bridge.groups().get(1));
IGNORE_EXCEPTIONS(bridge.groups().exists(1));
IGNORE_EXCEPTIONS(bridge.groups().create(CreateGroup::Entertainment({}, "")));
IGNORE_EXCEPTIONS(bridge.groups().remove(1));
IGNORE_EXCEPTIONS(bridge.schedules().getAll());
IGNORE_EXCEPTIONS(bridge.schedules().get(1));
IGNORE_EXCEPTIONS(bridge.schedules().exists(1));
IGNORE_EXCEPTIONS(bridge.schedules().create(CreateSchedule()));
IGNORE_EXCEPTIONS(bridge.schedules().remove(1));
IGNORE_EXCEPTIONS(bridge.scenes().getAll());
IGNORE_EXCEPTIONS(bridge.scenes().get("1"));
IGNORE_EXCEPTIONS(bridge.scenes().exists("1"));
IGNORE_EXCEPTIONS(bridge.scenes().create(CreateScene()));
IGNORE_EXCEPTIONS(bridge.scenes().remove("1"));
IGNORE_EXCEPTIONS(bridge.sensors().getAll());
IGNORE_EXCEPTIONS(bridge.sensors().get(1));
IGNORE_EXCEPTIONS(bridge.sensors().exists(1));
IGNORE_EXCEPTIONS(bridge.sensors().create(CreateSensor("", "", "", "", "", "")));
IGNORE_EXCEPTIONS(bridge.sensors().search());
IGNORE_EXCEPTIONS(bridge.sensors().getNewDevices());
IGNORE_EXCEPTIONS(bridge.sensors().remove(1));
IGNORE_EXCEPTIONS(bridge.rules().getAll());
IGNORE_EXCEPTIONS(bridge.rules().get(1));
IGNORE_EXCEPTIONS(bridge.rules().exists(1));
IGNORE_EXCEPTIONS(bridge.rules().create(CreateRule({}, {})));
IGNORE_EXCEPTIONS(bridge.rules().remove(1));
}
@@ -0,0 +1,195 @@
/**
\file test_BridgeConfig.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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/BridgeConfig.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
using namespace testing;
TEST(BridgeConfig, refresh)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json::object()));
baseCache->refresh();
BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max());
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + "/config", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json::object()));
config.refresh(true);
}
TEST(BridgeConfig, getWhitelistedUsers)
{
const nlohmann::json state {{"config",
{{"whitelist",
{{"abcd",
{{"name", "User A"}, {"last use date", "2020-04-01T10:00:04"},
{"create date", "2020-01-01T12:00:00"}}},
{"cdef",
{{"name", "User B"}, {"last use date", "2020-03-05T14:00:00"},
{"create date", "2020-02-01T02:03:40"}}}}}}}};
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(state));
baseCache->refresh();
BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max());
std::vector<WhitelistedUser> users = config.getWhitelistedUsers();
EXPECT_THAT(users,
UnorderedElementsAre(Truly([](const WhitelistedUser& u) { return u.key == "abcd" && u.name == "User A"; }),
Truly([](const WhitelistedUser& u) { return u.key == "cdef" && u.name == "User B"; })));
}
TEST(BridgeConfig, removeUser)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json::object()));
baseCache->refresh();
BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max());
const std::string userKey = "abcd";
EXPECT_CALL(*handler,
DELETEJson("/api/" + getBridgeUsername() + "/config/whitelist/" + userKey, nlohmann::json::object(),
getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json {"/config/whitelist/" + userKey + " deleted"}));
config.removeUser(userKey);
}
TEST(BridgeConfig, getLinkButton)
{
const nlohmann::json state {{"config", {{"linkbutton", true}}}};
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(state));
baseCache->refresh();
BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max());
EXPECT_TRUE(config.getLinkButton());
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + "/config", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json {{"linkbutton", false}}));
config.refresh(true);
EXPECT_FALSE(config.getLinkButton());
}
TEST(BridgeConfig, pressLinkButton)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json::object()));
baseCache->refresh();
BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max());
EXPECT_CALL(*handler,
PUTJson("/api/" + getBridgeUsername() + "/config", nlohmann::json {{"linkbutton", true}}, getBridgeIp(),
getBridgePort()))
.WillOnce(Return(nlohmann::json {{{"success", {{"/config/linkbutton", true}}}}}));
config.pressLinkButton();
}
TEST(BridgeConfig, touchLink)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json::object()));
baseCache->refresh();
BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max());
EXPECT_CALL(*handler,
PUTJson("/api/" + getBridgeUsername() + "/config", nlohmann::json {{"touchlink", true}}, getBridgeIp(),
getBridgePort()))
.WillOnce(Return(nlohmann::json {{{"success", {{"/config/touchlink", true}}}}}));
config.touchLink();
}
TEST(BridgeConfig, getMACAddress)
{
const nlohmann::json state {{"config", {{"mac", getBridgeMac()}}}};
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(state));
baseCache->refresh();
BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max());
EXPECT_EQ(getBridgeMac(), config.getMACAddress());
}
TEST(BridgeConfig, getUTCTime)
{
const std::string utc = "2020-06-01T10:00:00";
const nlohmann::json state {{"config", {{"UTC", utc}}}};
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(state));
baseCache->refresh();
BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max());
EXPECT_EQ(time::AbsoluteTime::parseUTC(utc).getBaseTime(), config.getUTCTime().getBaseTime());
}
TEST(BridgeConfig, getTimezone)
{
const std::string timezone = "ab";
const nlohmann::json state {{"config", {{"timezone", timezone}}}};
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
auto baseCache = std::make_shared<APICache>("", commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(state));
baseCache->refresh();
BridgeConfig config(baseCache, std::chrono::steady_clock::duration::max());
EXPECT_EQ(timezone, config.getTimezone());
}
+238
View File
@@ -0,0 +1,238 @@
/**
\file test_ColorUnits.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 <http://www.gnu.org/licenses/>.
**/
#include <random>
#include <hueplusplus/ColorUnits.h>
#include <gtest/gtest.h>
using namespace hueplusplus;
TEST(ColorGamut, contains)
{
ColorGamut gamut = gamut::maxGamut;
EXPECT_TRUE(gamut.contains({0.f, 0.5f}));
EXPECT_TRUE(gamut.contains({1.f, 0.f}));
EXPECT_TRUE(gamut.contains({0.5f, 0.5f}));
EXPECT_TRUE(gamut.contains({0.f, 1.f}));
EXPECT_TRUE(gamut.contains({0.f, 0.f}));
EXPECT_FALSE(gamut.contains({1.f, 1.f}));
EXPECT_FALSE(gamut.contains({-1.f, 1.f}));
}
TEST(ColorGamut, corrected)
{
ColorGamut gamut = gamut::maxGamut;
{
const XY xy {0.f, 0.5f};
const XY result = gamut.corrected(xy);
EXPECT_FLOAT_EQ(xy.x, result.x);
EXPECT_FLOAT_EQ(xy.y, result.y);
}
{
const XY xy {0.f, 1.f};
const XY result = gamut.corrected(xy);
EXPECT_FLOAT_EQ(xy.x, result.x);
EXPECT_FLOAT_EQ(xy.y, result.y);
}
{
const XY xy {1.f, 1.f};
const XY result = gamut.corrected(xy);
EXPECT_FLOAT_EQ(0.5f, result.x);
EXPECT_FLOAT_EQ(0.5f, result.y);
}
{
const XY xy {1.f, -1.f};
const XY result = gamut.corrected(xy);
EXPECT_FLOAT_EQ(1.f, result.x);
EXPECT_FLOAT_EQ(0.f, result.y);
}
}
TEST(RGB, toXY)
{
{
const RGB red {255, 0, 0};
XYBrightness xy = red.toXY();
EXPECT_FLOAT_EQ(xy.xy.x, 0.70060623f);
EXPECT_FLOAT_EQ(xy.xy.y, 0.299301f);
EXPECT_FLOAT_EQ(xy.brightness, 1.f);
}
{
const RGB red {255, 0, 0};
XYBrightness xy = red.toXY(gamut::gamutC);
EXPECT_FLOAT_EQ(xy.xy.x, 0.69557756f);
EXPECT_FLOAT_EQ(xy.xy.y, 0.30972576f);
EXPECT_FLOAT_EQ(xy.brightness, 1.f);
}
{
const RGB white {255, 255, 255};
XYBrightness xy = white.toXY();
EXPECT_FLOAT_EQ(xy.xy.x, 0.32272673f);
EXPECT_FLOAT_EQ(xy.xy.y, 0.32902291f);
EXPECT_FLOAT_EQ(xy.brightness, 1.f);
}
{
const RGB white {255, 255, 255};
XYBrightness xy = white.toXY(gamut::gamutA);
EXPECT_FLOAT_EQ(xy.xy.x, 0.32272673f);
EXPECT_FLOAT_EQ(xy.xy.y, 0.32902291f);
EXPECT_FLOAT_EQ(xy.brightness, 1.f);
}
{
const RGB white {255, 255, 255};
XYBrightness xy = white.toXY(gamut::gamutB);
EXPECT_FLOAT_EQ(xy.xy.x, 0.32272673f);
EXPECT_FLOAT_EQ(xy.xy.y, 0.32902291f);
EXPECT_FLOAT_EQ(xy.brightness, 1.f);
}
{
const RGB black {0, 0, 0};
XYBrightness xy = black.toXY(gamut::maxGamut);
EXPECT_FLOAT_EQ(xy.xy.x, 0.32272673f);
EXPECT_FLOAT_EQ(xy.xy.y, 0.32902291f);
EXPECT_FLOAT_EQ(xy.brightness, 0.0f);
}
}
TEST(RGB, toHueSaturation)
{
{
const RGB red {255, 0, 0};
HueSaturation hs = red.toHueSaturation();
EXPECT_EQ(0, hs.hue);
EXPECT_EQ(254, hs.saturation);
}
{
const RGB darkGreen {64, 128, 128};
HueSaturation hs = darkGreen.toHueSaturation();
EXPECT_EQ(38250, hs.hue);
EXPECT_EQ(127, hs.saturation);
}
{
const RGB white {255, 255, 255};
HueSaturation hs = white.toHueSaturation();
EXPECT_EQ(0, hs.hue);
EXPECT_EQ(0, hs.saturation);
}
{
const RGB black {0, 0, 0};
HueSaturation hs = black.toHueSaturation();
EXPECT_EQ(0, hs.hue);
EXPECT_EQ(0, hs.saturation);
}
}
TEST(RGB, fromXY)
{
{
const XYBrightness xyRed {{0.70060623f, 0.299301f}, 1.f};
const RGB red = RGB::fromXY(xyRed);
EXPECT_EQ(255, red.r);
EXPECT_EQ(0, red.g);
EXPECT_EQ(0, red.b);
const XYBrightness reversed = red.toXY();
EXPECT_FLOAT_EQ(xyRed.xy.x, reversed.xy.x);
EXPECT_FLOAT_EQ(xyRed.xy.y, reversed.xy.y);
EXPECT_FLOAT_EQ(xyRed.brightness, reversed.brightness);
}
{
const XYBrightness xyWhite {{0.32272673f, 0.32902291f}, 1.f};
const RGB white = RGB::fromXY(xyWhite);
EXPECT_EQ(255, white.r);
EXPECT_EQ(255, white.g);
EXPECT_EQ(255, white.b);
const XYBrightness reversed = white.toXY();
EXPECT_FLOAT_EQ(xyWhite.xy.x, reversed.xy.x);
EXPECT_FLOAT_EQ(xyWhite.xy.y, reversed.xy.y);
EXPECT_FLOAT_EQ(xyWhite.brightness, reversed.brightness);
}
{
const XYBrightness xyRed {{0.70060623f, 0.299301f}, 1.f};
const RGB red = RGB::fromXY(xyRed, gamut::gamutB);
const RGB red2 = RGB::fromXY({gamut::gamutB.corrected(xyRed.xy), xyRed.brightness});
EXPECT_EQ(red2.r, red.r);
EXPECT_EQ(red2.g, red.g);
EXPECT_EQ(red2.b, red.b);
}
// Statistical tests of conversion accuracy
// Fixed seed so the tests dont fail randomly
std::mt19937 rng {12374682};
std::uniform_int_distribution<int> dist(0, 255);
uint64_t N = 1000;
uint64_t totalDiffR = 0;
uint64_t totalDiffG = 0;
uint64_t totalDiffB = 0;
int maxDiffR = 0;
int maxDiffG = 0;
int maxDiffB = 0;
for (int i = 0; i < N; ++i)
{
const RGB rgb {
static_cast<uint8_t>(dist(rng)), static_cast<uint8_t>(dist(rng)), static_cast<uint8_t>(dist(rng))};
const XYBrightness xy = rgb.toXY();
const RGB back = RGB::fromXY(xy);
int diffR = (rgb.r - back.r) * (rgb.r - back.r);
int diffG = (rgb.g - back.g) * (rgb.g - back.g);
int diffB = (rgb.b - back.b) * (rgb.b - back.b);
totalDiffR += diffR;
totalDiffG += diffG;
totalDiffB += diffB;
maxDiffR = std::max(diffR, maxDiffR);
maxDiffG = std::max(diffG, maxDiffG);
maxDiffB = std::max(diffB, maxDiffB);
}
float varR = (float)totalDiffR / N;
float varG = (float)totalDiffG / N;
float varB = (float)totalDiffB / N;
EXPECT_LT(varR, 5.f);
EXPECT_LT(varG, 5.f);
EXPECT_LT(varB, 4.f);
EXPECT_LE(maxDiffR, 81);
EXPECT_LE(maxDiffG, 81);
EXPECT_LE(maxDiffB, 64);
}
TEST(ColorUnits, kelvinToMired)
{
EXPECT_EQ(10000, kelvinToMired(100));
EXPECT_EQ(500, kelvinToMired(2000));
EXPECT_EQ(303, kelvinToMired(3300));
EXPECT_EQ(250, kelvinToMired(4000));
EXPECT_EQ(200, kelvinToMired(5000));
EXPECT_EQ(167, kelvinToMired(6000));
}
TEST(ColorUnits, miredToKelvin)
{
EXPECT_EQ(100, miredToKelvin(10000));
EXPECT_EQ(2000, miredToKelvin(500));
EXPECT_EQ(3300, miredToKelvin(303));
EXPECT_EQ(4000, miredToKelvin(250));
EXPECT_EQ(5000, miredToKelvin(200));
EXPECT_EQ(6024, miredToKelvin(166)); // 6000 kelvin should be 166 mired, but is rounded
}
@@ -0,0 +1,200 @@
/**
\file test_ExtendedColorHueStrategy.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 <http://www.gnu.org/licenses/>.
**/
#include <memory>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "TestTransaction.h"
#include "testhelper.h"
#include "hueplusplus/ExtendedColorHueStrategy.h"
#include <nlohmann/json.hpp>
#include "mocks/mock_HttpHandler.h"
#include "mocks/mock_Light.h"
using namespace hueplusplus;
TEST(ExtendedColorHueStrategy, alertHueSaturation)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight light(handler);
const HueSaturation hueSat {200, 100};
// Needs to update the state so transactions are correctly trimmed
const auto setColorLambda = [&](const HueSaturation& hueSat, int transition) {
light.getState()["state"]["colormode"] = "hs";
light.getState()["state"]["on"] = true;
light.getState()["state"]["hue"] = hueSat.hue;
light.getState()["state"]["sat"] = hueSat.saturation;
return true;
};
// Invalid state
{
light.getState()["state"]["colormode"] = "invalid";
light.getState()["state"]["on"] = false;
EXPECT_FALSE(ExtendedColorHueStrategy().alertHueSaturation(hueSat, light));
}
// Colormode not ct is forwarded to SimpleColorHueStrategy
{
const nlohmann::json state = {{"colormode", "hs"}, {"on", true}, {"xy", {0.1, 0.1}}, {"hue", 300}, {"sat", 100},
{"bri", 254}, {"ct", 300}};
light.getState()["state"] = state;
EXPECT_CALL(Const(light), getColorHueSaturation())
.Times(AnyNumber())
.WillRepeatedly(Return(HueSaturation {300, 100}));
TestTransaction reverseTransaction = light.transaction().setColorHue(300).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(ExtendedColorHueStrategy().alertHueSaturation(hueSat, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode ct
{
const nlohmann::json state
= {{"colormode", "ct"}, {"on", true}, {"xy", {0.1, 0.1}}, {"sat", 100}, {"bri", 254}, {"ct", 300}};
light.getState()["state"] = state;
TestTransaction reverseTransaction = light.transaction().setColorTemperature(300).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(false));
EXPECT_FALSE(ExtendedColorHueStrategy().alertHueSaturation(hueSat, light));
light.getState()["state"] = state;
Mock::VerifyAndClearExpectations(handler.get());
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(ExtendedColorHueStrategy().alertHueSaturation(hueSat, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode ct, off
{
const nlohmann::json state
= {{"colormode", "ct"}, {"on", false}, {"xy", {0., 1.}}, {"sat", 100}, {"bri", 254}, {"ct", 300}};
light.getState()["state"] = state;
TestTransaction reverseTransaction = light.transaction().setColorTemperature(300).setOn(false).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(ExtendedColorHueStrategy().alertHueSaturation(hueSat, light));
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(ExtendedColorHueStrategy, alertXY)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight light(handler);
const XYBrightness xy {{0.1f, 0.1f}, 1.f};
// Needs to update the state so transactions are correctly trimmed
const auto setColorLambda = [&](const XYBrightness& xy, int transition) {
light.getState()["state"]["colormode"] = "xy";
light.getState()["state"]["on"] = true;
light.getState()["state"]["xy"] = {xy.xy.x, xy.xy.y};
light.getState()["state"]["bri"] = static_cast<int>(std::round(xy.brightness * 254.f));
return true;
};
// Invalid colormode
{
light.getState()["state"]["colormode"] = "invalid";
light.getState()["state"]["on"] = false;
EXPECT_FALSE(ExtendedColorHueStrategy().alertXY({{0.1f, 0.1f}, 1.f}, light));
}
// Colormode not ct is forwarded to SimpleColorHueStrategy
{
const nlohmann::json state = {{"colormode", "hs"}, {"on", true}, {"xy", {0.1, 0.1}}, {"hue", 200}, {"sat", 100},
{"bri", 254}, {"ct", 300}};
light.getState()["state"] = state;
EXPECT_CALL(Const(light), getBrightness()).Times(AnyNumber()).WillRepeatedly(Return(254));
HueSaturation hueSat {200, 100};
EXPECT_CALL(Const(light), getColorHueSaturation()).Times(AnyNumber()).WillRepeatedly(Return(hueSat));
TestTransaction reverseTransaction = light.transaction().setColor(hueSat).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(SimpleColorHueStrategy().alertXY(xy, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode ct
{
const nlohmann::json state
= { {"colormode", "ct"}, {"on", true}, {"xy", {0.1, 0.1}}, {"sat", 100}, {"bri", 128}, {"ct", 300} };
light.getState()["state"] = state;
EXPECT_CALL(Const(light), getBrightness()).Times(AnyNumber()).WillRepeatedly(Return(128));
TestTransaction reverseTransaction = light.transaction().setColorTemperature(300).setBrightness(128).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(false));
EXPECT_FALSE(ExtendedColorHueStrategy().alertXY(xy, light));
light.getState()["state"] = state;
Mock::VerifyAndClearExpectations(handler.get());
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(ExtendedColorHueStrategy().alertXY(xy, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode ct, off
{
const nlohmann::json state
= { {"colormode", "ct"}, {"on", false}, {"xy", {0., 1.}}, {"sat", 100}, {"bri", 254}, {"ct", 300} };
light.getState()["state"] = state;
EXPECT_CALL(Const(light), getBrightness()).Times(AnyNumber()).WillRepeatedly(Return(254));
TestTransaction reverseTransaction = light.transaction().setColorTemperature(300).setOn(false).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(ExtendedColorHueStrategy().alertXY(xy, light));
Mock::VerifyAndClearExpectations(handler.get());
}
}
@@ -0,0 +1,122 @@
/**
\file test_ExtendedColorTemperatureStrategy.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 <http://www.gnu.org/licenses/>.
**/
#include <memory>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "TestTransaction.h"
#include "testhelper.h"
#include "hueplusplus/ExtendedColorTemperatureStrategy.h"
#include <nlohmann/json.hpp>
#include "mocks/mock_HttpHandler.h"
#include "mocks/mock_Light.h"
using namespace hueplusplus;
TEST(ExtendedColorTemperatureStrategy, alertTemperature)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight light(handler);
const auto setCTLambda = [&](unsigned int ct, int transition) {
light.getState()["state"]["colormode"] = "ct";
light.getState()["state"]["on"] = true;
light.getState()["state"]["ct"] = ct;
return true;
};
// Invalid colormode
{
light.getState()["state"]["colormode"] = "invalid";
light.getState()["state"]["on"] = false;
EXPECT_EQ(false, ExtendedColorTemperatureStrategy().alertTemperature(400, light));
}
// Colormode ct forwarded to SimpleColorTemperatureStrategy
{
const nlohmann::json state = {{"colormode", "ct"}, {"on", true}, {"ct", 200}, {"xy", {0.1, 0.1}}, {"hue", 300},
{"sat", 100}, {"bri", 254}};
light.getState()["state"] = state;
TestTransaction reverseTransaction = light.transaction().setColorTemperature(200).setTransition(1);
light.getState()["state"] = state;
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Invoke(setCTLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(ExtendedColorTemperatureStrategy().alertTemperature(400, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode xy
{
const nlohmann::json state = {{"colormode", "xy"}, {"on", true}, {"ct", 200}, {"xy", {0.1, 0.1}}, {"hue", 300},
{"sat", 100}, {"bri", 254}};
light.getState()["state"] = state;
EXPECT_CALL(Const(light), getColorXY()).Times(AnyNumber()).WillRepeatedly(Return(XYBrightness{ {0.1f,0.1f},1.f }));
TestTransaction reverseTransaction = light.transaction().setColor(XY{ 0.1f,0.1f }).setTransition(1);
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Return(false));
EXPECT_FALSE(ExtendedColorTemperatureStrategy().alertTemperature(400, light));
InSequence s;
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Invoke(setCTLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(false));
EXPECT_FALSE(ExtendedColorTemperatureStrategy().alertTemperature(400, light));
light.getState()["state"] = state;
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Invoke(setCTLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(ExtendedColorTemperatureStrategy().alertTemperature(400, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode hs
{
const nlohmann::json state = { {"colormode", "hs"}, {"on", true}, {"ct", 200}, {"xy", {0.1, 0.1}}, {"hue", 300},
{"sat", 100}, {"bri", 254} };
light.getState()["state"] = state;
EXPECT_CALL(Const(light), getColorHueSaturation()).Times(AnyNumber()).WillRepeatedly(Return(HueSaturation{ 300,200 }));
TestTransaction reverseTransaction = light.transaction().setColor(HueSaturation{ 300,200 }).setTransition(1);
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Return(false));
EXPECT_FALSE(ExtendedColorTemperatureStrategy().alertTemperature(400, light));
InSequence s;
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Invoke(setCTLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(false));
EXPECT_FALSE(ExtendedColorTemperatureStrategy().alertTemperature(400, light));
light.getState()["state"] = state;
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Invoke(setCTLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(ExtendedColorTemperatureStrategy().alertTemperature(400, light));
Mock::VerifyAndClearExpectations(handler.get());
}
}
+301
View File
@@ -0,0 +1,301 @@
/**
\file test_Group.cpp
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
**/
#include <gtest/gtest.h>
#include "testhelper.h"
#include "hueplusplus/Group.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
using namespace testing;
class GroupTest : public Test
{
protected:
const std::string groupName = "Group 1";
const std::string type = "Room";
const std::string roomType = "Bedroom";
const bool on = true;
const int bri = 254;
const int hue = 10000;
const int sat = 254;
const std::string effect = "none";
const float x = 0.5f;
const float y = 0.6f;
const int ct = 250;
const std::string alert = "none";
const std::string colormode = "ct";
const bool any_on = true;
const bool all_on = false;
std::shared_ptr<MockHttpHandler> handler;
HueCommandAPI commands;
nlohmann::json groupState;
protected:
GroupTest()
: handler(std::make_shared<MockHttpHandler>()),
commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler),
groupState({{"name", groupName}, {"type", type}, {"class", roomType}, {"lights", {"1", "2", "4"}},
{"action",
{{"on", on}, {"bri", bri}, {"hue", hue}, {"sat", sat}, {"effect", effect},
{"xy", nlohmann::json::array({x, y})}, {"ct", ct}, {"alert", alert}, {"colormode", colormode}}},
{"state", {{"any_on", any_on}, {"all_on", all_on}}}})
{}
void expectGetState(int id)
{
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + "/groups/" + std::to_string(id), nlohmann::json::object(),
getBridgeIp(), getBridgePort()))
.WillOnce(Return(groupState));
}
};
TEST_F(GroupTest, Construtor)
{
{
const int id = 12;
expectGetState(id);
Group group(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(id, group.getId());
Mock::VerifyAndClearExpectations(handler.get());
}
{
const int id = 0;
expectGetState(id);
Group group(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(id, group.getId());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST_F(GroupTest, getName)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(groupName, Const(group).getName());
}
TEST_F(GroupTest, getType)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(type, Const(group).getType());
}
TEST_F(GroupTest, getLightIds)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(std::vector<int>({1, 2, 4}), Const(group).getLightIds());
}
TEST_F(GroupTest, getRoomType)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(roomType, Const(group).getRoomType());
}
TEST_F(GroupTest, getAllOn)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_EQ(all_on, group.getAllOn());
EXPECT_EQ(all_on, Const(group).getAllOn());
}
TEST_F(GroupTest, getAnyOn)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_EQ(any_on, group.getAnyOn());
EXPECT_EQ(any_on, Const(group).getAnyOn());
}
TEST_F(GroupTest, getActionOn)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_EQ(on, group.getActionOn());
EXPECT_EQ(on, Const(group).getActionOn());
}
TEST_F(GroupTest, getActionHueSaturation)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
std::pair<uint16_t, uint8_t> hueSat {hue, sat};
EXPECT_EQ(hueSat, group.getActionHueSaturation());
EXPECT_EQ(hueSat, Const(group).getActionHueSaturation());
}
TEST_F(GroupTest, getActionBrightness)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_EQ(bri, group.getActionBrightness());
EXPECT_EQ(bri, Const(group).getActionBrightness());
}
TEST_F(GroupTest, getActionColorTemperature)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_EQ(ct, group.getActionColorTemperature());
EXPECT_EQ(ct, Const(group).getActionColorTemperature());
}
TEST_F(GroupTest, getActionColorXY)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
std::pair<float, float> xy {x, y};
EXPECT_EQ(xy, group.getActionColorXY());
EXPECT_EQ(xy, Const(group).getActionColorXY());
}
TEST_F(GroupTest, getActionColorMode)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
EXPECT_EQ(colormode, group.getActionColorMode());
EXPECT_EQ(colormode, Const(group).getActionColorMode());
}
TEST_F(GroupTest, setName)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
const std::string name = "Test group";
nlohmann::json request = {{"name", name}};
nlohmann::json response = {{"success", {"/groups/1/name", name}}};
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/groups/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
group.setName(name);
}
TEST_F(GroupTest, setLights)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
const nlohmann::json lights = {"2", "4", "5"};
nlohmann::json request = {{"lights", lights}};
nlohmann::json response = {{"success", {"/groups/1/lights", lights}}};
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/groups/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
group.setLights(std::vector<int> {2, 4, 5});
}
TEST_F(GroupTest, setRoomType)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
const std::string type = "LivingRoom";
nlohmann::json request = {{"class", type}};
nlohmann::json response = {{"success", {"/groups/1/class", type}}};
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/groups/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
group.setRoomType(type);
}
TEST_F(GroupTest, setScene)
{
const int id = 1;
expectGetState(id);
Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
const std::string scene = "testScene";
nlohmann::json request = {{"scene", scene}};
nlohmann::json response = {{"success", {"/groups/1/action/scene", scene}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/groups/1/action", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
group.setScene(scene);
}
TEST_F(GroupTest, createSceneAction)
{
const int id = 1;
expectGetState(id);
const Group group(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
const std::string scene = "testScene";
nlohmann::json request = { {"scene", scene} };
hueplusplus::Action command = group.createSceneAction(scene);
EXPECT_EQ(hueplusplus::Action::Method::put, command.getMethod());
EXPECT_EQ("/api/" + getBridgeUsername() + "/groups/1/action", command.getAddress());
EXPECT_EQ(request, command.getBody());
}
TEST(CreateGroup, LightGroup)
{
EXPECT_EQ(nlohmann::json({{"lights", {"1"}}, {"type", "LightGroup"}, {"name", "Name"}, {"class", "Other"}}),
CreateGroup::LightGroup({1}, "Name").getRequest());
EXPECT_EQ(nlohmann::json({{"lights", {"2", "4"}}, {"type", "LightGroup"}, {"class", "Other"}}),
CreateGroup::LightGroup({2, 4}).getRequest());
}
TEST(CreateGroup, Entertainment)
{
EXPECT_EQ(nlohmann::json({{"lights", {"1"}}, {"type", "Entertainment"}, {"name", "Name"}, {"class", "Other"}}),
CreateGroup::Entertainment({1}, "Name").getRequest());
EXPECT_EQ(nlohmann::json({{"lights", {"2", "4"}}, {"type", "Entertainment"}, {"class", "Other"}}),
CreateGroup::Entertainment({2, 4}).getRequest());
}
TEST(CreateGroup, Zone)
{
EXPECT_EQ(nlohmann::json({{"lights", {"1"}}, {"type", "Zone"}, {"name", "Name"}, {"class", "Other"}}),
CreateGroup::Zone({1}, "Name").getRequest());
EXPECT_EQ(nlohmann::json({{"lights", {"2", "4"}}, {"type", "Zone"}, {"class", "Other"}}),
CreateGroup::Zone({2, 4}).getRequest());
}
TEST(CreateGroup, Room)
{
EXPECT_EQ(nlohmann::json({{"lights", {"1"}}, {"type", "Room"}, {"name", "Name"}, {"class", "Bedroom"}}),
CreateGroup::Room({1}, "Name", "Bedroom").getRequest());
EXPECT_EQ(nlohmann::json({{"lights", {"1"}}, {"type", "Room"}, {"name", "Name"}, {"class", "Other"}}),
CreateGroup::Room({1}, "Name").getRequest());
EXPECT_EQ(nlohmann::json({{"lights", {"2", "4"}}, {"type", "Room"}, {"class", "Other"}}),
CreateGroup::Room({2, 4}).getRequest());
}
@@ -0,0 +1,242 @@
/**
\file test_HueCommandAPI.cpp
Copyright Notice\n
Copyright (C) 2018 Jan Rogall - developer\n
Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
**/
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "hueplusplus/Bridge.h"
#include <nlohmann/json.hpp>
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
TEST(HueCommandAPI, PUTRequest)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> httpHandler = std::make_shared<MockHttpHandler>();
HueCommandAPI api(getBridgeIp(), getBridgePort(), getBridgeUsername(), httpHandler);
nlohmann::json request;
nlohmann::json result = nlohmann::json::object();
result["ok"] = true;
// empty path
{
EXPECT_CALL(*httpHandler, PUTJson("/api/" + getBridgeUsername(), request, getBridgeIp(), 80))
.WillOnce(Return(result));
EXPECT_EQ(result, api.PUTRequest("", request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// not empty path, starting with slash
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, PUTJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Return(result));
EXPECT_EQ(result, api.PUTRequest(path, request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// not empty path, not starting with slash
{
const std::string path = "test";
EXPECT_CALL(*httpHandler, PUTJson("/api/" + getBridgeUsername() + '/' + path, request, getBridgeIp(), 80))
.WillOnce(Return(result));
EXPECT_EQ(result, api.PUTRequest(path, request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// recoverable error
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, PUTJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::connection_reset))))
.WillOnce(Return(result));
EXPECT_EQ(result, api.PUTRequest(path, request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// recoverable error x2
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, PUTJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::connection_reset))))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::connection_reset))));
EXPECT_THROW(api.PUTRequest(path, request), std::system_error);
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// unrecoverable error
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, PUTJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::not_enough_memory))));
EXPECT_THROW(api.PUTRequest(path, request), std::system_error);
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// api returns error
{
const std::string path = "/test";
const nlohmann::json errorResponse{{"error", {{"type", 10}, {"address", path}, {"description", "Stuff"}}}};
EXPECT_CALL(*httpHandler, PUTJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Return(errorResponse));
EXPECT_THROW(api.PUTRequest(path, request), HueAPIResponseException);
Mock::VerifyAndClearExpectations(httpHandler.get());
}
}
TEST(HueCommandAPI, GETRequest)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> httpHandler = std::make_shared<MockHttpHandler>();
HueCommandAPI api(getBridgeIp(), getBridgePort(), getBridgeUsername(), httpHandler);
nlohmann::json request;
nlohmann::json result = nlohmann::json::object();
result["ok"] = true;
// empty path
{
EXPECT_CALL(*httpHandler, GETJson("/api/" + getBridgeUsername(), request, getBridgeIp(), 80))
.WillOnce(Return(result));
EXPECT_EQ(result, api.GETRequest("", request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// not empty path, starting with slash
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, GETJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Return(result));
EXPECT_EQ(result, api.GETRequest(path, request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// not empty path, not starting with slash
{
const std::string path = "test";
EXPECT_CALL(*httpHandler, GETJson("/api/" + getBridgeUsername() + '/' + path, request, getBridgeIp(), 80))
.WillOnce(Return(result));
EXPECT_EQ(result, api.GETRequest(path, request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// recoverable error
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, GETJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::connection_reset))))
.WillOnce(Return(result));
EXPECT_EQ(result, api.GETRequest(path, request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// recoverable error x2
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, GETJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::connection_reset))))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::connection_reset))));
EXPECT_THROW(api.GETRequest(path, request), std::system_error);
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// unrecoverable error
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, GETJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::not_enough_memory))));
EXPECT_THROW(api.GETRequest(path, request), std::system_error);
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// api returns error
{
const std::string path = "/test";
const nlohmann::json errorResponse{{"error", {{"type", 10}, {"address", path}, {"description", "Stuff"}}}};
EXPECT_CALL(*httpHandler, GETJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Return(errorResponse));
EXPECT_THROW(api.GETRequest(path, request), HueAPIResponseException);
Mock::VerifyAndClearExpectations(httpHandler.get());
}
}
TEST(HueCommandAPI, DELETERequest)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> httpHandler = std::make_shared<MockHttpHandler>();
HueCommandAPI api(getBridgeIp(), getBridgePort(), getBridgeUsername(), httpHandler);
nlohmann::json request;
nlohmann::json result = nlohmann::json::object();
result["ok"] = true;
// empty path
{
EXPECT_CALL(*httpHandler, DELETEJson("/api/" + getBridgeUsername(), request, getBridgeIp(), 80))
.WillOnce(Return(result));
EXPECT_EQ(result, api.DELETERequest("", request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// not empty path, starting with slash
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, DELETEJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Return(result));
EXPECT_EQ(result, api.DELETERequest(path, request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// not empty path, not starting with slash
{
const std::string path = "test";
EXPECT_CALL(*httpHandler, DELETEJson("/api/" + getBridgeUsername() + '/' + path, request, getBridgeIp(), 80))
.WillOnce(Return(result));
EXPECT_EQ(result, api.DELETERequest(path, request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// recoverable error
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, DELETEJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::connection_reset))))
.WillOnce(Return(result));
EXPECT_EQ(result, api.DELETERequest(path, request));
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// recoverable error x2
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, DELETEJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::connection_reset))))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::connection_reset))));
EXPECT_THROW(api.DELETERequest(path, request), std::system_error);
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// unrecoverable error
{
const std::string path = "/test";
EXPECT_CALL(*httpHandler, DELETEJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Throw(std::system_error(std::make_error_code(std::errc::not_enough_memory))));
EXPECT_THROW(api.DELETERequest(path, request), std::system_error);
Mock::VerifyAndClearExpectations(httpHandler.get());
}
// api returns error
{
const std::string path = "/test";
const nlohmann::json errorResponse{{"error", {{"type", 10}, {"address", path}, {"description", "Stuff"}}}};
EXPECT_CALL(*httpHandler, DELETEJson("/api/" + getBridgeUsername() + path, request, getBridgeIp(), 80))
.WillOnce(Return(errorResponse));
EXPECT_THROW(api.DELETERequest(path, request), HueAPIResponseException);
Mock::VerifyAndClearExpectations(httpHandler.get());
}
}
+787
View File
@@ -0,0 +1,787 @@
/**
\file test_HueLight.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 <http://www.gnu.org/licenses/>.
**/
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "hueplusplus/Bridge.h"
#include "hueplusplus/Light.h"
#include <nlohmann/json.hpp>
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
class HueLightTest : public ::testing::Test
{
protected:
std::shared_ptr<MockHttpHandler> handler;
nlohmann::json hue_bridge_state;
Bridge test_bridge;
protected:
HueLightTest()
: handler(std::make_shared<MockHttpHandler>()),
hue_bridge_state({{"lights",
{{"1",
{{"state",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"},
{"reachable", true}, {"effect", "none"}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Dimmable light"},
{"name", "Hue lamp 1"}, {"modelid", "LWB004"}, {"manufacturername", "Philips"},
{"productname", "Hue bloom"}, {"uniqueid", "00:00:00:00:00:00:00:00-00"},
{"swversion", "5.50.1.19085"}, {"luminaireuniqueid", "0000000"}}},
{"2",
{{"state",
{{"on", false}, {"bri", 0}, {"ct", 366}, {"hue", 12345}, {"sat", 123},
{"xy", {0.102, 0.102}}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true},
{"effect", "none"}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color light"},
{"name", "Hue lamp 2"}, {"modelid", "LST001"}, {"uniqueid", "11:11:11:11:11:11:11:11-11"},
{"swversion", "5.50.1.19085"}}},
{"3",
{{"state",
{{"on", false}, {"bri", 254}, {"ct", 366}, {"hue", 12345}, {"sat", 123},
{"xy", {0.102, 0.102}}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true},
{"effect", "none"}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}},
{"type", "Extended color light"}, {"name", "Hue lamp 3"}, {"modelid", "LCT010"},
{"manufacturername", "Philips"}, {"productname", "Hue bloom"},
{"swversion", "5.50.1.19085"}}}}}}),
test_bridge(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler)
{
using namespace ::testing;
EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername(), nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(hue_bridge_state));
}
~HueLightTest() {};
};
TEST_F(HueLightTest, Constructor)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
Light test_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
Light test_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_3 = test_bridge.lights().get(3);
}
TEST_F(HueLightTest, on)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 255;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/3/state/on"] = true;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(true, test_light_1.on(33));
EXPECT_EQ(false, test_light_2.on());
EXPECT_EQ(true, test_light_3.on(255));
}
TEST_F(HueLightTest, off)
{
using namespace ::testing;
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/1/state/transitiontime"] = 33;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/1/state/on"] = false;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(true, test_light_1.off(33));
EXPECT_EQ(true, test_light_2.off());
EXPECT_EQ(true, test_light_3.off(255));
}
TEST_F(HueLightTest, isOn)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(true, ctest_light_1.isOn());
EXPECT_EQ(false, ctest_light_2.isOn());
EXPECT_EQ(false, ctest_light_3.isOn());
EXPECT_EQ(true, test_light_1.isOn());
EXPECT_EQ(false, test_light_2.isOn());
EXPECT_EQ(false, test_light_3.isOn());
}
TEST_F(HueLightTest, getId)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(1, ctest_light_1.getId());
EXPECT_EQ(2, ctest_light_2.getId());
EXPECT_EQ(3, ctest_light_3.getId());
EXPECT_EQ(1, test_light_1.getId());
EXPECT_EQ(2, test_light_2.getId());
EXPECT_EQ(3, test_light_3.getId());
}
TEST_F(HueLightTest, getType)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ("Dimmable light", ctest_light_1.getType());
EXPECT_EQ("Color light", ctest_light_2.getType());
EXPECT_EQ("Extended color light", ctest_light_3.getType());
EXPECT_EQ("Dimmable light", test_light_1.getType());
EXPECT_EQ("Color light", test_light_2.getType());
EXPECT_EQ("Extended color light", test_light_3.getType());
}
TEST_F(HueLightTest, getName)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ("Hue lamp 1", ctest_light_1.getName());
EXPECT_EQ("Hue lamp 2", ctest_light_2.getName());
EXPECT_EQ("Hue lamp 3", ctest_light_3.getName());
EXPECT_EQ("Hue lamp 1", test_light_1.getName());
EXPECT_EQ("Hue lamp 2", test_light_2.getName());
EXPECT_EQ("Hue lamp 3", test_light_3.getName());
}
TEST_F(HueLightTest, getModelId)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ("LWB004", ctest_light_1.getModelId());
EXPECT_EQ("LST001", ctest_light_2.getModelId());
EXPECT_EQ("LCT010", ctest_light_3.getModelId());
EXPECT_EQ("LWB004", test_light_1.getModelId());
EXPECT_EQ("LST001", test_light_2.getModelId());
EXPECT_EQ("LCT010", test_light_3.getModelId());
}
TEST_F(HueLightTest, getUId)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ("00:00:00:00:00:00:00:00-00", ctest_light_1.getUId());
EXPECT_EQ("11:11:11:11:11:11:11:11-11", ctest_light_2.getUId());
EXPECT_EQ("", ctest_light_3.getUId());
EXPECT_EQ("00:00:00:00:00:00:00:00-00", test_light_1.getUId());
EXPECT_EQ("11:11:11:11:11:11:11:11-11", test_light_2.getUId());
EXPECT_EQ("", test_light_3.getUId());
}
TEST_F(HueLightTest, getManufacturername)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ("Philips", ctest_light_1.getManufacturername());
EXPECT_EQ("", ctest_light_2.getManufacturername());
EXPECT_EQ("Philips", ctest_light_3.getManufacturername());
EXPECT_EQ("Philips", test_light_1.getManufacturername());
EXPECT_EQ("", test_light_2.getManufacturername());
EXPECT_EQ("Philips", test_light_3.getManufacturername());
}
TEST_F(HueLightTest, getProductname)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ("Hue bloom", ctest_light_1.getProductname());
EXPECT_EQ("", ctest_light_2.getProductname());
EXPECT_EQ("Hue bloom", ctest_light_3.getProductname());
EXPECT_EQ("Hue bloom", test_light_1.getProductname());
EXPECT_EQ("", test_light_2.getProductname());
EXPECT_EQ("Hue bloom", test_light_3.getProductname());
}
TEST_F(HueLightTest, getLuminaireUId)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ("0000000", ctest_light_1.getLuminaireUId());
EXPECT_EQ("", ctest_light_2.getLuminaireUId());
EXPECT_EQ("", ctest_light_3.getLuminaireUId());
EXPECT_EQ("0000000", test_light_1.getLuminaireUId());
EXPECT_EQ("", test_light_2.getLuminaireUId());
EXPECT_EQ("", test_light_3.getLuminaireUId());
}
TEST_F(HueLightTest, getSwVersion)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ("5.50.1.19085", ctest_light_1.getSwVersion());
EXPECT_EQ("5.50.1.19085", ctest_light_2.getSwVersion());
EXPECT_EQ("5.50.1.19085", ctest_light_3.getSwVersion());
EXPECT_EQ("5.50.1.19085", test_light_1.getSwVersion());
EXPECT_EQ("5.50.1.19085", test_light_2.getSwVersion());
EXPECT_EQ("5.50.1.19085", test_light_3.getSwVersion());
}
TEST_F(HueLightTest, setName)
{
using namespace ::testing;
nlohmann::json expected_request({});
expected_request["name"] = "Baskj189";
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/1/name"] = expected_request["name"];
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/name", expected_request, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/name", expected_request, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/3/name"] = expected_request["name"];
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/name", expected_request, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(true, test_light_1.setName(expected_request["name"].get<std::string>()));
EXPECT_EQ(false, test_light_2.setName(expected_request["name"].get<std::string>()));
EXPECT_EQ(true, test_light_3.setName(expected_request["name"].get<std::string>()));
}
TEST_F(HueLightTest, getColorType)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(ColorType::NONE, ctest_light_1.getColorType());
EXPECT_EQ(ColorType::GAMUT_A, ctest_light_2.getColorType());
EXPECT_EQ(ColorType::GAMUT_C_TEMPERATURE, ctest_light_3.getColorType());
EXPECT_EQ(ColorType::NONE, test_light_1.getColorType());
EXPECT_EQ(ColorType::GAMUT_A, test_light_2.getColorType());
EXPECT_EQ(ColorType::GAMUT_C_TEMPERATURE, test_light_3.getColorType());
}
TEST_F(HueLightTest, hasBrightnessControl)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(true, ctest_light_1.hasBrightnessControl());
EXPECT_EQ(true, ctest_light_2.hasBrightnessControl());
EXPECT_EQ(true, ctest_light_3.hasBrightnessControl());
EXPECT_EQ(true, test_light_1.hasBrightnessControl());
EXPECT_EQ(true, test_light_2.hasBrightnessControl());
EXPECT_EQ(true, test_light_3.hasBrightnessControl());
}
TEST_F(HueLightTest, hasTemperatureControl)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, ctest_light_1.hasTemperatureControl());
EXPECT_EQ(false, ctest_light_2.hasTemperatureControl());
EXPECT_EQ(true, ctest_light_3.hasTemperatureControl());
EXPECT_EQ(false, test_light_1.hasTemperatureControl());
EXPECT_EQ(false, test_light_2.hasTemperatureControl());
EXPECT_EQ(true, test_light_3.hasTemperatureControl());
}
TEST_F(HueLightTest, hasColorControl)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, ctest_light_1.hasColorControl());
EXPECT_EQ(true, ctest_light_2.hasColorControl());
EXPECT_EQ(true, ctest_light_3.hasColorControl());
EXPECT_EQ(false, test_light_1.hasColorControl());
EXPECT_EQ(true, test_light_2.hasColorControl());
EXPECT_EQ(true, test_light_3.hasColorControl());
}
TEST_F(HueLightTest, setBrightness)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/3/state/on"] = true;
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/3/state/bri"] = 253;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.setBrightness(200));
EXPECT_EQ(true, test_light_2.setBrightness(0, 2));
EXPECT_EQ(true, test_light_3.setBrightness(253, 0));
}
TEST_F(HueLightTest, getBrightness)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(254, ctest_light_1.getBrightness());
EXPECT_EQ(0, ctest_light_2.getBrightness());
EXPECT_EQ(254, ctest_light_3.getBrightness());
EXPECT_EQ(254, test_light_1.getBrightness());
EXPECT_EQ(0, test_light_2.getBrightness());
EXPECT_EQ(254, test_light_3.getBrightness());
}
TEST_F(HueLightTest, setColorTemperature)
{
using namespace ::testing;
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/3/state/ct"] = 153;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/3/state/on"] = true;
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.setColorTemperature(153));
EXPECT_EQ(false, test_light_2.setColorTemperature(400, 2));
EXPECT_EQ(true, test_light_3.setColorTemperature(100, 0));
}
TEST_F(HueLightTest, getColorTemperature)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(0, ctest_light_1.getColorTemperature());
EXPECT_EQ(0, ctest_light_2.getColorTemperature());
EXPECT_EQ(366, ctest_light_3.getColorTemperature());
EXPECT_EQ(0, test_light_1.getColorTemperature());
EXPECT_EQ(0, test_light_2.getColorTemperature());
EXPECT_EQ(366, test_light_3.getColorTemperature());
}
TEST_F(HueLightTest, setColorHue)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/3/state/hue"] = 65500;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/3/state/on"] = true;
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.setColorHue(153));
EXPECT_EQ(false, test_light_2.setColorHue(30000, 2));
EXPECT_EQ(true, test_light_3.setColorHue(65500, 0));
}
TEST_F(HueLightTest, setColorSaturation)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/3/state/sat"] = 250;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/3/state/on"] = true;
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.setColorSaturation(0));
EXPECT_EQ(false, test_light_2.setColorSaturation(140, 2));
EXPECT_EQ(true, test_light_3.setColorSaturation(250, 0));
}
TEST_F(HueLightTest, setColorHueSaturation)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/3/state/on"] = true;
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/3/state/hue"] = 65500;
prep_ret[3] = nlohmann::json::object();
prep_ret[3]["success"] = nlohmann::json::object();
prep_ret[3]["success"]["/lights/3/state/sat"] = 250;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.setColorHueSaturation({153, 0}));
EXPECT_EQ(false, test_light_2.setColorHueSaturation({30000, 140}, 2));
EXPECT_EQ(true, test_light_3.setColorHueSaturation({65500, 250}, 0));
}
TEST_F(HueLightTest, getColorHueSaturation)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ((HueSaturation {0, 0}), ctest_light_1.getColorHueSaturation());
EXPECT_EQ((HueSaturation {12345, 123}), ctest_light_2.getColorHueSaturation());
EXPECT_EQ((HueSaturation {12345, 123}), ctest_light_3.getColorHueSaturation());
EXPECT_EQ((HueSaturation {0, 0}), test_light_1.getColorHueSaturation());
EXPECT_EQ((HueSaturation {12345, 123}), test_light_2.getColorHueSaturation());
EXPECT_EQ((HueSaturation {12345, 123}), test_light_3.getColorHueSaturation());
}
TEST_F(HueLightTest, setColorXY)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/3/state/xy"][0] = 0.4232;
prep_ret[2]["success"]["/lights/3/state/xy"][1] = 0.1231;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/3/state/on"] = true;
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.setColorXY({{0.01f, 0.f}, 1.f}));
EXPECT_EQ(false, test_light_2.setColorXY({{0.123f, 1.f}, 1.f}, 2));
EXPECT_EQ(true, test_light_3.setColorXY({{0.4232f, 0.1231f}, 1.f}, 0));
}
TEST_F(HueLightTest, getColorXY)
{
const Light ctest_light_1 = test_bridge.lights().get(1);
const Light ctest_light_2 = test_bridge.lights().get(2);
const Light ctest_light_3 = test_bridge.lights().get(3);
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ((XYBrightness {{0.f, 0.f}, 0.f}), ctest_light_1.getColorXY());
EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 0.f}), ctest_light_2.getColorXY());
EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 1.f}), ctest_light_3.getColorXY());
EXPECT_EQ((XYBrightness {{0.f, 0.f}, 0.f}), test_light_1.getColorXY());
EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 0.f}), test_light_2.getColorXY());
EXPECT_EQ((XYBrightness {{0.102f, 0.102f}, 1.f}), test_light_3.getColorXY());
}
TEST_F(HueLightTest, setColorRGB)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/3/state/xy"][0] = 0.1596;
prep_ret[2]["success"]["/lights/3/state/xy"][1] = 0.1437;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/3/state/on"] = true;
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/3/state/transitiontime"] = 0;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.setColorRGB({0, 0, 0}, 0));
EXPECT_EQ(false, test_light_2.setColorRGB({32, 64, 128}, 2));
EXPECT_EQ(true, test_light_3.setColorRGB({64, 128, 255}, 0));
}
TEST_F(HueLightTest, alert)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/1/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/3/state/alert"] = "select";
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(prep_ret));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.alert());
EXPECT_EQ(false, test_light_2.alert());
EXPECT_EQ(true, test_light_3.alert());
}
TEST_F(HueLightTest, alertTemperature)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.alertTemperature(400));
EXPECT_EQ(false, test_light_2.alertTemperature(100));
EXPECT_EQ(false, test_light_3.alertTemperature(0));
}
TEST_F(HueLightTest, alertHueSaturation)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.alertHueSaturation({0, 255}));
EXPECT_EQ(false, test_light_2.alertHueSaturation({3000, 100}));
EXPECT_EQ(false, test_light_3.alertHueSaturation({50000, 0}));
}
TEST_F(HueLightTest, alertXY)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.alertXY({{0.1f, 0.1f}, 1.f}));
EXPECT_EQ(false, test_light_2.alertXY({{0.2434f, 0.2344f}, 1.f}));
EXPECT_EQ(false, test_light_3.alertXY({{0.1234f, 0.1234f}, 1.f}));
}
TEST_F(HueLightTest, setColorLoop)
{
using namespace ::testing;
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/2/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/lights/3/state", _, getBridgeIp(), 80))
.Times(1)
.WillOnce(Return(nlohmann::json::array()));
Light test_light_1 = test_bridge.lights().get(1);
Light test_light_2 = test_bridge.lights().get(2);
Light test_light_3 = test_bridge.lights().get(3);
EXPECT_EQ(false, test_light_1.setColorLoop(true));
EXPECT_EQ(false, test_light_2.setColorLoop(false));
EXPECT_EQ(false, test_light_3.setColorLoop(true));
}
@@ -0,0 +1,174 @@
/**
\file test_HueLightFactory.cpp
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/HueDeviceTypes.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
TEST(LightFactory, createLight_noGamut)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
LightFactory factory(HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler),
std::chrono::steady_clock::duration::max());
nlohmann::json lightState
= {{"state",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color temperature light"},
{"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"},
{"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}};
Light test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::TEMPERATURE);
lightState["type"] = "Dimmable light";
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::NONE);
lightState["type"] = "On/Off light";
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::NONE);
lightState["type"] = "unknown light type";
ASSERT_THROW(factory.createLight(lightState, 1), HueException);
}
TEST(LightFactory, createLight_gamutCapabilities)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
LightFactory factory(HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler),
std::chrono::steady_clock::duration::max());
nlohmann::json lightState
= {{"state",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color light"},
{"name", "Hue ambiance lamp 1"}, {"modelid", "LTW001"}, {"manufacturername", "Philips"},
{"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"},
{"capabilities", {{"control", {{"colorgamuttype", "A"}}}}}};
Light test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A);
lightState["capabilities"]["control"]["colorgamuttype"] = "B";
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B);
lightState["capabilities"]["control"]["colorgamuttype"] = "C";
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C);
lightState["capabilities"]["control"]["colorgamuttype"] = "Other";
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_OTHER);
// With color temperature
lightState["type"] = "Extended color light";
lightState["capabilities"]["control"]["colorgamuttype"] = "A";
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A_TEMPERATURE);
lightState["capabilities"]["control"]["colorgamuttype"] = "B";
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B_TEMPERATURE);
lightState["capabilities"]["control"]["colorgamuttype"] = "C";
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C_TEMPERATURE);
lightState["capabilities"]["control"]["colorgamuttype"] = "Other";
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_OTHER_TEMPERATURE);
}
TEST(LightFactory, createLight_gamutModelid)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
LightFactory factory(HueCommandAPI(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler),
std::chrono::steady_clock::duration::max());
const std::string gamutAModel = "LST001";
const std::string gamutBModel = "LCT001";
const std::string gamutCModel = "LCT010";
nlohmann::json lightState
= {{"state",
{{"on", true}, {"bri", 254}, {"ct", 366}, {"alert", "none"}, {"colormode", "ct"}, {"reachable", true}}},
{"swupdate", {{"state", "noupdates"}, {"lastinstall", nullptr}}}, {"type", "Color light"},
{"name", "Hue ambiance lamp 1"}, {"modelid", gamutAModel}, {"manufacturername", "Philips"},
{"uniqueid", "00:00:00:00:00:00:00:00-00"}, {"swversion", "5.50.1.19085"}};
Light test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A);
lightState["modelid"] = gamutBModel;
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B);
lightState["modelid"] = gamutCModel;
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C);
// With color temperature
lightState["type"] = "Extended color light";
lightState["modelid"] = gamutAModel;
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_A_TEMPERATURE);
lightState["modelid"] = gamutBModel;
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_B_TEMPERATURE);
lightState["modelid"] = gamutCModel;
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::GAMUT_C_TEMPERATURE);
// Unknown model
lightState["modelid"] = "Unknown model";
test_light_1 = factory.createLight(lightState, 1);
EXPECT_EQ(test_light_1.getColorType(), ColorType::UNDEFINED);
}
+55
View File
@@ -0,0 +1,55 @@
/**
\file test_Main.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 <http://www.gnu.org/licenses/>.
**/
#include <gtest/gtest.h>
#include <hueplusplus/LibConfig.h>
namespace
{
class TestConfig : public hueplusplus::Config
{
public:
TestConfig()
{
preAlertDelay = postAlertDelay = upnpTimeout = bridgeRequestDelay = requestUsernameDelay
= requestUsernameAttemptInterval = std::chrono::seconds(0);
}
};
// Environment sets config to disable all delays and speed up tests
class Environment : public ::testing::Environment
{
public:
~Environment() override {}
void SetUp() override { hueplusplus::Config::instance() = TestConfig(); }
void TearDown() override {}
};
} // namespace
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
::testing::AddGlobalTestEnvironment(new Environment());
return RUN_ALL_TESTS();
}
@@ -0,0 +1,75 @@
/**
\file test_NewDeviceList.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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/HueException.h>
#include <hueplusplus/NewDeviceList.h>
#include <gtest/gtest.h>
using namespace hueplusplus;
using namespace testing;
TEST(NewDeviceList, Constructor)
{
{
NewDeviceList list("none", {});
EXPECT_TRUE(list.getNewDevices().empty());
EXPECT_FALSE(list.hasLastScanTime());
EXPECT_FALSE(list.isScanActive());
EXPECT_THROW(list.getLastScanTime(), HueException);
}
{
const std::map<int, std::string> devices = {{1, "a"}, {2, "b"}, {3, "c"}};
NewDeviceList list("active", devices);
EXPECT_FALSE(list.hasLastScanTime());
EXPECT_TRUE(list.isScanActive());
EXPECT_EQ(devices, list.getNewDevices());
EXPECT_THROW(list.getLastScanTime(), HueException);
}
{
const std::string timestamp = "2020-03-01T00:10:00";
NewDeviceList list(timestamp, {});
EXPECT_TRUE(list.hasLastScanTime());
EXPECT_FALSE(list.isScanActive());
EXPECT_EQ(time::AbsoluteTime::parseUTC(timestamp).getBaseTime(), list.getLastScanTime().getBaseTime());
}
}
TEST(NewDeviceList, parse)
{
{
NewDeviceList list = NewDeviceList::parse({});
EXPECT_FALSE(list.hasLastScanTime());
EXPECT_FALSE(list.isScanActive());
EXPECT_TRUE(list.getNewDevices().empty());
EXPECT_THROW(list.getLastScanTime(), HueException);
}
{
const std::map<int, std::string> devices = {{1, "a"}, {2, "b"}, {3, "c"}};
const std::string timestamp = "2020-03-01T00:10:00";
NewDeviceList list = NewDeviceList::parse(
{{"1", {{"name", "a"}}}, {"2", {{"name", "b"}}}, {"3", {{"name", "c"}}}, {"lastscan", timestamp}});
EXPECT_TRUE(list.hasLastScanTime());
EXPECT_FALSE(list.isScanActive());
EXPECT_EQ(time::AbsoluteTime::parseUTC(timestamp).getBaseTime(), list.getLastScanTime().getBaseTime());
EXPECT_EQ(devices, list.getNewDevices());
}
}
@@ -0,0 +1,300 @@
/**
\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 <http://www.gnu.org/licenses/>.
**/
#include <gtest/gtest.h>
#include "testhelper.h"
#include "hueplusplus/ResourceList.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
using namespace testing;
class TestResource
{
public:
TestResource(int id, std::shared_ptr<APICache> baseCache) {}
TestResource(int id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState) : id(id) { }
void refresh(bool force = false) { }
public:
int id;
};
class TestResourceFactory
{
public:
void refresh(bool force = false) { }
};
class TestStringResource
{
public:
TestStringResource(const std::string& id, std::shared_ptr<APICache> baseCache) {}
TestStringResource(const std::string& id, HueCommandAPI api, std::chrono::steady_clock::duration refreshDuration, const nlohmann::json& currentState)
: id(id)
{ }
void refresh(bool force = false) { }
public:
std::string id;
};
class TestCreateType
{
public:
MOCK_CONST_METHOD0(getRequest, nlohmann::json());
};
TEST(ResourceList, refresh)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string path = "/resources";
{
ResourceList<TestResource, int> 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<APICache>("", commands, std::chrono::steady_clock::duration::max(), nullptr);
ResourceList<TestResource, int> 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<MockHttpHandler>();
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<TestResource, int> 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<TestResource(int, const nlohmann::json&, const std::shared_ptr<APICache>&)> factory;
EXPECT_CALL(factory, Call(id, state, std::shared_ptr<APICache>()))
.WillOnce(Return(TestResource(id, commands, std::chrono::steady_clock::duration::max(), nullptr)));
ResourceList<TestResource, int> 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<TestStringResource, std::string> 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<TestResourceFactory, int> 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<TestResourceFactory, int> list(commands, path, std::chrono::steady_clock::duration::max(),
[](int, const nlohmann::json&, const std::shared_ptr<APICache>&) { 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<MockHttpHandler>();
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<TestResource, int> 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<MockHttpHandler>();
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<TestResource, int> 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<MockHttpHandler>();
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<TestResource, int> 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(SearchableResourceList, search)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string path = "/resources";
SearchableResourceList<TestResource> list(commands, path, std::chrono::steady_clock::duration::max());
const nlohmann::json response = {{{"success", {{path, "Searching for new devices"}}}}};
EXPECT_CALL(*handler,
POSTJson("/api/" + getBridgeUsername() + path, nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
list.search();
EXPECT_CALL(*handler,
POSTJson("/api/" + getBridgeUsername() + path, nlohmann::json({{"deviceid", {"abcd", "def", "fgh"}}}),
getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
list.search({"abcd", "def", "fgh"});
}
TEST(SearchableResourceList, getNewDevices)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string path = "/resources";
SearchableResourceList<TestResource> list(commands, path, std::chrono::steady_clock::duration::max());
const nlohmann::json response = {{"lastscan", "active"}, {"1", {{"name", "A"}}}};
EXPECT_CALL(*handler,
GETJson(
"/api/" + getBridgeUsername() + path + "/new", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
NewDeviceList newDevices = list.getNewDevices();
EXPECT_TRUE(newDevices.isScanActive());
EXPECT_THAT(newDevices.getNewDevices(), ElementsAre(std::make_pair(1, "A")));
}
TEST(CreateableResourceList, create)
{
auto handler = std::make_shared<MockHttpHandler>();
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<ResourceList<TestResource, int>, TestCreateType> 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()));
EXPECT_CALL(*handler, GETJson("/api/" + getBridgeUsername() + path, _, getBridgeIp(), getBridgePort()))
.Times(AnyNumber())
.WillRepeatedly(Return(nlohmann::json::object()));
TestCreateType params;
EXPECT_CALL(params, getRequest()).Times(2).WillRepeatedly(Return(request));
EXPECT_EQ(2, list.create(params));
EXPECT_EQ(0, list.create(params));
}
+270
View File
@@ -0,0 +1,270 @@
/**
\file test_Rule.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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/Rule.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
using namespace testing;
TEST(Condition, Constructor)
{
const std::string address = "/api/abcd/test";
const std::string value = "test value";
Condition condition(address, Condition::Operator::eq, value);
EXPECT_EQ(address, condition.getAddress());
EXPECT_EQ(Condition::Operator::eq, condition.getOperator());
EXPECT_EQ(value, condition.getValue());
}
TEST(Condition, toJson)
{
Condition condition("/abcd", Condition::Operator::lt, "3");
EXPECT_EQ(nlohmann::json({{"address", "/abcd"}, {"operator", "lt"}, {"value", "3"}}), condition.toJson());
}
TEST(Condition, parse)
{
Condition condition = Condition::parse(nlohmann::json({{"address", "/abcd"}, {"operator", "lt"}, {"value", "3"}}));
EXPECT_EQ("/abcd", condition.getAddress());
EXPECT_EQ(Condition::Operator::lt, condition.getOperator());
EXPECT_EQ("3", condition.getValue());
EXPECT_THROW(Condition::parse(nlohmann::json({{"address", "/abcd"}, {"operator", "something"}, {"value", "3"}})),
HueException);
}
TEST(Condition, operatorString)
{
using Op = Condition::Operator;
std::map<Op, std::string> values = {{Op::eq, "eq"}, {Op::gt, "gt"}, {Op::lt, "lt"}, {Op::dx, "dx"},
{Op::ddx, "ddx"}, {Op::stable, "stable"}, {Op::notStable, "not stable"}, {Op::in, "in"}, {Op::notIn, "not in"}};
for (const auto& pair : values)
{
Condition c("", pair.first, "");
// Check that correct string is
EXPECT_EQ(pair.second, c.toJson().at("operator"));
EXPECT_EQ(pair.first,
Condition::parse(nlohmann::json {{"address", "/abcd"}, {"operator", pair.second}, {"value", "3"}})
.getOperator());
}
}
class RuleTest : public Test
{
protected:
std::shared_ptr<MockHttpHandler> handler;
HueCommandAPI commands;
nlohmann::json ruleState;
RuleTest()
: handler(std::make_shared<MockHttpHandler>()),
commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler),
ruleState({{"name", "Rule 1"}, {"owner", "testOwner"}, {"created", "2020-06-01T10:00:00"},
{"lasttriggered", "none"}, {"timestriggered", 0}, {"status", "enabled"},
{"conditions", {{{"address", "testAddress"}, {"operator", "eq"}, {"value", "10"}}}},
{"actions", {{{"address", "testAction"}, {"method", "PUT"}, {"body", {}}}}}})
{ }
void expectGetState(int id)
{
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + "/rules/" + std::to_string(id), _, getBridgeIp(), getBridgePort()))
.WillOnce(Return(ruleState));
}
Rule getRule(int id = 1)
{
expectGetState(id);
return Rule(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
}
};
TEST_F(RuleTest, getName)
{
const std::string name = "Rule name";
ruleState["name"] = name;
const Rule rule = getRule();
EXPECT_EQ(name, rule.getName());
}
TEST_F(RuleTest, setName)
{
Rule rule = getRule();
const std::string name = "Test rule";
nlohmann::json request = {{"name", name}};
nlohmann::json response = {{"success", {"/rules/1/name", name}}};
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/rules/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(1);
rule.setName(name);
}
TEST_F(RuleTest, getCreated)
{
const std::string timestamp = "2020-06-01T10:00:00";
ruleState["created"] = timestamp;
const Rule rule = getRule();
EXPECT_EQ(time::AbsoluteTime::parseUTC(timestamp).getBaseTime(), rule.getCreated().getBaseTime());
}
TEST_F(RuleTest, getLastTriggered)
{
const std::string timestamp = "2020-06-01T10:00:00";
ruleState["lasttriggered"] = timestamp;
const Rule rule = getRule();
EXPECT_EQ(time::AbsoluteTime::parseUTC(timestamp).getBaseTime(), rule.getLastTriggered().getBaseTime());
ruleState["lasttriggered"] = "none";
const Rule rule2 = getRule();
EXPECT_EQ(std::chrono::system_clock::time_point(std::chrono::seconds(0)), rule2.getLastTriggered().getBaseTime());
}
TEST_F(RuleTest, getTimesTriggered)
{
const int times = 20;
ruleState["timestriggered"] = times;
EXPECT_EQ(times, getRule().getTimesTriggered());
}
TEST_F(RuleTest, isEnabled)
{
ruleState["status"] = "enabled";
EXPECT_TRUE(getRule().isEnabled());
ruleState["status"] = "disabled";
EXPECT_FALSE(getRule().isEnabled());
}
TEST_F(RuleTest, setEnabled)
{
Rule rule = getRule();
{
nlohmann::json request = {{"status", "enabled"}};
nlohmann::json response = {{"success", {"/rules/1/status", "enabled"}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/rules/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(1);
rule.setEnabled(true);
}
{
nlohmann::json request = {{"status", "disabled"}};
nlohmann::json response = {{"success", {"/rules/1/status", "disabled"}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/rules/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(1);
rule.setEnabled(false);
}
}
TEST_F(RuleTest, getOwner)
{
const std::string owner = "testowner";
ruleState["owner"] = owner;
EXPECT_EQ(owner, getRule().getOwner());
}
TEST_F(RuleTest, getConditions)
{
std::vector<Condition> conditions
= {Condition("/a/b/c", Condition::Operator::eq, "12"), Condition("/d/c", Condition::Operator::dx, "")};
ruleState["conditions"] = {conditions[0].toJson(), conditions[1].toJson()};
const std::vector<Condition> result = getRule().getConditions();
ASSERT_EQ(2, result.size());
EXPECT_EQ(conditions[0].toJson(), result[0].toJson());
EXPECT_EQ(conditions[1].toJson(), result[1].toJson());
}
TEST_F(RuleTest, getActions)
{
nlohmann::json action0 {{"address", "/a/b"}, {"method", "PUT"}, {"body", {{"value", "test"}}}};
nlohmann::json action1 {{"address", "/c/d"}, {"method", "POST"}, {"body", {{"32", 1}}}};
ruleState["actions"] = {action0, action1};
const std::vector<hueplusplus::Action> result = getRule().getActions();
ASSERT_EQ(2, result.size());
EXPECT_EQ(action0, result[0].toJson());
EXPECT_EQ(action1, result[1].toJson());
}
TEST_F(RuleTest, setConditions)
{
std::vector<Condition> conditions
= {Condition("/a/b/c", Condition::Operator::eq, "12"), Condition("/d/c", Condition::Operator::dx, "")};
const nlohmann::json request = {{"conditions", {conditions[0].toJson(), conditions[1].toJson()}}};
Rule rule = getRule();
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/rules/1", request, getBridgeIp(), getBridgePort()));
expectGetState(1);
rule.setConditions(conditions);
}
TEST_F(RuleTest, setActions)
{
using hueplusplus::Action;
nlohmann::json action0 {{"address", "/a/b"}, {"method", "PUT"}, {"body", {{"value", "test"}}}};
nlohmann::json action1 {{"address", "/c/d"}, {"method", "POST"}, {"body", {{"32", 1}}}};
const nlohmann::json request = {{"actions", {action0, action1}}};
const std::vector<Action> actions = {Action(action0), Action(action1)};
Rule rule = getRule();
EXPECT_CALL(*handler, PUTJson("/api/" + getBridgeUsername() + "/rules/1", request, getBridgeIp(), getBridgePort()));
expectGetState(1);
rule.setActions(actions);
}
TEST(CreateRule, setName)
{
const std::string name = "New rule";
const nlohmann::json request = {{"conditions", nullptr}, {"actions", nullptr}, {"name", name}};
EXPECT_EQ(request, CreateRule({}, {}).setName(name).getRequest());
}
TEST(CreateRule, setStatus)
{
{
const nlohmann::json request = {{"conditions", nullptr}, {"actions", nullptr}, {"status", "enabled"}};
EXPECT_EQ(request, CreateRule({}, {}).setStatus(true).getRequest());
}
{
const nlohmann::json request = {{"conditions", nullptr}, {"actions", nullptr}, {"status", "disabled"}};
EXPECT_EQ(request, CreateRule({}, {}).setStatus(false).getRequest());
}
}
TEST(CreateRule, Constructor)
{
using hueplusplus::Action;
std::vector<Condition> conditions
= {Condition("/a/b/c", Condition::Operator::eq, "12"), Condition("/d/c", Condition::Operator::dx, "")};
nlohmann::json action0 {{"address", "/a/b"}, {"method", "PUT"}, {"body", {{"value", "test"}}}};
nlohmann::json action1 {{"address", "/c/d"}, {"method", "POST"}, {"body", {{"32", 1}}}};
const std::vector<Action> actions = {Action(action0), Action(action1)};
const nlohmann::json request
= {{"conditions", {conditions[0].toJson(), conditions[1].toJson()}}, {"actions", {action0, action1}}};
EXPECT_EQ(request, CreateRule(conditions, actions).getRequest());
}
+501
View File
@@ -0,0 +1,501 @@
/**
\file test_Scene.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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/Scene.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
using namespace testing;
TEST(LightState, on)
{
EXPECT_FALSE(LightState(nlohmann::json::object()).isOn());
EXPECT_TRUE(LightState(nlohmann::json {{"on", true}}).isOn());
EXPECT_FALSE(LightState(nlohmann::json {{"on", false}}).isOn());
}
TEST(LightState, Brightness)
{
EXPECT_FALSE(LightState(nlohmann::json::object()).hasBrightness());
const int bri = 125;
nlohmann::json json {{"bri", bri}};
const LightState state {json};
EXPECT_TRUE(state.hasBrightness());
EXPECT_EQ(bri, state.getBrightness());
}
TEST(LightState, HueSat)
{
EXPECT_FALSE(LightState(nlohmann::json::object()).hasHueSat());
EXPECT_FALSE(LightState(nlohmann::json {{"hue", 0}}).hasHueSat());
EXPECT_FALSE(LightState(nlohmann::json {{"sat", 0}}).hasHueSat());
const int hue = 12553;
const int sat = 240;
nlohmann::json json {{"hue", hue}, {"sat", sat}};
const LightState state {json};
EXPECT_TRUE(state.hasHueSat());
EXPECT_EQ(hue, state.getHueSat().hue);
EXPECT_EQ(sat, state.getHueSat().saturation);
}
TEST(LightState, XY)
{
EXPECT_FALSE(LightState(nlohmann::json::object()).hasXY());
const float x = 0.6f;
const float y = 0.3f;
nlohmann::json json {{"xy", {x, y}}, {"bri", 255}};
const LightState state {json};
EXPECT_TRUE(state.hasXY());
EXPECT_FLOAT_EQ(x, state.getXY().xy.x);
EXPECT_FLOAT_EQ(y, state.getXY().xy.y);
EXPECT_FLOAT_EQ(1.f, state.getXY().brightness);
}
TEST(LightState, Ct)
{
EXPECT_FALSE(LightState(nlohmann::json::object()).hasCt());
const int ct = 260;
nlohmann::json json {{"ct", ct}};
const LightState state {json};
EXPECT_TRUE(state.hasCt());
EXPECT_EQ(ct, state.getCt());
}
TEST(LightState, Effect)
{
EXPECT_FALSE(LightState(nlohmann::json::object()).hasEffect());
nlohmann::json json {{"effect", "colorloop"}};
const LightState state {json};
EXPECT_TRUE(state.hasEffect());
EXPECT_TRUE(state.getColorloop());
EXPECT_FALSE(LightState(nlohmann::json {{"effect", "none"}}).getColorloop());
}
TEST(LightState, TransitionTime)
{
EXPECT_EQ(4, LightState(nlohmann::json::object()).getTransitionTime());
EXPECT_EQ(0, LightState(nlohmann::json {{"transitiontime", 0}}).getTransitionTime());
}
TEST(LightState, toJson)
{
const nlohmann::json json {{"on", false}, {"bri", 254}, {"ct", 400}};
EXPECT_EQ(json, LightState(json).toJson());
}
TEST(LightStateBuilder, create)
{
{
const nlohmann::json json {{"on", false}, {"bri", 254}, {"ct", 400}, {"effect", "colorloop"}};
EXPECT_EQ(
json, LightStateBuilder().setOn(false).setBrightness(254).setCt(400).setColorloop(true).create().toJson());
}
{
const nlohmann::json json {{"xy", {0.5f, 0.5f}}, {"effect", "none"}};
EXPECT_EQ(json, LightStateBuilder().setXY({0.5f, 0.5f}).setColorloop(false).create().toJson());
}
{
const nlohmann::json json {{"hue", 360}, {"sat", 230}, {"transitiontime", 4}};
EXPECT_EQ(json, LightStateBuilder().setHueSat({360, 230}).setTransitionTime(4).create().toJson());
}
}
class SceneTest : public Test
{
public:
std::shared_ptr<MockHttpHandler> handler;
HueCommandAPI commands;
nlohmann::json sceneState;
SceneTest()
: handler(std::make_shared<MockHttpHandler>()),
commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler),
sceneState({{"name", "Test scene"}, {"type", "GroupScene"}, {"group", "4"}, {"lights", {"3", "4", "5"}},
{"owner", "testowner"}, {"recycle", false}, {"locked", false},
{"appdata", {{"data", "test-data"}, {"version", 2}}}, {"picture", ""},
{"lastupdated", "2020-04-23T12:00:04"}, {"version", 2},
{"lightstates",
{{"3", {{"on", false}, {"bri", 100}, {"xy", {0.3, 0.2}}}},
{"4", {{"on", true}, {"bri", 200}, {"xy", {0.3, 0.2}}, {"effect", "colorloop"}}},
{"5", {{"on", true}, {"bri", 100}, {"xy", {0.3, 0.2}}}}}}})
{ }
void expectGetState(const std::string& id)
{
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + "/scenes/" + id, nlohmann::json::object(), getBridgeIp(),
getBridgePort()))
.WillOnce(Return(sceneState));
}
};
TEST_F(SceneTest, Constructor)
{
const std::string id = "asd89263";
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(id, scene.getId());
}
TEST_F(SceneTest, getName)
{
const std::string id = "125abets8912";
const std::string name = "Scene name";
sceneState["name"] = name;
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(name, scene.getName());
}
TEST_F(SceneTest, getType)
{
const std::string id = "125abets8912";
{
sceneState["type"] = "GroupScene";
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(Scene::Type::groupScene, scene.getType());
}
{
sceneState["type"] = "LightScene";
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(Scene::Type::lightScene, scene.getType());
}
}
TEST_F(SceneTest, getGroupId)
{
const std::string id = "125abets8912";
{
sceneState["group"] = "3";
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(3, scene.getGroupId());
}
{
sceneState["type"] = "LightScene";
sceneState.erase("group");
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(0, scene.getGroupId());
}
}
TEST_F(SceneTest, getLightIds)
{
const std::string id = "125asav3";
sceneState["lights"] = {"3", "4", "5"};
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_THAT(scene.getLightIds(), UnorderedElementsAre(3, 4, 5));
}
TEST_F(SceneTest, getOwner)
{
const std::string id = "125asav3";
const std::string owner = "testowner";
sceneState["owner"] = owner;
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(owner, scene.getOwner());
}
TEST_F(SceneTest, getRecycle)
{
const std::string id = "125asav3";
const bool recycle = true;
sceneState["recycle"] = recycle;
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(recycle, scene.getRecycle());
}
TEST_F(SceneTest, isLocked)
{
const std::string id = "125asav3";
const bool locked = true;
sceneState["locked"] = locked;
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(locked, scene.isLocked());
}
TEST_F(SceneTest, getAppdata)
{
const std::string id = "125asav3";
const std::string appdata = "some data";
const int version = 10;
sceneState["appdata"] = {{"version", version}, {"data", appdata}};
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(version, scene.getAppdataVersion());
EXPECT_EQ(appdata, scene.getAppdata());
}
TEST_F(SceneTest, getPicture)
{
const std::string id = "125asav3";
const std::string picture = "abcpicture";
sceneState["picture"] = picture;
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(picture, scene.getPicture());
}
TEST_F(SceneTest, getLastUpdated)
{
const std::string id = "125asav3";
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
const time::AbsoluteTime lastUpdated = scene.getLastUpdated();
EXPECT_EQ(time::parseUTCTimestamp("2020-04-23T12:00:04"), lastUpdated.getBaseTime());
}
TEST_F(SceneTest, getVersion)
{
const std::string id = "125asav3";
const int version = 2;
sceneState["version"] = version;
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(version, scene.getVersion());
}
TEST_F(SceneTest, getLightstates)
{
const std::string id = "125asav3";
{
const std::map<int, LightState> lightstates {
{3, LightStateBuilder().setOn(false).setBrightness(100).setXY({0.3, 0.2}).create()},
{4, LightStateBuilder().setOn(false).setBrightness(200).setXY({0.3, 0.2}).setColorloop(true).create()},
{5, LightStateBuilder().setOn(true).setBrightness(100).setXY({0.3, 0.2}).create()}};
nlohmann::json lightstatesJson;
for (const auto& entry : lightstates)
{
lightstatesJson[std::to_string(entry.first)] = entry.second.toJson();
}
sceneState["lightstates"] = lightstatesJson;
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
const std::map<int, LightState> result = scene.getLightStates();
EXPECT_EQ(lightstates, result);
}
// No lightstates (old scene)
{
sceneState.erase("lightstates");
expectGetState(id);
const Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_TRUE(scene.getLightStates().empty());
}
}
TEST_F(SceneTest, refresh)
{
const std::string id = "125asav3";
expectGetState(id);
Scene scene(id, commands, std::chrono::seconds(0), nullptr);
expectGetState(id);
scene.refresh(true);
}
TEST_F(SceneTest, setName)
{
const std::string id = "125asav3";
expectGetState(id);
Scene scene(id, commands, std::chrono::seconds(0), nullptr);
const std::string name = "Scene name";
nlohmann::json request = {{"name", name}};
nlohmann::json response = {{"success", {"/scenes/" + id + "/name", name}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
scene.setName(name);
}
TEST_F(SceneTest, setLightIds)
{
const std::string id = "125asav3";
expectGetState(id);
Scene scene(id, commands, std::chrono::seconds(0), nullptr);
const std::vector<int> lightIds = {3, 4, 6, 8};
nlohmann::json request = {{"lights", {"3", "4", "6", "8"}}};
nlohmann::json response = {{"success", {"/scenes/" + id + "/lights", {"3", "4", "6", "8"}}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
scene.setLightIds(lightIds);
}
TEST_F(SceneTest, setAppdata)
{
const std::string id = "125asav3";
expectGetState(id);
Scene scene(id, commands, std::chrono::seconds(0), nullptr);
const std::string appdata = "New appdata";
const int version = 3;
nlohmann::json request = {{"appdata", {{"version", version}, {"data", appdata}}}};
nlohmann::json response = {{"success", {"/scenes/" + id + "/appdata/version", version}},
{"success", {"/scenes/" + id + "/appdata/data", appdata}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
scene.setAppdata(appdata, version);
}
TEST_F(SceneTest, setLightStates)
{
const std::string id = "125asav3";
const std::map<int, LightState> lightstates {
{3, LightStateBuilder().setOn(false).setBrightness(100).setCt(200).create()},
{5, LightStateBuilder().setOn(true).setBrightness(200).setXY({0.3, 0.2}).create()}};
nlohmann::json lightstatesJson;
for (const auto& entry : lightstates)
{
lightstatesJson[std::to_string(entry.first)] = entry.second.toJson();
}
expectGetState(id);
Scene scene(id, commands, std::chrono::seconds(0), nullptr);
nlohmann::json request = {{"lightstates", lightstatesJson}};
nlohmann::json response = {{"success", {"/scenes/" + id + "/lights/3/state/on", false}},
{"success", {"/scenes/" + id + "/lights/3/state/bri", 100}},
{"success", {"/scenes/" + id + "/lights/3/state/ct", 200}},
{"success", {"/scenes/" + id + "/lights/5/state/on", true}},
{"success", {"/scenes/" + id + "/lights/5/state/bri", 200}},
{"success", {"/scenes/" + id + "/lights/5/state/xy", {0.3, 0.2}}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
scene.setLightStates(lightstates);
}
TEST_F(SceneTest, storeCurrentLightState)
{
const std::string id = "125asav3";
expectGetState(id);
Scene scene(id, commands, std::chrono::seconds(0), nullptr);
{
nlohmann::json request = {{"storelightstate", true}};
nlohmann::json response = {{"success", {"/scenes/" + id + "/storelightstate", true}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
scene.storeCurrentLightState();
}
{
const int transitiontime = 3;
nlohmann::json request = {{"storelightstate", true}, {"transitiontime", 3}};
nlohmann::json response = {{"success", {"/scenes/" + id + "/storelightstate", true}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/scenes/" + id, request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
scene.storeCurrentLightState(transitiontime);
}
}
TEST_F(SceneTest, recall)
{
const std::string id = "125asav3";
// LightScene
{
sceneState["type"] = "LightScene";
expectGetState(id);
nlohmann::json request = {{"scene", id}};
nlohmann::json response = {{"success", {"/groups/0/action/scene", id}}};
Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_CALL(*handler,
PUTJson("/api/" + getBridgeUsername() + "/groups/0/action", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
scene.recall();
Mock::VerifyAndClearExpectations(handler.get());
}
// GroupScene
{
sceneState["type"] = "GroupScene";
std::string groupId = "3";
sceneState["group"] = groupId;
expectGetState(id);
nlohmann::json request = {{"scene", id}};
nlohmann::json response = {{"success", {"/groups/" + groupId + "/action/scene", id}}};
Scene scene(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_CALL(*handler,
PUTJson("/api/" + getBridgeUsername() + "/groups/" + groupId + "/action", request, getBridgeIp(),
getBridgePort()))
.WillOnce(Return(response));
scene.recall();
}
}
TEST(CreateScene, setName)
{
const std::string name = "New scene";
const nlohmann::json request = {{"name", name}};
EXPECT_EQ(request, CreateScene().setName(name).getRequest());
}
TEST(CreateScene, setGroupId)
{
const int groupId = 23;
const nlohmann::json request = {{"group", "23"}, {"type", "GroupScene"}};
EXPECT_EQ(request, CreateScene().setGroupId(groupId).getRequest());
EXPECT_THROW(CreateScene().setGroupId(2).setLightIds({1}), HueException);
}
TEST(CreateScene, setLightIds)
{
const std::vector<int> lightIds = {3, 4, 5, 9};
const nlohmann::json request = {{"lights", {"3", "4", "5", "9"}}, {"type", "LightScene"}};
EXPECT_EQ(request, CreateScene().setLightIds(lightIds).getRequest());
EXPECT_THROW(CreateScene().setLightIds(lightIds).setGroupId(1), HueException);
}
TEST(CreateScene, setRecycle)
{
const nlohmann::json request = {{"recycle", true}};
EXPECT_EQ(request, CreateScene().setRecycle(true).getRequest());
}
TEST(CreateScene, setAppdata)
{
const std::string data = "testdata";
const int version = 3;
const nlohmann::json request = {{"appdata", {{"data", data}, {"version", version}}}};
EXPECT_EQ(request, CreateScene().setAppdata(data, version).getRequest());
}
TEST(CreateScene, setLightStates)
{
const std::map<int, LightState> lightStates
= {{1, LightStateBuilder().setOn(true).create()}, {5, LightStateBuilder().setCt(300).create()}};
const nlohmann::json request = {{"lightstates", {{"1", {{"on", true}}}, {"5", {{"ct", 300}}}}}};
EXPECT_EQ(request, CreateScene().setLightStates(lightStates).getRequest());
}
+330
View File
@@ -0,0 +1,330 @@
/**
\file test_Schedule.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 <http://www.gnu.org/licenses/>.
**/
#include <gtest/gtest.h>
#include <hueplusplus/Schedule.h>
#include "testhelper.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
using namespace testing;
class ScheduleTest : public Test
{
protected:
std::shared_ptr<MockHttpHandler> handler;
HueCommandAPI commands;
nlohmann::json scheduleState;
ScheduleTest()
: handler(std::make_shared<MockHttpHandler>()),
commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler),
scheduleState({{"name", "Schedule 1"}, {"description", "nice schedule"},
{"command", {{"address", "/test"}, {"body", {}}, {"method", "PUT"}}}, {"created", "2020-03-03T23:00:03"},
{"localtime", "T13:00:00/T14:00:00"}, {"status", "enabled"}, {"autodelete", false},
{"starttime", "2020-04-01T00:00:00"}})
{}
void expectGetState(int id)
{
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + "/schedules/" + std::to_string(id), nlohmann::json::object(),
getBridgeIp(), getBridgePort()))
.WillOnce(Return(scheduleState));
}
};
TEST_F(ScheduleTest, Constructor)
{
{
const int id = 13;
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(id, schedule.getId());
Mock::VerifyAndClearExpectations(handler.get());
}
{
const int id = 0;
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(id, schedule.getId());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST_F(ScheduleTest, getName)
{
const int id = 1;
const std::string name = "Schedule name";
scheduleState["name"] = name;
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(name, schedule.getName());
}
TEST_F(ScheduleTest, getDescription)
{
const int id = 1;
const std::string description = "Schedule description";
scheduleState["description"] = description;
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(description, schedule.getDescription());
}
TEST_F(ScheduleTest, getCommand)
{
const int id = 1;
const std::string addr = "/api/blabla";
const nlohmann::json body = {{"test", "value"}};
scheduleState["command"] = {{"address", addr}, {"body", body}, {"method", "PUT"}};
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
hueplusplus::Action command = schedule.getCommand();
EXPECT_EQ(addr, command.getAddress());
EXPECT_EQ(body, command.getBody());
EXPECT_EQ(hueplusplus::Action::Method::put, command.getMethod());
}
TEST_F(ScheduleTest, getTime)
{
const int id = 1;
const std::string time = "T13:00:00/T14:00:00";
scheduleState["localtime"] = time;
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
time::TimePattern pattern = schedule.getTime();
EXPECT_EQ(time, pattern.toString());
}
TEST_F(ScheduleTest, getStatus)
{
const int id = 1;
{
scheduleState["status"] = "enabled";
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(true, schedule.isEnabled());
}
{
scheduleState["status"] = "disabled";
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(false, schedule.isEnabled());
}
}
TEST_F(ScheduleTest, getAutodelete)
{
const int id = 1;
const bool autodelete = true;
scheduleState["autodelete"] = autodelete;
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(autodelete, schedule.getAutodelete());
}
TEST_F(ScheduleTest, getCreated)
{
const int id = 1;
const std::string created = "2020-03-03T08:20:53";
scheduleState["created"] = created;
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(created, schedule.getCreated().toString());
}
TEST_F(ScheduleTest, getStartTime)
{
const int id = 1;
const std::string starttime = "2020-03-03T08:20:53";
scheduleState["starttime"] = starttime;
expectGetState(id);
const Schedule schedule(id, commands, std::chrono::seconds(0), nullptr);
EXPECT_EQ(starttime, schedule.getStartTime().toString());
}
TEST_F(ScheduleTest, setName)
{
const int id = 1;
expectGetState(id);
Schedule schedule(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
const std::string name = "Test schedule";
nlohmann::json request = {{"name", name}};
nlohmann::json response = {{"success", {"/schedules/1/name", name}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
schedule.setName(name);
}
TEST_F(ScheduleTest, setDescription)
{
const int id = 1;
expectGetState(id);
Schedule schedule(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
const std::string description = "Test schedule description";
nlohmann::json request = {{"description", description}};
nlohmann::json response = {{"success", {"/schedules/1/description", description}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
schedule.setDescription(description);
}
TEST_F(ScheduleTest, setCommand)
{
const int id = 1;
expectGetState(id);
Schedule schedule(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
const hueplusplus::Action command({{"address", "abcd"}, {"body", {}}, {"method", "PUT"}});
nlohmann::json request = {{"command", command.toJson()}};
nlohmann::json response = {{"success", {"/schedules/1/command", command.toJson()}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
schedule.setCommand(command);
}
TEST_F(ScheduleTest, setTime)
{
const int id = 1;
expectGetState(id);
Schedule schedule(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
time::TimePattern time {time::AbsoluteVariedTime(std::chrono::system_clock::now())};
nlohmann::json request = {{"localtime", time.toString()}};
nlohmann::json response = {{"success", {"/schedules/1/localtime", time.toString()}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
schedule.setTime(time);
}
TEST_F(ScheduleTest, setStatus)
{
const int id = 1;
expectGetState(id);
Schedule schedule(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
{
nlohmann::json request = {{"status", "enabled"}};
nlohmann::json response = {{"success", {"/schedules/1/status", "enabled"}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
schedule.setEnabled(true);
}
{
nlohmann::json request = {{"status", "disabled"}};
nlohmann::json response = {{"success", {"/schedules/1/status", "disabled"}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
schedule.setEnabled(false);
}
}
TEST_F(ScheduleTest, setAutodelete)
{
const int id = 1;
expectGetState(id);
Schedule schedule(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
const bool autodelete = false;
nlohmann::json request = {{"autodelete", autodelete}};
nlohmann::json response = {{"success", {"/schedules/1/autodelete", autodelete}}};
EXPECT_CALL(
*handler, PUTJson("/api/" + getBridgeUsername() + "/schedules/1", request, getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
expectGetState(id);
schedule.setAutodelete(autodelete);
}
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", {}}};
hueplusplus::Action command {commandJson};
const nlohmann::json request = {{"command", commandJson}};
EXPECT_EQ(request, CreateSchedule().setCommand(command).getRequest());
}
TEST(CreateSchedule, setTime)
{
const time::AbsoluteVariedTime 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(true).getRequest());
}
{
const nlohmann::json request = {{"status", "disabled"}};
EXPECT_EQ(request, CreateSchedule().setStatus(false).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());
}
}
+257
View File
@@ -0,0 +1,257 @@
/**
\file test_Sensor.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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/Sensor.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
using namespace testing;
class SensorTest : public Test
{
protected:
std::shared_ptr<MockHttpHandler> handler;
HueCommandAPI commands;
nlohmann::json state;
protected:
SensorTest()
: handler(std::make_shared<MockHttpHandler>()),
commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler),
state({{"type", "testSensor"}, {"name", "Test sensor"}, {"swversion", "1.2.3.4"}, {"modelid", "test"},
{"manufacturername", "testManuf"}, {"uniqueid", "00:00:00:00:00:00:00:00-00"},
{"productname", "Test sensor"}, {"config", nlohmann::json::object()},
{"state", nlohmann::json::object()}})
{ }
Sensor getSensor(int id = 1)
{
EXPECT_CALL(*handler,
GETJson(
"/api/" + getBridgeUsername() + "/sensors/" + std::to_string(id), _, getBridgeIp(), getBridgePort()))
.WillOnce(Return(state));
return Sensor(id, commands, std::chrono::steady_clock::duration::max(), nullptr);
}
};
TEST(Alert, alertFromString)
{
EXPECT_EQ(Alert::none, alertFromString("none"));
EXPECT_EQ(Alert::select, alertFromString("select"));
EXPECT_EQ(Alert::lselect, alertFromString("lselect"));
EXPECT_EQ(Alert::none, alertFromString("anything"));
}
TEST(Alert, alertToString)
{
EXPECT_EQ("none", alertToString(Alert::none));
EXPECT_EQ("select", alertToString(Alert::select));
EXPECT_EQ("lselect", alertToString(Alert::lselect));
}
TEST_F(SensorTest, on)
{
EXPECT_FALSE(getSensor().hasOn());
state["config"]["on"] = true;
EXPECT_TRUE(getSensor().hasOn());
EXPECT_TRUE(getSensor().isOn());
EXPECT_CALL(*handler,
PUTJson("/api/" + getBridgeUsername() + "/sensors/1/config", nlohmann::json({{"on", false}}), getBridgeIp(),
getBridgePort()))
.WillOnce(Return(nlohmann::json {{{"success", {{"/sensors/1/config/on", false}}}}}));
getSensor().setOn(false);
}
TEST_F(SensorTest, BatteryState)
{
EXPECT_FALSE(getSensor().hasBatteryState());
state["config"]["battery"] = 90;
EXPECT_TRUE(getSensor().hasBatteryState());
EXPECT_EQ(90, getSensor().getBatteryState());
int percent = 10;
EXPECT_CALL(*handler,
PUTJson("/api/" + getBridgeUsername() + "/sensors/1/config", nlohmann::json({{"battery", percent}}),
getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json {{{"success", {{"/sensors/1/config/battery", percent}}}}}));
getSensor().setBatteryState(percent);
}
TEST_F(SensorTest, Alert)
{
EXPECT_FALSE(getSensor().hasAlert());
state["config"]["alert"] = "none";
EXPECT_TRUE(getSensor().hasAlert());
EXPECT_EQ(Alert::none, getSensor().getLastAlert());
std::string alert = "lselect";
EXPECT_CALL(*handler,
PUTJson("/api/" + getBridgeUsername() + "/sensors/1/config", nlohmann::json({{"alert", alert}}), getBridgeIp(),
getBridgePort()))
.WillOnce(Return(nlohmann::json {{{"success", {{"/sensors/1/config/alert", alert}}}}}));
getSensor().sendAlert(Alert::lselect);
}
TEST_F(SensorTest, Reachable)
{
EXPECT_FALSE(getSensor().hasReachable());
state["config"]["reachable"] = false;
EXPECT_TRUE(getSensor().hasReachable());
EXPECT_FALSE(getSensor().isReachable());
}
TEST_F(SensorTest, UserTest)
{
EXPECT_FALSE(getSensor().hasUserTest());
state["config"]["usertest"] = false;
EXPECT_TRUE(getSensor().hasUserTest());
EXPECT_CALL(*handler,
PUTJson("/api/" + getBridgeUsername() + "/sensors/1/config", nlohmann::json({{"usertest", true}}),
getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json {{{"success", {{"/sensors/1/config/usertest", true}}}}}));
getSensor().setUserTest(true);
}
TEST_F(SensorTest, 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";
EXPECT_CALL(*handler,
PUTJson("/api/" + getBridgeUsername() + "/sensors/1/config", nlohmann::json({{"url", newUrl}}), getBridgeIp(),
getBridgePort()))
.WillOnce(Return(nlohmann::json {{{"success", {{"/sensors/1/config/url", newUrl}}}}}));
getSensor().setURL(newUrl);
}
TEST_F(SensorTest, getPendingConfig)
{
EXPECT_TRUE(getSensor().getPendingConfig().empty());
state["config"]["pending"] = nullptr;
EXPECT_TRUE(getSensor().getPendingConfig().empty());
state["config"]["pending"] = {"abc", "cde", "def"};
EXPECT_THAT(getSensor().getPendingConfig(), UnorderedElementsAre("abc", "cde", "def"));
}
TEST_F(SensorTest, LEDIndication)
{
EXPECT_FALSE(getSensor().hasLEDIndication());
state["config"]["ledindication"] = true;
EXPECT_TRUE(getSensor().hasLEDIndication());
EXPECT_TRUE(getSensor().getLEDIndication());
EXPECT_CALL(*handler,
PUTJson("/api/" + getBridgeUsername() + "/sensors/1/config", nlohmann::json({{"ledindication", false}}),
getBridgeIp(), getBridgePort()))
.WillOnce(Return(nlohmann::json {{{"success", {{"/sensors/1/config/ledindication", false}}}}}));
getSensor().setLEDIndication(false);
}
TEST_F(SensorTest, getConfig)
{
EXPECT_EQ(state["config"], getSensor().getConfig());
state["config"]["attribute"] = false;
EXPECT_EQ(state["config"], getSensor().getConfig());
}
TEST_F(SensorTest, setConfigAttribute)
{
const std::string key = "attribute";
const nlohmann::json value = "some 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}}}}}));
getSensor().setConfigAttribute(key, value);
}
TEST_F(SensorTest, getLastUpdated)
{
time::AbsoluteTime none = getSensor().getLastUpdated();
EXPECT_EQ(std::chrono::seconds(0), none.getBaseTime().time_since_epoch());
const std::string timestamp = "2020-05-02T12:00:01";
state["state"]["lastupdated"] = timestamp;
time::AbsoluteTime time = time::AbsoluteTime::parseUTC(timestamp);
EXPECT_EQ(time.getBaseTime(), getSensor().getLastUpdated().getBaseTime());
}
TEST_F(SensorTest, getState)
{
nlohmann::json stateContent = {{"bla", "bla"}};
state["state"] = stateContent;
EXPECT_EQ(stateContent, getSensor().getState());
}
TEST_F(SensorTest, setStateAttribute)
{
const std::string key = "attribute";
const nlohmann::json value = "some 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}}}}}));
getSensor().setStateAttribute(key, value);
}
TEST_F(SensorTest, isCertified)
{
EXPECT_FALSE(getSensor().isCertified());
state["capabilities"]["certified"] = true;
EXPECT_TRUE(getSensor().isCertified());
}
TEST_F(SensorTest, isPrimary)
{
EXPECT_FALSE(getSensor().isPrimary());
state["capabilities"]["primary"] = true;
EXPECT_TRUE(getSensor().isPrimary());
}
TEST_F(SensorTest, asSensorType)
{
// Test both rvalue and const access
{
const Sensor s = getSensor();
EXPECT_THROW(s.asSensorType<sensors::DaylightSensor>(), HueException);
}
EXPECT_THROW(getSensor().asSensorType<sensors::DaylightSensor>(), HueException);
state["type"] = sensors::DaylightSensor::typeStr;
sensors::DaylightSensor ds = getSensor().asSensorType<sensors::DaylightSensor>();
EXPECT_EQ(1, ds.getId());
const Sensor s = getSensor();
ds = s.asSensorType<sensors::DaylightSensor>();
EXPECT_EQ(s.getId(), ds.getId());
}
+392
View File
@@ -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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/CLIPSensors.h>
#include <hueplusplus/ZLLSensors.h>
#include <gtest/gtest.h>
#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 <typename T>
class SensorImplTest : public Test
{
protected:
std::shared_ptr<MockHttpHandler> handler;
HueCommandAPI commands;
nlohmann::json state;
protected:
SensorImplTest()
: handler(std::make_shared<MockHttpHandler>()),
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(), nullptr));
}
};
// Sensors with shared methods
template <typename T>
class SensorOnTest : public SensorImplTest<T>
{ };
// Only need to test one CLIP type, because they share the basic methods
using SensorOnTypes
= Types<CLIPSwitch, ZGPSwitch, ZLLSwitch, ZLLPresence, ZLLTemperature, ZLLLightLevel, DaylightSensor>;
TYPED_TEST_SUITE(SensorOnTest, SensorOnTypes);
template <typename T>
class SensorBatteryTest : public SensorImplTest<T>
{ };
using SensorBatteryTypes = Types<CLIPSwitch, ZLLSwitch, ZLLPresence, ZLLTemperature, ZLLLightLevel, DaylightSensor>;
TYPED_TEST_SUITE(SensorBatteryTest, SensorBatteryTypes);
template <typename T>
class SensorReachableTest : public SensorImplTest<T>
{ };
using SensorReachableTypes = Types<CLIPSwitch, ZLLSwitch, ZLLPresence, ZLLTemperature, ZLLLightLevel>;
TYPED_TEST_SUITE(SensorReachableTest, SensorReachableTypes);
template <typename T>
class SensorUpdateTest : public SensorImplTest<T>
{ };
using SensorUpdateTypes = Types<CLIPSwitch, ZLLSwitch, ZLLPresence, ZLLLightLevel, ZLLTemperature, DaylightSensor>;
TYPED_TEST_SUITE(SensorUpdateTest, SensorUpdateTypes);
template <typename T>
class SensorAlertTest : public SensorImplTest<T>
{ };
using SensorAlertTypes = Types<ZLLSwitch, ZLLPresence, ZLLTemperature>;
TYPED_TEST_SUITE(SensorAlertTest, SensorAlertTypes);
template <typename T>
class SensorButtonTest : public SensorImplTest<T>
{ };
using SensorButtonTypes = Types<CLIPSwitch, ZLLSwitch, ZGPSwitch>;
TYPED_TEST_SUITE(SensorButtonTest, SensorButtonTypes);
template <typename T>
class SensorTemperatureTest : public SensorImplTest<T>
{ };
using SensorTemperatureTypes = Types<CLIPTemperature, ZLLTemperature>;
TYPED_TEST_SUITE(SensorTemperatureTest, SensorTemperatureTypes);
template <typename T>
class SensorLightLevelTest : public SensorImplTest<T>
{ };
using SensorLightLevelTypes = Types<CLIPLightLevel, ZLLLightLevel>;
TYPED_TEST_SUITE(SensorLightLevelTest, SensorLightLevelTypes);
template <typename T>
class SensorPresenceTest : public SensorImplTest<T>
{ };
using SensorPresenceTypes = Types<CLIPPresence, ZLLPresence>;
TYPED_TEST_SUITE(SensorPresenceTest, SensorPresenceTypes);
// Sensors with unique methods
class DaylightSensorTest : public SensorImplTest<DaylightSensor>
{ };
class ZLLPresenceTest : public SensorImplTest<ZLLPresence>
{ };
class CLIPSwitchTest : public SensorImplTest<CLIPSwitch>
{ };
class CLIPOpenCloseTest : public SensorImplTest<CLIPOpenClose>
{ };
class CLIPPresenceTest : public SensorImplTest<CLIPPresence>
{ };
class CLIPTemperatureTest : public SensorImplTest<CLIPTemperature>
{ };
class CLIPHumidityTest : public SensorImplTest<CLIPHumidity>
{ };
class CLIPGenericFlagTest : public SensorImplTest<CLIPGenericFlag>
{ };
class CLIPGenericStatusTest : public SensorImplTest<CLIPGenericStatus>
{ };
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);
}
+100
View File
@@ -0,0 +1,100 @@
/**
\file test_SensorList.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 <http://www.gnu.org/licenses/>.
**/
#include <gtest/gtest.h>
#include "testhelper.h"
#include "hueplusplus/SensorList.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
using namespace testing;
class BlaSensor
{
public:
BlaSensor(Sensor s) { }
static constexpr const char* typeStr = "bla";
};
TEST(SensorList, getAsType)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
SensorList sensors {commands, "/sensors", std::chrono::steady_clock::duration::max()};
const int id = 2;
const nlohmann::json response = {{std::to_string(id), {{"type", "Daylight"}}}};
EXPECT_CALL(*handler,
GETJson("/api/" + getBridgeUsername() + "/sensors", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
sensors::DaylightSensor daylightSensor = sensors.getAsType<sensors::DaylightSensor>(id);
EXPECT_THROW(sensors.getAsType<BlaSensor>(2), HueException);
}
TEST(SensorList, getAllByType)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
SensorList sensors {commands, "/sensors", std::chrono::steady_clock::duration::max()};
// Empty
{
const nlohmann::json response = nlohmann::json::object();
EXPECT_CALL(*handler,
GETJson(
"/api/" + getBridgeUsername() + "/sensors", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
EXPECT_TRUE(sensors.getAllByType<sensors::DaylightSensor>().empty());
}
// Not matching
{
const nlohmann::json response = {{"1", {{"type", "stuff"}}}};
EXPECT_CALL(*handler,
GETJson(
"/api/" + getBridgeUsername() + "/sensors", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
sensors.refresh();
EXPECT_TRUE(sensors.getAllByType<sensors::DaylightSensor>().empty());
}
// Some matching (daylight maybe not the best example, because there is always exactly one)
{
const nlohmann::json response = {{"1", {{"type", "stuff"}}}, {"2", {{"type", "Daylight"}}},
{"3", {{"type", "stuff"}}}, {"4", {{"type", "Daylight"}}}};
EXPECT_CALL(*handler,
GETJson(
"/api/" + getBridgeUsername() + "/sensors", nlohmann::json::object(), getBridgeIp(), getBridgePort()))
.WillOnce(Return(response));
sensors.refresh();
std::vector<sensors::DaylightSensor> result = sensors.getAllByType<sensors::DaylightSensor>();
EXPECT_THAT(result,
UnorderedElementsAre(Truly([](const auto& s) { return s.getId() == 2; }),
Truly([](const auto& s) { return s.getId() == 4; })));
}
}
@@ -0,0 +1,93 @@
/**
\file test_SimpleBrightnessStrategy.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 <http://www.gnu.org/licenses/>.
**/
#include <iostream>
#include <memory>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "hueplusplus/SimpleBrightnessStrategy.h"
#include <nlohmann/json.hpp>
#include "mocks/mock_HttpHandler.h"
#include "mocks/mock_Light.h"
using namespace hueplusplus;
TEST(SimpleBrightnessStrategy, setBrightness)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state";
nlohmann::json prep_ret
= {{{"success", {{"/lights/1/state/on", false}}}}, {{"success", {{"/lights/1/state/bri", 0}}}}};
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
test_light.getState()["state"]["on"] = true;
EXPECT_EQ(true, SimpleBrightnessStrategy().setBrightness(0, 4, test_light));
// Only set brightness, already off
test_light.getState()["state"]["on"] = false;
test_light.getState()["state"].erase("bri");
prep_ret = {{{"success", {{"/lights/1/state/bri", 0}}}}};
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
EXPECT_EQ(true, SimpleBrightnessStrategy().setBrightness(0, 4, test_light));
prep_ret = {{{"success", {{"/lights/1/state/on", true}}}}, {{"success", {{"/lights/1/state/bri", 50}}}}};
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
test_light.getState()["state"]["bri"] = 0;
EXPECT_EQ(true, SimpleBrightnessStrategy().setBrightness(50, 6, test_light));
test_light.getState()["state"]["on"] = true;
test_light.getState()["state"]["bri"] = 50;
// No request because state matches
EXPECT_EQ(true, SimpleBrightnessStrategy().setBrightness(50, 6, test_light));
prep_ret[1]["success"]["/lights/1/state/bri"] = 254;
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
test_light.getState()["state"]["on"] = false;
test_light.getState()["state"]["bri"] = 50;
EXPECT_EQ(true, SimpleBrightnessStrategy().setBrightness(255, 6, test_light));
}
TEST(SimpleBrightnessStrategy, getBrightness)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
test_light.getState()["state"]["bri"] = 200;
EXPECT_EQ(200, SimpleBrightnessStrategy().getBrightness(test_light));
test_light.getState()["state"]["bri"] = 0;
EXPECT_EQ(0, SimpleBrightnessStrategy().getBrightness(static_cast<const Light>(test_light)));
}
@@ -0,0 +1,486 @@
/**
\file test_SimpleColorHuewStrategy.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 <http://www.gnu.org/licenses/>.
**/
#include <memory>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "TestTransaction.h"
#include "testhelper.h"
#include "hueplusplus/SimpleColorHueStrategy.h"
#include <nlohmann/json.hpp>
#include "mocks/mock_HttpHandler.h"
#include "mocks/mock_Light.h"
using namespace hueplusplus;
TEST(SimpleColorHueStrategy, setColorHue)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state";
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/1/state/transitiontime"] = 6;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/1/state/on"] = true;
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/1/state/hue"] = 30500;
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
test_light.getState()["state"]["on"] = true;
test_light.getState()["state"]["hue"] = 200;
test_light.getState()["state"]["colormode"] = "hs";
EXPECT_EQ(true, SimpleColorHueStrategy().setColorHue(200, 4, test_light));
test_light.getState()["state"]["on"] = false;
EXPECT_EQ(true, SimpleColorHueStrategy().setColorHue(30500, 6, test_light));
}
TEST(SimpleColorHueStrategy, setColorSaturation)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state";
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/1/state/transitiontime"] = 6;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/1/state/on"] = true;
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/1/state/sat"] = 254;
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
test_light.getState()["state"]["on"] = true;
test_light.getState()["state"]["sat"] = 100;
test_light.getState()["state"]["colormode"] = "hs";
EXPECT_EQ(true, SimpleColorHueStrategy().setColorSaturation(100, 4, test_light));
test_light.getState()["state"]["on"] = false;
EXPECT_EQ(true, SimpleColorHueStrategy().setColorSaturation(255, 6, test_light));
}
TEST(SimpleColorHueStrategy, setColorHueSaturation)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state";
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/1/state/transitiontime"] = 6;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/1/state/on"] = true;
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/1/state/hue"] = 30500;
prep_ret[3] = nlohmann::json::object();
prep_ret[3]["success"] = nlohmann::json::object();
prep_ret[3]["success"]["/lights/1/state/sat"] = 254;
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
test_light.getState()["state"]["on"] = true;
test_light.getState()["state"]["sat"] = 100;
test_light.getState()["state"]["hue"] = 200;
test_light.getState()["state"]["colormode"] = "hs";
EXPECT_EQ(true, SimpleColorHueStrategy().setColorHueSaturation({200, 100}, 4, test_light));
test_light.getState()["state"]["on"] = false;
EXPECT_EQ(true, SimpleColorHueStrategy().setColorHueSaturation({30500, 255}, 6, test_light));
}
TEST(SimpleColorHueStrategy, setColorXY)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state";
nlohmann::json prep_ret
= {{{"success", {{"/lights/1/state/transitiontime", 6}}}}, {{"success", {{"/lights/1/state/on", true}}}},
{{"success", {{"/lights/1/state/xy", {0.2355, 0.1234}}}}}, {{"success", {{"/lights/1/state/bri", 254}}}}};
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
test_light.getState()["state"]["on"] = true;
test_light.getState()["state"]["xy"][0] = 0.1f;
test_light.getState()["state"]["xy"][1] = 0.1f;
test_light.getState()["state"]["bri"] = 254;
test_light.getState()["state"]["colormode"] = "xy";
EXPECT_EQ(true, SimpleColorHueStrategy().setColorXY({{0.1f, 0.1f}, 1.f}, 4, test_light));
test_light.getState()["state"]["on"] = false;
test_light.getState()["state"]["bri"] = 0;
EXPECT_EQ(true, SimpleColorHueStrategy().setColorXY({{0.2355f, 0.1234f}, 1.f}, 6, test_light));
}
TEST(SimpleColorHueStrategy, setColorLoop)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state";
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/1/state/on"] = true;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/1/state/effect"] = "colorloop";
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
test_light.getState()["state"]["on"] = true;
test_light.getState()["state"]["effect"] = "colorloop";
EXPECT_EQ(true, SimpleColorHueStrategy().setColorLoop(true, test_light));
test_light.getState()["state"]["on"] = false;
test_light.getState()["state"]["effect"] = "none";
EXPECT_EQ(true, SimpleColorHueStrategy().setColorLoop(true, test_light));
}
TEST(SimpleColorHueStrategy, alertHueSaturation)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight light(handler);
// Invalid colormode
{
light.getState()["state"]["colormode"] = "invalid";
light.getState()["state"]["on"] = false;
EXPECT_EQ(false, SimpleColorHueStrategy().alertHueSaturation({30000, 128}, light));
}
const HueSaturation hueSat {200, 100};
// Needs to update the state so transactions are correctly trimmed
const auto setColorLambda = [&](const HueSaturation& hueSat, int transition) {
light.getState()["state"]["colormode"] = "hs";
light.getState()["state"]["on"] = true;
light.getState()["state"]["hue"] = hueSat.hue;
light.getState()["state"]["sat"] = hueSat.saturation;
return true;
};
// Colormode hs
{
const nlohmann::json state
= {{"colormode", "hs"}, {"on", true}, {"xy", {0.1, 0.1}}, {"hue", 300}, {"sat", 100}, {"bri", 254}};
light.getState()["state"] = state;
EXPECT_CALL(Const(light), getBrightness()).Times(AnyNumber()).WillRepeatedly(Return(254));
EXPECT_CALL(Const(light), getColorHueSaturation())
.Times(AnyNumber())
.WillRepeatedly(Return(HueSaturation {300, 100}));
TestTransaction reverseTransaction = light.transaction().setColorHue(300).setTransition(1);
// Set color fails
{
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Return(false));
EXPECT_FALSE(SimpleColorHueStrategy().alertHueSaturation(hueSat, light));
}
// Alert call fails
{
InSequence s;
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(false));
EXPECT_FALSE(SimpleColorHueStrategy().alertHueSaturation(hueSat, light));
}
light.getState()["state"] = state;
// Reverse transaction fails
{
InSequence s;
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectPut(handler).WillOnce(Return(nlohmann::json::object()));
EXPECT_FALSE(SimpleColorHueStrategy().alertHueSaturation(hueSat, light));
}
light.getState()["state"] = state;
Mock::VerifyAndClearExpectations(handler.get());
// Successful
{
InSequence s;
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(SimpleColorHueStrategy().alertHueSaturation(hueSat, light));
}
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode hs, off
{
const nlohmann::json state
= {{"colormode", "hs"}, {"on", false}, {"xy", {0.1, 0.1}}, {"hue", 300}, {"sat", 100}, {"bri", 254}};
light.getState()["state"] = state;
TestTransaction reverseTransaction = light.transaction().setColorHue(300).setOn(false).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(SimpleColorHueStrategy().alertHueSaturation(hueSat, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode xy
{
const nlohmann::json state
= {{"colormode", "xy"}, {"on", true}, {"xy", {0.1, 0.1}}, {"sat", 100}, {"bri", 254}};
light.getState()["state"] = state;
EXPECT_CALL(Const(light), getColorXY())
.Times(AnyNumber())
.WillRepeatedly(Return(XYBrightness {{0.1f, 0.1f}, 1.f}));
TestTransaction reverseTransaction = light.transaction().setColor(XY {0.1f, 0.1f}).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(false));
EXPECT_FALSE(SimpleColorHueStrategy().alertHueSaturation(hueSat, light));
light.getState()["state"] = state;
Mock::VerifyAndClearExpectations(handler.get());
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(SimpleColorHueStrategy().alertHueSaturation(hueSat, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode xy, off
{
const nlohmann::json state = {{"colormode", "xy"}, {"on", false}, {"xy", {0., 1.}}, {"sat", 100}, {"bri", 254}};
EXPECT_CALL(Const(light), getColorXY())
.Times(AnyNumber())
.WillRepeatedly(Return(XYBrightness {{0.f, 1.f}, 1.f}));
light.getState()["state"] = state;
TestTransaction reverseTransaction = light.transaction().setColor(XY {0.f, 1.f}).setOn(false).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorHueSaturation(hueSat, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(SimpleColorHueStrategy().alertHueSaturation(hueSat, light));
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(SimpleColorHueStrategy, alertXY)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight light(handler);
// Invalid colormode
{
light.getState()["state"]["colormode"] = "invalid";
light.getState()["state"]["on"] = false;
EXPECT_EQ(false, SimpleColorHueStrategy().alertXY({{0.1f, 0.1f}, 1.f}, light));
}
const XYBrightness xy {{0.1f, 0.1f}, 1.f};
// Needs to update the state so transactions are correctly trimmed
const auto setColorLambda = [&](const XYBrightness& xy, int transition) {
light.getState()["state"]["colormode"] = "xy";
light.getState()["state"]["on"] = true;
light.getState()["state"]["xy"] = {xy.xy.x, xy.xy.y};
light.getState()["state"]["bri"] = static_cast<int>(std::round(xy.brightness * 254.f));
return true;
};
// Colormode hs
{
const nlohmann::json state
= {{"colormode", "hs"}, {"on", true}, {"xy", {0.1, 0.1}}, {"hue", 200}, {"sat", 100}, {"bri", 254}};
light.getState()["state"] = state;
EXPECT_CALL(Const(light), getBrightness()).Times(AnyNumber()).WillRepeatedly(Return(254));
HueSaturation hueSat {200, 100};
EXPECT_CALL(Const(light), getColorHueSaturation()).Times(AnyNumber()).WillRepeatedly(Return(hueSat));
TestTransaction reverseTransaction = light.transaction().setColor(hueSat).setTransition(1);
// Set color fails
{
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Return(false));
EXPECT_FALSE(SimpleColorHueStrategy().alertXY(xy, light));
}
// Alert call fails
{
InSequence s;
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(false));
EXPECT_FALSE(SimpleColorHueStrategy().alertXY(xy, light));
}
light.getState()["state"] = state;
// Reverse transaction fails
{
InSequence s;
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectPut(handler).WillOnce(Return(nlohmann::json::object()));
EXPECT_FALSE(SimpleColorHueStrategy().alertXY(xy, light));
}
light.getState()["state"] = state;
Mock::VerifyAndClearExpectations(handler.get());
// Successful
{
InSequence s;
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(SimpleColorHueStrategy().alertXY(xy, light));
}
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode hs, off
{
const nlohmann::json state
= {{"colormode", "hs"}, {"on", false}, {"xy", {0.1, 0.1}}, {"hue", 200}, {"sat", 100}, {"bri", 254}};
light.getState()["state"] = state;
TestTransaction reverseTransaction
= light.transaction().setColor(HueSaturation {200, 100}).setOn(false).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(SimpleColorHueStrategy().alertXY(xy, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode xy
{
const nlohmann::json state
= {{"colormode", "xy"}, {"on", true}, {"xy", {0.1, 0.1}}, {"sat", 100}, {"bri", 254}};
light.getState()["state"] = state;
EXPECT_CALL(Const(light), getColorXY()).Times(AnyNumber()).WillRepeatedly(Return(xy));
// No reverse transaction sent, because color already matches
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(false));
EXPECT_FALSE(SimpleColorHueStrategy().alertXY(xy, light));
light.getState()["state"] = state;
Mock::VerifyAndClearExpectations(handler.get());
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
EXPECT_TRUE(SimpleColorHueStrategy().alertXY(xy, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// Colormode xy, off
{
const nlohmann::json state = {{"colormode", "xy"}, {"on", false}, {"xy", {0., 1.}}, {"sat", 100}, {"bri", 254}};
EXPECT_CALL(Const(light), getColorXY())
.Times(AnyNumber())
.WillRepeatedly(Return(XYBrightness {{0.f, 1.f}, 1.f}));
light.getState()["state"] = state;
// Brightness unchanged, so not requested
TestTransaction reverseTransaction = light.transaction().setColor(XY {0.f, 1.f}).setOn(false).setTransition(1);
InSequence s;
EXPECT_CALL(light, setColorXY(xy, 1)).WillOnce(Invoke(setColorLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(SimpleColorHueStrategy().alertXY(xy, light));
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(SimpleColorHueStrategy, getColorHueSaturation)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
test_light.getState()["state"]["hue"] = 5000;
test_light.getState()["state"]["sat"] = 128;
EXPECT_EQ((HueSaturation {5000, 128}), SimpleColorHueStrategy().getColorHueSaturation(test_light));
test_light.getState()["state"]["hue"] = 50000;
test_light.getState()["state"]["sat"] = 158;
EXPECT_EQ((HueSaturation {50000, 158}),
SimpleColorHueStrategy().getColorHueSaturation(static_cast<const Light>(test_light)));
}
TEST(SimpleColorHueStrategy, getColorXY)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
test_light.getState()["state"]["xy"][0] = 0.1234;
test_light.getState()["state"]["xy"][1] = 0.1234;
test_light.getState()["state"]["bri"] = 254;
EXPECT_EQ((XYBrightness {{0.1234f, 0.1234f}, 1.f}), SimpleColorHueStrategy().getColorXY(test_light));
test_light.getState()["state"]["xy"][0] = 0.12;
test_light.getState()["state"]["xy"][1] = 0.6458;
EXPECT_EQ((XYBrightness {{0.12f, 0.6458f}, 1.f}), SimpleColorHueStrategy().getColorXY(Const(test_light)));
}
@@ -0,0 +1,154 @@
/**
\file test_SimpleColorTemperatureStrategy.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 <http://www.gnu.org/licenses/>.
**/
#include <iostream>
#include <memory>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "TestTransaction.h"
#include "testhelper.h"
#include "hueplusplus/SimpleColorTemperatureStrategy.h"
#include <nlohmann/json.hpp>
#include "mocks/mock_HttpHandler.h"
#include "mocks/mock_Light.h"
using namespace hueplusplus;
TEST(SimpleColorTemperatureStrategy, setColorTemperature)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
const std::string statePath = "/api/" + getBridgeUsername() + "/lights/1/state";
nlohmann::json prep_ret;
prep_ret = nlohmann::json::array();
prep_ret[0] = nlohmann::json::object();
prep_ret[0]["success"] = nlohmann::json::object();
prep_ret[0]["success"]["/lights/1/state/transitiontime"] = 6;
prep_ret[1] = nlohmann::json::object();
prep_ret[1]["success"] = nlohmann::json::object();
prep_ret[1]["success"]["/lights/1/state/on"] = true;
prep_ret[2] = nlohmann::json::object();
prep_ret[2]["success"] = nlohmann::json::object();
prep_ret[2]["success"]["/lights/1/state/ct"] = 155;
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
test_light.getState()["state"]["on"] = true;
test_light.getState()["state"]["ct"] = 200;
test_light.getState()["state"]["colormode"] = "ct";
EXPECT_EQ(true, SimpleColorTemperatureStrategy().setColorTemperature(200, 4, test_light));
test_light.getState()["state"]["on"] = false;
EXPECT_EQ(true, SimpleColorTemperatureStrategy().setColorTemperature(155, 6, test_light));
prep_ret = {{{"success", {{"/lights/1/state/transitiontime", 6}}}}, {{"success", {{"/lights/1/state/ct", 153}}}}};
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
EXPECT_EQ(true, SimpleColorTemperatureStrategy().setColorTemperature(0, 6, test_light));
prep_ret[1]["success"]["/lights/1/state/ct"] = 500;
EXPECT_CALL(*handler, PUTJson(statePath, _, getBridgeIp(), getBridgePort())).Times(1).WillOnce(Return(prep_ret));
EXPECT_EQ(true, SimpleColorTemperatureStrategy().setColorTemperature(600, 6, test_light));
}
TEST(SimpleColorTemperatureStrategy, alertTemperature)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight light(handler);
const auto setCTLambda = [&](unsigned int ct, int transition) {
light.getState()["state"]["colormode"] = "ct";
light.getState()["state"]["on"] = true;
light.getState()["state"]["ct"] = ct;
return true;
};
// Invalid colormode
{
light.getState()["state"]["colormode"] = "invalid";
light.getState()["state"]["on"] = false;
EXPECT_EQ(false, SimpleColorTemperatureStrategy().alertTemperature(400, light));
}
// on
{
const nlohmann::json state = {{"colormode", "ct"}, {"on", true}, {"ct", 200}};
light.getState()["state"] = state;
TestTransaction reverseTransaction = light.transaction().setColorTemperature(200).setTransition(1);
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Return(false));
EXPECT_FALSE(SimpleColorTemperatureStrategy().alertTemperature(400, light));
InSequence s;
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Invoke(setCTLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(false));
EXPECT_FALSE(SimpleColorTemperatureStrategy().alertTemperature(400, light));
light.getState()["state"] = state;
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Invoke(setCTLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(SimpleColorTemperatureStrategy().alertTemperature(400, light));
Mock::VerifyAndClearExpectations(handler.get());
}
// off
{
const nlohmann::json state = {{"colormode", "ct"}, {"on", false}, {"ct", 200}};
light.getState()["state"] = state;
TestTransaction reverseTransaction = light.transaction().setColorTemperature(200).setOn(false).setTransition(1);
EXPECT_CALL(light, setColorTemperature(400, 1)).WillOnce(Invoke(setCTLambda));
EXPECT_CALL(light, alert()).WillOnce(Return(true));
reverseTransaction.expectSuccessfulPut(handler, Exactly(1));
EXPECT_TRUE(SimpleColorTemperatureStrategy().alertTemperature(400, light));
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(SimpleColorTemperatureStrategy, getColorTemperature)
{
using namespace ::testing;
std::shared_ptr<MockHttpHandler> handler(std::make_shared<MockHttpHandler>());
EXPECT_CALL(
*handler, GETJson("/api/" + getBridgeUsername() + "/lights/1", nlohmann::json::object(), getBridgeIp(), 80))
.Times(AtLeast(1))
.WillRepeatedly(Return(nlohmann::json::object()));
MockLight test_light(handler);
test_light.getState()["state"]["ct"] = 200;
EXPECT_EQ(200, SimpleColorTemperatureStrategy().getColorTemperature(test_light));
test_light.getState()["state"]["ct"] = 500;
EXPECT_EQ(500, SimpleColorTemperatureStrategy().getColorTemperature(static_cast<const Light>(test_light)));
}
@@ -0,0 +1,637 @@
/**
\file test_StateTransaction.cpp
Copyright Notice\n
Copyright (C) 2020 Jan Rogall - developer\n
Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/StateTransaction.h>
#include <gtest/gtest.h>
#include "testhelper.h"
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
using namespace testing;
TEST(StateTransaction, commit)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
{
EXPECT_CALL(*handler, PUTJson(_, _, getBridgeIp(), getBridgePort())).Times(0);
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Explicit off overrides brightness
{
nlohmann::json request = {{"on", false}, {"bri", 100}};
nlohmann::json response = {{{"success", {{"/path/on", false}}}}, {{"success", {{"/path/bri", 100}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setOn(false).setBrightness(100).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Do not trim
{
const nlohmann::json request = {{"on", false}, {"bri", 100}};
nlohmann::json state = {{"on", false}, {"bri", 100}};
nlohmann::json response = {{{"success", {{"/path/on", false}}}}, {{"success", {{"/path/bri", 100}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setOn(false).setBrightness(100).commit(false));
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, toAction)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
nlohmann::json request = {{"on", false}, {"bri", 100}};
hueplusplus::Action command
= StateTransaction(commands, "/path", nullptr).setOn(false).setBrightness(100).toAction();
Mock::VerifyAndClearExpectations(handler.get());
EXPECT_EQ(hueplusplus::Action::Method::put, command.getMethod());
EXPECT_EQ(request, command.getBody());
EXPECT_EQ(requestPath, command.getAddress());
}
TEST(StateTransaction, setOn)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
// Set on
{
nlohmann::json request = {{"on", true}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setOn(true).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Set off
{
nlohmann::json request = {{"on", false}};
nlohmann::json response = {{{"success", {{"/path/on", false}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setOn(false).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Fail
{
nlohmann::json request = {{"on", false}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_FALSE(StateTransaction(commands, "/path", nullptr).setOn(false).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// No change requested
{
nlohmann::json state = {{"on", false}};
EXPECT_CALL(*handler, PUTJson(_, _, getBridgeIp(), getBridgePort())).Times(0);
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setOn(false).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, setBrightness)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
// No state
{
const int bri = 128;
nlohmann::json request = {{"on", true}, {"bri", bri}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/bri", bri}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setBrightness(bri).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Clamp to 254
{
const int bri = 254;
nlohmann::json request = {{"on", true}, {"bri", bri}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/bri", bri}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setBrightness(255).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Already off
{
const int bri = 0;
nlohmann::json request = {{"bri", bri}};
nlohmann::json response = {{{"success", {{"/path/bri", bri}}}}};
nlohmann::json state = {{"on", false}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setBrightness(bri).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// No change requested
{
const int bri = 120;
nlohmann::json state = {{"on", true}, {"bri", bri}};
EXPECT_CALL(*handler, PUTJson(_, _, getBridgeIp(), getBridgePort())).Times(0);
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setBrightness(bri).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Fixed unexpected on/off flickering
{
const int hue = 32;
nlohmann::json state = {{"on", false}, {"bri", 20}};
nlohmann::json request = { {"hue", hue}, {"bri", 0} };
nlohmann::json response = {{{"success", {{"/path/hue", hue}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setBrightness(0).setColorHue(hue).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, setColorHue)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
// No state
{
const int hue = 2159;
nlohmann::json request = {{"on", true}, {"hue", hue}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/hue", hue}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setColorHue(hue).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Already on
{
const int hue = 2159;
nlohmann::json request = {{"hue", hue}};
nlohmann::json response = {{{"success", {{"/path/hue", hue}}}}};
nlohmann::json state = {{"on", true}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorHue(hue).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Wrong colormode
{
const int hue = 2159;
nlohmann::json request = {{"hue", hue}};
nlohmann::json response = {{{"success", {{"/path/hue", hue}}}}};
nlohmann::json state = {{"on", true}, {"hue", hue}, {"colormode", "ct"}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorHue(hue).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// No request
{
const int hue = 2159;
nlohmann::json state = {{"on", true}, {"hue", hue}, {"colormode", "hs"}};
EXPECT_CALL(*handler, PUTJson(_, _, getBridgeIp(), getBridgePort())).Times(0);
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorHue(hue).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, setColorSaturation)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
// No state
{
const int sat = 125;
nlohmann::json request = {{"on", true}, {"sat", sat}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/sat", sat}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setColorSaturation(sat).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Clamp to 254
{
const int sat = 254;
nlohmann::json request = {{"on", true}, {"sat", sat}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/sat", sat}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setColorSaturation(255).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Already on
{
const int sat = 125;
nlohmann::json request = {{"sat", sat}};
nlohmann::json response = {{{"success", {{"/path/sat", sat}}}}};
nlohmann::json state = {{"on", true}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorSaturation(sat).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Wrong colormode
{
const int sat = 125;
nlohmann::json request = {{"sat", sat}};
nlohmann::json response = {{{"success", {{"/path/sat", sat}}}}};
nlohmann::json state = {{"on", true}, {"sat", sat}, {"colormode", "ct"}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorSaturation(sat).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// No request
{
const int sat = 125;
nlohmann::json state = {{"on", true}, {"sat", sat}, {"colormode", "hs"}};
EXPECT_CALL(*handler, PUTJson(_, _, getBridgeIp(), getBridgePort())).Times(0);
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorSaturation(sat).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, setColorXY)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
// No state
{
const float x = 0.5f;
const float y = 0.8f;
nlohmann::json request = {{"on", true}, {"xy", {x, y}}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/xy", {x, y}}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setColor(XY {x, y}).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Clamp
{
const float x = 1.f;
const float y = 0.f;
nlohmann::json request = {{"on", true}, {"xy", {x, y}}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/xy", {x, y}}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setColor(XY {2.f, -1.f}).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Already on
{
const float x = 0.5f;
const float y = 0.8f;
nlohmann::json request = {{"xy", {x, y}}};
nlohmann::json response = {{{"success", {{"/path/xy", {x, y}}}}}};
nlohmann::json state = {{"on", true}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColor(XY {x, y}).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Wrong colormode
{
const float x = 0.5f;
const float y = 0.8f;
nlohmann::json request = {{"xy", {x, y}}};
nlohmann::json response = {{{"success", {{"/path/xy", {x, y}}}}}};
nlohmann::json state = {{"on", true},
{"xy",
{
x,
y,
}},
{"colormode", "hs"}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColor(XY {x, y}).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// No request
{
const float x = 0.5f;
const float y = 0.8f;
nlohmann::json state = {{"on", true},
{"xy",
{
x,
y,
}},
{"colormode", "xy"}};
EXPECT_CALL(*handler, PUTJson(_, _, getBridgeIp(), getBridgePort())).Times(0);
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColor(XY {x, y}).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, setColorTemperature)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
// No state
{
const int ct = 240;
nlohmann::json request = {{"on", true}, {"ct", ct}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/ct", ct}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setColorTemperature(ct).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Clamp
{
const int ct = 500;
nlohmann::json request = {{"ct", ct}};
nlohmann::json response = {{{"success", {{"/path/ct", ct}}}}};
nlohmann::json state = {{"on", true}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorTemperature(520).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Already on
{
const int ct = 240;
nlohmann::json request = {{"ct", ct}};
nlohmann::json response = {{{"success", {{"/path/ct", ct}}}}};
nlohmann::json state = {{"on", true}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorTemperature(ct).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Wrong colormode
{
const int ct = 240;
nlohmann::json request = {{"ct", ct}};
nlohmann::json response = {{{"success", {{"/path/ct", ct}}}}};
nlohmann::json state = {{"on", true}, {"ct", ct}, {"colormode", "hs"}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorTemperature(ct).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// No request
{
const int ct = 240;
nlohmann::json state = {{"on", true}, {"ct", ct}, {"colormode", "ct"}};
EXPECT_CALL(*handler, PUTJson(_, _, getBridgeIp(), getBridgePort())).Times(0);
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorTemperature(ct).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, setColorLoop)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
// Set on
{
nlohmann::json request = {{"on", true}, {"effect", "colorloop"}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/effect", "colorloop"}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setColorLoop(true).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Set off
{
nlohmann::json request = {{"on", true}, {"effect", "none"}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/effect", "none"}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setColorLoop(false).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// No request
{
nlohmann::json state = {{"on", true}, {"effect", "colorloop"}};
EXPECT_CALL(*handler, PUTJson(_, _, getBridgeIp(), getBridgePort())).Times(0);
EXPECT_TRUE(StateTransaction(commands, "/path", &state).setColorLoop(true).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, incrementBrightness)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
{
const int inc = 20;
nlohmann::json request = {{"bri_inc", inc}};
nlohmann::json response = {{{"success", {{"/path/bri_inc", inc}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).incrementBrightness(inc).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Clamp
{
const int inc = -254;
nlohmann::json request = {{"bri_inc", inc}};
nlohmann::json response = {{{"success", {{"/path/bri_inc", inc}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).incrementBrightness(-300).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, incrementSaturation)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
{
const int inc = 20;
nlohmann::json request = {{"sat_inc", inc}};
nlohmann::json response = {{{"success", {{"/path/sat_inc", inc}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).incrementSaturation(inc).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Clamp
{
const int inc = -254;
nlohmann::json request = {{"sat_inc", inc}};
nlohmann::json response = {{{"success", {{"/path/sat_inc", inc}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).incrementSaturation(-300).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, incrementHue)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
{
const int inc = 20;
nlohmann::json request = {{"hue_inc", inc}};
nlohmann::json response = {{{"success", {{"/path/hue_inc", inc}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).incrementHue(inc).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Clamp
{
const int inc = -65534;
nlohmann::json request = {{"hue_inc", inc}};
nlohmann::json response = {{{"success", {{"/path/hue_inc", inc}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).incrementHue(-300000).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, incrementColorTemperature)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
{
const int inc = 20;
nlohmann::json request = {{"ct_inc", inc}};
nlohmann::json response = {{{"success", {{"/path/ct_inc", inc}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).incrementColorTemperature(inc).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Clamp
{
const int inc = -65534;
nlohmann::json request = {{"ct_inc", inc}};
nlohmann::json response = {{{"success", {{"/path/ct_inc", inc}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).incrementColorTemperature(-300000).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, incrementColorXY)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
{
const float incX = 0.2f;
const float incY = -0.4f;
nlohmann::json request = {{"xy_inc", {incX, incY}}};
nlohmann::json response = {{{"success", {{"/path/xy_inc", {incX, incY}}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).incrementColorXY(incX, incY).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Clamp
{
const float incX = 0.5f;
const float incY = -0.5f;
nlohmann::json request = {{"xy_inc", {incX, incY}}};
nlohmann::json response = {{{"success", {{"/path/xy_inc", {incX, incY}}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).incrementColorXY(1.f, -1.f).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, setTransition)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
{
nlohmann::json request = {{"on", true}, {"transitiontime", 2}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}, {{"success", {{"/path/transitiontime", 2}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setOn(true).setTransition(2).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// No transition time 4
{
nlohmann::json request = {{"on", true}};
nlohmann::json response = {{{"success", {{"/path/on", true}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setOn(true).setTransition(4).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// No request with only transition
{
EXPECT_CALL(*handler, PUTJson(_, _, getBridgeIp(), getBridgePort())).Times(0);
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).setTransition(2).commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, alert)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
{
nlohmann::json request = {{"alert", "select"}};
nlohmann::json response = {{{"success", {{"/path/alert", "select"}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).alert().commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Also alert when in state
{
nlohmann::json request = {{"alert", "select"}};
nlohmann::json response = {{{"success", {{"/path/alert", "select"}}}}};
nlohmann::json state = {{"alert", "select"}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).alert().commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, longAlert)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
{
nlohmann::json request = {{"alert", "lselect"}};
nlohmann::json response = {{{"success", {{"/path/alert", "lselect"}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).longAlert().commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Also alert when in state
{
nlohmann::json request = {{"alert", "lselect"}};
nlohmann::json response = {{{"success", {{"/path/alert", "lselect"}}}}};
nlohmann::json state = {{"alert", "lselect"}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).longAlert().commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
TEST(StateTransaction, stopAlert)
{
auto handler = std::make_shared<MockHttpHandler>();
HueCommandAPI commands(getBridgeIp(), getBridgePort(), getBridgeUsername(), handler);
const std::string requestPath = "/api/" + getBridgeUsername() + "/path";
{
nlohmann::json request = {{"alert", "none"}};
nlohmann::json response = {{{"success", {{"/path/alert", "none"}}}}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", nullptr).stopAlert().commit());
Mock::VerifyAndClearExpectations(handler.get());
}
// Also alert when in state
{
nlohmann::json request = {{"alert", "none"}};
nlohmann::json response = {{{"success", {{"/path/alert", "none"}}}}};
nlohmann::json state = {{"alert", "none"}};
EXPECT_CALL(*handler, PUTJson(requestPath, request, getBridgeIp(), getBridgePort())).WillOnce(Return(response));
EXPECT_TRUE(StateTransaction(commands, "/path", &state).stopAlert().commit());
Mock::VerifyAndClearExpectations(handler.get());
}
}
+543
View File
@@ -0,0 +1,543 @@
/**
\file test_TimePattern.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 <http://www.gnu.org/licenses/>.
**/
#include <hueplusplus/HueException.h>
#include <hueplusplus/TimePattern.h>
#include <gtest/gtest.h>
using namespace hueplusplus::time;
using std::chrono::system_clock;
using namespace std::chrono_literals;
TEST(Time, parseTimestamp)
{
std::tm tm {};
tm.tm_year = 2020 - 1900;
tm.tm_mon = 3 - 1;
tm.tm_mday = 24;
tm.tm_hour = 12;
tm.tm_min = 45;
tm.tm_sec = 0;
// Auto detect daylight savings time
tm.tm_isdst = -1;
const std::time_t ctime = std::mktime(&tm);
const auto timePoint = system_clock::from_time_t(ctime);
EXPECT_EQ(timePoint, parseTimestamp("2020-03-24T12:45:00"));
}
TEST(Time, timepointToTimestamp)
{
std::tm tm {};
tm.tm_year = 2020 - 1900;
tm.tm_mon = 3 - 1;
tm.tm_mday = 24;
tm.tm_hour = 12;
tm.tm_min = 45;
tm.tm_sec = 0;
// Auto detect daylight savings time
tm.tm_isdst = -1;
const std::time_t ctime = std::mktime(&tm);
const auto timePoint = system_clock::from_time_t(ctime);
EXPECT_EQ("2020-03-24T12:45:00", timepointToTimestamp(timePoint));
EXPECT_EQ(timePoint, parseTimestamp(timepointToTimestamp(timePoint)));
}
TEST(Time, parseDuration)
{
EXPECT_EQ(1h + 24min + 1s, parseDuration("01:24:01"));
EXPECT_EQ(22h + 59min + 49s, parseDuration("22:59:49"));
EXPECT_EQ(0s, parseDuration("00:00:00"));
}
TEST(Time, durationTo_hh_mm_ss)
{
EXPECT_EQ("00:00:00", durationTo_hh_mm_ss(0s));
EXPECT_EQ("01:32:05", durationTo_hh_mm_ss(1h + 32min + 5s));
EXPECT_EQ("20:45:13", durationTo_hh_mm_ss(20h + 45min + 13s));
const auto duration = 20h + 45min + 13s;
EXPECT_EQ(duration, parseDuration(durationTo_hh_mm_ss(duration)));
}
TEST(AbsoluteVariedTime, Constructor)
{
system_clock::time_point now = system_clock::now();
{
AbsoluteVariedTime time(now);
EXPECT_EQ(now, time.getBaseTime());
EXPECT_EQ(0s, time.getRandomVariation());
}
system_clock::duration variation = 4h + 2min;
{
AbsoluteVariedTime time(now, variation);
EXPECT_EQ(now, time.getBaseTime());
EXPECT_EQ(variation, time.getRandomVariation());
}
}
TEST(AbsoluteVariedTime, toString)
{
const system_clock::time_point timePoint = parseTimestamp("2020-03-03T20:53:03");
EXPECT_EQ("2020-03-03T20:53:03", AbsoluteVariedTime(timePoint).toString());
const system_clock::duration noVariation = 0s;
EXPECT_EQ("2020-03-03T20:53:03", AbsoluteVariedTime(timePoint, noVariation).toString());
const system_clock::duration variation = 1h + 2min + 1s;
EXPECT_EQ("2020-03-03T20:53:03A01:02:01", AbsoluteVariedTime(timePoint, variation).toString());
}
TEST(AbsoluteTime, parseUTC)
{
AbsoluteTime absolute = AbsoluteTime::parseUTC("2020-03-03T20:53:03");
std::time_t ctime = system_clock::to_time_t(absolute.getBaseTime());
std::tm* pTm = std::gmtime(&ctime);
ASSERT_NE(nullptr, pTm);
std::tm tm = *pTm;
EXPECT_EQ(2020 - 1900, tm.tm_year);
EXPECT_EQ(3 - 1, tm.tm_mon);
EXPECT_EQ(20, tm.tm_hour);
EXPECT_EQ(53, tm.tm_min);
EXPECT_EQ(3, tm.tm_sec);
}
TEST(Weekdays, Constructor)
{
EXPECT_TRUE(Weekdays().isNone());
EXPECT_TRUE(Weekdays(0).isMonday());
EXPECT_TRUE(Weekdays(6).isSunday());
}
TEST(Weekdays, isXXX)
{
Weekdays none = Weekdays::none();
EXPECT_TRUE(none.isNone());
EXPECT_FALSE(none.isAll());
EXPECT_FALSE(none.isMonday());
EXPECT_FALSE(none.isTuesday());
EXPECT_FALSE(none.isWednesday());
EXPECT_FALSE(none.isThursday());
EXPECT_FALSE(none.isFriday());
EXPECT_FALSE(none.isSaturday());
EXPECT_FALSE(none.isSunday());
Weekdays all = Weekdays::all();
EXPECT_FALSE(all.isNone());
EXPECT_TRUE(all.isAll());
EXPECT_TRUE(all.isMonday());
EXPECT_TRUE(all.isTuesday());
EXPECT_TRUE(all.isWednesday());
EXPECT_TRUE(all.isThursday());
EXPECT_TRUE(all.isFriday());
EXPECT_TRUE(all.isSaturday());
EXPECT_TRUE(all.isSunday());
// Test that for all days, only their own isXXX function is true
std::vector<Weekdays> days {Weekdays::monday(), Weekdays::tuesday(), Weekdays::wednesday(), Weekdays::thursday(),
Weekdays::friday(), Weekdays::saturday(), Weekdays::sunday()};
using BoolGetter = bool (Weekdays::*)() const;
std::vector<BoolGetter> getters {&Weekdays::isMonday, &Weekdays::isTuesday, &Weekdays::isWednesday,
&Weekdays::isThursday, &Weekdays::isFriday, &Weekdays::isSaturday, &Weekdays::isSunday};
for (int i = 0; i < days.size(); ++i)
{
Weekdays day = days[i];
EXPECT_FALSE(day.isNone());
EXPECT_FALSE(day.isAll());
for (int j = 0; j < getters.size(); ++j)
{
EXPECT_EQ(j == i, (day.*getters[j])()) << "on Day " << i << ": getter for day " << j << " has wrong result";
}
}
}
TEST(Weekdays, unionWith)
{
Weekdays day = Weekdays::monday().unionWith(Weekdays::saturday());
EXPECT_TRUE(day.isMonday());
EXPECT_TRUE(day.isSaturday());
day = Weekdays::monday() | Weekdays::tuesday() | Weekdays::all();
EXPECT_TRUE(day.isAll());
}
TEST(Weekdays, equals)
{
EXPECT_EQ(Weekdays::monday(), Weekdays(0));
EXPECT_EQ(Weekdays::none(), Weekdays());
EXPECT_EQ(Weekdays::monday() | Weekdays::tuesday(), Weekdays::monday().unionWith(Weekdays::tuesday()));
EXPECT_NE(Weekdays::none(), Weekdays(0));
EXPECT_NE(Weekdays::all(), Weekdays::monday());
}
TEST(Weekdays, toString)
{
EXPECT_EQ("001", Weekdays(0).toString());
EXPECT_EQ("064", Weekdays(6).toString());
EXPECT_EQ("112", (Weekdays(6) | Weekdays(5) | Weekdays(4)).toString());
}
TEST(RecurringTime, Constructor)
{
{
const auto time = 6h + 4min;
const Weekdays days = Weekdays::all();
const RecurringTime recurring(time, days);
EXPECT_EQ(time, recurring.getDaytime());
EXPECT_EQ(0s, recurring.getRandomVariation());
EXPECT_EQ(days, recurring.getWeekdays());
}
{
const auto time = 2h + 3min + 2s;
const Weekdays days = Weekdays::monday() | Weekdays::friday();
const auto variation = 40min;
const RecurringTime recurring(time, days, variation);
EXPECT_EQ(time, recurring.getDaytime());
EXPECT_EQ(variation, recurring.getRandomVariation());
EXPECT_EQ(days, recurring.getWeekdays());
}
}
TEST(RecurringTime, toString)
{
const auto time = 0h + 4min;
const RecurringTime recurring(time, Weekdays::monday());
EXPECT_EQ("W001/T00:04:00", recurring.toString());
const RecurringTime variation(time, Weekdays::monday(), 1s);
EXPECT_EQ("W001/T00:04:00A00:00:01", variation.toString());
}
TEST(TimeInterval, Constructor)
{
{
const auto start = 1h + 40min;
const auto end = 11h + 25s;
const TimeInterval interval(start, end);
EXPECT_EQ(start, interval.getStartTime());
EXPECT_EQ(end, interval.getEndTime());
EXPECT_EQ(Weekdays::all(), interval.getWeekdays());
}
{
const auto start = 0s;
const auto end = 20h;
const Weekdays days = Weekdays::friday() | Weekdays::saturday();
const TimeInterval interval(start, end, days);
EXPECT_EQ(start, interval.getStartTime());
EXPECT_EQ(end, interval.getEndTime());
EXPECT_EQ(days, interval.getWeekdays());
}
}
TEST(TimeInterval, toString)
{
{
const TimeInterval interval(1h + 40min, 11h + 25s);
EXPECT_EQ("T01:40:00/T11:00:25", interval.toString());
}
{
const TimeInterval interval(0h, 20h + 1s, Weekdays::monday());
EXPECT_EQ("W001/T00:00:00/T20:00:01", interval.toString());
}
}
TEST(Timer, Constructor)
{
{
const auto duration = 1min + 20s;
const Timer timer(duration);
EXPECT_FALSE(timer.isRecurring());
EXPECT_EQ(1, timer.getNumberOfExecutions());
EXPECT_EQ(duration, timer.getExpiryTime());
EXPECT_EQ(0s, timer.getRandomVariation());
}
{
const auto duration = 1min + 20s;
const auto variation = 1h;
const Timer timer(duration, variation);
EXPECT_FALSE(timer.isRecurring());
EXPECT_EQ(1, timer.getNumberOfExecutions());
EXPECT_EQ(duration, timer.getExpiryTime());
EXPECT_EQ(variation, timer.getRandomVariation());
}
{
const auto duration = 1min + 20s;
const int num = 0;
const Timer timer(duration, num);
EXPECT_TRUE(timer.isRecurring());
EXPECT_EQ(num, timer.getNumberOfExecutions());
EXPECT_EQ(duration, timer.getExpiryTime());
EXPECT_EQ(0s, timer.getRandomVariation());
}
{
const auto duration = 1min + 20s;
const int num = 10;
const auto variation = 20min;
const Timer timer(duration, num, variation);
EXPECT_TRUE(timer.isRecurring());
EXPECT_EQ(num, timer.getNumberOfExecutions());
EXPECT_EQ(duration, timer.getExpiryTime());
EXPECT_EQ(variation, timer.getRandomVariation());
}
}
TEST(Timer, toString)
{
{
const Timer timer(1min + 20s);
EXPECT_EQ("PT00:01:20", timer.toString());
}
{
const Timer timer(1min + 20s, 1h);
EXPECT_EQ("PT00:01:20A01:00:00", timer.toString());
}
{
const Timer timer(1min + 20s, Timer::infiniteExecutions);
EXPECT_EQ("R/PT00:01:20", timer.toString());
}
{
const Timer timer(1min + 20s, 1);
EXPECT_EQ("PT00:01:20", timer.toString());
}
{
const Timer timer(1min + 20s, 15);
EXPECT_EQ("R15/PT00:01:20", timer.toString());
}
{
const Timer timer(1min + 20s, 5, 1h);
EXPECT_EQ("R05/PT00:01:20A01:00:00", timer.toString());
}
{
const Timer timer(1min + 20s, Timer::infiniteExecutions, 1h);
EXPECT_EQ("R/PT00:01:20A01:00:00", timer.toString());
}
}
TEST(TimePattern, Undefined)
{
{
TimePattern pattern;
EXPECT_EQ(TimePattern::Type::undefined, pattern.getType());
}
{
TimePattern pattern = TimePattern::parse("");
EXPECT_EQ(TimePattern::Type::undefined, pattern.getType());
}
{
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 AbsoluteVariedTime 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)
{
{
const AbsoluteVariedTime abs(system_clock::now(), 20s);
const TimePattern pattern(abs);
ASSERT_EQ(TimePattern::Type::absolute, pattern.getType());
EXPECT_EQ(abs.getBaseTime(), pattern.asAbsolute().getBaseTime());
EXPECT_EQ(abs.getRandomVariation(), pattern.asAbsolute().getRandomVariation());
}
const system_clock::time_point timePoint = parseTimestamp("2020-03-03T20:53:03");
{
const TimePattern pattern = TimePattern::parse("2020-03-03T20:53:03");
const AbsoluteVariedTime expected(timePoint);
ASSERT_EQ(TimePattern::Type::absolute, pattern.getType());
EXPECT_EQ(expected.getBaseTime(), pattern.asAbsolute().getBaseTime());
EXPECT_EQ(expected.getRandomVariation(), pattern.asAbsolute().getRandomVariation());
}
{
const system_clock::duration variation = 1h + 2min + 1s;
const TimePattern pattern = TimePattern::parse("2020-03-03T20:53:03A01:02:01");
const AbsoluteVariedTime expected(timePoint, variation);
ASSERT_EQ(TimePattern::Type::absolute, pattern.getType());
EXPECT_EQ(expected.getBaseTime(), pattern.asAbsolute().getBaseTime());
EXPECT_EQ(expected.getRandomVariation(), pattern.asAbsolute().getRandomVariation());
}
}
TEST(TimePattern, Recurring)
{
{
const RecurringTime rec(12h + 30min, Weekdays::monday(), 1h);
const TimePattern pattern(rec);
ASSERT_EQ(TimePattern::Type::recurring, pattern.getType());
EXPECT_EQ(rec.getDaytime(), pattern.asRecurring().getDaytime());
EXPECT_EQ(rec.getWeekdays(), pattern.asRecurring().getWeekdays());
EXPECT_EQ(rec.getRandomVariation(), pattern.asRecurring().getRandomVariation());
}
{
const TimePattern pattern = TimePattern::parse("W001/T12:30:00");
const RecurringTime expected(12h + 30min, Weekdays::monday());
ASSERT_EQ(TimePattern::Type::recurring, pattern.getType());
EXPECT_EQ(expected.getDaytime(), pattern.asRecurring().getDaytime());
EXPECT_EQ(expected.getWeekdays(), pattern.asRecurring().getWeekdays());
EXPECT_EQ(expected.getRandomVariation(), pattern.asRecurring().getRandomVariation());
}
{
const TimePattern pattern = TimePattern::parse("W001/T12:30:00A01:00:00");
const RecurringTime expected(12h + 30min, Weekdays::monday(), 1h);
ASSERT_EQ(TimePattern::Type::recurring, pattern.getType());
EXPECT_EQ(expected.getDaytime(), pattern.asRecurring().getDaytime());
EXPECT_EQ(expected.getWeekdays(), pattern.asRecurring().getWeekdays());
EXPECT_EQ(expected.getRandomVariation(), pattern.asRecurring().getRandomVariation());
}
}
TEST(TimePattern, Interval)
{
{
const TimeInterval interval(12h + 30min, 13h + 20min, Weekdays::friday());
const TimePattern pattern(interval);
ASSERT_EQ(TimePattern::Type::interval, pattern.getType());
EXPECT_EQ(interval.getStartTime(), pattern.asInterval().getStartTime());
EXPECT_EQ(interval.getEndTime(), pattern.asInterval().getEndTime());
EXPECT_EQ(interval.getWeekdays(), pattern.asInterval().getWeekdays());
}
{
const TimeInterval expected(12h + 30min, 13h + 20min + 12s);
const TimePattern pattern = TimePattern::parse("T12:30:00/T13:20:12");
ASSERT_EQ(TimePattern::Type::interval, pattern.getType());
EXPECT_EQ(expected.getStartTime(), pattern.asInterval().getStartTime());
EXPECT_EQ(expected.getEndTime(), pattern.asInterval().getEndTime());
EXPECT_EQ(expected.getWeekdays(), pattern.asInterval().getWeekdays());
}
{
const TimeInterval expected(12h + 30min, 13h + 20min + 12s, Weekdays::monday());
const TimePattern pattern = TimePattern::parse("W001/T12:30:00/T13:20:12");
ASSERT_EQ(TimePattern::Type::interval, pattern.getType());
EXPECT_EQ(expected.getStartTime(), pattern.asInterval().getStartTime());
EXPECT_EQ(expected.getEndTime(), pattern.asInterval().getEndTime());
EXPECT_EQ(expected.getWeekdays(), pattern.asInterval().getWeekdays());
}
}
TEST(TimePattern, Timer)
{
{
const Timer timer(1h + 30min, 5, 20s);
const TimePattern pattern(timer);
ASSERT_EQ(TimePattern::Type::timer, pattern.getType());
EXPECT_EQ(timer.getExpiryTime(), pattern.asTimer().getExpiryTime());
EXPECT_EQ(timer.getRandomVariation(), pattern.asTimer().getRandomVariation());
EXPECT_EQ(timer.getNumberOfExecutions(), pattern.asTimer().getNumberOfExecutions());
}
{
const Timer expected(1h + 30min + 20s);
const TimePattern pattern = TimePattern::parse("PT01:30:20");
ASSERT_EQ(TimePattern::Type::timer, pattern.getType());
EXPECT_EQ(expected.getExpiryTime(), pattern.asTimer().getExpiryTime());
EXPECT_EQ(expected.getRandomVariation(), pattern.asTimer().getRandomVariation());
EXPECT_EQ(expected.getNumberOfExecutions(), pattern.asTimer().getNumberOfExecutions());
}
{
const Timer expected(1h + 30min + 20s, 20s);
const TimePattern pattern = TimePattern::parse("PT01:30:20A00:00:20");
ASSERT_EQ(TimePattern::Type::timer, pattern.getType());
EXPECT_EQ(expected.getExpiryTime(), pattern.asTimer().getExpiryTime());
EXPECT_EQ(expected.getRandomVariation(), pattern.asTimer().getRandomVariation());
EXPECT_EQ(expected.getNumberOfExecutions(), pattern.asTimer().getNumberOfExecutions());
}
{
const Timer expected(1h + 30min + 20s, Timer::infiniteExecutions);
const TimePattern pattern = TimePattern::parse("R/PT01:30:20");
ASSERT_EQ(TimePattern::Type::timer, pattern.getType());
EXPECT_EQ(expected.getExpiryTime(), pattern.asTimer().getExpiryTime());
EXPECT_EQ(expected.getRandomVariation(), pattern.asTimer().getRandomVariation());
EXPECT_EQ(expected.getNumberOfExecutions(), pattern.asTimer().getNumberOfExecutions());
}
{
const Timer expected(1h + 30min + 20s, Timer::infiniteExecutions, 20s);
const TimePattern pattern = TimePattern::parse("R/PT01:30:20A00:00:20");
ASSERT_EQ(TimePattern::Type::timer, pattern.getType());
EXPECT_EQ(expected.getExpiryTime(), pattern.asTimer().getExpiryTime());
EXPECT_EQ(expected.getRandomVariation(), pattern.asTimer().getRandomVariation());
EXPECT_EQ(expected.getNumberOfExecutions(), pattern.asTimer().getNumberOfExecutions());
}
{
const Timer expected(1h + 30min + 20s, 5);
const TimePattern pattern = TimePattern::parse("R05/PT01:30:20");
ASSERT_EQ(TimePattern::Type::timer, pattern.getType());
EXPECT_EQ(expected.getExpiryTime(), pattern.asTimer().getExpiryTime());
EXPECT_EQ(expected.getRandomVariation(), pattern.asTimer().getRandomVariation());
EXPECT_EQ(expected.getNumberOfExecutions(), pattern.asTimer().getNumberOfExecutions());
}
{
const Timer expected(1h + 30min + 20s, 5, 20s);
const TimePattern pattern = TimePattern::parse("R05/PT01:30:20A00:00:20");
ASSERT_EQ(TimePattern::Type::timer, pattern.getType());
EXPECT_EQ(expected.getExpiryTime(), pattern.asTimer().getExpiryTime());
EXPECT_EQ(expected.getRandomVariation(), pattern.asTimer().getRandomVariation());
EXPECT_EQ(expected.getNumberOfExecutions(), pattern.asTimer().getNumberOfExecutions());
}
}
+54
View File
@@ -0,0 +1,54 @@
/**
\file test_UPnP.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 <http://www.gnu.org/licenses/>.
**/
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "iostream"
#include "testhelper.h"
#include "hueplusplus/LibConfig.h"
#include "hueplusplus/UPnP.h"
#include <nlohmann/json.hpp>
#include "mocks/mock_HttpHandler.h"
using namespace hueplusplus;
const std::vector<std::pair<std::string, std::string>> expected_uplug_dev
= {{"http://192.168.2.1:1900/gatedesc.xml", "Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19"},
{"http://192.168.2.116:80/description.xml", "Linux/3.14.0 UPnP/1.0 IpBridge/1.21.0"}};
TEST(UPnP, getDevices)
{
std::shared_ptr<MockHttpHandler> handler = std::make_shared<MockHttpHandler>();
EXPECT_CALL(*handler,
sendMulticast("M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: "
"\"ssdp:discover\"\r\nMX: 5\r\nST: ssdp:all\r\n\r\n",
"239.255.255.250", 1900, Config::instance().getUPnPTimeout()))
.Times(1)
.WillRepeatedly(::testing::Return(getMulticastReply()));
UPnP uplug;
std::vector<std::pair<std::string, std::string>> foundDevices = uplug.getDevices(handler);
EXPECT_EQ(foundDevices, expected_uplug_dev);
}
+283
View File
@@ -0,0 +1,283 @@
/**
\file testhelper.h
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 <http://www.gnu.org/licenses/>.
**/
#ifndef _TEST_HELPER_H
#define _TEST_HELPER_H
inline std::string getBridgeIp()
{
return "192.168.2.116"; //!< IP-Address of the fake hue bridge in dotted
//!< decimal notation like "192.168.2.1"
}
inline int getBridgePort()
{
return 80;
}
inline std::string getBridgeUsername()
{
return "83b7780291a6ceffbe0bd049104df"; //!< Username that is used to access
//!< the fake hue bridge
}
inline std::string getBridgeId()
{
return "111111FFFE11E111";
}
inline std::string getBridgeUuid()
{
return "1f111f11-da11-11e1-1b11-11111111e111";
}
inline std::string getBridgeMac()
{
return "11111111e111";
}
inline std::string getBridgeXml()
{
return R"xml(<?xml version="1.0" encoding="UTF-8" ?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<URLBase>http://192.168.2.116:80/</URLBase>
<device>
<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>
<friendlyName>Philips hue (192.168.2.116)</friendlyName>
<manufacturer>Royal Philips Electronics</manufacturer>
<manufacturerURL>http://www.philips.com</manufacturerURL>
<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>
<modelName>Philips hue bridge 2015</modelName>
<modelNumber>BSB002</modelNumber>
<modelURL>http://www.meethue.com</modelURL>
<serialNumber>11111111e111</serialNumber>
<UDN>uuid:1f111f11-da11-11e1-1b11-11111111e111</UDN>
<presentationURL>index.html</presentationURL>
<iconList>
<icon>
<mimetype>image/png</mimetype>
<height>48</height>
<width>48</width>
<depth>24</depth>
<url>hue_logo_0.png</url>
</icon>
</iconList>
</device>
</root>)xml";
}
inline std::vector<std::string> getMulticastReply()
{
return {"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=300\r\n"
"DATE: Wed, 21 Jan 1970 05:42:21 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://192.168.2.1:1900/gatedesc.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: 000c0000-0dd0-00b0-0da0-00a000e000c0\r\n"
"SERVER: Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19\r\n"
"X-User-Agent: redsonic\r\n"
"ST: upnp:rootdevice\r\n"
"USN: uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00::upnp:rootdevice",
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=300\r\n"
"DATE: Wed, 21 Jan 1970 05:42:21 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://192.168.2.1:1900/gatedesc.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: 000c0000-0dd0-00b0-0da0-00a000e000c0\r\n"
"SERVER: Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19\r\n"
"X-User-Agent: redsonic\r\n"
"ST: uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00\r\n"
"USN: uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00",
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=300\r\n"
"DATE: Wed, 21 Jan 1970 05:42:21 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://192.168.2.1:1900/gatedesc.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: 000c0000-0dd0-00b0-0da0-00a000e000c0\r\n"
"SERVER: Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19\r\n"
"X-User-Agent: redsonic\r\n"
"ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
"USN: "
"uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00::urn:schemas-upnp-org:device:"
"InternetGatewayDevice:1",
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=300\r\n"
"DATE: Wed, 21 Jan 1970 05:42:21 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://192.168.2.1:1900/gatedesc.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: 000c0000-0dd0-00b0-0da0-00a000e000c0\r\n"
"SERVER: Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19\r\n"
"X-User-Agent: redsonic\r\n"
"ST: urn:schemas-upnp-org:service:Layer3Forwarding:1\r\n"
"USN: "
"uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00::urn:schemas-upnp-org:service:"
"Layer3Forwarding:1",
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=300\r\n"
"DATE: Wed, 21 Jan 1970 05:42:21 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://192.168.2.1:1900/gatedesc.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: 000c0000-0dd0-00b0-0da0-00a000e000c0\r\n"
"SERVER: Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19\r\n"
"X-User-Agent: redsonic\r\n"
"ST: uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00\r\n"
"USN: uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00",
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=300\r\n"
"DATE: Wed, 21 Jan 1970 05:42:21 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://192.168.2.1:1900/gatedesc.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: 000c0000-0dd0-00b0-0da0-00a000e000c0\r\n"
"SERVER: Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19\r\n"
"X-User-Agent: redsonic\r\n"
"ST: urn:schemas-upnp-org:device:WANDevice:1\r\n"
"USN: "
"uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00::urn:schemas-upnp-org:device:"
"WANDevice:1",
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=300\r\n"
"DATE: Wed, 21 Jan 1970 05:42:21 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://192.168.2.1:1900/gatedesc.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: 000c0000-0dd0-00b0-0da0-00a000e000c0\r\n"
"SERVER: Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19\r\n"
"X-User-Agent: redsonic\r\n"
"ST: urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1\r\n"
"USN: "
"uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00::urn:schemas-upnp-org:service:"
"WANCommonInterfaceConfig:1",
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=300\r\n"
"DATE: Wed, 21 Jan 1970 05:42:21 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://192.168.2.1:1900/gatedesc.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: 000c0000-0dd0-00b0-0da0-00a000e000c0\r\n"
"SERVER: Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19\r\n"
"X-User-Agent: redsonic\r\n"
"ST: uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00\r\n"
"USN: uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00",
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=300\r\n"
"DATE: Wed, 21 Jan 1970 05:42:21 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://192.168.2.1:1900/gatedesc.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: 000c0000-0dd0-00b0-0da0-00a000e000c0\r\n"
"SERVER: Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19\r\n"
"X-User-Agent: redsonic\r\n"
"ST: urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n"
"USN: "
"uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00::urn:schemas-upnp-org:device:"
"WANConnectionDevice:1",
"HTTP/1.1 200 OK\r\n"
"CACHE-CONTROL: max-age=300\r\n"
"DATE: Wed, 21 Jan 1970 05:42:21 GMT\r\n"
"EXT:\r\n"
"LOCATION: http://192.168.2.1:1900/gatedesc.xml\r\n"
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
"01-NLS: 000c0000-0dd0-00b0-0da0-00a000e000c0\r\n"
"SERVER: Linux/2.6.36, UPnP/1.0, Portable SDK for UPnP devices/1.6.19\r\n"
"X-User-Agent: redsonic\r\n"
"ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n"
"USN: "
"uuid:0f0000b0-f0da-0ad0-00b0-0000000fdf00::urn:schemas-upnp-org:service:"
"WANIPConnection:1",
"HTTP/1.1 200 OK\r\n"
"HOST: 239.255.255.250:1900\r\n"
"EXT:\r\n"
"CACHE-CONTROL: max-age=100\r\n"
"LOCATION: http://192.168.2.116:80/description.xml\r\n"
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.21.0\r\n"
"hue-bridgeid: 111111FFFE11E111\r\n"
"ST: upnp:rootdevice\r\n"
"USN: uuid:1f111f11-da11-11e1-1b11-11111111e111::upnp:rootdevice",
"HTTP/1.1 200 OK\r\n"
"HOST: 239.255.255.250:1900\r\n"
"EXT:\r\n"
"CACHE-CONTROL: max-age=100\r\n"
"LOCATION: http://192.168.2.116:80/description.xml\r\n"
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.21.0\r\n"
"hue-bridgeid: 111111FFFE11E111\r\n"
"ST: uuid:1f111f11-da11-11e1-1b11-11111111e111\r\n"
"USN: uuid:1f111f11-da11-11e1-1b11-11111111e111",
"HTTP/1.1 200 OK\r\n"
"HOST: 239.255.255.250:1900\r\n"
"EXT:\r\n"
"CACHE-CONTROL: max-age=100\r\n"
"LOCATION: http://192.168.2.116:80/description.xml\r\n"
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.21.0\r\n"
"hue-bridgeid: 111111FFFE11E111\r\n"
"ST: urn:schemas-upnp-org:device:basic:1\r\n"
"USN: uuid:1f111f11-da11-11e1-1b11-11111111e111",
"HTTP/1.1 200 OK\r\n"
"HOST: 239.255.255.250:1900\r\n"
"EXT:\r\n"
"CACHE-CONTROL: max-age=100\r\n"
"LOCATION: http://192.168.2.116:80/description.xml\r\n"
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.21.0\r\n"
"hue-bridgeid: 111111FFFE11E111\r\n"
"ST: upnp:rootdevice\r\n"
"USN: uuid:1f111f11-da11-11e1-1b11-11111111e111::upnp:rootdevice",
"HTTP/1.1 200 OK\r\n"
"HOST: 239.255.255.250:1900\r\n"
"EXT:\r\n"
"CACHE-CONTROL: max-age=100\r\n"
"LOCATION: http://192.168.2.116:80/description.xml\r\n"
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.21.0\r\n"
"hue-bridgeid: 111111FFFE11E111\r\n"
"ST: uuid:1f111f11-da11-11e1-1b11-11111111e111\r\n"
"USN: uuid:1f111f11-da11-11e1-1b11-11111111e111",
"HTTP/1.1 200 OK\r\n"
"HOST: 239.255.255.250:1900\r\n"
"EXT:\r\n"
"CACHE-CONTROL: max-age=100\r\n"
"LOCATION: http://192.168.2.116:80/description.xml\r\n"
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.21.0\r\n"
"hue-bridgeid: 111111FFFE11E111\r\n"
"ST: urn:schemas-upnp-org:device:basic:1\r\n"
"USN: uuid:1f111f11-da11-11e1-1b11-11111111e111"};
}
#endif