This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/585-celix-conditions
in repository https://gitbox.apache.org/repos/asf/celix.git


The following commit(s) were added to refs/heads/feature/585-celix-conditions 
by this push:
     new 908f932b Add error injection tests for fw bundle and factory
908f932b is described below

commit 908f932b8d8b3e232d783434e6e89cd6a5ff2e77
Author: Pepijn Noltes <[email protected]>
AuthorDate: Sat Jul 1 13:41:26 2023 +0200

    Add error injection tests for fw bundle and factory
---
 libs/framework/gtest/CMakeLists.txt                |  7 +-
 ...onsTestSuite.cc => FrameworkBundleTestSuite.cc} | 52 ++++++--------
 .../FrameworkBundleWithErrorInjectionTestSuite.cc  | 84 ++++++++++++++++++++++
 ...ryTestSuite.cc => FrameworkFactoryTestSuite.cc} | 18 ++---
 .../FrameworkFactoryWithErrorInjectionTestSuite.cc | 61 ++++++++++++++++
 libs/framework/include/celix/Framework.h           |  3 +-
 libs/framework/include/celix/FrameworkFactory.h    | 39 +++++-----
 libs/framework/include/celix_framework_factory.h   |  5 +-
 libs/framework/src/celix_framework_bundle.c        | 56 +++++++++------
 libs/framework/src/celix_framework_bundle.h        |  7 ++
 libs/framework/src/celix_framework_factory.c       | 22 +++---
 libs/framework/src/framework.c                     | 42 ++++++++---
 12 files changed, 297 insertions(+), 99 deletions(-)

diff --git a/libs/framework/gtest/CMakeLists.txt 
b/libs/framework/gtest/CMakeLists.txt
index 695053ab..ceac5a3f 100644
--- a/libs/framework/gtest/CMakeLists.txt
+++ b/libs/framework/gtest/CMakeLists.txt
@@ -53,13 +53,13 @@ set(CELIX_FRAMEWORK_TEST_SOURCES
     src/DependencyManagerTestSuite.cc
     src/CxxBundleContextTestSuite.cc
     src/HelloWorldCxxActivator.cc
-    src/CxxFrameworkFactoryTestSuite.cc
+    src/FrameworkFactoryTestSuite.cc
     src/CxxBundleActivatorTestSuite.cc
     src/BundleArchiveTestSuite.cc
     src/CelixLauncherTestSuite.cc
     src/CelixBundleCacheTestSuite.cc
     src/ScheduledEventTestSuite.cc
-    src/FrameworkConditionsTestSuite.cc
+    src/FrameworkBundleTestSuite.cc
 )
 
 add_executable(test_framework ${CELIX_FRAMEWORK_TEST_SOURCES})
@@ -134,6 +134,8 @@ if (LINKER_WRAP_SUPPORTED)
             src/CelixBundleContextBundlesWithErrorTestSuite.cc
             src/CelixBundleCacheErrorInjectionTestSuite.cc
             src/ScheduledEventWithErrorInjectionTestSuite.cc
+            src/FrameworkBundleWithErrorInjectionTestSuite.cc
+            src/FrameworkFactoryWithErrorInjectionTestSuite.cc
     )
     target_compile_definitions(test_framework_with_ei PRIVATE
             SIMPLE_TEST_BUNDLE1_LOCATION="${SIMPLE_TEST_BUNDLE1}"
@@ -152,6 +154,7 @@ if (LINKER_WRAP_SUPPORTED)
             Celix::unistd_ei
             Celix::hash_map_ei
             Celix::properties_ei
+            Celix::threads_ei
             GTest::gtest GTest::gtest_main
     )
 
diff --git a/libs/framework/gtest/src/FrameworkConditionsTestSuite.cc 
b/libs/framework/gtest/src/FrameworkBundleTestSuite.cc
similarity index 70%
rename from libs/framework/gtest/src/FrameworkConditionsTestSuite.cc
rename to libs/framework/gtest/src/FrameworkBundleTestSuite.cc
index cb955f99..1eb7c626 100644
--- a/libs/framework/gtest/src/FrameworkConditionsTestSuite.cc
+++ b/libs/framework/gtest/src/FrameworkBundleTestSuite.cc
@@ -23,106 +23,100 @@
 #include "celix/FrameworkFactory.h"
 #include "celix_condition.h"
 
