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 2022/05/29 17:16:48 UTC
[celix] branch feature/update_component_and_pattern_documentation updated: Updates dm component document and examples.
This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a commit to branch feature/update_component_and_pattern_documentation
in repository https://gitbox.apache.org/repos/asf/celix.git
The following commit(s) were added to refs/heads/feature/update_component_and_pattern_documentation by this push:
new b74930c8 Updates dm component document and examples.
b74930c8 is described below
commit b74930c86d44cd9b5d1138fe83ddb3586d253192
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Sun May 29 19:16:35 2022 +0200
Updates dm component document and examples.
Also:
- Adds small chapter for `celix::lb` and `celix::query` shell commands
- Update find_package(CppUTest) to optional so that Celix can be build
with tests, but without CppUTest.
---
documents/bundles.md | 12 +-
documents/components.md | 373 ++++++++++++---------
documents/services.md | 22 +-
.../component_with_service_dependency_activator.c | 30 +-
.../src/ComponentWithProvidedServiceActivator.cc | 7 +-
libs/utils/CMakeLists.txt | 112 ++++---
6 files changed, 321 insertions(+), 235 deletions(-)
diff --git a/documents/bundles.md b/documents/bundles.md
index bd18f7b1..908442eb 100644
--- a/documents/bundles.md
+++ b/documents/bundles.md
@@ -286,4 +286,14 @@ add_celix_container(test_container BUNDLES
)
```
-See [Apache Celix CMake Commands](cmake_commands) for more detailed information.
\ No newline at end of file
+See [Apache Celix CMake Commands](cmake_commands) for more detailed information.
+
+# The `celix::lb` shell command
+To interactively see the installed bundles the `celix::lb` shell command (list bundles) can be used.
+
+Examples of supported `lb` command lines are:
+ - `celix::lb` - Show an overview of the installed bundles with their bundle id, bundle state, bundle name and
+ bundle group.
+ - `lb` - Same as `celix::lb` (as long as there is no colliding other `lb` commands).
+ - `lb -s` - Same as `celix::lb` but instead of showing the bundle name the bundle symbolic name is printed.
+ - `lb -u` - Same as `celix::lb` but instead of showing the bundle name the bundle update location is printed.
diff --git a/documents/components.md b/documents/components.md
index 86e2986a..2f20555b 100644
--- a/documents/components.md
+++ b/documents/components.md
@@ -25,49 +25,51 @@ Components can provide services and depend on services. Components are configure
Service dependencies will influence the component's lifecycle as a component will only be active when all required
dependencies are available.
-The DM is responsible for managing the component's service dependencies, the component's lifecycle and when
+The DM is responsible for managing the co7mponent's service dependencies, the component's lifecycle and when
to register/unregister the component's provided services.
Note that the Apache Celix Dependency Manager is inspired by the [Apache Felix Dependency Manager](http://felix.apache.org/documentation/subprojects/apache-felix-dependency-manager.html), adapted to Apache Celix and the C/C++ usage.
# Component Lifecycle
-Each DM Component has its own lifecycle.
+Each component has its own lifecycle.
A component's lifecycle state model is depicted in the state diagram below.
![Component Life Cycle](diagrams/component_lifecycle.png)
-The DM can be used to configure a component's lifecycle callbacks, the following component's lifecycle callbacks can
+The DM can be used to configure a component's lifecycle callbacks, the following component's lifecycle callbacks can
be configured:
- - `init`
- - `start`
- - `stop`
- - `deinit`
-
+- `init`
+- `start`
+- `stop`
+- `deinit`
+
These callbacks are used in the intermediate component's lifecycle states `Initializing`, `Starting`, `Suspending`, `Resuming`, `Stopping` and `Deinitializing` and the lifecycle callbacks are always called from the Celix event thread.
-A DM Component has the following lifecycle states:
-- `Inactive`: _The component is inactive and the DM is not managing the component yet._
-- `Waiting For Required`: _The component is waiting for required service dependencies._
-- `Initializing`: _The component has found its required dependencies and is initializing:
- Calling the `init` callback._
-- `Initialized And Waiting For Required`: _The component has been initialized, but is waiting for required
- dependencies._
- _Note: that this can mean: that during `init` callback new service dependencies was added or that the component
- was active, but a required service dependency is removed and as result the component is not active anymore._
-- `Starting`: _The component has found its required dependencies and is starting: Calling the `start` callback and
- registering it's provided services.
-- `Tracking Optional`: _The component has found its required dependencies and is started. It is still tracking for
- additional optional and required services._
-- `Suspending`: _The component has found its required dependencies, but is suspending to prepare for a service change:
- Unregistering its provided service and calling the `stop` callback._
-- `Suspended`: _The component has found its required dependencies and is suspended so that a service change can be
- processed._
-- `Resuming`: _The component has found its required dependencies, a service change has been processed, and it is
- resuming: Calling the `start` callback and registering its provided services.
-- `Stopping`: _The component has lost one or more of its required dependencies and is stopping: Unregistering its
- provided service and calling the `stop` callback._
-- `Deinitializing`: _The component is being removed and is deinitializing: Calling the `deinit` callback._
+A component has the following lifecycle states:
+- `Inactive`: The component is inactive and the DM is not managing the component yet.
+- `Waiting For Required`: The component is waiting for required service dependencies.
+- `Initializing`: The component has found its required dependencies and is initializing by
+ calling the `init` callback.
+- `Initialized And Waiting For Required`: The component has been initialized, but is waiting for required
+ dependencies.
+ _Note: that this can mean that:
+ - During the `init` callback, 1 or more unavailable required service dependencies where added.
+ - The component was active, but 1 or more required service dependency where removed and as result the
+ component is not active anymore.
+- `Starting`: The component has found its required dependencies and is starting by calling the `start` callback and
+ registering the components provided services.
+- `Tracking Optional`: The component has found its required dependencies and is started. It is still tracking for
+ additional optional and required services.
+- `Suspending`: The component has found its required dependencies, but is suspending to prepare for a service change by
+ unregistering the components provided service and calling the `stop` callback.
+- `Suspended`: The component has found its required dependencies and is suspended so that a service change can be
+ processed.
+- `Resuming`: The component has found its required dependencies, a service change has been processed, and it is
+ resuming by calling the `start` callback and registering the components provided services.
+- `Stopping`: The component has lost one or more of its required dependencies and is stopping by unregistering the
+ components provided service and calling the `stop` callback.
+- `Deinitializing`: The component is being removed and is deinitializing by calling the `deinit` callback.
## Component API
@@ -77,33 +79,34 @@ The DM Component C api can be found in the `celix_dm_component.h` header and the
## Example: Create and configure component's lifecycle callbacks in C
The following example shows how a simple component can be created and managed with the DM in C.
Because the component's lifecycle is managed by the DM, this also means that if configured correctly no additional
-code is needed to remove and destroy the DM component and its implementation.
+code is needed to remove and destroy the DM component and its implementation.
Remarks for the C example:
- 1. Although this is a C component. The simple component functions have been design for a component approach,
- using the component pointer as first argument.
- 2. The component implementation can be any POCO, as long as its lifecycle and destroy function follow a
- component approach (one argument with the component implementation pointer as type)
- 3. Creates the DM component, but note that this component is not yet known to the DM. This makes it possible to
- first configure the DM component over multiple function calls, before adding - and as result
- activating - the DM component to the DM.
- 4. Configures the component implementation in the DM component, so that the implementation pointer can be used
- in the configured component callbacks.
- 5. Configures the component lifecycle callbacks to the DM Component. These callbacks should accept the component
- implementation as its only argument. The `CELIX_DM_COMPONENT_SET_CALLBACKS` marco is used instead of the
- `celix_dmComponent_setCallbacks` function so that the component implementation type can directly be used
- in the lifecycle callbacks (instead of `void*`).
- 6. Configures the component destroy implementation callback to the Dm Component. This callback will be called when
- the DM component is removed from the DM and has become inactive. The callback will be called from the Celix event
- thread. The advantages of configuring this callback is that the DM manages when the callback needs to be called;
- this removes some complexity for the users. The `CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION` marco
- is used instead of the `celix_dmComponent_setImplementationDestroyFunction` function so that the component
- implementation type can be directly used in the callback (instead of `void*`).
- 7. Adds the DM Component the DM and as result the DM will activate manage the component.
- 8. No additional code is needed to clean up components and as such no activator stop callback function needs to be
- configured. The generated bundle activator will ensure that all component are removed from the DM when the
- bundle is stopped and the DM will ensure that the components are deactivated and destroyed correctly.
-
+1. Although this is a C component. The simple component functions have been design for a component approach,
+ using the component pointer as first argument.
+2. The component implementation can be any POCO, as long as its lifecycle and destroy function signature follow a
+ component approach: A single argument, with as type the component implementation pointer and an int return
+ for the component lifecycle functions and a void return for the component destroy function.
+3. Creates the DM component, but note that the DM component is not yet known to the DM. This makes it possible to
+ first configure the DM component over multiple function calls, before adding it to the DM.
+4. Configures the component implementation in the DM component, so that the implementation pointer can be used
+ in the configured component callbacks.
+5. Configures the component lifecycle callbacks to the DM Component. These callbacks should accept the component
+ implementation as its only argument. The `CELIX_DM_COMPONENT_SET_CALLBACKS` marco is used instead of the
+ `celix_dmComponent_setCallbacks` function so that the component implementation type can directly be used
+ in the lifecycle callbacks (instead of `void*`).
+6. Configures the component destroy implementation callback to the Dm Component. This callback will be called when
+ the DM component is removed from the DM and has become inactive. The callback will be called from the Celix event
+ thread. The advantages of configuring this callback is that the DM manages when the callback needs to be called;
+ this removes some complexity for the users. The `CELIX_DM_COMPONENT_SET_IMPLEMENTATION_DESTROY_FUNCTION` marco
+ is used instead of the `celix_dmComponent_setImplementationDestroyFunction` function so that the component
+ implementation type can be directly used in the callback (instead of `void*`).
+7. Adds the DM Component the DM and as result the DM will that point on manage the components' lifecycle, service
+ dependencies and provided services.
+8. No additional code is needed to clean up components and as such no activator stop callback function needs to be
+ configured. The generated bundle activator will ensure that all components are removed from the DM when the
+ bundle is stopped and the DM will ensure that the components are deactivated and destroyed correctly.
+
```C
//src/simple_component_activator.c
#include <stdio.h>
@@ -181,26 +184,26 @@ CELIX_GEN_BUNDLE_ACTIVATOR(simple_component_activator_t, simpleComponentActivato
```
## Example: Create and configure component's lifecycle callbacks in C++
-The following example shows how a simple component can be created and managed with the DM in C++.
-For C++ the DM will manage the components and also ensures that component implementations are kept in scope for as
-long as the components are managed by the DM.
+The following example shows how a simple component can be created and managed with the DM in C++.
+For C++ the DM will manage the component and also ensures that component implementation is kept in scope for as
+long as the component is managed by the DM.
Remarks for the C++ example:
1. For C++ the DM can directly work on classes and as result lifecycle callback can be class methods.
-2. Creates a component implementation using a unique_ptr.
+2. Creates a component implementation using a unique_ptr.
3. Create a C++ DM Component and directly add it to the DM. For C++ DM Component needs to be "build" first, before the
- DM will activate them. This way C++ components can be build using a fluent api and marked ready with a `build()`
- method call.
- As component implementation the DM accepts a unique_pt, shared_ptr, value type of no implementation. If no
- implementation is provided the DM will create a component implementation using the template argument and
- assuming a default constructor (e.g. `ctx->getDependencyManager()->createComponent<CmpWithDefaultCTOR>()`).
+ DM will manage them. This way C++ components can be build using a fluent api and marked complete with a `build()`
+ method call.
+ For a component implementation the DM accepts a unique_ptr, a shared_ptr, a value type or no implementation. If no
+ implementation is provided the DM will create a component implementation using the template argument and
+ assuming a default constructor (e.g. `ctx->getDependencyManager()->createComponent<CmpWithDefaultCTOR>()`).
4. Configures the component lifecycle callbacks as class methods. The DM will call these callbacks using the
- component implementation as object instance.
-5. "Builds" the component. C++ component will only be managed by the DM after they are build. This makes it possible
- to configure a component over multiple method calls before marking the component as ready (build).
- The generated C++ bundle activator will also enable all components created during the bundle activation, to ensure
- that the build behaviour is backwards compatible with previous released DM implementation. It is preferred that
- users explicitly build their components when they are completely configured.
+ component implementation raw pointer as object instance (`this`).
+5. "Builds" the component. C++ components will only be managed by the DM after they are build. This makes it possible
+ to configure a component over multiple method calls before marking the component complete (build).
+ The generated C++ bundle activator will also enable all components created during the bundle activation, this is
+ done to ensure that the build behaviour is backwards compatible with previous released DM implementation.
+ It is preferred that users explicitly build their components when they are completely configured.
```C++
//src/SimpleComponentActivator.cc
@@ -245,23 +248,23 @@ CELIX_GEN_CXX_BUNDLE_ACTIVATOR(SimpleComponentActivator)
```
# Component's Provided Services
-Components can be configured to provide services. These provided services will correspondent to service registration
-when a component is in the `Tracking Optional` state.
+Components can be configured to provide services. These provided services will result in service registrations
+when a component is `Starting` or `Resuming` (i.e. when a component goes to the `Tracking Optional` state).
-If a component provide services, these service will have an additional metadata property - named "component.uuid"
-that couples the services to the component named "component.uuid".
+If a component provide services, these services will have an additional automatically added service property - named "component.uuid" - next to its configured provided service properties. The "component.uuid" service property can be
+used to identify if a service is provided by a component and which component.
## Example: Component with a provided service in C
-The following example shows how a component that provide a `celix_shell_command` service.
+The following example shows how a component that provide a `celix_shell_command` service.
Remarks for the C example:
-1. C and C services do not support inheritance. So even if a C component provides a certain service it is not an
- instance of said service. This also means the C service struct provided by a component needs to be stored
+1. C services do not support inheritance. So even if a C component provides a certain service, it is not an
+ instance of said service. This also means the C service struct provided by a component needs to be stored
separately. In this example this is done storing the service struct in the bundle activator data. Note
- that the bundle activator data "outlives" the component, because all components are removed before a bundle
+ that the bundle activator data "outlives" the component, because all components are removed before a bundle
is completely stopped.
2. Configures a provided service (interface) for the component. The service will not directly be registered, but
- instead will be registered if the component enters the state `Tracking Optional`.
+ instead will be registered in the component states `Starting` and `Resuming`.
```C
//src/component_with_provided_service_activator.c
@@ -338,28 +341,28 @@ CELIX_GEN_BUNDLE_ACTIVATOR(
```
## Example: Component with a provided service in C++
-The following example shows how a C++ component that provide a C++ `celix::IShellCommand` service
-and a C `elix_shell_command` service. For a C++ component it's possible to provide C and C++ services.
+The following example shows how a C++ component that provide a C++ `celix::IShellCommand` service
+and a C `elix_shell_command` service. For a C++ component it's possible to provide C and C++ services.
Remarks for the C++ example:
1. If a component provides a C++ services, it also expected that the component implementation inherits the service
- interface.
+ interface.
2. The overridden `executeCommand` method of `celix::IShellCommand`.
-3. Methods of C service interfaces can be implemented as class methods, but the bundle activator should ensure that
- the underlining C service interface structs are assigned with compatible C function pointers.
-4. Creating a component using only a template argument. The DM will construct - using a default constructor - a
+3. Methods of C service interfaces can be implemented as class methods, but the bundle activator should ensure that
+ the underlining C service interface structs are assigned with compatible C function pointers.
+4. Creating a component using only a template argument. The DM will construct - using a default constructor - a
component implementation instance.
5. Configures the component to provide a C++ `celix::IShellCommand` service. Note that because the component
- implementation is an instance of `celix::IShellCommand` no additional storage is needed. The service will not
- directly be registered, but instead will be registered if the component enters the state `Tracking Optional`.
-6. Set the C `executeCommand` function pointer of the `celix_shell_command_t` service interface struct to a
- capture-less lambda expression. The lambda expression is used to forward the call to the `executeCCommand`
- class method. Note the capture-less lambda expression can decay to function pointers.
-7. Configures the component to provide a C `celix_shell_command_t` service. Note that for a C service, the
- `createUnassociatedProvidedService` must be used, because the component does not inherit `celix_shell_command_t`.
- The service will not directly be registered, but instead will be registered if the component enters the state
- `Tracking Optional`..
-8. "Build" the component so the DM will manage and try to activate the component.
+ implementation is an instance of `celix::IShellCommand` no additional storage is needed. The service will not
+ directly be registered, but instead will be registered in the components states `Starting` and `Resuming`.
+6. Set the C `executeCommand` function pointer of the `celix_shell_command_t` service interface struct to a
+ capture-less lambda expression. The lambda expression is used to forward the call to the `executeCCommand`
+ class method. Note the capture-less lambda expression can decay to C-style function pointers.
+7. Configures the component to provide a C `celix_shell_command_t` service. Note that for a C service, the
+ `createUnassociatedProvidedService` must be used, because the component does not inherit `celix_shell_command_t`.
+ The service will not directly be registered, but instead will be registered in the component states `Starting` and
+ `Resuming`.
+8. "Build" the component so the DM will manage the component.
```C++
@@ -377,7 +380,8 @@ public:
const std::vector<std::string>& /*commandArgs*/,
FILE* outStream,
FILE* /*errorStream*/) override {
- fprintf(outStream, "Hello from cmp. C++ command called %i times. commandLine is %s\n", cxxCallCount++, commandLine.c_str());
+ fprintf(outStream, "Hello from cmp. C++ command called %i times. commandLine is %s\n",
+ cxxCallCount++, commandLine.c_str());
} // <-----------------------------------------------------------------------------------------------------------<2>
void executeCCommand(const char* commandLine, FILE* outStream) {
@@ -394,7 +398,7 @@ public:
auto& cmp = ctx->getDependencyManager()->createComponent<ComponentWithProvidedService>(); // <---------------<4>
cmp.createProvidedService<celix::IShellCommand>()
- .addProperty(celix::IShellCommand::COMMAND_NAME, "HelloComponent"); // <---------------------------------<5>
+ .addProperty(celix::IShellCommand::COMMAND_NAME, "HelloComponent"); // <-----------------------------<5>
auto shellCmd = std::make_shared<celix_shell_command_t>();
shellCmd->handle = static_cast<void*>(&cmp.getInstance());
@@ -405,7 +409,7 @@ public:
}; // <------------------------------------------------------------------------------------------------------<6>
cmp.createUnassociatedProvidedService(std::move(shellCmd), CELIX_SHELL_COMMAND_SERVICE_NAME)
- .addProperty(CELIX_SHELL_COMMAND_NAME, "hello_component"); // < -----------------------------------------<7>
+ .addProperty(CELIX_SHELL_COMMAND_NAME, "hello_component"); // < -------------------------------------<7>
cmp.build(); // <--------------------------------------------------------------------------------------------<8>
}
@@ -416,41 +420,68 @@ CELIX_GEN_CXX_BUNDLE_ACTIVATOR(ComponentWithProvidedServiceActivator)
```
# Component's Service Dependencies
-Components can be configured to have service dependencies. These service dependencies will influence the component's
+Components can be configured to have service dependencies. These service dependencies will influence the component's
lifecycle. Components can have optional and required service dependencies. When service dependencies are required the
-component can only be active if all required dependencies are available;
+component can only be active if all required dependencies are available; where available means at least 1 matching
+service dependency is found.
-When configuring service dependencies, callbacks can be configured when services are being added, removed or when a
-new highest ranking service is available. It is also possible to configure callbacks which only inject/remove service
-pointers, or service pointers with their properties (metadata) and even service pointers with their properties and
-their providing bundle. Service dependency callbacks will always be called from the Celix event thread.
+When configuring service dependencies, callbacks can be configured for handling services that are being added,
+removed or for when a new highest ranking service is available.
+
+Service dependency callbacks can be configured with 3 different types of argument signatures:
+- A single argument for the service pointer (raw pointer or shared_ptr);
+- A service pointer (raw pointer or shared_ptr) as first argument and the service properties as second argument.
+- A service pointer (raw pointer or shared_ptr) as first argument, the service properties as second argument and
+ the bundle providing the service as third argument.
+
+Service dependency callbacks will always be called from the Celix event thread.
A service change (injection/removal) can be handled by the component using a Locking-strategy or a suspend-strategy.
-This strategy can be configured per service dependency and expect the following behaviour from the component
+This strategy can be configured per service dependency and expect the following behaviour from the component
implementation:
-- Locking-strategy: The component implementation must ensure that the service pointers (and if applicable the service
- properties and its bundle) are protected using a locking mechanism (e.g. a mutex). This should ensure that services
- are no longer in use after they are removed (or replaced) from a component.
+- Locking-strategy: The component implementation must ensure that the stored service pointers (and if applicable the
+ service properties and its bundle) are protected using a locking mechanism (e.g. a mutex).
+ This should ensure that services are no longer in use after they are removed (or replaced) from a component and
+ thus can be safely deleted from memory.
- Suspend-strategy: The DM will ensure that before service dependency callbacks are called, all provided services
- are (temporary) unregistered and the component is suspended (using the components' stop callback). This should mean
- that there are no active users - through the provided services or active threads - of the service dependencies
+ are (temporary) unregistered and the component is suspended (using the components' `stop` callback). This should mean
+ that there are no active users - through the provided services or active threads - of the service dependencies
anymore and that service changes can safely be handling without locking. The component implementation must ensure
- that after `stop` callback no active thread, thread pools, timers, etc that use service dependencies are active
- anymore.
+ that after a `stop` callback there are no active threads, thread pools, timers, etc - that use service dependencies -
+ are active anymore.
## Example: Component with a service dependencies in C
The following example shows how a C component that has two service dependency on the `celix_shell_command_t` service.
-One service dependency is a required dependency with a suspend-strategy and uses a `set ` callback which ensure
+One service dependency is a required dependency with a suspend-strategy and uses a `set ` callback which ensure
that a single service is injected and that is always the highest ranking service. Note that the highest ranking
-service can be `NULL` if there are no service.
+service can be `NULL` if there are no other matching services.
-The other dependency is an optional dependency with a locking-strategy and uses a `addWithProps` and
+The other dependency is an optional dependency with a locking-strategy and uses a `addWithProps` and
`removeWithProps` callback. These callbacks will be called for every `celix_shell_command_t` service being added/removed
-and will be called with not only the service pointer, but also the service metadata properties.
+and will be called with not only the service pointer, but also the service properties.
Remarks for the C example:
-1. TODO
+1. Creates a mutex to protect the `cmdShells` field which is configured with a locking-strategy service dependency.
+2. Updates the `highestRankingCmdShell` field without locking. Note that because the service dependency is
+ configured with a suspend-strategy the `componentWithServiceDependency_setHighestRankingShellCommand` function
+ will only be called when the component is in the `Suspended` state or when it is not in the `Active` compound state.
+3. Locks the mutex and adds the newly added service to the `cmdShells` list. Note that because the service dependency
+ is configured with a locking-strategy the `componentWithServiceDependency_addShellCommand` and
+ `componentWithServiceDependency_removeShellCommand` functions can be called from any component lifecycle state.
+4. Creates a new DM service dependency object. Note that the DM service dependency is not yet known to the DM Component.
+5. Configures for which service name the service dependency will track services for. Optionally it is also possible
+ to fine tune the tracked service by providing a service version range and/or service filter.
+6. Configures the update strategy for the service dependency to suspend-strategy.
+7. Configures the service dependency as a required service dependency.
+8. Creates an empty service dependency callback options struct. This struct can be used to configure different
+ service dependency callbacks.
+9. Configures the `set` service dependency callback to `componentWithServiceDependency_setHighestRankingShellCommand`
+10. Configures the dependency manager to use the callbacks configures in opts.
+11. Adds the DM service dependency object to the DM component object.
+12. Configures the update strategy for the service dependency to locking-strategy.
+13. Configures the service dependency as an optional service dependency.
+14. Configures the `addWithProps` service dependency callback to `componentWithServiceDependency_addShellCommand`.
```C
//src/component_with_service_dependency_activator.c
@@ -462,13 +493,13 @@ Remarks for the C example:
typedef struct component_with_service_dependency {
celix_shell_command_t* highestRankingCmdShell; //only updated when component is not active or suspended
- celix_thread_mutex_t mutex;
+ celix_thread_mutex_t mutex; //protects cmdShells
celix_array_list_t* cmdShells;
} component_with_service_dependency_t;
static component_with_service_dependency_t* componentWithServiceDependency_create() {
component_with_service_dependency_t* cmp = calloc(1, sizeof(*cmp));
- celixThreadMutex_create(&cmp->mutex, NULL);
+ celixThreadMutex_create(&cmp->mutex, NULL); // <-----------------------------------------------------------------<1>
cmp->cmdShells = celix_arrayList_create();
return cmp;
}
@@ -483,7 +514,7 @@ static void componentWithServiceDependency_setHighestRankingShellCommand(
component_with_service_dependency_t* cmp,
celix_shell_command_t* shellCmd) {
printf("New highest ranking service (can be NULL): %p\n", shellCmd);
- cmp->highestRankingCmdShell = shellCmd;
+ cmp->highestRankingCmdShell = shellCmd; // <---------------------------------------------------------------------<2>
}
static void componentWithServiceDependency_addShellCommand(
@@ -492,7 +523,7 @@ static void componentWithServiceDependency_addShellCommand(
const celix_properties_t* props) {
long id = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1);
printf("Adding shell command service with service.id %li\n", id);
- celixThreadMutex_lock(&cmp->mutex);
+ celixThreadMutex_lock(&cmp->mutex); // <-------------------------------------------------------------------------<3>
celix_arrayList_add(cmp->cmdShells, shellCmd);
celixThreadMutex_unlock(&cmp->mutex);
}
@@ -527,22 +558,22 @@ static celix_status_t componentWithServiceDependencyActivator_start(component_wi
componentWithServiceDependency_destroy);
//create mandatory service dependency with cardinality one and with a suspend-strategy
- celix_dm_service_dependency_t* dep1 = celix_dmServiceDependency_create();
- celix_dmServiceDependency_setService(dep1, CELIX_SHELL_COMMAND_SERVICE_NAME, NULL, NULL);
- celix_dmServiceDependency_setStrategy(dep1, DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND);
- celix_dmServiceDependency_setRequired(dep1, true);
- celix_dm_service_dependency_callback_options_t opts1 = CELIX_EMPTY_DM_SERVICE_DEPENDENCY_CALLBACK_OPTIONS;
- opts1.set = (void*)componentWithServiceDependency_setHighestRankingShellCommand;
- celix_dmServiceDependency_setCallbacksWithOptions(dep1, &opts1);
- celix_dmComponent_addServiceDependency(dmCmp, dep1);
+ celix_dm_service_dependency_t* dep1 = celix_dmServiceDependency_create(); // <-----------------------------------<4>
+ celix_dmServiceDependency_setService(dep1, CELIX_SHELL_COMMAND_SERVICE_NAME, NULL, NULL); // <-------------------<5>
+ celix_dmServiceDependency_setStrategy(dep1, DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND); // <------------------------<6>
+ celix_dmServiceDependency_setRequired(dep1, true); // <----------------------------------------------------------<7>
+ celix_dm_service_dependency_callback_options_t opts1 = CELIX_EMPTY_DM_SERVICE_DEPENDENCY_CALLBACK_OPTIONS; // <--<8>
+ opts1.set = (void*)componentWithServiceDependency_setHighestRankingShellCommand; // <----------------------------<9>
+ celix_dmServiceDependency_setCallbacksWithOptions(dep1, &opts1); // <-------------------------------------------<10>
+ celix_dmComponent_addServiceDependency(dmCmp, dep1); // <-------------------------------------------------------<11>
//create optional service dependency with cardinality many and with a locking-strategy
celix_dm_service_dependency_t* dep2 = celix_dmServiceDependency_create();
celix_dmServiceDependency_setService(dep2, CELIX_SHELL_COMMAND_SERVICE_NAME, NULL, NULL);
- celix_dmServiceDependency_setStrategy(dep2, DM_SERVICE_DEPENDENCY_STRATEGY_LOCKING);
- celix_dmServiceDependency_setRequired(dep2, false);
+ celix_dmServiceDependency_setStrategy(dep2, DM_SERVICE_DEPENDENCY_STRATEGY_LOCKING); // <----------------------<12>
+ celix_dmServiceDependency_setRequired(dep2, false); // <--------------------------------------------------------<13>
celix_dm_service_dependency_callback_options_t opts2 = CELIX_EMPTY_DM_SERVICE_DEPENDENCY_CALLBACK_OPTIONS;
- opts2.addWithProps = (void*)componentWithServiceDependency_addShellCommand;
+ opts2.addWithProps = (void*)componentWithServiceDependency_addShellCommand; // <-------------------------------<14>
opts2.removeWithProps = (void*)componentWithServiceDependency_removeShellCommand;
celix_dmServiceDependency_setCallbacksWithOptions(dep2, &opts2);
celix_dmComponent_addServiceDependency(dmCmp, dep2);
@@ -560,23 +591,39 @@ CELIX_GEN_BUNDLE_ACTIVATOR(
```
## Example: Component with a service dependencies in C++
-The following example shows how a C++ component that has two service dependency. One
+The following example shows how a C++ component that has two service dependency. One
service dependency for the C++ `celix::IShellCommand` service and one for the C `celix_shell_command_t` service.
-The `celix::IShellCommand` service dependency is a required dependency with a suspend-strategy and uses a
-`set ` callback which ensure that a single service is injected and that is always the highest ranking service.
+The `celix::IShellCommand` service dependency is a required dependency with a suspend-strategy and uses a
+`set` callback which ensure that a single service is injected and that is always the highest ranking service.
Note that the highest ranking service can be an empty shared_ptr if there are no service.
The `celix_shell_command_t` service dependency is an optional dependency with a locking-strategy and uses a
-`addWithProperties` and `removeWithProperties` callback.
+`addWithProperties` and `removeWithProperties` callback.
These callbacks will be called for every `celix_shell_command_t` service being added/removed
-and will be called with not only the service shared_ptr, but also the service metadata properties.
+and will be called with not only the service shared_ptr, but also the service properties.
-Note that for C++ component service dependencies there is no real different between a C++ or a C service; Both
-are injected as shared_ptr, and as optional properties or bundle argument the C++ variant are provided.
+Note that for C++ component service dependencies, there is no real different between a C++ or a C service dependency;
+In both cases the service pointers are injected using shared_ptr and if applicable the service properties and
+bundle argument are also provided as shared_ptr using the C++ `celix::Properties` and `celix::Bundle`.
Remarks for the C++ example:
-1. TODO
+1. Creates a mutex to protect the `shellCommands` field which is configured with a locking-strategy service dependency.
+2. Updates the `highestRankingShellCmd` field without locking. Note that because the service dependency is
+ configured with a suspend-strategy the `ComponentWithServiceDependency::setHighestRankingShellCommand` method
+ will only be called when the component is in the `Suspended` state or when it is not in the `Active` compound state.
+3. Locks the mutex and adds the newly added service to the `shellCommands` list. Note that because the service
+ dependency is configured with a locking-strategy the `ComponentWithServiceDependency::addCShellCmd` and
+ `ComponentWithServiceDependency::removeCShellCmd` methods can be called from any component lifecycle state.
+4. Creates a new DM service dependency object, the service dependency is considered incomplete until the
+ service dependency, component or DM is build. Note that the `celix::dm::Component::createServiceDependency` method
+ is called without provided a service name, the service name will be inferred using the `celix::typeName`.
+5. Configures the service dependency set callback.
+6. Configures the service dependency as a required service dependency.
+7. Configures the update strategy for the service dependency to suspend-strategy.
+8. Creates another new DM service dependency object and in this case also explicitly provides the service name
+ to use (`CELIX_SHELL_COMMAND_SERVICE_NAME`).
+9. Builds the component and as result also builds the components' service dependencies (i.e. marking them as complete).
```C++
//src/ComponentWithServiceDependencyActivator.cc
@@ -588,7 +635,7 @@ class ComponentWithServiceDependency {
public:
void setHighestRankingShellCommand(const std::shared_ptr<celix::IShellCommand>& cmdSvc) {
std::cout << "New highest ranking service (can be NULL): " << (intptr_t)cmdSvc.get() << std::endl;
- highestRankingShellCmd = cmdSvc;
+ highestRankingShellCmd = cmdSvc; // <------------------------------------------------------------------------<2>
}
void addCShellCmd(
@@ -596,7 +643,7 @@ public:
const std::shared_ptr<const celix::Properties>& props) {
auto id = props->getAsLong(celix::SERVICE_ID, -1);
std::cout << "Adding shell command service with service.id: " << id << std::endl;
- std::lock_guard lck{mutex};
+ std::lock_guard lck{mutex}; // <-----------------------------------------------------------------------------<3>
shellCommands.emplace(id, cmdSvc);
}
@@ -610,7 +657,7 @@ public:
}
private:
std::shared_ptr<celix::IShellCommand> highestRankingShellCmd{};
- std::mutex mutex{}; //protect below
+ std::mutex mutex{}; //protect shellCommands // <-----------------------------------------------------------------<1>
std::unordered_map<long, std::shared_ptr<celix_shell_command_t>> shellCommands{};
};
@@ -618,19 +665,19 @@ class ComponentWithServiceDependencyActivator {
public:
explicit ComponentWithServiceDependencyActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
using Cmp = ComponentWithServiceDependency;
- auto& cmp = ctx->getDependencyManager()->createComponent<Cmp>(); // <-------------<1>
+ auto& cmp = ctx->getDependencyManager()->createComponent<Cmp>();
- cmp.createServiceDependency<celix::IShellCommand>()
- .setCallbacks(&Cmp::setHighestRankingShellCommand)
- .setRequired(true)
- .setStrategy(DependencyUpdateStrategy::suspend);
+ cmp.createServiceDependency<celix::IShellCommand>() // <-----------------------------------------------------<4>
+ .setCallbacks(&Cmp::setHighestRankingShellCommand) // <----------------------------------------------<5>
+ .setRequired(true) // <------------------------------------------------------------------------------<6>
+ .setStrategy(DependencyUpdateStrategy::suspend); // <------------------------------------------------<7>
- cmp.createServiceDependency<celix_shell_command_t>(CELIX_SHELL_COMMAND_SERVICE_NAME)
- .setCallbacks(&Cmp::addCShellCmd, &Cmp::removeCShellCmd)
+ cmp.createServiceDependency<celix_shell_command_t>(CELIX_SHELL_COMMAND_SERVICE_NAME) // <--------------------<8>
+ .setCallbacks(&Cmp::addCShellCmd, &Cmp::removeCShellCmd)
.setRequired(false)
.setStrategy(DependencyUpdateStrategy::locking);
- cmp.build(); // <--------------------------------------------------------------------------------------------<8>
+ cmp.build(); // <--------------------------------------------------------------------------------------------<9>
}
};
@@ -638,10 +685,20 @@ CELIX_GEN_CXX_BUNDLE_ACTIVATOR(ComponentWithServiceDependencyActivator)
```
# When will a component be suspended
-
-TODO explain that a component will be suspended with service dependency on suspend strategy, a matching service update
-and if there is a svc inject callback configured. TODO check also start/stop method?
-
-# TODOs
-
-overview of function (e.g. setFilter for dependencies, etc)
\ No newline at end of file
+Components will only suspend if:
+- The component is in the state `Tracking Optional`;
+- The component has at least 1 service dependency where the update strategy is configured as suspend-strategy;
+- There is a service update event ongoing, where the update service event matches 1 of the components'
+ suspend-strategy service dependencies;
+- And least one of the component's matching suspend-strategy service dependency has a configured service injection/
+ removal callback configured.
+
+# The `celix::dm` shell command
+To interactively see the available components, their current lifecycle state, provided service and service dependencies
+the `celix::dm` shell command can be used.
+
+Examples of supported `dm` command lines are:
+- `celix::dm` - Show an overview of all components in the Celix framework. Only shows component lifecycle state.
+- `dm` - Same as `celix::dm` (as long as there is no colliding other `dm` commands).
+- `dm full` - Show a detailed overview of all components in the Celix framework. This also shows the provided
+ services and service dependencies of each component.
\ No newline at end of file
diff --git a/documents/services.md b/documents/services.md
index ae7e17bd..81e22a25 100644
--- a/documents/services.md
+++ b/documents/services.md
@@ -365,7 +365,7 @@ The Celix framework provides service usage through callbacks - instead of direct
to ensure that services are prevented from removal while the services are still in use without forwarding
this responsibility to the user; i.e. by adding an api to "lock" and "unlock" services for usage.
-###Example: Using a service in C
+### Example: Using a service in C
```C
#include <stdio.h>
#include <celix_api.h>
@@ -400,7 +400,7 @@ static celix_status_t use_command_service_example_stop(use_command_service_examp
CELIX_GEN_BUNDLE_ACTIVATOR(use_command_service_example_data_t, use_command_service_example_start, use_command_service_example_stop)
```
-###Example: Using a service in C++
+### Example: Using a service in C++
```C++
//src/UsingCommandServicesExample.cc
#include <celix/IShellCommand.h>
@@ -474,7 +474,7 @@ C++ service trackers are created and opened asynchronized, but closed synchroniz
The closing is done synchronized so that users can be sure that after a `celix::ServiceTracker::close()` call the
added callbacks will not be invoked anymore.
-###Example: Tracking services in C
+### Example: Tracking services in C
```C
//src/track_command_services_example.c
#include <stdio.h>
@@ -535,7 +535,7 @@ static celix_status_t track_command_services_example_stop(track_command_services
CELIX_GEN_BUNDLE_ACTIVATOR(track_command_services_example_data_t, track_command_services_example_start, track_command_services_example_stop)
```
-###Example: Tracking services in C++
+### Example: Tracking services in C++
```C++
//src/TrackingCommandServicesExample.cc
#include <unordered_map>
@@ -625,3 +625,17 @@ Service tracker callback with a synchronized service registration
![Register Service Async](diagrams/services_tracker_services_rem_seq.png)
Service tracker callback with a synchronized service un-registration
---
+
+# The `celix::query` shell command
+To interactively see the which service and service trackers are available the `celix::query` shell command
+can be used.
+
+Examples of supported `query` command lines are:
+- `celix::query` - Show an overview of registered services and active service trackers per bundle.
+- `query` - Same as `celix::query` (as long as there is no colliding other `query` commands).
+- `query -v` - Show a detailed overview of registered services and active service trackers per bundle.
+ For registered services the services properties are also printed and for active service trackers the number
+ of tracked services is also printed.
+- `query foo` - Show an overview of registered services and active service tracker where "foo" is
+ (case-insensitive) part of the provided/tracked service name.
+- `query (service.id>=10)` - Shown an overview of registered services which match the provided LDAP filter.
diff --git a/examples/celix-examples/readme_c_examples/src/component_with_service_dependency_activator.c b/examples/celix-examples/readme_c_examples/src/component_with_service_dependency_activator.c
index e44347e1..79883b40 100644
--- a/examples/celix-examples/readme_c_examples/src/component_with_service_dependency_activator.c
+++ b/examples/celix-examples/readme_c_examples/src/component_with_service_dependency_activator.c
@@ -26,13 +26,13 @@
typedef struct component_with_service_dependency {
celix_shell_command_t* highestRankingCmdShell; //only updated when component is not active or suspended
- celix_thread_mutex_t mutex;
+ celix_thread_mutex_t mutex; //protects cmdShells
celix_array_list_t* cmdShells;
} component_with_service_dependency_t;
static component_with_service_dependency_t* componentWithServiceDependency_create() {
component_with_service_dependency_t* cmp = calloc(1, sizeof(*cmp));
- celixThreadMutex_create(&cmp->mutex, NULL);
+ celixThreadMutex_create(&cmp->mutex, NULL); // <-----------------------------------------------------------------<1>
cmp->cmdShells = celix_arrayList_create();
return cmp;
}
@@ -47,7 +47,7 @@ static void componentWithServiceDependency_setHighestRankingShellCommand(
component_with_service_dependency_t* cmp,
celix_shell_command_t* shellCmd) {
printf("New highest ranking service (can be NULL): %p\n", shellCmd);
- cmp->highestRankingCmdShell = shellCmd;
+ cmp->highestRankingCmdShell = shellCmd; // <---------------------------------------------------------------------<2>
}
static void componentWithServiceDependency_addShellCommand(
@@ -56,7 +56,7 @@ static void componentWithServiceDependency_addShellCommand(
const celix_properties_t* props) {
long id = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1);
printf("Adding shell command service with service.id %li\n", id);
- celixThreadMutex_lock(&cmp->mutex);
+ celixThreadMutex_lock(&cmp->mutex); // <-------------------------------------------------------------------------<3>
celix_arrayList_add(cmp->cmdShells, shellCmd);
celixThreadMutex_unlock(&cmp->mutex);
}
@@ -91,22 +91,22 @@ static celix_status_t componentWithServiceDependencyActivator_start(component_wi
componentWithServiceDependency_destroy);
//create mandatory service dependency with cardinality one and with a suspend-strategy
- celix_dm_service_dependency_t* dep1 = celix_dmServiceDependency_create();
- celix_dmServiceDependency_setService(dep1, CELIX_SHELL_COMMAND_SERVICE_NAME, NULL, NULL);
- celix_dmServiceDependency_setStrategy(dep1, DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND);
- celix_dmServiceDependency_setRequired(dep1, true);
- celix_dm_service_dependency_callback_options_t opts1 = CELIX_EMPTY_DM_SERVICE_DEPENDENCY_CALLBACK_OPTIONS;
- opts1.set = (void*)componentWithServiceDependency_setHighestRankingShellCommand;
- celix_dmServiceDependency_setCallbacksWithOptions(dep1, &opts1);
- celix_dmComponent_addServiceDependency(dmCmp, dep1);
+ celix_dm_service_dependency_t* dep1 = celix_dmServiceDependency_create(); // <-----------------------------------<4>
+ celix_dmServiceDependency_setService(dep1, CELIX_SHELL_COMMAND_SERVICE_NAME, NULL, NULL); // <-------------------<5>
+ celix_dmServiceDependency_setStrategy(dep1, DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND); // <------------------------<6>
+ celix_dmServiceDependency_setRequired(dep1, true); // <----------------------------------------------------------<7>
+ celix_dm_service_dependency_callback_options_t opts1 = CELIX_EMPTY_DM_SERVICE_DEPENDENCY_CALLBACK_OPTIONS; // <--<8>
+ opts1.set = (void*)componentWithServiceDependency_setHighestRankingShellCommand; // <----------------------------<9>
+ celix_dmServiceDependency_setCallbacksWithOptions(dep1, &opts1); // <-------------------------------------------<10>
+ celix_dmComponent_addServiceDependency(dmCmp, dep1); // <-------------------------------------------------------<11>
//create optional service dependency with cardinality many and with a locking-strategy
celix_dm_service_dependency_t* dep2 = celix_dmServiceDependency_create();
celix_dmServiceDependency_setService(dep2, CELIX_SHELL_COMMAND_SERVICE_NAME, NULL, NULL);
- celix_dmServiceDependency_setStrategy(dep2, DM_SERVICE_DEPENDENCY_STRATEGY_LOCKING);
- celix_dmServiceDependency_setRequired(dep2, false);
+ celix_dmServiceDependency_setStrategy(dep2, DM_SERVICE_DEPENDENCY_STRATEGY_LOCKING); // <----------------------<12>
+ celix_dmServiceDependency_setRequired(dep2, false); // <--------------------------------------------------------<13>
celix_dm_service_dependency_callback_options_t opts2 = CELIX_EMPTY_DM_SERVICE_DEPENDENCY_CALLBACK_OPTIONS;
- opts2.addWithProps = (void*)componentWithServiceDependency_addShellCommand;
+ opts2.addWithProps = (void*)componentWithServiceDependency_addShellCommand; // <-------------------------------<14>
opts2.removeWithProps = (void*)componentWithServiceDependency_removeShellCommand;
celix_dmServiceDependency_setCallbacksWithOptions(dep2, &opts2);
celix_dmComponent_addServiceDependency(dmCmp, dep2);
diff --git a/examples/celix-examples/readme_cxx_examples/src/ComponentWithProvidedServiceActivator.cc b/examples/celix-examples/readme_cxx_examples/src/ComponentWithProvidedServiceActivator.cc
index 31b9758e..73be1ab5 100644
--- a/examples/celix-examples/readme_cxx_examples/src/ComponentWithProvidedServiceActivator.cc
+++ b/examples/celix-examples/readme_cxx_examples/src/ComponentWithProvidedServiceActivator.cc
@@ -31,7 +31,8 @@ public:
const std::vector<std::string>& /*commandArgs*/,
FILE* outStream,
FILE* /*errorStream*/) override {
- fprintf(outStream, "Hello from cmp. C++ command called %i times. commandLine is %s\n", cxxCallCount++, commandLine.c_str());
+ fprintf(outStream, "Hello from cmp. C++ command called %i times. commandLine is %s\n",
+ cxxCallCount++, commandLine.c_str());
} // <-----------------------------------------------------------------------------------------------------------<2>
void executeCCommand(const char* commandLine, FILE* outStream) {
@@ -48,7 +49,7 @@ public:
auto& cmp = ctx->getDependencyManager()->createComponent<ComponentWithProvidedService>(); // <---------------<4>
cmp.createProvidedService<celix::IShellCommand>()
- .addProperty(celix::IShellCommand::COMMAND_NAME, "HelloComponent"); // <---------------------------------<5>
+ .addProperty(celix::IShellCommand::COMMAND_NAME, "HelloComponent"); // <-----------------------------<5>
auto shellCmd = std::make_shared<celix_shell_command_t>();
shellCmd->handle = static_cast<void*>(&cmp.getInstance());
@@ -59,7 +60,7 @@ public:
}; // <------------------------------------------------------------------------------------------------------<6>
cmp.createUnassociatedProvidedService(std::move(shellCmd), CELIX_SHELL_COMMAND_SERVICE_NAME)
- .addProperty(CELIX_SHELL_COMMAND_NAME, "hello_component"); // < -----------------------------------------<7>
+ .addProperty(CELIX_SHELL_COMMAND_NAME, "hello_component"); // < -------------------------------------<7>
cmp.build(); // <--------------------------------------------------------------------------------------------<8>
}
diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt
index b33781a8..1cdae108 100644
--- a/libs/utils/CMakeLists.txt
+++ b/libs/utils/CMakeLists.txt
@@ -84,60 +84,64 @@ add_library(Celix::utils ALIAS utils)
if (ENABLE_TESTING)
add_subdirectory(gtest)
- find_package(CppUTest REQUIRED)
-
- include_directories(SYSTEM PRIVATE ${CppUTest_INCLUDE_DIR})
- include_directories(include)
- include_directories(src)
-
- add_executable(hash_map_test private/test/hash_map_test.cpp)
- target_link_libraries(hash_map_test Celix::utils CppUTest::CppUTest pthread)
-
- add_executable(array_list_test private/test/array_list_test.cpp)
- target_link_libraries(array_list_test Celix::utils CppUTest::CppUTest pthread)
-
- add_executable(celix_threads_test private/test/celix_threads_test.cpp)
- target_link_libraries(celix_threads_test Celix::utils CppUTest::CppUTest ${CppUTest_EXT_LIBRARIES} pthread)
-
- add_executable(linked_list_test private/test/linked_list_test.cpp)
- target_link_libraries(linked_list_test Celix::utils CppUTest::CppUTest pthread)
-
- add_executable(properties_test private/test/properties_test.cpp)
- target_link_libraries(properties_test CppUTest::CppUTest CppUTest::CppUTestExt Celix::utils pthread)
-
- add_executable(utils_test private/test/utils_test.cpp)
- target_link_libraries(utils_test CppUTest::CppUTest Celix::utils pthread)
-
- add_executable(ip_utils_test private/test/ip_utils_test.cpp)
- target_link_libraries(ip_utils_test CppUTest::CppUTest Celix::utils pthread)
-
- add_executable(filter_test private/test/filter_test.cpp)
- target_link_libraries(filter_test CppUTest::CppUTest Celix::utils pthread)
-
- add_executable(version_test private/test/version_test.cpp)
- target_link_libraries(version_test CppUTest::CppUTest Celix::utils pthread)
-
- configure_file(private/resources-test/properties.txt ${CMAKE_CURRENT_BINARY_DIR}/resources-test/properties.txt COPYONLY)
-
- add_test(NAME run_array_list_test COMMAND array_list_test)
- add_test(NAME run_hash_map_test COMMAND hash_map_test)
- add_test(NAME run_celix_threads_test COMMAND celix_threads_test)
- add_test(NAME run_linked_list_test COMMAND linked_list_test)
- add_test(NAME run_properties_test COMMAND properties_test)
- add_test(NAME run_utils_test COMMAND utils_test)
- add_test(NAME run_ip_utils_test COMMAND ip_utils_test)
- add_test(NAME filter_test COMMAND filter_test)
- add_test(NAME version_test COMMAND version_test)
-
- setup_target_for_coverage(array_list_test)
- setup_target_for_coverage(hash_map_test)
- setup_target_for_coverage(celix_threads_test)
- setup_target_for_coverage(linked_list_test)
- setup_target_for_coverage(properties_test)
- setup_target_for_coverage(utils_test)
- setup_target_for_coverage(ip_utils_test)
- setup_target_for_coverage(filter_test)
- setup_target_for_coverage(version_test)
+ find_package(CppUTest)
+
+ if (CppUTest_FOUND)
+ include_directories(SYSTEM PRIVATE ${CppUTest_INCLUDE_DIR})
+ include_directories(include)
+ include_directories(src)
+
+ add_executable(hash_map_test private/test/hash_map_test.cpp)
+ target_link_libraries(hash_map_test Celix::utils CppUTest::CppUTest pthread)
+
+ add_executable(array_list_test private/test/array_list_test.cpp)
+ target_link_libraries(array_list_test Celix::utils CppUTest::CppUTest pthread)
+
+ add_executable(celix_threads_test private/test/celix_threads_test.cpp)
+ target_link_libraries(celix_threads_test Celix::utils CppUTest::CppUTest ${CppUTest_EXT_LIBRARIES} pthread)
+
+ add_executable(linked_list_test private/test/linked_list_test.cpp)
+ target_link_libraries(linked_list_test Celix::utils CppUTest::CppUTest pthread)
+
+ add_executable(properties_test private/test/properties_test.cpp)
+ target_link_libraries(properties_test CppUTest::CppUTest CppUTest::CppUTestExt Celix::utils pthread)
+
+ add_executable(utils_test private/test/utils_test.cpp)
+ target_link_libraries(utils_test CppUTest::CppUTest Celix::utils pthread)
+
+ add_executable(ip_utils_test private/test/ip_utils_test.cpp)
+ target_link_libraries(ip_utils_test CppUTest::CppUTest Celix::utils pthread)
+
+ add_executable(filter_test private/test/filter_test.cpp)
+ target_link_libraries(filter_test CppUTest::CppUTest Celix::utils pthread)
+
+ add_executable(version_test private/test/version_test.cpp)
+ target_link_libraries(version_test CppUTest::CppUTest Celix::utils pthread)
+
+ configure_file(private/resources-test/properties.txt ${CMAKE_CURRENT_BINARY_DIR}/resources-test/properties.txt COPYONLY)
+
+ add_test(NAME run_array_list_test COMMAND array_list_test)
+ add_test(NAME run_hash_map_test COMMAND hash_map_test)
+ add_test(NAME run_celix_threads_test COMMAND celix_threads_test)
+ add_test(NAME run_linked_list_test COMMAND linked_list_test)
+ add_test(NAME run_properties_test COMMAND properties_test)
+ add_test(NAME run_utils_test COMMAND utils_test)
+ add_test(NAME run_ip_utils_test COMMAND ip_utils_test)
+ add_test(NAME filter_test COMMAND filter_test)
+ add_test(NAME version_test COMMAND version_test)
+
+ setup_target_for_coverage(array_list_test)
+ setup_target_for_coverage(hash_map_test)
+ setup_target_for_coverage(celix_threads_test)
+ setup_target_for_coverage(linked_list_test)
+ setup_target_for_coverage(properties_test)
+ setup_target_for_coverage(utils_test)
+ setup_target_for_coverage(ip_utils_test)
+ setup_target_for_coverage(filter_test)
+ setup_target_for_coverage(version_test)
+ else ()
+ message(WARNING "Cannot find CppUTest, deprecated cpputest-based unit test will not be added")
+ endif () #end CppUTest_FOUND
endif ()