Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Common/Utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ o2_target_root_dictionary(CommonUtils
include/CommonUtils/IRFrameSelector.h
include/CommonUtils/DebugStreamer.h)

# Extra dictionaries only needed if tests are built
if(BUILD_TESTING)
o2_add_library(CommonUtilsTest
SOURCES src/ConfigurableParamTest.cxx
PUBLIC_LINK_LIBRARIES O2::CommonUtils)
o2_target_root_dictionary(CommonUtilsTest
HEADERS include/CommonUtils/ConfigurableParamTest.h)
endif()

o2_add_test(TreeStream
COMPONENT_NAME CommonUtils
LABELS utils
Expand Down Expand Up @@ -87,6 +96,12 @@ o2_add_test(EnumFlags
SOURCES test/testEnumFlags.cxx
PUBLIC_LINK_LIBRARIES O2::CommonUtils)

o2_add_test(ConfigurableParam
COMPONENT_NAME CommonUtils
LABELS utils
SOURCES test/testConfigurableParam.cxx
PUBLIC_LINK_LIBRARIES O2::CommonUtilsTest)

o2_add_executable(treemergertool
COMPONENT_NAME CommonUtils
SOURCES src/TreeMergerTool.cxx
Expand Down
45 changes: 45 additions & 0 deletions Common/Utils/include/CommonUtils/ConfigurableParamTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#ifndef COMMON_CONFIGURABLE_PARAM_TEST_H_
#define COMMON_CONFIGURABLE_PARAM_TEST_H_

#include "CommonUtils/ConfigurableParam.h"
#include "CommonUtils/ConfigurableParamHelper.h"

namespace o2::conf::test
{
struct TestParam : public o2::conf::ConfigurableParamHelper<TestParam> {
enum TestEnum : uint8_t {
A,
B,
C
};

int iValue{42};
float fValue{3.14};
double dValue{3.14};
bool bValue{true};
unsigned uValue{1};
long lValue{1};
unsigned long ulValue{1};
long long llValue{1};
unsigned long long ullValue{1};
std::string sValue = "default";
int iValueProvenanceTest{0};
TestEnum eValue = TestEnum::C;
int caValue[3] = {0, 1, 2};

O2ParamDef(TestParam, "TestParam");
};
} // namespace o2::conf::test

#endif
21 changes: 21 additions & 0 deletions Common/Utils/src/CommonUtilsTestLinkDef.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#ifdef __CLING__

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;

#pragma link C++ class o2::conf::test::TestParam + ;
#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::test::TestParam> + ;

#endif
62 changes: 58 additions & 4 deletions Common/Utils/src/ConfigurableParam.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,30 @@ bool keyInTree(boost::property_tree::ptree* pt, const std::string& key)
return reply;
}

// Convert a type info to the appropiate literal suffix
std::string getLiteralSuffixFromType(const std::type_info& type)
{
if (type == typeid(float)) {
return "f";
}
if (type == typeid(long double)) {
return "l";
}
if (type == typeid(unsigned int)) {
return "u";
}
if (type == typeid(unsigned long)) {
return "ul";
}
if (type == typeid(long long)) {
return "ll";
}
if (type == typeid(unsigned long long)) {
return "ull";
}
return "";
}

// ------------------------------------------------------------------

void EnumRegistry::add(const std::string& key, const TDataMember* dm)
Expand Down Expand Up @@ -204,12 +228,42 @@ void ConfigurableParam::setValue(std::string const& key, std::string const& valu
initialize();
}
assert(sPtree);
auto setValueImpl = [&](std::string const& value) {
sPtree->put(key, value);
auto changed = updateThroughStorageMapWithConversion(key, value);
if (changed != EParamUpdateStatus::Failed) {
sValueProvenanceMap->find(key)->second = kRT; // set to runtime
}
};
try {
if (sPtree->get_optional<std::string>(key).is_initialized()) {
sPtree->put(key, valuestring);
auto changed = updateThroughStorageMapWithConversion(key, valuestring);
if (changed != EParamUpdateStatus::Failed) {
sValueProvenanceMap->find(key)->second = kRT; // set to runtime
try {
// try first setting value without stripping a literal suffix
setValueImpl(valuestring);
} catch (...) {
// try second stripping the expected literal suffix value for fundamental types
auto iter = sKeyToStorageMap->find(key);
if (iter == sKeyToStorageMap->end()) {
std::cerr << "Error in setValue (string) key is not known\n";
return;
}
const auto expectedSuffix = getLiteralSuffixFromType(iter->second.first);
if (!expectedSuffix.empty()) {
auto valuestringLower = valuestring;
std::transform(valuestring.cbegin(), valuestring.cend(), valuestringLower.begin(), tolower);
if (valuestringLower.ends_with(expectedSuffix)) {
std::string strippedValue = valuestringLower.substr(0, valuestringLower.length() - expectedSuffix.length());
setValueImpl(strippedValue);
} else {
// check if it has a different suffix and throw
for (const auto& suffix : {"f", "l", "u", "ul", "ll", "ull"}) {
if (valuestringLower.ends_with(suffix) && suffix != expectedSuffix) {
throw std::invalid_argument("Wrong type suffix: expected " + expectedSuffix + " but got " + suffix);
}
}
throw; // just rethrow the original exception
}
}
}
}
} catch (std::exception const& e) {
Expand Down
13 changes: 13 additions & 0 deletions Common/Utils/src/ConfigurableParamTest.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#include "CommonUtils/ConfigurableParamTest.h"
O2ParamImpl(o2::conf::test::TestParam);
145 changes: 145 additions & 0 deletions Common/Utils/test/testConfigurableParam.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#define BOOST_TEST_MODULE Test ConfigurableParams
#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK

#include <boost/test/unit_test.hpp>
#include <boost/property_tree/ptree.hpp>
#include <filesystem>

#include "CommonUtils/ConfigurableParamTest.h"

using namespace o2::conf;
using namespace o2::conf::test;

BOOST_AUTO_TEST_CASE(ConfigurableParam_Basic)
{
// Tests the default parameters and also getter helpers.
auto& param = TestParam::Instance();
BOOST_CHECK_EQUAL(param.iValue, 42);
BOOST_CHECK_EQUAL(param.dValue, 3.14);
BOOST_CHECK_EQUAL(param.bValue, true);
BOOST_CHECK_EQUAL(param.sValue, "default");
BOOST_CHECK_EQUAL(static_cast<int>(param.eValue), 2);

BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs<int>("TestParam.iValue"), 42);
BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs<double>("TestParam.dValue"), 3.14);
BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs<bool>("TestParam.bValue"), true);
BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs<std::string>("TestParam.sValue"), "default");
}