-class FrameworkConditionsTestSuite : public ::testing::Test {
+class FrameworkBundleTestSuite : public ::testing::Test {
   public:
     const int USE_SERVICE_TIMEOUT_IN_MS = 500;
+    const std::string trueFilter = std::string{"("} + CELIX_CONDITION_ID + "=" 
+ CELIX_CONDITION_ID_TRUE + ")";
+    const std::string readyFilter =
+        std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_READY + ")";
+    const std::string errorFilter =
+        std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_ERROR + ")";
 
-    FrameworkConditionsTestSuite() = default;
+    FrameworkBundleTestSuite() = default;
 };
 
-TEST_F(FrameworkConditionsTestSuite, ConditionTrueAndFrameworkReadyTest) {
+TEST_F(FrameworkBundleTestSuite, ConditionTrueAndFrameworkReadyTest) {
     // Given a Celix framework (with conditions enabled (default))
     auto fw = celix::createFramework();
     auto ctx = fw->getFrameworkBundleContext();
 
     // Then the condition service with id "true" is available
-    auto filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_TRUE + ")";
-    auto count = 
ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME).setFilter(filter).build();
+    auto count = 
ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME).setFilter(trueFilter).build();
     EXPECT_EQ(1, count);
 
     // And the condition service with id "framework.error" will not become 
available
-    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_ERROR + ")";
     count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
-                .setFilter(filter)
+                .setFilter(errorFilter)
                 
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
                 .build();
     EXPECT_EQ(0, count);
 
     // But the condition service with id "framework.ready" will become 
available
-    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_READY + ")";
     count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
-                .setFilter(filter)
+                .setFilter(readyFilter)
                 
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
                 .build();
     EXPECT_EQ(1, count);
 }
 
-TEST_F(FrameworkConditionsTestSuite, ConditionTrueAndFrameworkErrorTest) {
+TEST_F(FrameworkBundleTestSuite, ConditionTrueAndFrameworkErrorTest) {
     // Given a Celix framework which is configured to start an invalid bundle
     auto fw = celix::createFramework({{celix::AUTO_START_0, 
"non-existing-bundle.zip"}});
     auto ctx = fw->getFrameworkBundleContext();
 
     // Then the condition service with id "true" is available
-    auto filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_TRUE + ")";
-    auto count = 
ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME).setFilter(filter).build();
+    auto count = 
ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME).setFilter(trueFilter).build();
     EXPECT_EQ(1, count);
 
     // And the condition service with id "framework.ready" does not become 
available (framework startup error)
-    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_READY + ")";
     count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
-                .setFilter(filter)
+                .setFilter(readyFilter)
                 
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
                 .build();
     EXPECT_EQ(0, count);
 
     // But the condition service with id "framework.error" will become 
available
-    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_ERROR + ")";
     count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
-                .setFilter(filter)
+                .setFilter(errorFilter)
                 
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
                 .build();
     EXPECT_EQ(1, count);
 }
 
-TEST_F(FrameworkConditionsTestSuite, FrameworkReadyRegisteredLastTest) {
+TEST_F(FrameworkBundleTestSuite, FrameworkReadyRegisteredLastTest) {
     // Given a Celix framework which is configured to start a bundle with a 
condition test service
     auto fw = celix::createFramework({{celix::AUTO_START_0, 
COND_TEST_BUNDLE_LOC}});
     auto ctx = fw->getFrameworkBundleContext();
 
     // Then the condition service with id "true" is available
-    auto filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_TRUE + ")";
-    auto count = 
ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME).setFilter(filter).build();
+    auto count = 
ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME).setFilter(trueFilter).build();
     EXPECT_EQ(1, count);
 
     // And the condition service with id "framework.error" will not become 
available
-    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_ERROR + ")";
     count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
-                .setFilter(filter)
+                .setFilter(errorFilter)
                 
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
                 .build();
     EXPECT_EQ(0, count);
 
     // But the condition service with id "framework.ready" will become 
available
-    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_READY + ")";
     count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
-                .setFilter(filter)
+                .setFilter(readyFilter)
                 
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
                 .build();
     EXPECT_EQ(1, count);
 
     // And the condition service with id "test" is available
-    filter = std::string{"("} + CELIX_CONDITION_ID + "=test)";
+    auto testFilter = std::string{"("} + CELIX_CONDITION_ID + "=test)";
     count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
-                .setFilter(filter)
+                .setFilter(testFilter)
                 
