From 46a021d5de6e7448c79f621e0525e920a6206425 Mon Sep 17 00:00:00 2001 From: Kevin Rocard Date: Thu, 19 Feb 2015 18:18:17 +0100 Subject: C pfw bindings The pfw can not currently be used from c code. Add an c api. It does not target a perfect one/one mapping with the c++ one, but rather aim ease of use and type safety (as far as possible in c). Signed-off-by: Kevin Rocard --- bindings/CMakeLists.txt | 5 + bindings/c/CMakeLists.txt | 60 ++++++ bindings/c/ParameterFramework.cpp | 397 ++++++++++++++++++++++++++++++++++++ bindings/c/ParameterFramework.h | 265 ++++++++++++++++++++++++ bindings/c/Test.cpp | 414 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1141 insertions(+) create mode 100644 bindings/c/CMakeLists.txt create mode 100644 bindings/c/ParameterFramework.cpp create mode 100644 bindings/c/ParameterFramework.h create mode 100644 bindings/c/Test.cpp (limited to 'bindings') diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index ba59221..446b52f 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -30,3 +30,8 @@ option(PYTHON_BINDINGS "Python library to use the pfw from python" ON) if(PYTHON_BINDINGS) add_subdirectory(python) endif() + +option(C_BINDINGS "Python library to use the pfw from python" ON) +if(C_BINDINGS) + add_subdirectory(c) +endif() diff --git a/bindings/c/CMakeLists.txt b/bindings/c/CMakeLists.txt new file mode 100644 index 0000000..b7eeff8 --- /dev/null +++ b/bindings/c/CMakeLists.txt @@ -0,0 +1,60 @@ +# Copyright (c) 2015, Intel Corporation +# 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. + +add_library(cparameter SHARED ParameterFramework.cpp) + +include_directories("${PROJECT_SOURCE_DIR}/parameter/include") + +install(FILES ParameterFramework.h + DESTINATION "include/parameter/c") + +target_link_libraries(cparameter parameter) + +if(BUILD_TESTING) + # Add catch unit test framework + # TODO Use gtest as it is the team recommendation + # Unfortunately gtest is very hard to setup as not binary distributed + # catch is only one header so it is very easy + # Catch can be downloaded from: + # https://raw.github.com/philsquared/Catch/master/single_include/catch.hpp + # Then append the download folder to the CMAKE_INCLUDE_PATH variable or + # copy it in a standard location (/usr/include on most linux distribution). + find_path(CATCH_HEADER catch.hpp) + include_directories(${CATCH_HEADER}) + + # Add unit test + add_executable(cparameterUnitTest Test.cpp) + # Do not warn about passing a null pointer for arguments marked as requiring + # a non-null value by the "nonnull" function attribute. + # This is done as the unit tests check that invalid api use result in + # proper failure. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nonnull") + + target_link_libraries(cparameterUnitTest cparameter) + add_test(cparameterUnitTest cparameterUnitTest) +endif() diff --git a/bindings/c/ParameterFramework.cpp b/bindings/c/ParameterFramework.cpp new file mode 100644 index 0000000..efc7d99 --- /dev/null +++ b/bindings/c/ParameterFramework.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2015, Intel Corporation + * 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. + */ + +#include "ParameterFramework.h" +#include + +#include +#include +#include +#include + +#include +#include +#include + +using std::string; + +/** Rename long pfw types to short ones in pfw namespace. */ +namespace pfw +{ + typedef ISelectionCriterionInterface Criterion; + typedef std::map Criteria; + typedef CParameterMgrPlatformConnector Pfw; +} + +/** Class to abstract the boolean+string status api. */ +class Status +{ +public: + /** Fail without an instance of status. */ + static bool failure() { return false; } + /** Fail with the given error msg. */ + bool failure(const string &msg) { mMsg = msg; return false; } + /** Success (no error message). */ + bool success() { mMsg.clear(); return true; } + + /** Forward a status operation. + * @param success[in] the operaton status to forward + * or forward a previous failure if omitted + */ + bool forward(bool success = false) { + if (success) { mMsg.clear(); } + return success; + } + /** Error message accessors. + * + * Pfw api requires to provide a reference to a string in order + * for it to log. This function provide a reference to a string that + * will be added to the error message on failure. + */ + string &msg() { return mMsg; } +private: + string mMsg; +}; + +/////////////////////////////// +///////////// Log ///////////// +/////////////////////////////// + +/** Default log callback. Log to cout or cerr depending on level. */ +static void defaultLogCb(void *, PfwLogLevel level, const char *logLine) { + switch (level) { + case pfwLogInfo: + std::cout << logLine << std::endl; + break; + case pfwLogWarning: + std::cerr << logLine << std::endl; + break; + }; +} + +static PfwLogger defaultLogger = { NULL, &defaultLogCb }; + +class LogWrapper : public CParameterMgrPlatformConnector::ILogger +{ +public: + LogWrapper(const PfwLogger &logger) : mLogger(logger) {} + LogWrapper() : mLogger() {} + virtual ~LogWrapper() {} +private: + virtual void log(bool bIsWarning, const string &strLog) + { + // A LogWrapper should NOT be register to the pfw (thus log called) + // if logCb is NULL. + assert(mLogger.logCb != NULL); + mLogger.logCb(mLogger.userCtx, + bIsWarning ? pfwLogWarning : pfwLogInfo, + strLog.c_str()); + } + PfwLogger mLogger; +}; + +/////////////////////////////// +///////////// Core //////////// +/////////////////////////////// + +struct PfwHandler_ +{ + void setLogger(const PfwLogger *logger); + bool createCriteria(const PfwCriterion criteria[], size_t criterionNb); + + pfw::Criteria criteria; + pfw::Pfw *pfw; + /** Status of the last called function. + * Is mutable because even a const function can fail. + */ + mutable Status lastStatus; +private: + LogWrapper mLogger; +}; + + +PfwHandler *pfwCreate() +{ + return new PfwHandler(); +} + +void pfwDestroy(PfwHandler *handle) +{ + if(handle != NULL and handle->pfw != NULL) { + delete handle->pfw; + } + delete handle; +} + +void PfwHandler::setLogger(const PfwLogger *logger) +{ + if (logger != NULL and logger->logCb == NULL) { + return; // There is no callback, do not log => do not add a logger + } + mLogger = logger != NULL ? *logger : defaultLogger; + pfw->setLogger(&mLogger); +} + + +bool PfwHandler::createCriteria(const PfwCriterion criteriaArray[], size_t criterionNb) +{ + Status &status = lastStatus; + // Add criteria + for (size_t criterionIndex = 0; criterionIndex < criterionNb; ++criterionIndex) { + const PfwCriterion &criterion = criteriaArray[criterionIndex]; + if (criterion.name == NULL) { + return status.failure("Criterion name is NULL"); + } + if (criterion.values == NULL) { + return status.failure("Criterion values is NULL"); + } + // Check that the criterion does not exist + if (criteria.find(criterion.name) != criteria.end()) { + return status.failure("Criterion \"" + string(criterion.name) + + "\" already exist"); + } + + // Create criterion type + ISelectionCriterionTypeInterface *type = + pfw->createSelectionCriterionType(criterion.inclusive); + assert(type != NULL); + // Add criterion values + for (size_t valueIndex = 0; criterion.values[valueIndex] != NULL; ++valueIndex) { + int value; + if (criterion.inclusive) { + // Check that (int)1 << valueIndex would not overflow (UB) + if(std::numeric_limits::max() >> valueIndex == 0) { + return status.failure("Too many values for criterion " + + string(criterion.name)); + } + value = 1 << valueIndex; + } else { + value = valueIndex; + } + const char * valueName = criterion.values[valueIndex]; + if(not type->addValuePair(value, valueName)) { + return status.failure("Could not add value " + string(valueName) + + " to criterion " + criterion.name); + } + } + // Create criterion and add it to the pfw + criteria[criterion.name] = pfw->createSelectionCriterion(criterion.name, type); + } + return status.success(); +} + + +bool pfwStart(PfwHandler *handle, const char *configPath, + const PfwCriterion criteria[], size_t criterionNb, + const PfwLogger *logger) +{ + // Check that the api is correctly used + if (handle == NULL) { return Status::failure(); } + Status &status = handle->lastStatus; + + if (handle->pfw != NULL) { + return status.failure("Can not start an already started parameter framework"); + } + if (configPath == NULL) { + return status.failure("char *configPath is NULL, " + "while starting the parameter framework"); + } + if (criteria == NULL) { + return status.failure("char *criteria is NULL, " + "while starting the parameter framework " + "(config path is " + string(configPath) + ")"); + } + // Create a pfw + handle->pfw = new CParameterMgrPlatformConnector(configPath); + + handle->setLogger(logger); + + if (not handle->createCriteria(criteria, criterionNb)) { + return status.failure(); + } + + return status.forward(handle->pfw->start(status.msg())); +} + +const char *pfwGetLastError(const PfwHandler *handle) +{ + return handle == NULL ? NULL : handle->lastStatus.msg().c_str(); +} + +static pfw::Criterion *getCriterion(const pfw::Criteria &criteria, + const string &name) +{ + pfw::Criteria::const_iterator it = criteria.find(name); + return it == criteria.end() ? NULL : it->second; +} + +bool pfwSetCriterion(PfwHandler *handle, const char name[], int value) +{ + if (handle == NULL) { return Status::failure(); } + Status &status = handle->lastStatus; + if (name == NULL) { + return status.failure("char *name of the criterion is NULL, " + "while setting a criterion."); + } + if (handle->pfw == NULL) { + return status.failure("Can not set criterion \"" + string(name) + + "\" as the parameter framework is not started."); + } + pfw::Criterion *criterion = getCriterion(handle->criteria, name); + if (criterion == NULL) { + return status.failure("Can not set criterion " + string(name) + " as does not exist"); + } + criterion->setCriterionState(value); + return status.success(); +} +bool pfwGetCriterion(const PfwHandler *handle, const char name[], int *value) +{ + if (handle == NULL) { return Status::failure(); } + Status &status = handle->lastStatus; + if (name == NULL) { + return status.failure("char *name of the criterion is NULL, " + "while getting a criterion."); + } + if (handle->pfw == NULL) { + return status.failure("Can not get criterion \"" + string(name) + + "\" as the parameter framework is not started."); + } + if (value == NULL) { + return status.failure("Can not get criterion \"" + string(name) + + "\" as the out value is NULL."); + } + pfw::Criterion *criterion = getCriterion(handle->criteria, name); + if (criterion == NULL) { + return status.failure("Can not get criterion " + string(name) + " as it does not exist"); + } + *value = criterion->getCriterionState(); + return status.success(); +} + +bool pfwApplyConfigurations(const PfwHandler *handle) +{ + if (handle == NULL) { return Status::failure(); } + Status &status = handle->lastStatus; + if (handle->pfw == NULL) { + return status.failure("Can not commit criteria " + "as the parameter framework is not started."); + } + handle->pfw->applyConfigurations(); + return status.success(); +} + +/////////////////////////////// +/////// Parameter access ////// +/////////////////////////////// + +struct PfwParameterHandler_ +{ + PfwHandler &pfw; + CParameterHandle ¶meter; +}; + +PfwParameterHandler *pfwBindParameter(PfwHandler *handle, const char path[]) +{ + if (handle == NULL) { return NULL; } + Status &status = handle->lastStatus; + if (path == NULL) { + status.failure("Can not bind a parameter without its path"); + return NULL; + } + if (handle->pfw == NULL) { + status.failure("The parameter framework is not started, " + "while trying to bind parameter \"" + string(path) + "\")"); + return NULL; + } + + CParameterHandle *paramHandle; + paramHandle = handle->pfw->createParameterHandle(path, status.msg()); + if (paramHandle == NULL) { + return NULL; + } + + status.success(); + PfwParameterHandler publicHandle = {*handle, *paramHandle}; + return new PfwParameterHandler(publicHandle); +} + +void pfwUnbindParameter(PfwParameterHandler *handle) +{ + if (handle == NULL) { return; } + delete &handle->parameter; + delete handle; +} + + +bool pfwGetIntParameter(const PfwParameterHandler *handle, int32_t *value) +{ + if (handle == NULL) { return Status::failure(); } + Status &status = handle->pfw.lastStatus; + if (value == NULL) { + return status.failure("int32_t *value is NULL, " + "while trying to get parameter \"" + + handle->parameter.getPath() + "\" value as int)"); + } + return status.forward(handle->parameter.getAsSignedInteger(*value, status.msg())); +} +bool pfwSetIntParameter(PfwParameterHandler *handle, int32_t value) +{ + if (handle == NULL) { return Status::failure(); } + Status &status = handle->pfw.lastStatus; + return status.forward(handle->parameter.setAsSignedInteger(value, status.msg())); +} + +bool pfwGetStringParameter(const PfwParameterHandler *handle, const char *value[]) +{ + if (handle == NULL) { return Status::failure(); } + Status &status = handle->pfw.lastStatus; + if (value == NULL) { + return status.failure("char **value is NULL, " + "while trying to get parameter \"" + + handle->parameter.getPath() + "\" value as string)"); + } + *value = NULL; + string retValue; + bool success = handle->parameter.getAsString(retValue, status.msg()); + if (not success) { return status.forward(); } + + *value = strdup(retValue.c_str()); + return status.success(); +} + +bool pfwSetStringParameter(PfwParameterHandler *handle, const char value[]) +{ + if (handle == NULL) { return Status::failure(); } + Status &status = handle->pfw.lastStatus; + return status.forward(handle->parameter.setAsString(value, status.msg())); +} + +void pfwFree(void *ptr) { std::free(ptr); } + diff --git a/bindings/c/ParameterFramework.h b/bindings/c/ParameterFramework.h new file mode 100644 index 0000000..20cc820 --- /dev/null +++ b/bindings/c/ParameterFramework.h @@ -0,0 +1,265 @@ +/** @copyright + * Copyright (c) 2015, Intel Corporation + * 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. + */ + +/** @file Simplified parameter framework C API. + * This API does not target a perfect one/one mapping with the c++ one, + * but rather aim ease of use and type safety (as far as possible in c). + * All function are reentrant and function call on a pfw (PfwHandle) + * does not impact any other pfw. + * Ie. There is no shared resources between pfw instances. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** Lots of function in this API require non null pointer parameter. + * Such arguments are marked NONNULL. + */ +#define NONNULL __attribute__((nonnull)) +#define NONNULL_(...) __attribute__((nonnull (__VA_ARGS__))) +#define USERESULT __attribute__((warn_unused_result)) + +/** Private handle to a parameter framework. + * A PfwHandler* is valid if: + * - it was created by pfwCreate + * - it has not been destroyed by pfwDestroyParameter + * - is not NULL + * A valid handle MUST be provided to all pfw related method. + * A valid handler MUST be destroyed with pfwDestroy before programme + * termination. + * @note Forward declaration to break header dependency. + */ +struct PfwHandler_; +/** Typedef for use ease. @see PfwHandler_. */ +typedef struct PfwHandler_ PfwHandler; + +/////////////////////////////// +///////////// Log ///////////// +/////////////////////////////// +/** Pfw log level for the callback. */ +typedef enum { + pfwLogInfo = 55, //< Random value to avoid unfortunate mismatch. + pfwLogWarning +} PfwLogLevel; + +/** Type of the parameter framework log callback. + * @param userCtx[in] Arbitrary context provided during callback registration. + * @param level[in] Log level of the log line. + * @param logLine[in] Log line (without end line control character like '\n') + * to be logged. The pointer is invalidate after function + * return or if any pfw function is called. + */ +typedef void PfwLogCb(void *userCtx, PfwLogLevel level, const char *logLine); + +/** Logger containing a callback method and its context. */ +typedef struct { + /** User defined arbitrary value that will be provided to all logCb call. */ + void *userCtx; + /** Callback that will be called. + * If NULL nothing will be logged. + */ + PfwLogCb *logCb; +} PfwLogger; + +/////////////////////////////// +///////////// Core //////////// +/////////////////////////////// + +/** Structure of a parameter framework criterion. */ +typedef struct { + /** Name of the criterion in the pfw configuration rules. */ + const char *name; //< Must not be null. + bool inclusive; //< True if the criterion is inclusive, false if exclusive. + /** Null terminated list of criterion value names. + * @example { "Red", "Green", "Blue", NULL } + * + * For an exclusive criterion, the list must not contain more elements then + * INT_MAX. + * For an inclusive criterion, the list must not contain more elements then + * sizeof(int) * BIT_CHAR - 1. + * Ie: (int)1 << n must *not* overflow (UB), + * were n is the number of element in the + * list. @see pfwSetCriterion + */ + const char **values; //< Must not be null. +} PfwCriterion; + + +/** Create a parameter framework instance. + * Can not fail except for memory allocation. + */ +PfwHandler *pfwCreate() USERESULT; + +/** Destroy a parameter framework. Can not fail. */ +void pfwDestroy(PfwHandler *handle) NONNULL; + +/** Start a parameter framework. + * @param handle[in] @see PfwHandler + * @param configPath[in] Path to the file containing the pfw configuration. + * @param criteria[in] An array of PfwCriterion. + * @param criterionNb[in] The number of PfwCriterion in criteria. + * @param logger[in] the logger to use for all operation. + * If NULL, log infos to standard output and + * errors to standard error. + * @return true on success, false on failure. + */ +bool pfwStart(PfwHandler *handle, const char *configPath, + const PfwCriterion criteria[], size_t criterionNb, + const PfwLogger *loggger) NONNULL_(1, 2, 3) USERESULT; + +/** @return a string describing the last call result. + * If the last pfw function call succeeded, return an empty string. + * If the last pfw function call failed, return a message explaining the error cause. + * The return pointer is invalidated if any pfw method is called on the SAME + * PfwHandle. + * + * Each PfwHandle own it's last error message. It is not static nor TLS. + * As a result, calling a pfw function with a NULL PfwHandler will result in a + * failure WITHOUT updating the last error. + */ +const char *pfwGetLastError(const PfwHandler *handle) NONNULL; + +/** Set a criterion value given its name and value. + * @param handle[in] @see PfwHandler + * @param name[in] The name of the criterion that need to be changed. + * @param value If the criterion is exclusive, the index of the new value. + * If the criterion is inclusive, a bit field where each bit + * correspond to the value index. + * For an inclusive criterion defined as such: { "Red", "Green", "Blue", NULL } + * to set the value Green and Blue, value has to be 1<<1 | 1<<2 = 0b110 = 6. + * For an exclusive criterion defined as such: { "Car", "Boat", "Plane", NULL } + * to set the value Plane, value has to be 2. + * + * Criterion change do not have impact on the parameters value + * (no configuration applied) until the changes are committed using pfwApplyConfigurations. + * + * @return true on success and false on failure. + */ +bool pfwSetCriterion(PfwHandler *handle, const char name[], int value) NONNULL USERESULT; +/** Get a criterion value given its name. + * Same usage as pfwSetCriterion except that value is an out param. + * Get criterion will return the last value setted with pfwSetCriterion independantly of pfwCommitCritenio. + */ +bool pfwGetCriterion(const PfwHandler *handle, const char name[], int *value) NONNULL USERESULT; + +/** Commit criteria change and change parameters according to the configurations. + * Criterion do not have impact on the parameters value when changed, + * instead they are staged and only feed to the rule engine + * (who then impact parameter values according to the configuration) when + * committed with this function. + * + * @param handle[in] @see PfwHandler + * @return true on success and false on failure. + */ +bool pfwApplyConfigurations(const PfwHandler *handle) NONNULL USERESULT; + +/////////////////////////////// +/////// Parameter access ////// +/////////////////////////////// + +/** Handler to a pfw parameter. + * A PfwParameterHandler* is valid if: + * - it was created by pfwBindParameter + * - it has not been destroyed by pfwDestroyParameter + * - is not NULL + * - the pfwHandle used to created is still valid (ie. it must not outlive + * its parent pfw) + * A valid handle MUST be provided to all pfw parameter related method. + * Any created handle MUST be destroyed (with pfwDestroyParameter) before + * the PfwHandler that was used for its creation. + * @note Forward declaration to break header dependency. + */ +struct PfwParameterHandler_; +typedef struct PfwParameterHandler_ PfwParameterHandler; + +/** Construct the handle to a parameter given its path. + * The PfwHandle MUST stay valid until PfwParameterHandler destruction. + * @return a PfwParameterHandler on success, NULL on error. + * @see pfwGetLastError for error detail. + */ +PfwParameterHandler *pfwBindParameter(PfwHandler *handle, const char path[]) NONNULL; +/** Destroy a parameter handle. Can not fail. */ +void pfwUnbindParameter(PfwParameterHandler *handle) NONNULL; + +/** Access the value of a previously bind int parameter. + * @param handle[in] Handler to a valid parameter. + * @param value[in] Non null pointer to an integer that will + * hold the parameter value on success, undefined otherwise. + * return true of success, false on failure. + */ +bool pfwGetIntParameter(const PfwParameterHandler *handle, int32_t *value ) NONNULL USERESULT; + +/** Set the value of a previously bind int parameter. + * @param handle[in] Handler to a valid parameter. + * @param value[in] The parameter value to set. + * return true of success, false on failure. + */ +bool pfwSetIntParameter(PfwParameterHandler *handle, int32_t value) NONNULL USERESULT; + +/** Access the value of a previously bind string parameter. + * @param handle[in] Handler to a valid parameter. + * @param value[out] Non null pointer on a string. + * Will point on the parameter value string on success, + * NULL on failure. + * The callee MUST free the returned string using pfwFree after use. + * @return true on success, false on failure. + */ +bool pfwGetStringParameter(const PfwParameterHandler *handle, const char *value[]) NONNULL; + +/** Set the value of a previously bind string parameter. + * @param handle[in] Handler to a valid parameter + * @param value[in] Non null pointer to a null terminated string to set. + */ +bool pfwSetStringParameter(PfwParameterHandler *handle, const char value[]) NONNULL USERESULT; + +/** Frees the memory space pointed to by ptr, + * which must have been returned by a previous call to the pfw. + * + * @param ptr[in] pointer to the memory to free. + * @see man 3 free for usage. + * @note Wrapper around the standard free to avoid problems + * in case of a different pfw and client allocator. + */ +void pfwFree(void *ptr); + +#undef NONNULL +#undef NONNULL_ +#undef USERESULT + +#ifdef __cplusplus +} +#endif diff --git a/bindings/c/Test.cpp b/bindings/c/Test.cpp new file mode 100644 index 0000000..c450e60 --- /dev/null +++ b/bindings/c/Test.cpp @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2015, Intel Corporation + * 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. + */ + +#include "ParameterFramework.h" + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() +#include + +#include +#include +#include + +#include +#include +#include +extern "C" +{ +#include +} + +struct Test +{ + /** @return true if str is empty. */ + bool empty(const char *str) + { + REQUIRE(str != NULL); + return *str == '\0'; + } + + void REQUIRE_FAILURE(bool success) + { + THEN("It should be an error") { + INFO("Previous pfw log: \n" + logLines); + CAPTURE(pfwGetLastError(pfw)); + CHECK(not success); + CHECK(not empty(pfwGetLastError(pfw))); + } + } + + void REQUIRE_SUCCESS(bool success) + { + THEN("It should be a success") { + INFO("Previous pfw log: \n" + logLines); + CAPTURE(pfwGetLastError(pfw)); + CHECK(success); + CHECK(empty(pfwGetLastError(pfw))); + } + } + + /** Class to create a temporary file */ + class TmpFile + { + public: + TmpFile(const std::string &content) { + char tmpName[] = "./tmpPfwUnitTestXXXXXX"; + mFd = mkstemp(tmpName); + CAPTURE(errno); + REQUIRE(mFd != -1); + mPath = tmpName; + write(mFd, content.c_str(), content.length()); + } + ~TmpFile() { + CHECK(close(mFd) != -1); + unlink(mPath.c_str()); + } + operator const char *() const { return mPath.c_str(); } + const std::string &path() const { return mPath; } + private: + std::string mPath; + int mFd; + }; + + /** Log in logLines. */ + static void logCb(void *voidLogLines, PfwLogLevel level, const char *logLine) + { + std::string &logLines = *reinterpret_cast(voidLogLines); + switch(level) { + case pfwLogWarning: + logLines += "Warning: "; + break; + case pfwLogInfo: + logLines += "Info: "; + } + logLines += logLine; + logLines += '\n'; + } + + /** Log buffer, will only be display in case of failure */ + std::string logLines; + + /** Pfw handler used in the tests. */ + PfwHandler *pfw; + +}; + +TEST_CASE_METHOD(Test, "Parameter-framework c api use") { + // Create criteria + const char *letterList[] = {"a", "b", "c", NULL}; + const char *numberList[] = {"1", "2", "3", NULL}; + const PfwCriterion criteria[] = { + {"inclusiveCrit", true, letterList}, + {"exclusiveCrit", false, numberList}, + }; + size_t criterionNb = sizeof(criteria)/sizeof(criteria[0]); + PfwLogger logger = {&logLines, logCb}; + + // Create valid pfw config file + const char *intParameterPath = "/test/system/integer"; + const char *stringParameterPath = "/test/system/string"; + TmpFile system("\ + \ + \ + \ + \ + \ + \ + "); + TmpFile libraries("\ + \ + \ + "); + TmpFile config("\ + \ + \ + \ + "); + + GIVEN("A created parameter framework") { + pfw = pfwCreate(); + REQUIRE(pfw != NULL); + + THEN("Error message should be empty") { + CHECK(empty(pfwGetLastError(pfw))); + } + + WHEN("The pfw is started without an handler") { + CHECK(not pfwStart(NULL, config, criteria, criterionNb, &logger)); + } + WHEN("The pfw is started without a config path") { + REQUIRE_FAILURE(pfwStart(pfw, NULL, criteria, criterionNb, &logger)); + } + WHEN("The pfw is started without an existent file") { + REQUIRE_FAILURE(pfwStart(pfw, "/doNotExist", criteria, criterionNb, &logger)); + } + + WHEN("The pfw is started without a criteria list") { + REQUIRE_FAILURE(pfwStart(pfw, config, NULL, criterionNb, &logger)); + } + WHEN("The pfw is started with duplicated criterion value") { + const PfwCriterion duplicatedCriteria[] = { + {"duplicated name", true, letterList}, + {"duplicated name", false, numberList}, + }; + REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 2, &logger)); + } + WHEN("The pfw is started with duplicated criterion value state") { + const char * values[] = {"a", "a", NULL}; + const PfwCriterion duplicatedCriteria[] = {{"name", true, values}}; + + WHEN("Using test logger") { + REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 1, &logger)); + } + WHEN("Using default logger") { + // Test coverage of default logger warning + REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 1, NULL)); + } + } + WHEN("The pfw is started with NULL name criterion") { + const PfwCriterion duplicatedCriteria[] = {{NULL, true, letterList}}; + REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 1, &logger)); + } + WHEN("The pfw is started with NULL criterion state list") { + const PfwCriterion duplicatedCriteria[] = {{"name", true, NULL}}; + REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 1, &logger)); + } + GIVEN("A criteria with lots of values") + { + // Build a criterion with as many value as there is bits in int. + std::vector names(sizeof(int) * CHAR_BIT + 1, 'a'); + names.back() = '\0'; + std::vector values(names.size()); + for(size_t i = 0; i < values.size(); ++i) { + values[i] = &names[i]; + } + values.back() = NULL; + /* The pfw c api requires criterion values to be a NULL terminated + * array of string. Each string is a pointer to a NULL terminated + * array of char. The pfw requires each string to be different + * from all others, ie strcmp(values[i], values[j]) != 0 for any + * i j. + * + * In order to generate easily an array of different strings, + * instantiate one string (names) big enough + * (@see PfwCriterion::values). + * Then instantiate an array of pointer (values), + * each pointing to a different position in the previously + * created string. + * + * Representation of the names and values vectors. + * + * n = number of bit in an int + * <--- n+1 elements ---> + * names = |a|a|a|a|...|a|a|a|\0| + * ^ ^ ^ + * values[0] = ´ | | + * values[1] = --´ | + * ... | + * values[n - 1] = -----------´ + * values[n] = NULL + * + */ + const PfwCriterion duplicatedCriteria[] = {{"name", true, &values[0]}}; + + WHEN("The pfw is started with a too long criterion state list") { + REQUIRE_FAILURE(pfwStart(pfw, config, duplicatedCriteria, 1, &logger)); + } + WHEN("The pfw is started with max length criterion state list") { + values[values.size() - 2] = NULL; // Hide last value + REQUIRE_SUCCESS(pfwStart(pfw, config, duplicatedCriteria, 1, &logger)); + } + } + + WHEN("The pfw is started with zero criteria") { + REQUIRE_SUCCESS(pfwStart(pfw, config, criteria, 0, &logger)); + } + + WHEN("The pfw is started twice a pfw") { + REQUIRE_SUCCESS(pfwStart(pfw, config, criteria, criterionNb, &logger)); + REQUIRE_FAILURE(pfwStart(pfw, config, criteria, criterionNb, &logger)); + } + + WHEN("The pfw is started without a logger callback") { + PfwLogger noLog = { NULL, NULL }; + REQUIRE_SUCCESS(pfwStart(pfw, config, criteria, criterionNb, &noLog)); + } + WHEN("The pfw is started with default logger") { + REQUIRE_SUCCESS(pfwStart(pfw, config, criteria, criterionNb, NULL)); + } + + WHEN("Get criterion of a stopped pfw") { + int value; + REQUIRE_FAILURE(pfwGetCriterion(pfw, criteria[0].name, &value)); + } + WHEN("Set criterion of a stopped pfw") { + REQUIRE_FAILURE(pfwSetCriterion(pfw, criteria[0].name, 1)); + } + WHEN("Commit criteria of a stopped pfw") { + REQUIRE_FAILURE(pfwApplyConfigurations(pfw)); + } + + WHEN("Bind parameter with a stopped pfw") { + REQUIRE(pfwBindParameter(pfw, intParameterPath) == NULL); + } + + WHEN("The pfw is started correctly") + { + REQUIRE_SUCCESS(pfwStart(pfw, config, criteria, criterionNb, &logger)); + int value; + + WHEN("Get criterion without an handle") { + REQUIRE(not pfwGetCriterion(NULL, criteria[0].name, &value)); + } + WHEN("Get criterion without a name") { + REQUIRE_FAILURE(pfwGetCriterion(pfw, NULL, &value)); + } + WHEN("Get criterion without an output value") { + REQUIRE_FAILURE(pfwGetCriterion(pfw, criteria[0].name, NULL)); + } + WHEN("Get not existing criterion") { + REQUIRE_FAILURE(pfwGetCriterion(pfw, "Do not exist", &value)); + } + THEN("All criterion should value 0") { + for(size_t i = 0; i < criterionNb; ++i) { + const char *criterionName = criteria[i].name; + CAPTURE(criterionName); + REQUIRE_SUCCESS(pfwGetCriterion(pfw, criterionName, &value)); + REQUIRE(value == 0); + } + } + + WHEN("Set criterion without an handle") { + REQUIRE(not pfwSetCriterion(NULL, criteria[0].name, 1)); + } + WHEN("Set criterion without a name") { + REQUIRE_FAILURE(pfwSetCriterion(pfw, NULL, 2)); + } + WHEN("Set not existing criterion") { + REQUIRE_FAILURE(pfwSetCriterion(pfw, "Do not exist", 3)); + } + WHEN("Set criterion value") { + for(size_t i = 0; i < criterionNb; ++i) { + const char *criterionName = criteria[i].name; + CAPTURE(criterionName); + REQUIRE_SUCCESS(pfwSetCriterion(pfw, criterionName, 3)); + } + THEN("Get criterion value should return what was set") { + for(size_t i = 0; i < criterionNb; ++i) { + const char *criterionName = criteria[i].name; + CAPTURE(criterionName); + REQUIRE_SUCCESS(pfwGetCriterion(pfw, criterionName, &value)); + REQUIRE(value == 3); + } + } + } + WHEN("Commit criteria without a pfw") { + REQUIRE(not pfwApplyConfigurations(NULL)); + } + WHEN("Commit criteria of a started pfw") { + REQUIRE_SUCCESS(pfwApplyConfigurations(pfw)); + } + + WHEN("Bind parameter without a pfw") { + REQUIRE(pfwBindParameter(NULL, intParameterPath) == NULL); + } + WHEN("Bind parameter without a path") { + REQUIRE_FAILURE(pfwBindParameter(pfw, NULL) != NULL); + } + WHEN("Bind a non existing parameter") { + REQUIRE_FAILURE(pfwBindParameter(pfw, "do/not/exist") != NULL); + } + + WHEN("Set an int parameter without a parameter handle") { + REQUIRE(not pfwSetIntParameter(NULL, value)); + } + WHEN("Get an int parameter without a parameter handle") { + REQUIRE(not pfwGetIntParameter(NULL, &value)); + } + + GIVEN("An integer parameter handle") { + PfwParameterHandler *param = pfwBindParameter(pfw, intParameterPath); + REQUIRE_SUCCESS(param != NULL); + + WHEN("Get an int parameter without an output value") { + REQUIRE_FAILURE(pfwGetIntParameter(param, NULL)); + } + + WHEN("Set parameter out of range") { + REQUIRE_FAILURE(pfwSetIntParameter(param, 101)); + } + + WHEN("Set parameter") { + REQUIRE_SUCCESS(pfwSetIntParameter(param, 11)); + THEN("Get parameter should return what was set") { + REQUIRE_SUCCESS(pfwGetIntParameter(param, &value)); + REQUIRE(value == 11); + } + } + + pfwUnbindParameter(param); + } + + GIVEN("An string parameter handle") { + PfwParameterHandler *param = pfwBindParameter(pfw, stringParameterPath); + REQUIRE_SUCCESS(param != NULL); + + WHEN("Get an int parameter without an output value") { + REQUIRE_FAILURE(pfwGetStringParameter(param, NULL)); + } + + WHEN("Set parameter out of range") { + REQUIRE_FAILURE(pfwSetStringParameter(param, "ko_1234567")); + } + + WHEN("Set parameter") { + const char *value; + REQUIRE_SUCCESS(pfwSetStringParameter(param, "ok")); + THEN("Get parameter should return what was set") { + REQUIRE_SUCCESS(pfwGetStringParameter(param, &value)); + REQUIRE(value == std::string("ok")); + pfwFree((void *)value); + } + } + + pfwUnbindParameter(param); + } + } + + pfwDestroy(pfw); + } +} + +SCENARIO("Get last error without a pfw") { + THEN("Should return NULL") { + CHECK(pfwGetLastError(NULL) == NULL); + } +} -- cgit v1.1