BOOST_AUTO_TEST_CASE(ConfigurableParam_SG_Fundamental)
{
// tests runtime setting and getting for fundamental types
ConfigurableParam::setValue("TestParam.iValue", "100");
ConfigurableParam::setValue("TestParam.dValue", "2.718");
ConfigurableParam::setValue("TestParam.bValue", "0");
ConfigurableParam::setValue("TestParam.sValue", "modified");
ConfigurableParam::setValue("TestParam.eValue", "0");

auto& param = TestParam::Instance();
param.printKeyValues();
BOOST_CHECK_EQUAL(param.iValue, 100);
BOOST_CHECK_EQUAL(param.dValue, 2.718);
BOOST_CHECK_EQUAL(param.bValue, false);
BOOST_CHECK_EQUAL(param.sValue, "modified");
BOOST_CHECK_EQUAL(static_cast<int>(param.eValue), 0);
}

BOOST_AUTO_TEST_CASE(ConfigurableParam_SG_CArray)
{
// tests setting and getting for a c-style array type
auto& param = TestParam::Instance();
BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs<int>("TestParam.caValue[0]"), 0);
BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs<int>("TestParam.caValue[1]"), 1);
BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs<int>("TestParam.caValue[2]"), 2);

ConfigurableParam::setValue("TestParam.caValue[1]", "99");
BOOST_CHECK_EQUAL(ConfigurableParam::getValueAs<int>("TestParam.caValue[1]"), 99);
}