.setTimeout(std::chrono::milliseconds{USE_SERVICE_TIMEOUT_IN_MS})
                 .build();
     EXPECT_EQ(1, count);
 
     // And the service.id of the framework.ready condition is higher than the 
service.id of the test condition
     //(white-box test, framework.ready condition is registered last)
-    filter = std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_READY + ")";
-    long readySvcId = ctx->findServiceWithName(CELIX_CONDITION_SERVICE_NAME, 
filter);
-    filter = std::string{"("} + CELIX_CONDITION_ID + "=test)";
-    long testySvcId = ctx->findServiceWithName(CELIX_CONDITION_SERVICE_NAME, 
filter);
+    long readySvcId = ctx->findServiceWithName(CELIX_CONDITION_SERVICE_NAME, 
readyFilter);
+    long testySvcId = ctx->findServiceWithName(CELIX_CONDITION_SERVICE_NAME, 
testFilter);
     EXPECT_GT(readySvcId, testySvcId);
 }
diff --git 
a/libs/framework/gtest/src/FrameworkBundleWithErrorInjectionTestSuite.cc 
b/libs/framework/gtest/src/FrameworkBundleWithErrorInjectionTestSuite.cc
new file mode 100644
index 00000000..3f9f4938
--- /dev/null
+++ b/libs/framework/gtest/src/FrameworkBundleWithErrorInjectionTestSuite.cc
@@ -0,0 +1,84 @@
+/*
+ * 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/FrameworkFactory.h"
+#include "celix_condition.h"
+#include "celix_framework_bundle.h"
+
+#include "celix_properties_ei.h"
+#include "celix_threads_ei.h"
+#include "malloc_ei.h"
+
+class FrameworkBundleWithErrorInjectionTestSuite : public ::testing::Test {
+  public:
+    const int USE_SERVICE_TIMEOUT_IN_MS = 500;
+    const std::string readyFilter =
+        std::string{"("} + CELIX_CONDITION_ID + "=" + 
CELIX_CONDITION_ID_FRAMEWORK_READY + ")";
+
+    FrameworkBundleWithErrorInjectionTestSuite() = default;
+
+    ~FrameworkBundleWithErrorInjectionTestSuite() noexcept override {
+        // reset error injections
+        celix_ei_expect_calloc(nullptr, 0, nullptr);
+        celix_ei_expect_celixThreadMutex_create(nullptr, 0, CELIX_SUCCESS);
+        celix_ei_expect_celix_properties_create(nullptr, 0, nullptr);
+    }
+};
+
+TEST_F(FrameworkBundleWithErrorInjectionTestSuite, 
ErroCreatingFrameworkBundleTest) {
+    // Wen an error injection for calloc is primed when called from 
celix_frameworkBundle_create
+    celix_ei_expect_calloc((void*)celix_frameworkBundle_create, 0, nullptr);
+    ;
+
+    // Then an exception is expected when creating a framework instance
+    EXPECT_ANY_THROW(celix::createFramework());
+
+    // When an error injection for celixThreadMutex_create is primed when 
called from celix_frameworkBundle_create
+    
celix_ei_expect_celixThreadMutex_create((void*)celix_frameworkBundle_create, 0, 
ENOMEM);
+
+    // Then an exception is expected when creating a framework instance
+    EXPECT_ANY_THROW(celix::createFramework());
+}
+
+TEST_F(FrameworkBundleWithErrorInjectionTestSuite, 
ErrorStartingFrameworkBundleTest) {
+    // Wen an error injection for celix_properties_create is primed when 
called (indirectly) from
+    //  celix_frameworkBundle_start
+    
celix_ei_expect_celix_properties_create((void*)celix_frameworkBundle_start, 1, 
nullptr);
+
+    // Then an exception is expected when creating a framework instance
+    EXPECT_ANY_THROW(celix::createFramework());
+}
+
+TEST_F(FrameworkBundleWithErrorInjectionTestSuite, 
ErrorRegisteringFrameworkReadyConditionTest) {
+    // When an error injection for celix_properties_create is primed when 
called from celix_frameworkBundle_readyCheck
+    
celix_ei_expect_celix_properties_create((void*)celix_frameworkBundle_readyCheck,
 0, nullptr);
+
+    // And a framework instance is created
+    auto fw = celix::createFramework();
+    auto ctx = fw->getFrameworkBundleContext();
+
+    // Then the framework.ready condition will not become available, due to an 
error creating properties for the service
+    auto count = ctx->useService<celix_condition>(CELIX_CONDITION_SERVICE_NAME)
+                     .setFilter(readyFilter)
+                     .setTimeout(std::chrono::milliseconds 
{USE_SERVICE_TIMEOUT_IN_MS})
+                     .build();
+    EXPECT_EQ(count, 0);
+}
diff --git a/libs/framework/gtest/src/CxxFrameworkFactoryTestSuite.cc 
b/libs/framework/gtest/src/FrameworkFactoryTestSuite.cc
similarity index 76%
rename from libs/framework/gtest/src/CxxFrameworkFactoryTestSuite.cc
rename to libs/framework/gtest/src/FrameworkFactoryTestSuite.cc
index 04ce44c8..9f7c0204 100644
--- a/libs/framework/gtest/src/CxxFrameworkFactoryTestSuite.cc
+++ b/libs/framework/gtest/src/FrameworkFactoryTestSuite.cc
@@ -23,18 +23,17 @@
 
 #include "celix/FrameworkFactory.h"
 
-class CxxFrameworkFactoryTestSuite : public ::testing::Test {
+class FrameworkFactoryTestSuite : public ::testing::Test {
 public:
-    std::shared_ptr<celix::Framework> framework{};
-    std::vector<std::shared_ptr<celix::ServiceRegistration>> registrations{};
+    FrameworkFactoryTestSuite() = default;
 };
 
-TEST_F(CxxFrameworkFactoryTestSuite, CreateDestroy) {
-    auto fw = celix::createFramework();
-    EXPECT_TRUE(fw->getFrameworkBundleContext()->getBundle().isSystemBundle());
+TEST_F(FrameworkFactoryTestSuite, CreateDestroy) {
+    auto framework = celix::createFramework();
+    
EXPECT_TRUE(framework->getFrameworkBundleContext()->getBundle().isSystemBundle());
 }
 
-TEST_F(CxxFrameworkFactoryTestSuite, WaitForStop) {
+TEST_F(FrameworkFactoryTestSuite, WaitForStop) {
     auto fw = celix::createFramework();
     std::future<void> sync;
     std::thread wait{[fw] {
@@ -46,9 +45,10 @@ TEST_F(CxxFrameworkFactoryTestSuite, WaitForStop) {
 
 struct cService{};
 
-TEST_F(CxxFrameworkFactoryTestSuite, DestroyFrameworkAndUnregisterServices) {
-    framework = celix::createFramework();
+TEST_F(FrameworkFactoryTestSuite, DestroyFrameworkAndUnregisterServices) {
+    auto framework = celix::createFramework();
     auto ctx = framework->getFrameworkBundleContext();
+    std::vector<std::shared_ptr<celix::ServiceRegistration>> registrations{};
     for (int i = 0; i < 10; ++i) {
         auto reg = ctx->registerService<cService>(std::make_shared<cService>())
                 .build();
diff --git 
a/libs/framework/gtest/src/FrameworkFactoryWithErrorInjectionTestSuite.cc 
b/libs/framework/gtest/src/FrameworkFactoryWithErrorInjectionTestSuite.cc
new file mode 100644
index 00000000..d8b4241c
--- /dev/null
+++ b/libs/framework/gtest/src/FrameworkFactoryWithErrorInjectionTestSuite.cc
@@ -0,0 +1,61 @@
+/*
+ * 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/FrameworkFactory.h"
+#include "celix_framework_factory.h"
+
+#include "celix_properties_ei.h"
+#include "malloc_ei.h"
+
+class FrameworkFactoryWithErrorInjectionTestSuite : public ::testing::Test {
+public:
+    FrameworkFactoryWithErrorInjectionTestSuite() = default;
+
+    ~FrameworkFactoryWithErrorInjectionTestSuite() noexcept override {
+        //reset the error injection
+        celix_ei_expect_malloc(nullptr, 0, nullptr);
+        celix_ei_expect_celix_properties_create(nullptr, 0, nullptr);
+        celix_ei_expect_celix_properties_copy(nullptr, 0, nullptr);
+    }
+};
+
+TEST_F(FrameworkFactoryWithErrorInjectionTestSuite, 
ErrorCreatingFrameworkTest) {
+    //When an error injection for celix_properties_copy is primed when called 
from anywhere
+    celix_ei_expect_celix_properties_copy(CELIX_EI_UNKNOWN_CALLER, 0, nullptr);
+
+    //Then an exception is expected when creating a framework instance
+    EXPECT_ANY_THROW(celix::createFramework());
+
+    //When an error injection for celix_properties_create is primed when 
called from
+    //celix_frameworkFactory_createFramework
+    
celix_ei_expect_celix_properties_create((void*)celix_frameworkFactory_createFramework,
 0, nullptr);
+
+    //Then a nullptr is returned when calling 
celix_frameworkFactory_createFramework with a nullptr properties
+    //(note celix::createFramework will always call 
celix_frameworkFactory_createFramework with a non-null properties)
+    EXPECT_EQ(nullptr, celix_frameworkFactory_createFramework(nullptr));
+
+    //When an error injection for calloc is primed when called (indirectly) 
from celix_frameworkFactory_createFramework
+    celix_ei_expect_calloc((void*)celix_frameworkFactory_createFramework, 1, 
nullptr);
+
+    //Then an exception is expected when creating a framework instance
+    EXPECT_ANY_THROW(celix::createFramework());
+}
+
diff --git a/libs/framework/include/celix/Framework.h 
b/libs/framework/include/celix/Framework.h
index e0e62eeb..ae86923b 100644
--- a/libs/framework/include/celix/Framework.h
+++ b/libs/framework/include/celix/Framework.h
@@ -20,6 +20,7 @@
 #pragma once
 
 #include <memory>
+#include <functional>
 
 #include "celix_framework.h"
 
@@ -105,7 +106,7 @@ namespace celix {
          * @warning Try not the depend on the C API from a C++ bundle. If 
features are missing these should be added to
          * the C++ API.
          */
