You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2023/01/08 15:33:29 UTC
[celix] 08/10: Refactor and improve properties and string hash map test suite and impl
This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a commit to branch feature/type_support_for_properties
in repository https://gitbox.apache.org/repos/asf/celix.git
commit ced3279168aae20863106951921b3b8e02ecea28
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Sun Jan 8 14:58:10 2023 +0100
Refactor and improve properties and string hash map test suite and impl
---
libs/framework/include/celix/Exception.h | 4 +
libs/utils/gtest/CMakeLists.txt | 1 +
libs/utils/gtest/src/CxxPropertiesTestSuite.cc | 34 ++-
libs/utils/gtest/src/CxxVersionTestSuite.cc | 93 ++++++++
libs/utils/gtest/src/HashMapTestSuite.cc | 20 ++
libs/utils/gtest/src/PropertiesTestSuite.cc | 110 +++++-----
libs/utils/gtest/src/VersionTestSuite.cc | 244 ++++++++-------------
.../include/celix/IOException.h} | 21 +-
libs/utils/include/celix/Properties.h | 70 +++++-
libs/utils/include/celix/Version.h | 9 +-
libs/utils/include/celix_long_hash_map.h | 8 +
libs/utils/include/celix_properties.h | 14 +-
libs/utils/include/celix_string_hash_map.h | 33 ++-
libs/utils/src/celix_hash_map.c | 48 +++-
libs/utils/src/properties.c | 51 +++--
libs/utils/src/version.c | 2 +-
16 files changed, 486 insertions(+), 276 deletions(-)
diff --git a/libs/framework/include/celix/Exception.h b/libs/framework/include/celix/Exception.h
index 1675567d..b7186634 100644
--- a/libs/framework/include/celix/Exception.h
+++ b/libs/framework/include/celix/Exception.h
@@ -27,7 +27,11 @@ namespace celix {
*/
class Exception : public std::exception {
public:
+#if __cplusplus >= 201703L //C++17 or higher
+ explicit Exception(std::string_view msg) : w{msg} {}
+#else
explicit Exception(std::string msg) : w{std::move(msg)} {}
+#endif
Exception(const Exception&) = default;
Exception(Exception&&) = default;
diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt
index 1e512fe6..894fba32 100644
--- a/libs/utils/gtest/CMakeLists.txt
+++ b/libs/utils/gtest/CMakeLists.txt
@@ -32,6 +32,7 @@ add_executable(test_utils
src/FileUtilsTestSuite.cc
src/PropertiesTestSuite.cc
src/VersionTestSuite.cc
+ src/CxxVersionTestSuite.cc
${CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS}
)
diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
index 142570c4..b2f39192 100644
--- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
@@ -27,12 +27,12 @@ class CxxPropertiesTestSuite : public ::testing::Test {
public:
};
-TEST_F(CxxPropertiesTestSuite, testCreateDestroy) {
+TEST_F(CxxPropertiesTestSuite, CreateDestroyTest) {
celix::Properties props{};
EXPECT_EQ(0, props.size());
}
-TEST_F(CxxPropertiesTestSuite, testFillAndLoop) {
+TEST_F(CxxPropertiesTestSuite, FillAndLoopTest) {
celix::Properties props{};
EXPECT_EQ(0, props.size());
@@ -66,7 +66,7 @@ TEST_F(CxxPropertiesTestSuite, testFillAndLoop) {
EXPECT_EQ(5, count);
}
-TEST_F(CxxPropertiesTestSuite, testLoopForSize0And1) {
+TEST_F(CxxPropertiesTestSuite, LoopForSize0And1Test) {
celix::Properties props0{};
for (const auto& pair : props0) {
FAIL() << "Should not get an loop entry with a properties size of 0. got key: " << pair.first;
@@ -82,7 +82,7 @@ TEST_F(CxxPropertiesTestSuite, testLoopForSize0And1) {
EXPECT_EQ(1, count);
}
-TEST_F(CxxPropertiesTestSuite, testCopy) {
+TEST_F(CxxPropertiesTestSuite, CopyTest) {
celix::Properties props{};
props["key1"] = "value1";
@@ -97,7 +97,7 @@ TEST_F(CxxPropertiesTestSuite, testCopy) {
EXPECT_EQ(v2, "value1_new");
}
-TEST_F(CxxPropertiesTestSuite, testWrap) {
+TEST_F(CxxPropertiesTestSuite, WrapTest) {
auto *props = celix_properties_create();
celix_properties_set(props, "test", "test");
@@ -112,7 +112,7 @@ TEST_F(CxxPropertiesTestSuite, testWrap) {
celix_properties_destroy(props);
}
-TEST_F(CxxPropertiesTestSuite, getType) {
+TEST_F(CxxPropertiesTestSuite, GetTypeTest) {
celix::Properties props{};
props.set("bool", true);
@@ -140,7 +140,7 @@ TEST_F(CxxPropertiesTestSuite, getType) {
EXPECT_EQ(props.getType("version"), celix::Properties::ValueType::Version);
}
-TEST_F(CxxPropertiesTestSuite, testGetAsVersion) {
+TEST_F(CxxPropertiesTestSuite, GetAsVersionTest) {
celix::Properties props;
// Test getting a version from a string property
@@ -163,8 +163,24 @@ TEST_F(CxxPropertiesTestSuite, testGetAsVersion) {
EXPECT_EQ(props.getAsVersion("key", celix::Version{4, 5, 6}), ver);
}
+TEST_F(CxxPropertiesTestSuite, StoreAndLoadTest) {
+ std::string path{"cxx_store_and_load_test.properties"};
+
+ celix::Properties props{};
+ props.set("key1", 1);
+ props.set("key2", 2);
+
+ EXPECT_NO_THROW(props.store(path));
+
+ celix::Properties loadedProps{};
+ EXPECT_NO_THROW(loadedProps = celix::Properties::load(path));
+ EXPECT_EQ(props.size(), loadedProps.size());
+
+ EXPECT_THROW(loadedProps = celix::Properties::load("non-existence"), celix::IOException);
+}
+
#if __cplusplus >= 201703L //C++17 or higher
-TEST_F(CxxPropertiesTestSuite, testStringView) {
+TEST_F(CxxPropertiesTestSuite, StringViewTest) {
constexpr std::string_view stringViewKey = "KEY1";
constexpr std::string_view stringViewValue = "VALUE1";
std::string stringKey{"KEY2"};
@@ -219,7 +235,7 @@ TEST_F(CxxPropertiesTestSuite, testStringView) {
}
}
-TEST_F(CxxPropertiesTestSuite, testUseOfConstexprInSetMethod) {
+TEST_F(CxxPropertiesTestSuite, UseOfConstexprInSetMethodTest) {
celix::Properties props{};
//Test if different bool "types" are correctly handled
diff --git a/libs/utils/gtest/src/CxxVersionTestSuite.cc b/libs/utils/gtest/src/CxxVersionTestSuite.cc
new file mode 100644
index 00000000..0055d5c4
--- /dev/null
+++ b/libs/utils/gtest/src/CxxVersionTestSuite.cc
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "celix/Version.h"
+
+class CxxVersionTestSuite : public ::testing::Test {};
+
+#include "gtest/gtest.h"
+#include "celix/Version.h"
+
+TEST_F(CxxVersionTestSuite, DefaultConstructorTest) {
+ celix::Version v;
+ EXPECT_EQ(0, v.getMajor());
+ EXPECT_EQ(0, v.getMinor());
+ EXPECT_EQ(0, v.getMicro());
+ EXPECT_EQ("", v.getQualifier());
+}
+
+TEST_F(CxxVersionTestSuite, ConstructorTest) {
+ celix::Version v1{1, 2, 3, "qualifier"};
+ EXPECT_EQ(1, v1.getMajor());
+ EXPECT_EQ(2, v1.getMinor());
+ EXPECT_EQ(3, v1.getMicro());
+ EXPECT_EQ("qualifier", v1.getQualifier());
+
+ #if __cplusplus >= 201703L //C++17 or higher
+ celix::Version v2{1, 2, 3, std::string_view{"qualifier"}};
+ EXPECT_EQ(1, v2.getMajor());
+ EXPECT_EQ(2, v2.getMinor());
+ EXPECT_EQ(3, v2.getMicro());
+ EXPECT_EQ("qualifier", v2.getQualifier());
+ #endif
+}
+
+TEST_F(CxxVersionTestSuite, MoveConstructorTest) {
+ celix::Version v1{2, 3, 4, "qualifier"};
+ celix::Version v2{std::move(v1)};
+
+ //v2 should have the values of v1 before the move
+ EXPECT_EQ(v2.getMajor(), 2);
+ EXPECT_EQ(v2.getMinor(), 3);
+ EXPECT_EQ(v2.getMicro(), 4);
+ EXPECT_EQ(v2.getQualifier(), "qualifier");
+}
+
+TEST_F(CxxVersionTestSuite, CopyConstructorTest) {
+ celix::Version v1{1, 2, 3, "qualifier"};
+ celix::Version v2 = v1;
+
+ EXPECT_EQ(v1, v2);
+ EXPECT_EQ(v1.getMajor(), v2.getMajor());
+ EXPECT_EQ(v1.getMinor(), v2.getMinor());
+ EXPECT_EQ(v1.getMicro(), v2.getMicro());
+ EXPECT_EQ(v1.getQualifier(), v2.getQualifier());
+}
+
+TEST_F(CxxVersionTestSuite, MapKeyTest) {
+ // Test using Version as key in std::map
+ std::map<celix::Version, int> map;
+ map[celix::Version{1, 2, 3}] = 1;
+ map[celix::Version{3, 2, 1}] = 2;
+
+ EXPECT_EQ(map[(celix::Version{1, 2, 3})], 1);
+ EXPECT_EQ(map[(celix::Version{3, 2, 1})], 2);
+}
+
+TEST_F(CxxVersionTestSuite, UnorderedMapKeyTest) {
+ // Test using Version as key in std::unordered_map
+ std::unordered_map<celix::Version, int> unorderedMap;
+ unorderedMap[celix::Version{1, 2, 3}] = 1;
+ unorderedMap[celix::Version{3, 2, 1}] = 2;
+
+ EXPECT_EQ(unorderedMap[(celix::Version{1, 2, 3})], 1);
+ EXPECT_EQ(unorderedMap[(celix::Version{3, 2, 1})], 2);
+}
diff --git a/libs/utils/gtest/src/HashMapTestSuite.cc b/libs/utils/gtest/src/HashMapTestSuite.cc
index 27f0589a..7f9010e3 100644
--- a/libs/utils/gtest/src/HashMapTestSuite.cc
+++ b/libs/utils/gtest/src/HashMapTestSuite.cc
@@ -632,3 +632,23 @@ TEST_F(HashMapTestSuite, EqualsZeroSizeMapTest) {
celix_stringHashMap_destroy(sMap);
celix_longHashMap_destroy(lMap);
}
+
+TEST_F(HashMapTestSuite, StoreKeysWeaklyTest) {
+ celix_string_hash_map_create_options_t opts{};
+ opts.removedCallbackData = (void*)0x1;
+ opts.storeKeysWeakly = true;
+ opts.removedKeyCallback = [](void* data, char* key) {
+ EXPECT_EQ(data, (void*)0x1);
+ free(key);
+ };
+ auto* sMap = celix_stringHashMap_createWithOptions(&opts);
+ EXPECT_FALSE(celix_stringHashMap_putLong(sMap, celix_utils_strdup("key1"), 1)); //new key -> takes ownership
+ EXPECT_TRUE(celix_stringHashMap_putLong(sMap, "key1", 2)); //replace key -> takes no ownership
+
+ EXPECT_FALSE(celix_stringHashMap_putLong(sMap, celix_utils_strdup("key2"), 3)); //new key -> takes ownership
+ EXPECT_TRUE(celix_stringHashMap_putLong(sMap, "key2", 4)); //replace key -> takes no ownership
+ celix_stringHashMap_remove(sMap, "key1");
+
+ celix_stringHashMap_destroy(sMap);
+}
+
diff --git a/libs/utils/gtest/src/PropertiesTestSuite.cc b/libs/utils/gtest/src/PropertiesTestSuite.cc
index 74e96f6e..f21b4049 100644
--- a/libs/utils/gtest/src/PropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/PropertiesTestSuite.cc
@@ -20,6 +20,7 @@
#include <gtest/gtest.h>
#include "celix_properties.h"
+#include "celix_utils.h"
using ::testing::MatchesRegex;
@@ -28,14 +29,14 @@ public:
};
-TEST_F(PropertiesTestSuite, create) {
+TEST_F(PropertiesTestSuite, CreateTest) {
auto* properties = celix_properties_create();
EXPECT_TRUE(properties);
celix_properties_destroy(properties);
}
-TEST_F(PropertiesTestSuite, load) {
+TEST_F(PropertiesTestSuite, LoadTest) {
char propertiesFile[] = "resources-test/properties.txt";
auto* properties = celix_properties_load(propertiesFile);
EXPECT_EQ(4, celix_properties_size(properties));
@@ -55,7 +56,21 @@ TEST_F(PropertiesTestSuite, load) {
celix_properties_destroy(properties);
}
-TEST_F(PropertiesTestSuite, asLong) {
+TEST_F(PropertiesTestSuite, StoreTest) {
+ const char* propertiesFile = "resources-test/properties_out.txt";
+ auto* properties = celix_properties_create();
+ char keyA[] = "x";
+ char keyB[] = "y";
+ char valueA[] = "1";
+ char valueB[] = "2";
+ celix_properties_set(properties, keyA, valueA);
+ celix_properties_set(properties, keyB, valueB);
+ celix_properties_store(properties, propertiesFile, nullptr);
+
+ celix_properties_destroy(properties);
+}
+
+TEST_F(PropertiesTestSuite, GetAsLongTest) {
celix_properties_t *props = celix_properties_create();
celix_properties_set(props, "t1", "42");
celix_properties_set(props, "t2", "-42");
@@ -84,21 +99,7 @@ TEST_F(PropertiesTestSuite, asLong) {
celix_properties_destroy(props);
}
-TEST_F(PropertiesTestSuite, store) {
- char propertiesFile[] = "resources-test/properties_out.txt";
- auto* properties = celix_properties_create();
- char keyA[] = "x";
- char keyB[] = "y";
- char valueA[] = "1";
- char valueB[] = "2";
- celix_properties_set(properties, keyA, valueA);
- celix_properties_set(properties, keyB, valueB);
- celix_properties_store(properties, propertiesFile, nullptr);
-
- celix_properties_destroy(properties);
-}
-
-TEST_F(PropertiesTestSuite, copy) {
+TEST_F(PropertiesTestSuite, CopyTest) {
char propertiesFile[] = "resources-test/properties.txt";
auto* properties = celix_properties_load(propertiesFile);
EXPECT_EQ(4, celix_properties_size(properties));
@@ -115,7 +116,7 @@ TEST_F(PropertiesTestSuite, copy) {
celix_properties_destroy(copy);
}
-TEST_F(PropertiesTestSuite, getSet) {
+TEST_F(PropertiesTestSuite, GetSetTest) {
auto* properties = celix_properties_create();
char keyA[] = "x";
char keyB[] = "y";
@@ -137,7 +138,7 @@ TEST_F(PropertiesTestSuite, getSet) {
celix_properties_destroy(properties);
}
-TEST_F(PropertiesTestSuite, getSetWithNULL) {
+TEST_F(PropertiesTestSuite, GetSetWithNullTest) {
auto* properties = celix_properties_create();
celix_properties_set(properties, nullptr, "value");
@@ -153,7 +154,7 @@ TEST_F(PropertiesTestSuite, getSetWithNULL) {
}
-TEST_F(PropertiesTestSuite, setUnset) {
+TEST_F(PropertiesTestSuite, SetUnsetTest) {
auto* properties = celix_properties_create();
char keyA[] = "x";
char *keyD = strndup("a", 1);
@@ -171,7 +172,7 @@ TEST_F(PropertiesTestSuite, setUnset) {
celix_properties_destroy(properties);
}
-TEST_F(PropertiesTestSuite, longTest) {
+TEST_F(PropertiesTestSuite, GetLongTest) {
auto* properties = celix_properties_create();
celix_properties_set(properties, "a", "2");
@@ -201,7 +202,7 @@ TEST_F(PropertiesTestSuite, longTest) {
celix_properties_destroy(properties);
}
-TEST_F(PropertiesTestSuite, boolTest) {
+TEST_F(PropertiesTestSuite, GetBoolTest) {
auto* properties = celix_properties_create();
celix_properties_set(properties, "a", "true");
@@ -233,7 +234,7 @@ TEST_F(PropertiesTestSuite, boolTest) {
celix_properties_destroy(properties);
}
-TEST_F(PropertiesTestSuite, fillTest) {
+TEST_F(PropertiesTestSuite, GetFillTest) {
celix_properties_t *props = celix_properties_create();
int testCount = 1000;
for (int i = 0; i < 1000; ++i) {
@@ -246,31 +247,34 @@ TEST_F(PropertiesTestSuite, fillTest) {
celix_properties_destroy(props);
}
-TEST_F(PropertiesTestSuite, setOverwrite) {
+TEST_F(PropertiesTestSuite, GetSetOverwrite) {
auto* props = celix_properties_create();
auto* version = celix_version_createEmptyVersion();
- const char* key = "key";
-
- celix_properties_set(props, key, "str1");
- EXPECT_STREQ("str1", celix_properties_get(props, key, ""));
- celix_properties_set(props, key, "str2");
- EXPECT_STREQ("str2", celix_properties_get(props, key, ""));
- celix_properties_setLong(props, key, 1);
- EXPECT_EQ(1, celix_properties_getAsLong(props, key, -1L));
- celix_properties_setDouble(props, key, 2.0);
- EXPECT_EQ(2.0, celix_properties_getAsLong(props, key, -2.0));
- celix_properties_setBool(props, key, false);
- EXPECT_EQ(false, celix_properties_getAsBool(props, key, true));
- celix_properties_setVersionWithoutCopy(props, key, version);
- EXPECT_EQ(version, celix_properties_getVersion(props, key, nullptr));
- celix_properties_set(props, key, "last");
+
+ {
+ char* key = celix_utils_strdup("key");
+ celix_properties_set(props, key, "str1");
+ free(key);
+ }
+ EXPECT_STREQ("str1", celix_properties_get(props, "key", ""));
+ celix_properties_set(props, "key", "str2");
+ EXPECT_STREQ("str2", celix_properties_get(props, "key", ""));
+ celix_properties_setLong(props, "key", 1);
+ EXPECT_EQ(1, celix_properties_getAsLong(props, "key", -1L));
+ celix_properties_setDouble(props, "key", 2.0);
+ EXPECT_EQ(2.0, celix_properties_getAsLong(props, "key", -2.0));
+ celix_properties_setBool(props, "key", false);
+ EXPECT_EQ(false, celix_properties_getAsBool(props, "key", true));
+ celix_properties_setVersionWithoutCopy(props, "key", version);
+ EXPECT_EQ(version, celix_properties_getVersion(props, "key", nullptr));
+ celix_properties_set(props, "key", "last");
celix_properties_destroy(props);
}
-TEST_F(PropertiesTestSuite, sizeAndIteratorTest) {
+TEST_F(PropertiesTestSuite, SizeAndIteratorTest) {
celix_properties_t *props = celix_properties_create();
EXPECT_EQ(0, celix_properties_size(props));
celix_properties_set(props, "a", "1");
@@ -291,7 +295,7 @@ TEST_F(PropertiesTestSuite, sizeAndIteratorTest) {
celix_properties_destroy(props);
}
-TEST_F(PropertiesTestSuite, getType) {
+TEST_F(PropertiesTestSuite, GetTypeTest) {
auto* props = celix_properties_create();
celix_properties_set(props, "string", "value");
celix_properties_setLong(props, "long", 123);
@@ -311,7 +315,7 @@ TEST_F(PropertiesTestSuite, getType) {
celix_properties_destroy(props);
}
-TEST_F(PropertiesTestSuite, getEntry) {
+TEST_F(PropertiesTestSuite, GetEntryTest) {
auto* props = celix_properties_create();
celix_properties_set(props, "key1", "value1");
celix_properties_setLong(props, "key2", 123);
@@ -354,7 +358,7 @@ TEST_F(PropertiesTestSuite, getEntry) {
celix_properties_destroy(props);
}
-TEST_F(PropertiesTestSuite, iteratorNextKey) {
+TEST_F(PropertiesTestSuite, IteratorNextKeyTest) {
auto* props = celix_properties_create();
celix_properties_set(props, "key1", "value1");
celix_properties_set(props, "key2", "value2");
@@ -374,7 +378,7 @@ TEST_F(PropertiesTestSuite, iteratorNextKey) {
celix_properties_destroy(props);
}
-TEST_F(PropertiesTestSuite, iteratorNext) {
+TEST_F(PropertiesTestSuite, IteratorNextTest) {
auto* props = celix_properties_create();
celix_properties_set(props, "key1", "value1");
celix_properties_set(props, "key2", "value2");
@@ -383,7 +387,7 @@ TEST_F(PropertiesTestSuite, iteratorNext) {
int count = 0;
auto iter = celix_properties_begin(props);
while (!celix_propertiesIterator_isEnd(&iter)) {
- EXPECT_NE(strstr(iter.entry.key, "key"), nullptr);
+ EXPECT_NE(strstr(iter.key, "key"), nullptr);
EXPECT_NE(strstr(iter.entry.value, "value"), nullptr);
count++;
celix_propertiesIterator_next(&iter);
@@ -395,7 +399,7 @@ TEST_F(PropertiesTestSuite, iteratorNext) {
celix_properties_destroy(props);
}
-TEST_F(PropertiesTestSuite, iterateOverProperties) {
+TEST_F(PropertiesTestSuite, IterateOverPropertiesTest) {
auto* props = celix_properties_create();
celix_properties_set(props, "key1", "value1");
celix_properties_set(props, "key2", "value2");
@@ -404,12 +408,12 @@ TEST_F(PropertiesTestSuite, iterateOverProperties) {
int innerCount = 0;
CELIX_PROPERTIES_ITERATE(props, outerIter) {
outerCount++;
- EXPECT_NE(strstr(outerIter.entry.key, "key"), nullptr);
+ EXPECT_NE(strstr(outerIter.key, "key"), nullptr);
// Inner loop to test nested iteration
CELIX_PROPERTIES_ITERATE(props, innerIter) {
innerCount++;
- EXPECT_NE(strstr(innerIter.entry.key, "key"), nullptr);
+ EXPECT_NE(strstr(innerIter.key, "key"), nullptr);
}
}
// Check that both entries were iterated over
@@ -428,7 +432,7 @@ TEST_F(PropertiesTestSuite, iterateOverProperties) {
celix_properties_destroy(props);
}
-TEST_F(PropertiesTestSuite, getVersion) {
+TEST_F(PropertiesTestSuite, GetVersionTest) {
auto* properties = celix_properties_create();
auto* emptyVersion = celix_version_createEmptyVersion();
@@ -483,7 +487,7 @@ TEST_F(PropertiesTestSuite, getVersion) {
celix_properties_destroy(properties);
}
-TEST_F(PropertiesTestSuite, TestEndOfProperties) {
+TEST_F(PropertiesTestSuite, EndOfPropertiesTest) {
auto* props = celix_properties_create();
celix_properties_set(props, "key1", "value1");
celix_properties_set(props, "key2", "value2");
@@ -495,7 +499,7 @@ TEST_F(PropertiesTestSuite, TestEndOfProperties) {
celix_properties_destroy(props);
}
-TEST_F(PropertiesTestSuite, TestEndOfEmptyProperties) {
+TEST_F(PropertiesTestSuite, EndOfEmptyPropertiesTest) {
auto* props = celix_properties_create();
celix_properties_iterator_t endIter = celix_properties_end(props);
@@ -504,3 +508,5 @@ TEST_F(PropertiesTestSuite, TestEndOfEmptyProperties) {
celix_properties_destroy(props);
}
+
+//TODO test replace and replace WithCopy
\ No newline at end of file
diff --git a/libs/utils/gtest/src/VersionTestSuite.cc b/libs/utils/gtest/src/VersionTestSuite.cc
index 90e5e9f1..54539c7b 100644
--- a/libs/utils/gtest/src/VersionTestSuite.cc
+++ b/libs/utils/gtest/src/VersionTestSuite.cc
@@ -24,208 +24,134 @@
#include "celix_utils.h"
#include "version.h"
-extern "C"
-{
-#include "version_private.h"
-}
-
-class VersionTestSuite : public ::testing::Test {};
-
-TEST_F(VersionTestSuite, create) {
+class VersionTestSuite : public ::testing::Test {
+public:
+ void expectVersion(const celix_version_t* version, int major, int minor, int micro, const char* qualifier = "") {
+ if (version) {
+ EXPECT_EQ(major, celix_version_getMajor(version));
+ EXPECT_EQ(minor, celix_version_getMinor(version));
+ EXPECT_EQ(micro, celix_version_getMicro(version));
+ EXPECT_STREQ(qualifier, celix_version_getQualifier(version));
+ }
+ }
+};
+
+TEST_F(VersionTestSuite, CreateTest) {
celix_version_t* version = nullptr;
- char * str;
- str = celix_utils_strdup("abc");
- EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, str, &version));
+ version = celix_version_create(1, 2, 3, "abc");
EXPECT_TRUE(version != nullptr);
- EXPECT_EQ(1, version->major);
- EXPECT_EQ(2, version->minor);
- EXPECT_EQ(3, version->micro);
- EXPECT_STREQ("abc", version->qualifier);
+ expectVersion(version, 1, 2, 3, "abc");
+ celix_version_destroy(version);
- version_destroy(version);
+ EXPECT_EQ(nullptr, celix_version_createVersion(-1, -2, -3, "abc"));
+ EXPECT_EQ(nullptr, celix_version_createVersion(-1, -2, -3, "abc|xyz"));
+
+
+ //Testing deprecated api
version = nullptr;
EXPECT_EQ(CELIX_SUCCESS, version_createVersion(1, 2, 3, nullptr, &version));
EXPECT_TRUE(version != nullptr);
- EXPECT_EQ(1, version->major);
- EXPECT_EQ(2, version->minor);
- EXPECT_EQ(3, version->micro);
- EXPECT_STREQ("", version->qualifier);
-
+ int major;
+ int minor;
+ int micro;
+ const char* q;
+ EXPECT_EQ(CELIX_SUCCESS, version_getMajor(version, &major));
+ EXPECT_EQ(CELIX_SUCCESS, version_getMinor(version, &minor));
+ EXPECT_EQ(CELIX_SUCCESS, version_getMicro(version, µ));
+ EXPECT_EQ(CELIX_SUCCESS, version_getQualifier(version, &q));
+ EXPECT_EQ(1, major);
+ EXPECT_EQ(2, minor);
+ EXPECT_EQ(3, micro);
+ EXPECT_STREQ("", q);
version_destroy(version);
- version = nullptr;
- free(str);
- str = celix_utils_strdup("abc");
- EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(-1, -2, -3, str, &version));
- version_destroy(version);
version = nullptr;
- free(str);
- str = celix_utils_strdup("abc|xyz");
- EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(1, 2, 3, str, &version));
-
- version_destroy(version);
- free(str);
+ EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(-1, -2, -3, "abc", &version));
+ EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersion(1, 2, 3, "abc|xyz", &version));
}
-TEST_F(VersionTestSuite, copy) {
+TEST_F(VersionTestSuite, CopyTest) {
auto* version = celix_version_create(1, 2, 3, "abc");
auto* copy = celix_version_copy(version);
EXPECT_NE(nullptr, version);
EXPECT_NE(nullptr, copy);
- EXPECT_EQ(1, celix_version_getMajor(copy));
- EXPECT_EQ(2, celix_version_getMinor(copy));
- EXPECT_EQ(3, celix_version_getMicro(copy));
- EXPECT_STREQ("abc", celix_version_getQualifier(copy));
+ expectVersion(version, 1, 2, 3, "abc");
celix_version_destroy(copy);
celix_version_destroy(version);
copy = celix_version_copy(nullptr); //returns "empty" version
EXPECT_NE(nullptr, copy);
- EXPECT_EQ(0, celix_version_getMajor(copy));
- EXPECT_EQ(0, celix_version_getMinor(copy));
- EXPECT_EQ(0, celix_version_getMicro(copy));
- EXPECT_STREQ("", celix_version_getQualifier(copy));
+ expectVersion(copy, 0, 0, 0, "");
celix_version_destroy(copy);
}
-TEST_F(VersionTestSuite, createFromString) {
- celix_version_t* version = nullptr;
- celix_status_t status = CELIX_SUCCESS;
- char * str;
-
- str = celix_utils_strdup("1");
- EXPECT_EQ(CELIX_SUCCESS, version_createVersionFromString(str, &version));
+TEST_F(VersionTestSuite, CreateFromStringTest) {
+ auto* version = celix_version_createVersionFromString("1");
EXPECT_TRUE(version != nullptr);
- EXPECT_EQ(1, version->major);
-
+ expectVersion(version, 1, 0, 0);
version_destroy(version);
- free(str);
- str = celix_utils_strdup("a");
- EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version));
-
- free(str);
- str = celix_utils_strdup("1.a");
- EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version));
-
- free(str);
- str = celix_utils_strdup("1.1.a");
- EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version));
-
- free(str);
- str = celix_utils_strdup("-1");
- EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString(str, &version));
+ EXPECT_EQ(nullptr, celix_version_createVersionFromString("a"));
+ EXPECT_EQ(nullptr, celix_version_createVersionFromString("1.a"));
+ EXPECT_EQ(nullptr, celix_version_createVersionFromString("1.1.a"));
+ EXPECT_EQ(nullptr, celix_version_createVersionFromString("-1"));
+ EXPECT_EQ(nullptr, celix_version_createVersionFromString("1.2.3.abc|xyz"));
- free(str);
- str = celix_utils_strdup("1.2");
- version = nullptr;
- EXPECT_EQ(CELIX_SUCCESS, version_createVersionFromString(str, &version));
+ version = celix_version_createVersionFromString("1.2");
EXPECT_TRUE(version != nullptr);
- EXPECT_EQ(1, version->major);
- EXPECT_EQ(2, version->minor);
-
+ expectVersion(version, 1, 2, 0);
version_destroy(version);
- free(str);
- str = celix_utils_strdup("1.2.3");
- version = nullptr;
- status = version_createVersionFromString(str, &version);
- EXPECT_EQ(CELIX_SUCCESS, status);
+ version = celix_version_createVersionFromString("1.2.3");
EXPECT_TRUE(version != nullptr);
- EXPECT_EQ(1, version->major);
- EXPECT_EQ(2, version->minor);
- EXPECT_EQ(3, version->micro);
-
+ expectVersion(version, 1, 2, 3);
version_destroy(version);
- free(str);
- str = celix_utils_strdup("1.2.3.abc");
- version = nullptr;
- status = version_createVersionFromString(str, &version);
- EXPECT_EQ(CELIX_SUCCESS, status);
- EXPECT_TRUE(version != nullptr);
- EXPECT_EQ(1, version->major);
- EXPECT_EQ(2, version->minor);
- EXPECT_EQ(3, version->micro);
- EXPECT_STREQ("abc", version->qualifier);
- version_destroy(version);
- free(str);
- str = celix_utils_strdup("1.2.3.abc_xyz");
- version = nullptr;
- status = version_createVersionFromString(str, &version);
- EXPECT_EQ(CELIX_SUCCESS, status);
+ version = celix_version_createVersionFromString("1.2.3.abc");
EXPECT_TRUE(version != nullptr);
- EXPECT_EQ(1, version->major);
- EXPECT_EQ(2, version->minor);
- EXPECT_EQ(3, version->micro);
- EXPECT_STREQ("abc_xyz", version->qualifier);
-
+ expectVersion(version, 1, 2, 3, "abc");
version_destroy(version);
- free(str);
- str = celix_utils_strdup("1.2.3.abc-xyz");
- version = nullptr;
- status = version_createVersionFromString(str, &version);
- EXPECT_EQ(CELIX_SUCCESS, status);
- EXPECT_TRUE(version != nullptr);
- EXPECT_EQ(1, version->major);
- EXPECT_EQ(2, version->minor);
- EXPECT_EQ(3, version->micro);
- EXPECT_STREQ("abc-xyz", version->qualifier);
+ version = celix_version_createVersionFromString("1.2.3.abc_xyz");
+ EXPECT_TRUE(version != nullptr);
+ expectVersion(version, 1, 2, 3, "abc_xyz");
version_destroy(version);
- free(str);
- str = celix_utils_strdup("1.2.3.abc|xyz");
- status = version_createVersionFromString(str, &version);
- EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, status);
- free(str);
-}
-
-TEST_F(VersionTestSuite, createEmptyVersion) {
- celix_version_t* version = nullptr;
- celix_status_t status = CELIX_SUCCESS;
- status = version_createEmptyVersion(&version);
- EXPECT_EQ(CELIX_SUCCESS, status);
+ version = celix_version_createVersionFromString("1.2.3.abc-xyz");
EXPECT_TRUE(version != nullptr);
- EXPECT_EQ(0, version->major);
- EXPECT_EQ(0, version->minor);
- EXPECT_EQ(0, version->micro);
- EXPECT_STREQ("", version->qualifier);
-
+ expectVersion(version, 1, 2, 3, "abc-xyz");
version_destroy(version);
-}
-TEST_F(VersionTestSuite, getters) {
- celix_version_t* version = nullptr;
- celix_status_t status = CELIX_SUCCESS;
- char * str;
- int major, minor, micro;
- const char *qualifier;
- str = celix_utils_strdup("abc");
- status = version_createVersion(1, 2, 3, str, &version);
- EXPECT_EQ(CELIX_SUCCESS, status);
+ //Testing deprecated api
+ version = nullptr;
+ EXPECT_EQ(CELIX_SUCCESS, version_createVersionFromString("1", &version));
EXPECT_TRUE(version != nullptr);
+ expectVersion(version, 1, 0, 0);
+ version_destroy(version);
- version_getMajor(version, &major);
- EXPECT_EQ(1, major);
-
- version_getMinor(version, &minor);
- EXPECT_EQ(2, minor);
-
- version_getMicro(version, µ);
- EXPECT_EQ(3, micro);
+ EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, version_createVersionFromString("a", &version));
+}
- version_getQualifier(version, &qualifier);
- EXPECT_STREQ("abc", qualifier);
+TEST_F(VersionTestSuite, CreateEmptyVersionTest) {
+ auto* version = celix_version_createEmptyVersion();
+ EXPECT_TRUE(version != nullptr);
+ expectVersion(version, 0, 0, 0, "");
+ version_destroy(version);
+ //Testing deprecated api
+ version = nullptr;
+ EXPECT_EQ(CELIX_SUCCESS, version_createEmptyVersion(&version));
+ EXPECT_TRUE(version != nullptr);
+ expectVersion(version, 0, 0, 0, "");
version_destroy(version);
- free(str);
}
-TEST_F(VersionTestSuite, compare) {
+TEST_F(VersionTestSuite, CompareTest) {
+ //TODO update to updated API
+
celix_version_t* version = nullptr;
celix_version_t* compare = nullptr;
celix_status_t status = CELIX_SUCCESS;
@@ -261,7 +187,7 @@ TEST_F(VersionTestSuite, compare) {
EXPECT_EQ(CELIX_SUCCESS, status);
EXPECT_TRUE(result < 0);
- // Compare againts a lower version
+ // Compare against a lower version
free(str);
str = celix_utils_strdup("abc");
version_destroy(compare);
@@ -278,7 +204,7 @@ TEST_F(VersionTestSuite, compare) {
free(str);
}
-TEST_F(VersionTestSuite, celix_version_compareToMajorMinor) {
+TEST_F(VersionTestSuite, CompareToMajorMinorTest) {
auto* version1 = celix_version_create(2, 2, 0, nullptr);
auto* version2 = celix_version_create(2, 2, 4, "qualifier");
@@ -300,7 +226,9 @@ TEST_F(VersionTestSuite, celix_version_compareToMajorMinor) {
celix_version_destroy(version2);
}
-TEST_F(VersionTestSuite, toString) {
+TEST_F(VersionTestSuite, ToStringTest) {
+ //TODO update to updated API
+
celix_version_t* version = nullptr;
celix_status_t status = CELIX_SUCCESS;
char * str;
@@ -333,7 +261,9 @@ TEST_F(VersionTestSuite, toString) {
free(str);
}
-TEST_F(VersionTestSuite,semanticCompatibility) {
+TEST_F(VersionTestSuite, SemanticCompatibilityTest) {
+ //TODO update to updated API
+
celix_version_t* provider = nullptr;
celix_version_t* compatible_user = nullptr;
celix_version_t* incompatible_user_by_major = nullptr;
@@ -367,7 +297,7 @@ TEST_F(VersionTestSuite,semanticCompatibility) {
version_destroy(incompatible_user_by_minor);
}
-TEST_F(VersionTestSuite, compareEmptyAndNullQualifier) {
+TEST_F(VersionTestSuite, CompareEmptyAndNullQualifierTest) {
//nullptr or "" qualifier should be the same
auto* v1 = celix_version_create(0, 0, 0, nullptr);
auto* v2 = celix_version_create(0, 0, 0, "");
@@ -379,7 +309,7 @@ TEST_F(VersionTestSuite, compareEmptyAndNullQualifier) {
celix_version_destroy(v2);
}
-TEST_F(VersionTestSuite, fillString) {
+TEST_F(VersionTestSuite, FillStringTest) {
// Create a version object
auto* version = celix_version_create(1, 2, 3, "alpha");
diff --git a/libs/framework/include/celix/Exception.h b/libs/utils/include/celix/IOException.h
similarity index 64%
copy from libs/framework/include/celix/Exception.h
copy to libs/utils/include/celix/IOException.h
index 1675567d..1715801e 100644
--- a/libs/framework/include/celix/Exception.h
+++ b/libs/utils/include/celix/IOException.h
@@ -19,20 +19,27 @@
#pragma once
#include <exception>
+#if __cplusplus >= 201703L //C++17 or higher
+#include <string_view>
+#endif
namespace celix {
/**
- * @brief Celix runtime Exception
+ * @brief Celix runtime IO Exception
*/
- class Exception : public std::exception {
+ class IOException : public std::exception {
public:
- explicit Exception(std::string msg) : w{std::move(msg)} {}
+#if __cplusplus >= 201703L //C++17 or higher
+ explicit IOException(std::string_view msg) : w{msg} {}
+#else
+ explicit IOException(std::string msg) : w{std::move(msg)} {}
+#endif
- Exception(const Exception&) = default;
- Exception(Exception&&) = default;
- Exception& operator=(const Exception&) = default;
- Exception& operator=(Exception&&) = default;
+ IOException(const IOException&) = default;
+ IOException(IOException&&) = default;
+ IOException& operator=(const IOException&) = default;
+ IOException& operator=(IOException&&) = default;
[[nodiscard]] const char* what() const noexcept override {
return w.c_str();
diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h
index 45bb0c8b..5be5a762 100644
--- a/libs/utils/include/celix/Properties.h
+++ b/libs/utils/include/celix/Properties.h
@@ -27,6 +27,7 @@
#include "celix_properties.h"
#include "celix_utils.h"
#include "celix/Version.h"
+#include "celix/IOException.h"
namespace celix {
@@ -75,12 +76,12 @@ namespace celix {
first = {};
second = {};
} else {
- first = iter.entry.key;
+ first = iter.key;
second = iter.entry.value;
}
}
- celix_properties_iterator_t iter{.index = -1, .entry = {}, ._data = {}};
+ celix_properties_iterator_t iter{.index = 0, .key = nullptr, .entry = {}, ._data = {}};
};
@@ -190,11 +191,19 @@ namespace celix {
#endif
/**
- * @brief Wraps C properties, but does not take ownership -> dtor will not destroy properties
+ * @brief Wrap C properties and returns it as const in a shared_ptr,
+ * but does not take ownership -> dtor will not destroy C properties.
*/
static std::shared_ptr<const Properties> wrap(const celix_properties_t* wrapProps) {
auto* cp = const_cast<celix_properties_t*>(wrapProps);
- return std::shared_ptr<const Properties>{new Properties{cp}};
+ return std::shared_ptr<const Properties>{new Properties{cp, false}};
+ }
+
+ /**
+ * @brief Wrap C properties and take ownership -> dtor will destroy C properties.
+ */
+ static Properties own(celix_properties_t* wrapProps) {
+ return Properties{wrapProps, true};
}
/**
@@ -598,15 +607,43 @@ namespace celix {
#endif
- //TODO test
- void store(const std::string& file, const std::string& header = {}) const {
- celix_properties_store(cProps.get(), file.c_str(), header.empty() ? nullptr : header.c_str());
+
+ /**
+ * @brief Store the property set to the given file path.
+ *
+ * This function writes the properties in the given set to the specified file path in a format suitable
+ * for loading with the load() function.
+ * If a non-empty header string is provided, it will be written as a comment at the beginning of the file.
+ *
+ * @param[in] file The file to store the properties to.
+ * @param[in] header An optional header string to include as a comment at the beginning of the file.
+ * @throws celix::IOException If an error occurs while writing to the file.
+ */
+#if __cplusplus >= 201703L //C++17 or higher
+ void store(std::string_view path, std::string_view header = {}) const {
+ storeTo(path.data(), header.empty() ? nullptr : header.data());
+ }
+#else
+ void store(const std::string& path, const std::string& header = {}) const {
+ storeTo(path.data(), header.empty() ? nullptr : header.data());
}
+#endif
- //TODO laod
+ /**
+ * @brief Loads properties from the file at the given path.
+ * @param[in] path The path to the file containing the properties.
+ * @return A new Properties object containing the properties from the file.
+ * @throws celix::IOException If the file cannot be opened or read.
+ */
+#if __cplusplus >= 201703L //C++17 or higher
+ static celix::Properties load(std::string_view path) { return loadFrom(path.data()); }
+#else
+ static celix::Properties load(const std::string& path) { return loadFrom(path.data()); }
+#endif
private:
- explicit Properties(celix_properties_t* props) : cProps{props, [](celix_properties_t*) { /*nop*/ }} {}
+ Properties(celix_properties_t* props, bool takeOwnership) :
+ cProps{props, [ownership = takeOwnership](celix_properties_t* p){ if (ownership) { celix_properties_destroy(p); }}} {}
static celix::Properties::ValueType getAndConvertType(
const std::shared_ptr<celix_properties_t>& cProperties,
@@ -628,6 +665,21 @@ namespace celix {
}
}
+ static celix::Properties loadFrom(const char* path) {
+ auto* cProps = celix_properties_load(path);
+ if (cProps) {
+ return celix::Properties::own(cProps);
+ }
+ throw celix::IOException{"Cannot load celix::Properties from path " + std::string{path}};
+ }
+
+ void storeTo(const char* path, const char* header) const {
+ auto status = celix_properties_store(cProps.get(), path, header);
+ if (status != CELIX_SUCCESS) {
+ throw celix::IOException{"Cannot store celix::Properties to " + std::string{path}};
+ }
+ }
+
std::shared_ptr<celix_properties_t> cProps;
};
diff --git a/libs/utils/include/celix/Version.h b/libs/utils/include/celix/Version.h
index ef50be0f..c8d2943d 100644
--- a/libs/utils/include/celix/Version.h
+++ b/libs/utils/include/celix/Version.h
@@ -26,10 +26,7 @@
namespace celix {
- //TODO CxxVersionTestSuite
//TODO doxygen
- //TODO test in unordered map and set
- //TODO test in map and set
class Version {
public:
Version() :
@@ -37,7 +34,7 @@ namespace celix {
qualifier{celix_version_getQualifier(cVersion.get())} {}
#if __cplusplus >= 201703L //C++17 or higher
- explicit Version(int major, int minor, int micro, std::string_view qualifier = {}) :
+ Version(int major, int minor, int micro, std::string_view qualifier = {}) :
cVersion{createVersion(celix_version_create(major, minor, micro, qualifier.empty() ? "" : qualifier.data()))},
qualifier{celix_version_getQualifier(cVersion.get())} {}
#else
@@ -110,7 +107,9 @@ namespace celix {
/**
* @brief Create a wrap around a C Celix version without taking ownership.
*/
- explicit Version(celix_version_t* v) : cVersion{v, [](celix_version_t *){/*nop*/}} {}
+ explicit Version(celix_version_t* v) :
+ cVersion{v, [](celix_version_t *){/*nop*/}},
+ qualifier{celix_version_getQualifier(cVersion.get())} {}
std::shared_ptr<celix_version_t> cVersion;
std::string qualifier; //cached qualifier of the const char* from celix_version_getQualifier
diff --git a/libs/utils/include/celix_long_hash_map.h b/libs/utils/include/celix_long_hash_map.h
index c0c36968..aacfef58 100644
--- a/libs/utils/include/celix_long_hash_map.h
+++ b/libs/utils/include/celix_long_hash_map.h
@@ -69,6 +69,9 @@ typedef struct celix_long_hash_map_create_options {
* only the simpleRemovedCallback will be used.
*
* Default is NULL.
+ *
+ * @param[in] removedValue The value that was removed from the hash map. This value is no longer used by the
+ * hash map and can be freed.
*/
void (*simpleRemovedCallback)(void* value) CELIX_OPTS_INIT;
@@ -89,6 +92,11 @@ typedef struct celix_long_hash_map_create_options {
* only the simpleRemovedCallback will be used.
*
* Default is NULL.
+ *
+ * @param[in] data The void pointer to the data that was provided when the callback was set as removedCallbackData.
+ * @param[in] removedKey The key of the value that was removed from the hash map.
+ * @param[in] removedValue The value that was removed from the hash map. This value is no longer used by the
+ * hash map and can be freed.
*/
void (*removedCallback)(void* data, long removedKey, celix_hash_map_value_t removedValue) CELIX_OPTS_INIT;
diff --git a/libs/utils/include/celix_properties.h b/libs/utils/include/celix_properties.h
index f20a2fe8..71bc0f53 100644
--- a/libs/utils/include/celix_properties.h
+++ b/libs/utils/include/celix_properties.h
@@ -70,7 +70,6 @@ typedef enum celix_properties_value_type {
* @brief A structure representing a single entry in a property set.
*/
typedef struct celix_properties_entry {
- const char* key; /**< The key of the entry*/
const char* value; /**< The string value or string representation of a non-string
typed value.*/
celix_properties_value_type_e valueType; /**< The type of the value of the entry */
@@ -94,10 +93,15 @@ typedef struct celix_properties_iterator {
/**
* @brief The index of the current iterator.
*/
- int index;
+ size_t index;
/**
- * @brief The current entry.
+ * @brief Te current key.
+ */
+ const char* key;
+
+ /**
+ * @brief The current value entry.
*/
celix_properties_entry_t entry;
@@ -152,6 +156,8 @@ celix_properties_t* celix_properties_loadFromString(const char *input);
/**
* @brief Store properties to a file.
*
+ * @note Properties values are always stored as string values, regardless of their actual underlining types.
+ *
* @param[in] properties The property set to store.
* @param[in] file The name of the file to store the properties to.
* @param[in] header An optional header to write to the file before the properties.
@@ -200,6 +206,8 @@ void celix_properties_set(celix_properties_t* properties, const char* key, const
/**
* @brief Set the value of a property without copying the key and value strings.
*
+ * @note If the setWithoutCopy replaced an exising value, the key will be freed by callee.
+ *
* @param[in] properties The property set to modify.
* @param[in] key The key of the property to set. This string will be used directly, so it must not be freed or modified
* after calling this function.
diff --git a/libs/utils/include/celix_string_hash_map.h b/libs/utils/include/celix_string_hash_map.h
index 323755cb..7dfe661f 100644
--- a/libs/utils/include/celix_string_hash_map.h
+++ b/libs/utils/include/celix_string_hash_map.h
@@ -68,11 +68,14 @@ typedef struct celix_string_hash_map_create_options {
* only the simpledRemoveCallback will be used.
*
* Default is NULL.
+ *
+ * @param[in] removedValue The value that was removed from the hash map. This value is no longer used by the
+ * hash map and can be freed.
*/
- void (*simpleRemovedCallback)(void* value) CELIX_OPTS_INIT;
+ void (*simpleRemovedCallback)(void* removedValue) CELIX_OPTS_INIT;
/**
- * Optional callback data, which will be provided to the removedCallback callback.
+ * Optional callback data, which will be provided to the removedCallback and removedKeyCallback callback.
*
* Default is NULL.
*/
@@ -88,14 +91,37 @@ typedef struct celix_string_hash_map_create_options {
* only the simpledRemoveCallback will be used.
*
* Default is NULL.
+ *
+ * @param[in] data The void pointer to the data that was provided when the callback was set as removedCallbackData.
+ * @param[in] removedKey The key of the value that was removed from the hash map.
+ * Note that the removedKey can still be in use if the a value entry for the same key is
+ * replaced, so this callback should not free the removedKey.
+ * @param[in] removedValue The value that was removed from the hash map. This value is no longer used by the
+ * hash map and can be freed.
*/
void (*removedCallback)(void* data, const char* removedKey, celix_hash_map_value_t removedValue) CELIX_OPTS_INIT;
+ /**
+ * @brief A removed key callback, which if provided will be called if a key is no longer used in the hash map.
+ *
+ * @param[in] data The void pointer to the data that was provided when the callback was set as removedCallbackData.
+ * @param[in] key The key that is no longer used in the hash map. if `storeKeysWeakly` was configured as true,
+ * the key can be freed.
+ */
+ void (*removedKeyCallback)(void* data, char* key) CELIX_OPTS_INIT;
+
/**
* @brief If set to true, the string hash map will not make of copy of the keys and assumes
* that the keys are in scope/memory for the complete lifecycle of the string hash map.
*
- * Note that this changes the default behaviour of the celix_stringHashMap_put* functions.
+ * When keys are stored weakly it is the caller responsibility to check the return value of
+ * celix_stringHashMap_put* function calls.
+ * If a celix_stringHashMap_put* function call returns true, the key is used in the hash map and the key
+ * should never be freed or freed in a configured removedKeyCallback.
+ * If a celix_stringHashMap_put* function call returns false, a value is replaced and the already existing
+ * key is reused. If the needed the caller should free the provided key.
+ *
+ * @note This changes the default behaviour of the celix_stringHashMap_put* functions.
*
* Default is false.
*/
@@ -137,6 +163,7 @@ typedef struct celix_string_hash_map_create_options {
.simpleRemovedCallback = NULL, \
.removedCallbackData = NULL, \
.removedCallback = NULL, \
+ .removedKeyCallback = NULL, \
.storeKeysWeakly = false, \
.initialCapacity = 0, \
.loadFactor = 0 \
diff --git a/libs/utils/src/celix_hash_map.c b/libs/utils/src/celix_hash_map.c
index 42e37ec6..995b0e6b 100644
--- a/libs/utils/src/celix_hash_map.c
+++ b/libs/utils/src/celix_hash_map.c
@@ -73,8 +73,9 @@ typedef struct celix_hash_map {
bool (*equalsKeyFunction)(const celix_hash_map_key_t* key1, const celix_hash_map_key_t* key2);
void (*simpleRemovedCallback)(void* value);
void* removedCallbackData;
- void (*removedStringKeyCallback)(void* data, const char* removedKey, celix_hash_map_value_t removedValue);
- void (*removedLongKeyCallback)(void* data, long removedKey, celix_hash_map_value_t removedValue);
+ void (*removedStringEntryCallback)(void* data, const char* removedKey, celix_hash_map_value_t removedValue);
+ void (*removedStringKeyCallback)(void* data, char* key);
+ void (*removedLongEntryCallback)(void* data, long removedKey, celix_hash_map_value_t removedValue);
bool storeKeysWeakly;
} celix_hash_map_t;
@@ -227,18 +228,24 @@ static void celix_hashMap_resize(celix_hash_map_t* map, size_t newCapacity) {
static void celix_hashMap_callRemovedCallback(celix_hash_map_t* map, celix_hash_map_entry_t* removedEntry) {
if (map->simpleRemovedCallback) {
map->simpleRemovedCallback(removedEntry->value.ptrValue);
- } else if (map->removedLongKeyCallback) {
- map->removedLongKeyCallback(map->removedCallbackData, removedEntry->key.longKey, removedEntry->value);
- } else if (map->removedStringKeyCallback) {
- map->removedStringKeyCallback(map->removedCallbackData, removedEntry->key.strKey, removedEntry->value);
+ } else if (map->removedLongEntryCallback) {
+ map->removedLongEntryCallback(map->removedCallbackData, removedEntry->key.longKey, removedEntry->value);
+ } else if (map->removedStringEntryCallback) {
+ map->removedStringEntryCallback(map->removedCallbackData, removedEntry->key.strKey, removedEntry->value);
+ }
+}
+
+static void celix_hashMap_destroyRemovedKey(celix_hash_map_t* map, char* removedKey) {
+ if (map->removedStringKeyCallback) {
+ map->removedStringKeyCallback(map->removedCallbackData, removedKey);
+ }
+ if (!map->storeKeysWeakly) {
+ free(removedKey);
}
}
static void celix_hashMap_destroyRemovedEntry(celix_hash_map_t* map, celix_hash_map_entry_t* removedEntry) {
celix_hashMap_callRemovedCallback(map, removedEntry);
- if (map->keyType == CELIX_HASH_MAP_STRING_KEY && !map->storeKeysWeakly) {
- free((char*)removedEntry->key.strKey);
- }
free(removedEntry);
}
@@ -344,7 +351,14 @@ static bool celix_hashMap_remove(celix_hash_map_t* map, const char* strKey, long
visit = visit->next;
}
if (removedEntry != NULL) {
+ char* removedKey = NULL;
+ if (map->keyType == CELIX_HASH_MAP_STRING_KEY) {
+ removedKey = (char*)removedEntry->key.strKey;
+ }
celix_hashMap_destroyRemovedEntry(map, removedEntry);
+ if (removedKey) {
+ celix_hashMap_destroyRemovedKey(map, removedKey);
+ }
return true;
}
return false;
@@ -367,7 +381,8 @@ static void celix_hashMap_init(
map->equalsKeyFunction = equalsKeyFn;
map->simpleRemovedCallback = NULL;
map->removedCallbackData = NULL;
- map->removedLongKeyCallback = NULL;
+ map->removedLongEntryCallback = NULL;
+ map->removedStringEntryCallback = NULL;
map->removedStringKeyCallback = NULL;
map->storeKeysWeakly = false;
}
@@ -378,7 +393,15 @@ static void celix_hashMap_clear(celix_hash_map_t* map) {
while (entry != NULL) {
celix_hash_map_entry_t* removedEntry = entry;
entry = entry->next;
+ \
+ char* removedKey = NULL;
+ if (map->keyType == CELIX_HASH_MAP_STRING_KEY) {
+ removedKey = (char*)removedEntry->key.strKey;
+ }
celix_hashMap_destroyRemovedEntry(map, removedEntry);
+ if (removedKey) {
+ celix_hashMap_destroyRemovedKey(map, removedKey);
+ }
}
map->buckets[i] = NULL;
}
@@ -427,7 +450,8 @@ celix_string_hash_map_t* celix_stringHashMap_createWithOptions(const celix_strin
celix_hashMap_init(&map->genericMap, CELIX_HASH_MAP_STRING_KEY, cap, fac, celix_stringHashMap_hash, celix_stringHashMap_equals);
map->genericMap.simpleRemovedCallback = opts->simpleRemovedCallback;
map->genericMap.removedCallbackData = opts->removedCallbackData;
- map->genericMap.removedStringKeyCallback = opts->removedCallback;
+ map->genericMap.removedStringEntryCallback = opts->removedCallback;
+ map->genericMap.removedStringKeyCallback = opts->removedKeyCallback;
map->genericMap.storeKeysWeakly = opts->storeKeysWeakly;
return map;
}
@@ -444,7 +468,7 @@ celix_long_hash_map_t* celix_longHashMap_createWithOptions(const celix_long_hash
celix_hashMap_init(&map->genericMap, CELIX_HASH_MAP_LONG_KEY, cap, fac, celix_longHashMap_hash, celix_longHashMap_equals);
map->genericMap.simpleRemovedCallback = opts->simpleRemovedCallback;
map->genericMap.removedCallbackData = opts->removedCallbackData;
- map->genericMap.removedLongKeyCallback = opts->removedCallback;
+ map->genericMap.removedLongEntryCallback = opts->removedCallback;
map->genericMap.storeKeysWeakly = false;
return map;
}
diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c
index 422a2bb7..dbbc2048 100644
--- a/libs/utils/src/properties.c
+++ b/libs/utils/src/properties.c
@@ -160,14 +160,12 @@ static char* celix_properties_createString(celix_properties_t* properties, const
static celix_status_t celix_properties_fillEntry(
celix_properties_t *properties,
celix_properties_entry_t* entry,
- const char *key,
const char *strValue,
const long* longValue,
const double* doubleValue,
const bool* boolValue,
celix_version_t* versionValue) {
char convertedValueBuffer[32];
- entry->key = celix_properties_createString(properties, key);
if (strValue != NULL) {
entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING;
entry->value = celix_properties_createString(properties, strValue);
@@ -209,7 +207,7 @@ static celix_status_t celix_properties_fillEntry(
entry->value = celix_version_toString(versionValue);
}
}
- if (entry->key == NULL || entry->value == NULL) {
+ if (entry->value == NULL) {
return CELIX_ENOMEM;
}
return CELIX_SUCCESS;
@@ -233,13 +231,11 @@ static celix_properties_entry_t* celix_properties_allocEntry(celix_properties_t*
* provided key and value strings.
*/
static celix_properties_entry_t* celix_properties_createEntryWithNoCopy(celix_properties_t *properties,
- char *key,
char *strValue) {
celix_properties_entry_t* entry = celix_properties_allocEntry(properties);
if (entry == NULL) {
return NULL;
}
- entry->key = key;
entry->value = strValue;
entry->valueType = CELIX_PROPERTIES_VALUE_TYPE_STRING;
entry->typed.strValue = strValue;
@@ -264,7 +260,7 @@ static celix_properties_entry_t* celix_properties_createEntry(
return NULL;
}
- celix_status_t status = celix_properties_fillEntry(properties, entry, key, strValue, longValue, doubleValue,
+ celix_status_t status = celix_properties_fillEntry(properties, entry, strValue, longValue, doubleValue,
boolValue, versionValue);
if (status != CELIX_SUCCESS) {
free(entry);
@@ -290,8 +286,13 @@ static void celix_properties_createAndSetEntry(
}
celix_properties_entry_t* entry = celix_properties_createEntry(properties, key, strValue, longValue, doubleValue,
boolValue, versionValue);
+ const char* mapKey = key;
+ if (!celix_stringHashMap_hasKey(properties->map, key)) {
+ //new entry, needs new allocated key;
+ mapKey = celix_properties_createString(properties, key);
+ }
if (entry != NULL) {
- celix_stringHashMap_put(properties->map, entry->key, entry);
+ celix_stringHashMap_put(properties->map, mapKey, entry);
}
}
@@ -308,10 +309,14 @@ static void celix_properties_freeString(celix_properties_t* properties, char* st
}
}
+static void celix_properties_removeKeyCallback(void* handle, char* key) {
+ celix_properties_t* properties = handle;
+ celix_properties_freeString(properties, key);
+}
+
static void celix_properties_removeEntryCallback(void* handle, const char* key __attribute__((unused)), celix_hash_map_value_t val) {
celix_properties_t* properties = handle;
celix_properties_entry_t* entry = val.ptrValue;
- celix_properties_freeString(properties, (char*)entry->key);
celix_properties_freeString(properties, (char*)entry->value);
if (entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION) {
celix_version_destroy(entry->typed.versionValue);
@@ -332,6 +337,7 @@ celix_properties_t* celix_properties_create(void) {
opts.initialCapacity = (unsigned int)ceil(CELIX_SHORT_PROPERTIES_OPTIMIZATION_ENTRIES_SIZE / 0.75);
opts.removedCallbackData = props;
opts.removedCallback = celix_properties_removeEntryCallback;
+ opts.removedKeyCallback = celix_properties_removeKeyCallback;
props->map = celix_stringHashMap_createWithOptions(&opts);
props->currentStringBufferIndex = 0;
props->currentEntriesBufferIndex = 0;
@@ -585,16 +591,16 @@ celix_properties_t* celix_properties_copy(const celix_properties_t *properties)
CELIX_PROPERTIES_ITERATE(properties, iter) {
if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) {
- celix_properties_set(copy, iter.entry.key, iter.entry.value);
+ celix_properties_set(copy, iter.key, iter.entry.value);
} else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_LONG) {
- celix_properties_setLong(copy, iter.entry.key, iter.entry.typed.longValue);
+ celix_properties_setLong(copy, iter.key, iter.entry.typed.longValue);
} else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_DOUBLE) {
- celix_properties_setDouble(copy, iter.entry.key, iter.entry.typed.doubleValue);
+ celix_properties_setDouble(copy, iter.key, iter.entry.typed.doubleValue);
} else if (iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_BOOL) {
- celix_properties_setBool(copy, iter.entry.key, iter.entry.typed.boolValue);
+ celix_properties_setBool(copy, iter.key, iter.entry.typed.boolValue);
} else /*version*/ {
assert(iter.entry.valueType == CELIX_PROPERTIES_VALUE_TYPE_VERSION);
- celix_properties_setVersion(copy, iter.entry.key, iter.entry.typed.versionValue);
+ celix_properties_setVersion(copy, iter.key, iter.entry.typed.versionValue);
}
}
return copy;
@@ -629,9 +635,12 @@ void celix_properties_set(celix_properties_t *properties, const char *key, const
void celix_properties_setWithoutCopy(celix_properties_t *properties, char *key, char *value) {
if (properties != NULL && key != NULL && value != NULL) {
- celix_properties_entry_t* entry = celix_properties_createEntryWithNoCopy(properties, key, value);
+ celix_properties_entry_t* entry = celix_properties_createEntryWithNoCopy(properties, value);
if (entry != NULL) {
- celix_stringHashMap_put(properties->map, entry->key, entry);
+ bool replaced = celix_stringHashMap_put(properties->map, key, entry);
+ if (replaced) {
+ free(key);
+ }
}
}
}
@@ -755,7 +764,8 @@ celix_properties_iterator_t celix_propertiesIterator_construct(const celix_prope
internalIter.props = properties;
celix_properties_iterator_t iter;
- iter.index = -1;
+ iter.index = 0;
+ iter.key = NULL;
memset(&iter.entry, 0, sizeof(iter.entry));
CELIX_BUILD_ASSERT(sizeof(celix_properties_iterator_internal_t) <= sizeof(iter._data));
@@ -767,7 +777,6 @@ celix_properties_iterator_t celix_propertiesIterator_construct(const celix_prope
bool celix_propertiesIterator_hasNext(celix_properties_iterator_t *iter) {
celix_properties_iterator_internal_t internalIter;
memcpy(&internalIter, iter->_data, sizeof(internalIter));
- //celix_stringHashMapIterator_next(&internalIter.mapIter);
return !celix_stringHashMapIterator_isEnd(&internalIter.mapIter);
}
@@ -780,8 +789,10 @@ const char* celix_propertiesIterator_nextKey(celix_properties_iterator_t *iter)
iter->index = (int)internalIter.mapIter.index;
celix_properties_entry_t* entry = internalIter.mapIter.value.ptrValue;
if (entry != NULL) {
+ iter->key = internalIter.mapIter.key;
memcpy(&iter->entry, entry, sizeof(iter->entry));
} else {
+ iter->key = NULL;
memset(&iter->entry, 0, sizeof(iter->entry));
}
celix_stringHashMapIterator_next(&internalIter.mapIter);
@@ -799,8 +810,10 @@ celix_properties_iterator_t celix_properties_begin(const celix_properties_t* pro
celix_properties_iterator_t iter;
iter.index = 0;
if (celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) {
+ iter.key = NULL;
memset(&iter.entry, 0, sizeof(iter.entry));
} else {
+ iter.key = internalIter.mapIter.key;
memcpy(&iter.entry, internalIter.mapIter.value.ptrValue, sizeof(iter.entry));
}
@@ -816,7 +829,7 @@ celix_properties_iterator_t celix_properties_end(const celix_properties_t* prope
celix_properties_iterator_t iter;
memset(&iter, 0, sizeof(iter));
- iter.index = (int)internalIter.mapIter.index; //TODO make inter.index size_t
+ iter.index = internalIter.mapIter.index;
memcpy(iter._data, &internalIter, sizeof(internalIter));
return iter;
}
@@ -828,8 +841,10 @@ void celix_propertiesIterator_next(celix_properties_iterator_t *iter) {
memcpy(iter->_data, &internalIter, sizeof(internalIter));
iter->index = internalIter.mapIter.index;
if (celix_stringHashMapIterator_isEnd(&internalIter.mapIter)) {
+ iter->key = NULL;
memset(&iter->entry, 0, sizeof(iter->entry));
} else {
+ iter->key = internalIter.mapIter.key;
memcpy(&iter->entry, internalIter.mapIter.value.ptrValue, sizeof(iter->entry));
}
}
diff --git a/libs/utils/src/version.c b/libs/utils/src/version.c
index 72a37942..b44b123f 100644
--- a/libs/utils/src/version.c
+++ b/libs/utils/src/version.c
@@ -107,7 +107,7 @@ celix_version_t* celix_version_create(int major, int minor, int micro, const cha
}
if (qualifier == NULL) {
- qualifier = "";
+ qualifier = CELIX_VERSION_EMPTY_QUALIFIER;
}
size_t qualifierLen = strlen(qualifier);
for (int i = 0; i < qualifierLen; i++) {