BOOST_AUTO_TEST_CASE(ConfigurableParam_Provenance)
{
// tests correct setting of provenance
BOOST_CHECK_EQUAL(ConfigurableParam::getProvenance("TestParam.iValueProvenanceTest"), ConfigurableParam::EParamProvenance::kCODE);
ConfigurableParam::setValue("TestParam.iValueProvenanceTest", "123");
BOOST_CHECK_EQUAL(ConfigurableParam::getProvenance("TestParam.iValueProvenanceTest"), ConfigurableParam::EParamProvenance::kRT);
}

BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_Ini)
{
// test for ini file serialization
const std::string testFileName = "test_config.ini";
auto iValueBefore = TestParam::Instance().iValue;
auto sValueBefore = TestParam::Instance().sValue;
ConfigurableParam::writeINI(testFileName);
ConfigurableParam::setValue("TestParam.iValue", "999");
ConfigurableParam::setValue("TestParam.sValue", testFileName);
ConfigurableParam::updateFromFile(testFileName);
BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore);
BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore);
std::remove(testFileName.c_str());
}

BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_Json)
{
// test for json file serialization
const std::string testFileName = "test_config.json";
auto iValueBefore = TestParam::Instance().iValue;
auto sValueBefore = TestParam::Instance().sValue;
ConfigurableParam::writeJSON(testFileName);
ConfigurableParam::setValue("TestParam.iValue", "999");
ConfigurableParam::setValue("TestParam.sValue", testFileName);
ConfigurableParam::updateFromFile(testFileName);
BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore);
BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore);
std::remove(testFileName.c_str());
}

BOOST_AUTO_TEST_CASE(ConfigurableParam_FileIO_ROOT)
{
// test for root file serialization
const std::string testFileName = "test_config.root";
auto iValueBefore = TestParam::Instance().iValue;
auto sValueBefore = TestParam::Instance().sValue;
TFile* testFile = TFile::Open(testFileName.c_str(), "RECREATE");
TestParam::Instance().serializeTo(testFile);
testFile->Close();
ConfigurableParam::setValue("TestParam.iValue", "999");
ConfigurableParam::setValue("TestParam.sValue", testFileName);
ConfigurableParam::fromCCDB(testFileName);
BOOST_CHECK_EQUAL(TestParam::Instance().iValue, iValueBefore);
BOOST_CHECK_EQUAL(TestParam::Instance().sValue, sValueBefore);
std::remove(testFileName.c_str());
}

BOOST_AUTO_TEST_CASE(ConfigurableParam_Cli)
{
// test setting values from as a cli arg string
ConfigurableParam::updateFromString("TestParam.iValue=55;TestParam.sValue=cli");
BOOST_CHECK_EQUAL(TestParam::Instance().iValue, 55);
BOOST_CHECK_EQUAL(TestParam::Instance().sValue, "cli");
}

BOOST_AUTO_TEST_CASE(ConfigurableParam_LiteralSuffix)
{
// test setting values with the correct literal suffix
ConfigurableParam::updateFromString("TestParam.fValue=42.f");
BOOST_CHECK_EQUAL(TestParam::Instance().fValue, 42.f);

ConfigurableParam::setValue("TestParam.ullValue", "999ull");
BOOST_CHECK_EQUAL(TestParam::Instance().ullValue, 999ULL);
// check using wrong literal suffix fails, prints error to std
ConfigurableParam::setValue("TestParam.ullValue", "888u");
BOOST_CHECK_NE(TestParam::Instance().ullValue, 888);
}