-        celix_framework_t * getCFramework() const {
+        celix_framework_t* getCFramework() const {
             return cFw.get();
         }
     private:
diff --git a/libs/framework/include/celix/FrameworkFactory.h 
b/libs/framework/include/celix/FrameworkFactory.h
index 4f1f3a2e..4872a8b0 100644
--- a/libs/framework/include/celix/FrameworkFactory.h
+++ b/libs/framework/include/celix/FrameworkFactory.h
@@ -22,24 +22,31 @@
 #include <memory>
 
 #include "celix/Properties.h"
-#include "celix/BundleContext.h"
 #include "celix/Framework.h"
+#include "celix/BundleContext.h"
 #include "celix_framework_factory.h"
 
 namespace celix {
-    /**
-     * @brief Create a new celix Framework instance.
-     */
-    inline std::shared_ptr<celix::Framework> createFramework(const 
celix::Properties& properties = {}) {
-        auto* copy = celix_properties_copy(properties.getCProperties());
-        auto* cFw= celix_frameworkFactory_createFramework(copy);
-        auto fwCtx = 
std::make_shared<celix::BundleContext>(celix_framework_getFrameworkContext(cFw));
-        std::shared_ptr<celix::Framework> framework{new 
celix::Framework{std::move(fwCtx), cFw}, [](celix::Framework* fw) {
-            auto* cFw = fw->getCFramework();
-            delete fw;
-            celix_framework_waitForEmptyEventQueue(cFw);
-            celix_frameworkFactory_destroyFramework(cFw);
-        }};
-        return framework;
+/**
+ * @brief Create a new celix Framework instance.
+ * @throws celix::Exception if the framework could not be created.
+ */
+inline std::shared_ptr<celix::Framework> createFramework(const 
celix::Properties& properties = {}) {
+    auto* copy = celix_properties_copy(properties.getCProperties());
+    if (!copy) {
+        throw celix::Exception{"Could not copy properties"};
     }
-}
\ No newline at end of file
+
+    auto* cFw = celix_frameworkFactory_createFramework(copy);
+    if (!cFw) {
+        throw celix::Exception{"Could not create framework"};
+    }
+
+    auto fwCtx = 
std::make_shared<celix::BundleContext>(celix_framework_getFrameworkContext(cFw));
+    return std::shared_ptr<celix::Framework>{new 
celix::Framework{std::move(fwCtx), cFw}, [](celix::Framework* fw) {
+                                                 auto* cFw = 
fw->getCFramework();
+                                                 delete fw;
+                                                 
celix_frameworkFactory_destroyFramework(cFw);
+                                             }};
+}
+} // namespace celix
\ No newline at end of file
diff --git a/libs/framework/include/celix_framework_factory.h 
b/libs/framework/include/celix_framework_factory.h
index 73b97ae0..bbb69c3d 100644
--- a/libs/framework/include/celix_framework_factory.h
+++ b/libs/framework/include/celix_framework_factory.h
@@ -29,8 +29,9 @@ extern "C" {
 
 /**
  * Creates a new framework. The framework will be in the started state.
- * @param config The framework configuration. Can be NULL.
- * @return a started framework or NULL
+ * @param[in] config The framework configuration. Can be NULL. This call will 
take ownership of the config and
+ *                   also destroy it when the call fails.
+ * @return a started framework or NULL. If NULL is returned the framework is 
not started.
  */
 CELIX_FRAMEWORK_EXPORT celix_framework_t* 
celix_frameworkFactory_createFramework(celix_properties_t *config);
 
diff --git a/libs/framework/src/celix_framework_bundle.c 
b/libs/framework/src/celix_framework_bundle.c
index 00e2aecb..65c91b8e 100644
--- a/libs/framework/src/celix_framework_bundle.c
+++ b/libs/framework/src/celix_framework_bundle.c
@@ -62,20 +62,28 @@ static celix_status_t 
celix_frameworkBundle_frameworkEvent(void* handle, framewo
 celix_status_t celix_frameworkBundle_create(celix_bundle_context_t* ctx, 
void** userData) {
     *userData = NULL;
     celix_framework_bundle_activator_t* act = calloc(1, sizeof(*act));
-    if (act) {
-        act->ctx = ctx;
-        act->trueConditionSvcId = -1L;
-        act->listener.handle = act;
-        act->listener.frameworkEvent = celix_frameworkBundle_frameworkEvent;
-        act->frameworkStartedEventReceived = false;
-        act->frameworkErrorEventReceived = false;
-        act->frameworkReadyOrErrorConditionSvcId = -1L;
-        act->checkFrameworkScheduledEventId = -1L;
-        act->conditionInstance.handle = act;
-        celixThreadMutex_create(&act->mutex, NULL);
-        *userData = act;
+    if (!act) {
+        return ENOMEM;
+    }
+
+    celix_status_t status = celixThreadMutex_create(&act->mutex, NULL);
+    if (status != CELIX_SUCCESS) {
+        free(act);
+        return status;
     }
-    return act != NULL ? CELIX_SUCCESS : CELIX_ENOMEM;
+
+    act->ctx = ctx;
+    act->trueConditionSvcId = -1L;
+    act->listener.handle = act;
+    act->listener.frameworkEvent = celix_frameworkBundle_frameworkEvent;
+    act->frameworkStartedEventReceived = false;
+    act->frameworkErrorEventReceived = false;
+    act->frameworkReadyOrErrorConditionSvcId = -1L;
+    act->checkFrameworkScheduledEventId = -1L;
+    act->conditionInstance.handle = act;
+    *userData = act;
+
+    return CELIX_SUCCESS;
 }
 
 static void 
celix_frameworkBundle_registerTrueCondition(celix_framework_bundle_activator_t* 
act) {
@@ -92,7 +100,7 @@ static void 
celix_frameworkBundle_registerTrueCondition(celix_framework_bundle_a
     }
 }
 
-static void celix_frameworkBundle_readyCheck(void* data) {
+void celix_frameworkBundle_readyCheck(void* data) {
     celix_framework_bundle_activator_t* act = data;
     celixThreadMutex_lock(&act->mutex);
     
@@ -141,21 +149,25 @@ celix_status_t celix_frameworkBundle_start(void* 
userData, celix_bundle_context_
 
     bool conditionsEnabled = celix_bundleContext_getPropertyAsBool(
         ctx, CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED, 
CELIX_FRAMEWORK_CONDITION_SERVICES_ENABLED_DEFAULT);
-    if (conditionsEnabled) {
-        celix_frameworkBundle_registerTrueCondition(act);
-        fw_addFrameworkListener(
-            celix_bundleContext_getFramework(ctx), 
celix_bundleContext_getBundle(ctx), &act->listener);
-        celix_frameworkBundle_startReadyCheck(act);
-        return act->trueConditionSvcId >= 0 ? CELIX_SUCCESS : 
CELIX_BUNDLE_EXCEPTION;
+    if (!conditionsEnabled) {
+        return CELIX_SUCCESS;
     }
 
+    celix_frameworkBundle_registerTrueCondition(act);
+    if (act->trueConditionSvcId < 0) {
+        return CELIX_BUNDLE_EXCEPTION;
+    }
+
+    fw_addFrameworkListener(
+        celix_bundleContext_getFramework(ctx), 
celix_bundleContext_getBundle(ctx), &act->listener);
+    celix_frameworkBundle_startReadyCheck(act);
+
     return CELIX_SUCCESS;
 }
 
 celix_status_t celix_frameworkBundle_stop(void* userData, 
celix_bundle_context_t* ctx) {
     celix_framework_bundle_activator_t* act = userData;
     celix_framework_t* framework = celix_bundleContext_getFramework(ctx);
-    celix_status_t status = CELIX_SUCCESS;
 
     // remove framework listener
     fw_removeFrameworkListener(framework, celix_bundleContext_getBundle(ctx), 
&act->listener);
@@ -179,7 +191,7 @@ celix_status_t celix_frameworkBundle_stop(void* userData, 
celix_bundle_context_t
 
     // framework shutdown
     celix_framework_shutdownAsync(framework);
-    return status;
+    return CELIX_SUCCESS;
 }
 
 celix_status_t celix_frameworkBundle_destroy(void* userData, 
celix_bundle_context_t* ctx __attribute__((unused))) {
diff --git a/libs/framework/src/celix_framework_bundle.h 
b/libs/framework/src/celix_framework_bundle.h
index 29776bc5..5a43cd03 100644
--- a/libs/framework/src/celix_framework_bundle.h
+++ b/libs/framework/src/celix_framework_bundle.h
@@ -47,6 +47,13 @@ celix_status_t celix_frameworkBundle_stop(void* userData, 
celix_bundle_context_t
  */
 celix_status_t celix_frameworkBundle_destroy(void* userData, 
celix_bundle_context_t* ctx);
 
+/**
+ * @brief The scheduled event callback for the framework bundle bundle ready 
check.
+ * @note Part of the header for testing purposes.
+ * @param[in] data The framework bundle bundle activator.
+ */
+void celix_frameworkBundle_readyCheck(void* data);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/framework/src/celix_framework_factory.c 
b/libs/framework/src/celix_framework_factory.c
index f97b8c54..c3b8f95a 100644
--- a/libs/framework/src/celix_framework_factory.c
+++ b/libs/framework/src/celix_framework_factory.c
@@ -27,16 +27,22 @@ framework_t* 
celix_frameworkFactory_createFramework(celix_properties_t *config)
         config = celix_properties_create();
     }
 
-    if (config != NULL) {
-        framework_create(&fw, config);
+    if (config == NULL) {
+        return NULL;
     }
-    if (fw != NULL) {
-        celix_status_t rc = framework_start(fw);
-        if (rc != CELIX_SUCCESS) {
-            framework_destroy(fw);
-            fw = NULL;
-        }
+
+    celix_status_t status = framework_create(&fw, config);
+    if (status != CELIX_SUCCESS) {
+        celix_properties_destroy(config);
+        return NULL;
     }
+
+    status = framework_start(fw);
+    if (status != CELIX_SUCCESS) {
+        framework_destroy(fw);
+        return NULL;
+    }
+
     return fw;
 }
 
diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c
index d84b2a22..88633507 100644
--- a/libs/framework/src/framework.c
+++ b/libs/framework/src/framework.c
@@ -183,6 +183,7 @@ static celix_status_t 
framework_autoInstallConfiguredBundles(celix_framework_t *
 static celix_status_t 
framework_autoInstallConfiguredBundlesForList(celix_framework_t *fw, const char 
*autoStart, celix_array_list_t *installedBundles);
 static celix_status_t 
framework_autoStartConfiguredBundlesForList(celix_framework_t* fw, const 
celix_array_list_t *installedBundles);
 static void celix_framework_addToEventQueue(celix_framework_t *fw, const 
celix_framework_event_t* event);
+static void celix_framework_stopAndJoinEventQueue(celix_framework_t* fw);
 
 struct fw_bundleListener {
        bundle_pt bundle;
@@ -237,6 +238,9 @@ typedef struct fw_frameworkListener * 
fw_framework_listener_pt;
 
 celix_status_t framework_create(framework_pt *out, celix_properties_t* config) 
{
     celix_framework_t* framework = calloc(1, sizeof(*framework));
+    if (!framework) {
+        return ENOMEM;
+    }
 
     celixThreadCondition_init(&framework->shutdown.cond, NULL);
     celixThreadMutex_create(&framework->shutdown.mutex, NULL);
@@ -442,9 +446,14 @@ celix_status_t fw_init(framework_pt framework) {
             status = CELIX_DO_IF(status, 
bundle_setActivator(framework->bundle, activator));
             status = CELIX_DO_IF(status, bundle_getContext(framework->bundle, 
&validateContext));
             status = CELIX_DO_IF(status, activator->create(validateContext, 
&activator->userData));
+            bool fwBundleCreated = status == CELIX_SUCCESS;
             status = CELIX_DO_IF(status, activator->start(activator->userData, 
validateContext));
 
+
             if (status != CELIX_SUCCESS) {
+                if (fwBundleCreated) {
+                    activator->destroy(activator->userData, validateContext);
+                }
                free(activator);
             }
         } else {
@@ -453,7 +462,8 @@ celix_status_t fw_init(framework_pt framework) {
     }
 
     if (status != CELIX_SUCCESS) {
-       fw_logCode(framework->logger, CELIX_LOG_LEVEL_ERROR, status, "Could not 
init framework");
+        fw_logCode(framework->logger, CELIX_LOG_LEVEL_ERROR, status, "Could 
not init framework");
+        celix_framework_stopAndJoinEventQueue(framework);
     }
 
        return status;
@@ -475,6 +485,11 @@ celix_status_t framework_start(celix_framework_t* 
framework) {
         bundle_setState(framework->bundle, CELIX_BUNDLE_STATE_ACTIVE);
     }
 
+    if (status != CELIX_SUCCESS) {
+        fw_log(framework->logger, CELIX_LOG_LEVEL_ERROR, "Could not initialize 
framework");
+        return status;
+    }
+
     celix_framework_bundle_entry_t* entry =
         
celix_framework_bundleEntry_getBundleEntryAndIncreaseUseCount(framework, 
framework->bundleId);
     CELIX_DO_IF(status, fw_fireBundleEvent(framework, 
OSGI_FRAMEWORK_BUNDLE_EVENT_STARTED, entry));
@@ -482,8 +497,9 @@ celix_status_t framework_start(celix_framework_t* 
framework) {
 
     if (status != CELIX_SUCCESS) {
         status = CELIX_BUNDLE_EXCEPTION;
-        fw_logCode(framework->logger, CELIX_LOG_LEVEL_ERROR, status, "Could 
not start framework");
+        fw_logCode(framework->logger, CELIX_LOG_LEVEL_ERROR, status, "Could 
not start framework bundle");
         fw_fireFrameworkEvent(framework, OSGI_FRAMEWORK_EVENT_ERROR, status);
+        return status;
     }
 
     celix_status_t startStatus = 
framework_autoStartConfiguredBundles(framework);
@@ -1181,6 +1197,19 @@ celix_status_t framework_waitForStop(framework_pt 
framework) {
     return CELIX_SUCCESS;
 }
 
+static void celix_framework_stopAndJoinEventQueue(celix_framework_t* fw) {
+    fw_log(fw->logger,
+           CELIX_LOG_LEVEL_TRACE,
+           "Stop and joining event loop thread for framework %s",
+           celix_framework_getUUID(fw));
+    celixThreadMutex_lock(&fw->dispatcher.mutex);
+    fw->dispatcher.active = false;
+    celixThreadCondition_broadcast(&fw->dispatcher.cond);
+    celixThreadMutex_unlock(&fw->dispatcher.mutex);
+    celixThread_join(fw->dispatcher.thread, NULL);
+    fw_log(fw->logger, CELIX_LOG_LEVEL_DEBUG, "Joined event loop thread for 
framework %s", celix_framework_getUUID(fw));
+}
+
 static void* framework_shutdown(void *framework) {
     framework_pt fw = (framework_pt) framework;
 
@@ -1236,14 +1265,7 @@ static void* framework_shutdown(void *framework) {
         celix_framework_bundleEntry_decreaseUseCount(fwEntry);
     }
 
-    //join dispatcher thread
-    celixThreadMutex_lock(&fw->dispatcher.mutex);
-    fw->dispatcher.active = false;
-    celixThreadCondition_broadcast(&fw->dispatcher.cond);
-    celixThreadMutex_unlock(&fw->dispatcher.mutex);
-    celixThread_join(fw->dispatcher.thread, NULL);
-    fw_log(fw->logger, CELIX_LOG_LEVEL_TRACE, "Joined event loop thread for 
framework %s", celix_framework_getUUID(framework));
-
+    celix_framework_stopAndJoinEventQueue(fw);
 
     celixThreadMutex_lock(&fw->shutdown.mutex);
     fw->shutdown.done = true;

Reply via email to