blob: cf9c08bf5d893a4479192aed41517c1406836325 [file] [log] [blame]
/*
* 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 <thread>
#include <chrono>
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <celix_log_utils.h>
#include "celix_api.h"
class CelixBundleContextBundlesTestSuite : public ::testing::Test {
public:
celix_framework_t* fw = nullptr;
celix_bundle_context_t *ctx = nullptr;
celix_properties_t *properties = nullptr;
const char * const TEST_BND1_LOC = "" SIMPLE_TEST_BUNDLE1_LOCATION "";
const char * const TEST_BND2_LOC = "" SIMPLE_TEST_BUNDLE2_LOCATION "";
const char * const TEST_BND3_LOC = "" SIMPLE_TEST_BUNDLE3_LOCATION "";
const char * const TEST_BND4_LOC = "" SIMPLE_TEST_BUNDLE4_LOCATION "";
const char * const TEST_BND5_LOC = "" SIMPLE_TEST_BUNDLE5_LOCATION "";
const char * const TEST_BND_WITH_EXCEPTION_LOC = "" TEST_BUNDLE_WITH_EXCEPTION_LOCATION "";
const char * const TEST_BND_UNRESOLVABLE_LOC = "" TEST_BUNDLE_UNRESOLVABLE_LOCATION "";
CelixBundleContextBundlesTestSuite() {
properties = properties_create();
properties_set(properties, "LOGHELPER_ENABLE_STDOUT_FALLBACK", "true");
properties_set(properties, "org.osgi.framework.storage.clean", "onFirstInit");
properties_set(properties, "org.osgi.framework.storage", ".cacheBundleContextTestFramework");
fw = celix_frameworkFactory_createFramework(properties);
ctx = framework_getContext(fw);
}
~CelixBundleContextBundlesTestSuite() override {
celix_frameworkFactory_destroyFramework(fw);
}
CelixBundleContextBundlesTestSuite(CelixBundleContextBundlesTestSuite&&) = delete;
CelixBundleContextBundlesTestSuite(const CelixBundleContextBundlesTestSuite&) = delete;
CelixBundleContextBundlesTestSuite& operator=(CelixBundleContextBundlesTestSuite&&) = delete;
CelixBundleContextBundlesTestSuite& operator=(const CelixBundleContextBundlesTestSuite&) = delete;
};
TEST_F(CelixBundleContextBundlesTestSuite, StartStopTest) {
//nop
}
TEST_F(CelixBundleContextBundlesTestSuite, InstallABundleTest) {
long bndId = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
ASSERT_TRUE(bndId >= 0);
}
//TEST_F(CelixBundleContextBundlesTestSuite, InstallBundleWithBadExport) {
// long bndId = celix_bundleContext_installBundle(ctx, BUNDLE_WITH_BAD_EXPORT_LOCATION, true);
// ASSERT_TRUE(bndId >= 0);
//}
TEST_F(CelixBundleContextBundlesTestSuite, InstallBundlesTest) {
long bndId = celix_bundleContext_installBundle(ctx, "non-existing.zip", true);
ASSERT_TRUE(bndId < 0);
bndId = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
ASSERT_TRUE(bndId >= 0);
bndId = celix_bundleContext_installBundle(ctx, TEST_BND4_LOC, true); //not loaded in subdir
ASSERT_TRUE(bndId < 0);
setenv(CELIX_BUNDLES_PATH_NAME, "subdir", true);
bndId = celix_bundleContext_installBundle(ctx, TEST_BND4_LOC, true); //subdir now part of CELIX_BUNDLES_PATH
unsetenv("subdir");
ASSERT_TRUE(bndId >= 0);
}
TEST_F(CelixBundleContextBundlesTestSuite, TestIsSystemBundle) {
celix_bundle_t* fwBnd = celix_framework_getFrameworkBundle(fw);
ASSERT_TRUE(celix_bundle_isSystemBundle(fwBnd));
long bndId = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
ASSERT_TRUE(bndId >= 0);
bool called = celix_bundleContext_useBundle(ctx, bndId, nullptr, [](void*, const celix_bundle_t* bnd) {
EXPECT_FALSE(celix_bundle_isSystemBundle(bnd));
});
EXPECT_TRUE(called);
}
TEST_F(CelixBundleContextBundlesTestSuite, UseBundlesTest) {
int callbackCount = 0;
auto use = [](void *handle, const bundle_t *bnd) {
int *c = (int*)handle;
*c += 1;
long id = celix_bundle_getId(bnd);
ASSERT_TRUE(id >= 0);
const auto* v = celix_bundle_getVersion(bnd);
ASSERT_TRUE(v != nullptr);
ASSERT_EQ(celix_version_getMajor(v), 1);
};
celix_bundleContext_useBundles(ctx, &callbackCount, use);
callbackCount = 0;
celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
size_t useCount = celix_bundleContext_useBundles(ctx, &callbackCount, use);
EXPECT_EQ(1, callbackCount);
EXPECT_EQ(1, useCount);
callbackCount = 0;
celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, false); //note installed, but not started
useCount = celix_bundleContext_useBundles(ctx, &callbackCount, use);
EXPECT_EQ(2, callbackCount);
EXPECT_EQ(2, useCount);
};
TEST_F(CelixBundleContextBundlesTestSuite, InstallAndUninstallBundlesTest) {
//install bundles
long bndId1 = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
long bndId2 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, false);
long bndId3 = celix_bundleContext_installBundle(ctx, TEST_BND3_LOC, true);
ASSERT_TRUE(bndId1 >= 0L);
ASSERT_TRUE(bndId2 >= 0L);
ASSERT_TRUE(bndId3 >= 0L);
ASSERT_TRUE(celix_bundleContext_isBundleInstalled(ctx, bndId1));
ASSERT_TRUE(celix_bundleContext_isBundleInstalled(ctx, bndId2));
ASSERT_TRUE(celix_bundleContext_isBundleInstalled(ctx, bndId3));
ASSERT_TRUE(celix_bundleContext_isBundleActive(ctx, bndId1));
ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId2)); //not auto started
ASSERT_TRUE(celix_bundleContext_isBundleActive(ctx, bndId3));
//uninstall bundles
ASSERT_TRUE(celix_bundleContext_uninstallBundle(ctx, bndId1));
ASSERT_TRUE(celix_bundleContext_uninstallBundle(ctx, bndId2));
ASSERT_TRUE(celix_bundleContext_uninstallBundle(ctx, bndId3));
ASSERT_FALSE(celix_bundleContext_isBundleInstalled(ctx, bndId1));
ASSERT_FALSE(celix_bundleContext_isBundleInstalled(ctx, bndId2));
ASSERT_FALSE(celix_bundleContext_isBundleInstalled(ctx, bndId3));
ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId1)); //not uninstall -> not active
ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId2));
ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId3));
//reinstall bundles
long bndId4 = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
long bndId5 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, false);
long bndId6 = celix_bundleContext_installBundle(ctx, TEST_BND3_LOC, true);
ASSERT_TRUE(bndId4 >= 0L);
ASSERT_TRUE(bndId1 == bndId4); //bundle cache -> reuse of bundle id.
ASSERT_TRUE(bndId5 >= 0L);
ASSERT_TRUE(bndId2 == bndId5); //bundle cache -> reuse of bundle id.
ASSERT_TRUE(bndId6 >= 0L);
ASSERT_TRUE(bndId3 == bndId6); //bundle cache -> reuse of bundle id.
}
TEST_F(CelixBundleContextBundlesTestSuite, StartBundleWithException) {
long bndId = celix_bundleContext_installBundle(ctx, TEST_BND_WITH_EXCEPTION_LOC, true);
ASSERT_TRUE(bndId > 0); //bundle is installed, but not started
bool called = celix_bundleContext_useBundle(ctx, bndId, nullptr, [](void */*handle*/, const celix_bundle_t *bnd) {
auto state = celix_bundle_getState(bnd);
ASSERT_EQ(state, CELIX_BUNDLE_STATE_RESOLVED);
ASSERT_EQ(state, OSGI_FRAMEWORK_BUNDLE_RESOLVED);
});
ASSERT_TRUE(called);
}
TEST_F(CelixBundleContextBundlesTestSuite, StartUnresolveableBundle) {
long bndId = celix_bundleContext_installBundle(ctx, TEST_BND_UNRESOLVABLE_LOC, true);
ASSERT_TRUE(bndId > 0); //bundle is installed, but not resolved
bool called = celix_bundleContext_useBundle(ctx, bndId, nullptr, [](void *, const celix_bundle_t *bnd) {
auto state = celix_bundle_getState(bnd);
ASSERT_EQ(CELIX_BUNDLE_STATE_INSTALLED, state);
ASSERT_EQ(OSGI_FRAMEWORK_BUNDLE_INSTALLED, celix_bundle_getState(bnd)); //NOTE the OSGI_ variant is equivalent
});
ASSERT_TRUE(called);
celix_bundleContext_startBundle(ctx, bndId);
called = celix_bundleContext_useBundle(ctx, bndId, nullptr, [](void *, const celix_bundle_t *bnd) {
auto state = celix_bundle_getState(bnd);
ASSERT_EQ(CELIX_BUNDLE_STATE_INSTALLED, state);
ASSERT_EQ(OSGI_FRAMEWORK_BUNDLE_INSTALLED, state); //NOTE the OSGI_ variant is equivalent
});
ASSERT_TRUE(called);
}
TEST_F(CelixBundleContextBundlesTestSuite, UseBundleTest) {
int count = 0;
bool called = celix_bundleContext_useBundle(ctx, 0, &count, [](void *handle, const bundle_t *bnd) {
int *c = (int*)handle;
*c += 1;
long id = celix_bundle_getId(bnd);
ASSERT_EQ(0, id);
});
ASSERT_TRUE(called);
ASSERT_EQ(1, count);
};
TEST_F(CelixBundleContextBundlesTestSuite, StopStartTest) {
long bndId1 = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
long bndId2 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, true);
long bndId3 = celix_bundleContext_installBundle(ctx, TEST_BND3_LOC, true);
ASSERT_TRUE(celix_bundleContext_isBundleInstalled(ctx, bndId1));
ASSERT_TRUE(celix_bundleContext_isBundleInstalled(ctx, bndId2));
ASSERT_TRUE(celix_bundleContext_isBundleInstalled(ctx, bndId3));
ASSERT_FALSE(celix_bundleContext_isBundleInstalled(ctx, 600 /*non existing*/));
celix_array_list_t *ids = celix_bundleContext_listInstalledBundles(ctx);
size_t size = celix_arrayList_size(ids);
EXPECT_EQ(3, size);
celix_arrayList_destroy(ids);
ids = celix_bundleContext_listBundles(ctx);
size = celix_arrayList_size(ids);
EXPECT_EQ(3, size);
int count = 0;
celix_bundleContext_useBundles(ctx, &count, [](void *handle, const celix_bundle_t *bnd) {
auto *c = (int*)handle;
ASSERT_EQ(CELIX_BUNDLE_STATE_ACTIVE, celix_bundle_getState(bnd));
ASSERT_EQ(OSGI_FRAMEWORK_BUNDLE_ACTIVE, celix_bundle_getState(bnd)); //NOTE the OSGI_ variant is the same.
*c += 1;
});
EXPECT_EQ(3, count);
for (size_t i = 0; i < size; ++i) {
bool stopped = celix_bundleContext_stopBundle(ctx, celix_arrayList_getLong(ids, (int)i));
EXPECT_TRUE(stopped);
}
bool stopped = celix_bundleContext_stopBundle(ctx, 42 /*non existing*/);
EXPECT_FALSE(stopped);
bool started = celix_bundleContext_startBundle(ctx, 42 /*non existing*/);
EXPECT_FALSE(started);
for (size_t i = 0; i < size; ++i) {
started = celix_bundleContext_startBundle(ctx, celix_arrayList_getLong(ids, (int)i));
EXPECT_TRUE(started);
}
count = 0;
celix_bundleContext_useBundles(ctx, &count, [](void *handle, const celix_bundle_t *bnd) {
auto *c = (int*)handle;
EXPECT_EQ(OSGI_FRAMEWORK_BUNDLE_ACTIVE, celix_bundle_getState(bnd));
ASSERT_EQ(OSGI_FRAMEWORK_BUNDLE_ACTIVE, celix_bundle_getState(bnd)); //NOTE the OSGI_ variant is equivalent
*c += 1;
});
EXPECT_EQ(3, count);
celix_arrayList_destroy(ids);
}
TEST_F(CelixBundleContextBundlesTestSuite, DoubleStopTest) {
long bndId = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
ASSERT_TRUE(bndId > 0);
ASSERT_TRUE(celix_bundleContext_isBundleInstalled(ctx, bndId));
bool called = celix_framework_useBundle(fw, false, bndId, nullptr, [](void *, const celix_bundle_t *bnd) {
ASSERT_EQ(CELIX_BUNDLE_STATE_ACTIVE, celix_bundle_getState(bnd));
ASSERT_EQ(OSGI_FRAMEWORK_BUNDLE_ACTIVE, celix_bundle_getState(bnd)); //NOTE the OSGI_ variant is equivalent
});
ASSERT_TRUE(called);
//first
celix_bundleContext_stopBundle(ctx, bndId);
called = celix_framework_useBundle(fw, false, bndId, nullptr, [](void *, const celix_bundle_t *bnd) {
ASSERT_EQ(CELIX_BUNDLE_STATE_RESOLVED, celix_bundle_getState(bnd));
ASSERT_EQ(OSGI_FRAMEWORK_BUNDLE_RESOLVED, celix_bundle_getState(bnd)); //NOTE the OSGI_ variant is equivalent
});
ASSERT_TRUE(called);
//second
celix_bundleContext_stopBundle(ctx, bndId);
called = celix_framework_useBundle(fw, false, bndId, nullptr, [](void *, const celix_bundle_t *bnd) {
ASSERT_EQ(CELIX_BUNDLE_STATE_RESOLVED, celix_bundle_getState(bnd));
ASSERT_EQ(OSGI_FRAMEWORK_BUNDLE_RESOLVED, celix_bundle_getState(bnd)); //NOTE the OSGI_ variant is equivalent
});
ASSERT_TRUE(called);
//first
celix_bundleContext_startBundle(ctx, bndId);
called = celix_framework_useBundle(fw, false, bndId, nullptr, [](void *, const celix_bundle_t *bnd) {
ASSERT_EQ(CELIX_BUNDLE_STATE_ACTIVE, celix_bundle_getState(bnd));
ASSERT_EQ(OSGI_FRAMEWORK_BUNDLE_ACTIVE, celix_bundle_getState(bnd)); //NOTE the OSGI_ variant is equivalent
});
ASSERT_TRUE(called);
//second
celix_bundleContext_startBundle(ctx, bndId);
called = celix_framework_useBundle(fw, false, bndId, nullptr, [](void *, const celix_bundle_t *bnd) {
ASSERT_EQ(CELIX_BUNDLE_STATE_ACTIVE, celix_bundle_getState(bnd));\
ASSERT_EQ(OSGI_FRAMEWORK_BUNDLE_ACTIVE, celix_bundle_getState(bnd)); //NOTE the OSGI_ variant is equivalent
});
ASSERT_TRUE(called);
}
TEST_F(CelixBundleContextBundlesTestSuite, TrackBundlesTest) {
struct data {
std::atomic<int> installedCount{0};
std::atomic<int> startedCount{0};
std::atomic<int> stoppedCount{0};
};
struct data data;
auto installed = [](void *handle, const bundle_t *bnd) {
auto *d = static_cast<struct data*>(handle);
EXPECT_TRUE(bnd != nullptr);
d->installedCount.fetch_add(1);
};
auto started = [](void *handle, const bundle_t *bnd) {
auto *d = static_cast<struct data*>(handle);
EXPECT_TRUE(bnd != nullptr);
d->startedCount.fetch_add(1);
};
auto stopped = [](void *handle, const bundle_t *bnd) {
auto *d = static_cast<struct data*>(handle);
if (bnd == nullptr) {
celix_logUtils_logToStdout("test", CELIX_LOG_LEVEL_ERROR, "bnd should not be null");
}
EXPECT_TRUE(bnd != nullptr);
d->stoppedCount.fetch_add(1);
};
celix_bundle_tracking_options_t opts{};
opts.callbackHandle = static_cast<void*>(&data);
opts.onInstalled = installed;
opts.onStarted = started;
opts.onStopped = stopped;
long bundleId1 = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
celix_framework_waitForEmptyEventQueue(fw);
EXPECT_TRUE(bundleId1 >= 0);
/*
* NOTE for bundles already installed (TEST_BND1) the callbacks are called on the
* thread of celix_bundleContext_trackBundlesWithOptions.
* For Bundles installed after the celix_bundleContext_trackBundlesWithOptions function
* the called are called on the Celix framework event queue thread.
*/
long trackerId = celix_bundleContext_trackBundlesWithOptions(ctx, &opts);
EXPECT_EQ(1, data.installedCount.load());
EXPECT_EQ(1, data.startedCount.load());
EXPECT_EQ(0, data.stoppedCount.load());
long bundleId2 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, true);
celix_framework_waitForEmptyEventQueue(fw);
EXPECT_TRUE(bundleId2 >= 0);
EXPECT_EQ(2, data.installedCount.load());
EXPECT_EQ(2, data.startedCount.load());
EXPECT_EQ(0, data.stoppedCount.load());
celix_bundleContext_uninstallBundle(ctx, bundleId2);
celix_framework_waitForEmptyEventQueue(fw);
EXPECT_EQ(2, data.installedCount.load());
EXPECT_EQ(2, data.startedCount.load());
EXPECT_EQ(1, data.stoppedCount.load());
long bundleId3 = celix_bundleContext_installBundle(ctx, TEST_BND3_LOC, true);
celix_framework_waitForEmptyEventQueue(fw);
EXPECT_TRUE(bundleId3 >= 0);
EXPECT_EQ(3, data.installedCount.load());
EXPECT_EQ(3, data.startedCount.load());
EXPECT_EQ(1, data.stoppedCount.load());
bundleId2 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, true);
celix_framework_waitForEmptyEventQueue(fw);
EXPECT_TRUE(bundleId2 >= 0);
EXPECT_EQ(4, data.installedCount.load());
EXPECT_EQ(4, data.startedCount.load());
EXPECT_EQ(1, data.stoppedCount.load());
celix_bundleContext_stopTracker(ctx, trackerId);
};
TEST_F(CelixBundleContextBundlesTestSuite, TrackBundlesTestAsync) {
struct data {
std::atomic<int> installedCount{0};
std::atomic<int> startedCount{0};
std::atomic<int> stoppedCount{0};
};
struct data data;
auto installed = [](void *handle, const bundle_t *bnd) {
auto *d = static_cast<struct data*>(handle);
EXPECT_TRUE(bnd != nullptr);
d->installedCount.fetch_add(1);
};
auto started = [](void *handle, const bundle_t *bnd) {
auto *d = static_cast<struct data*>(handle);
EXPECT_TRUE(bnd != nullptr);
d->startedCount.fetch_add(1);
};
auto stopped = [](void *handle, const bundle_t *bnd) {
auto *d = static_cast<struct data*>(handle);
if (bnd == nullptr) {
celix_logUtils_logToStdout("test", CELIX_LOG_LEVEL_ERROR, "bnd should not be null");
}
EXPECT_TRUE(bnd != nullptr);
d->stoppedCount.fetch_add(1);
};
celix_bundle_tracking_options_t opts{};
opts.callbackHandle = static_cast<void*>(&data);
opts.onInstalled = installed;
opts.onStarted = started;
opts.onStopped = stopped;
long bundleId1 = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
celix_framework_waitForEmptyEventQueue(fw);
EXPECT_TRUE(bundleId1 >= 0);
/*
* NOTE for bundles already installed (TEST_BND1) the callbacks are called on the
* thread of celix_bundleContext_trackBundlesWithOptions.
* For Bundles installed after the celix_bundleContext_trackBundlesWithOptions function
* the called are called on the Celix framework event queue thread.
*/
long trackerId = celix_bundleContext_trackBundlesWithOptionsAsync(ctx, &opts);
celix_bundleContext_waitForAsyncTracker(ctx, trackerId);
EXPECT_EQ(1, data.installedCount.load());
EXPECT_EQ(1, data.startedCount.load());
EXPECT_EQ(0, data.stoppedCount.load());
long bundleId2 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, true);
celix_framework_waitForEmptyEventQueue(fw);
EXPECT_TRUE(bundleId2 >= 0);
EXPECT_EQ(2, data.installedCount.load());
EXPECT_EQ(2, data.startedCount.load());
EXPECT_EQ(0, data.stoppedCount.load());
celix_bundleContext_uninstallBundle(ctx, bundleId2);
celix_framework_waitForEmptyEventQueue(fw);
EXPECT_EQ(2, data.installedCount.load());
EXPECT_EQ(2, data.startedCount.load());
EXPECT_EQ(1, data.stoppedCount.load());
long bundleId3 = celix_bundleContext_installBundle(ctx, TEST_BND3_LOC, true);
celix_framework_waitForEmptyEventQueue(fw);
EXPECT_TRUE(bundleId3 >= 0);
EXPECT_EQ(3, data.installedCount.load());
EXPECT_EQ(3, data.startedCount.load());
EXPECT_EQ(1, data.stoppedCount.load());
bundleId2 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, true);
celix_framework_waitForEmptyEventQueue(fw);
EXPECT_TRUE(bundleId2 >= 0);
EXPECT_EQ(4, data.installedCount.load());
EXPECT_EQ(4, data.startedCount.load());
EXPECT_EQ(1, data.stoppedCount.load());
celix_bundleContext_stopTrackerAsync(ctx, trackerId, nullptr, nullptr);
celix_bundleContext_waitForAsyncStopTracker(ctx, trackerId);
};
TEST_F(CelixBundleContextBundlesTestSuite, useBundlesConcurrentTest) {
struct data {
std::mutex mutex{};
std::condition_variable cond{};
bool inUse{false};
bool readyToExit{false};
};
struct data data{};
auto use = [](void *handle, const bundle_t *bnd) {
ASSERT_TRUE(bnd != nullptr);
struct data *d = static_cast<struct data*>(handle);
std::unique_lock<std::mutex> lock(d->mutex);
d->inUse = true;
d->cond.notify_all();
d->cond.wait(lock, [d]{return d->readyToExit;});
lock.unlock();
};
long bndId = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
auto call = [&] {
bool called = celix_bundleContext_useBundle(ctx, bndId, &data, use);
ASSERT_TRUE(called);
};
std::thread useThread{call};
std::thread uninstallThread{[&]{
std::unique_lock<std::mutex> lock(data.mutex);
data.cond.wait(lock, [&]{return data.inUse;});
lock.unlock();
std::cout << "trying to uninstall bundle ..." << std::endl;
celix_bundleContext_uninstallBundle(ctx, bndId);
std::cout << "done uninstalling bundle" << std::endl;
}};
//sleep 100 milli to give unregister a change to sink in
std::cout << "before sleep" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "after sleep" << std::endl;
std::cout << "setting readyToExitUseCall and notify" << std::endl;
std::unique_lock<std::mutex> lock(data.mutex);
data.readyToExit = true;
lock.unlock();
data.cond.notify_all();
useThread.join();
std::cout << "use thread joined" << std::endl;
uninstallThread.join();
std::cout << "uninstall thread joined" << std::endl;
};
TEST_F(CelixBundleContextBundlesTestSuite, BundleInfoTests) {
struct data {
int provideCount{0};
int requestedCount{0};
};
struct data data;
void (*updateCountFp)(void *, const celix_bundle_t*) = [](void *handle, const celix_bundle_t *bnd) {
auto *data = static_cast<struct data*>(handle);
auto *trackers = celix_bundle_listServiceTrackers(bnd);
auto *services = celix_bundle_listRegisteredServices(bnd);
data->requestedCount = celix_arrayList_size(trackers);
data->provideCount = celix_arrayList_size(services);
celix_bundle_destroyServiceTrackerList(trackers);
celix_bundle_destroyRegisteredServicesList(services);
};
bool called = celix_bundleContext_useBundle(ctx, 0, &data, updateCountFp);
EXPECT_TRUE(called);
EXPECT_EQ(0, data.provideCount);
EXPECT_EQ(0, data.requestedCount);
long svcId = celix_bundleContext_registerService(ctx, (void*)0x42, "NopService", NULL);
long trackerId = celix_bundleContext_trackServices(ctx, "AService", NULL, NULL, NULL);
called = celix_bundleContext_useBundle(ctx, 0, &data, updateCountFp);
EXPECT_TRUE(called);
EXPECT_EQ(1, data.provideCount);
EXPECT_EQ(1, data.requestedCount);
celix_bundleContext_unregisterService(ctx, svcId);
celix_bundleContext_stopTracker(ctx, trackerId);
}
TEST_F(CelixBundleContextBundlesTestSuite, StartStopBundleTrackerAsync) {
std::atomic<int> count{0};
auto cb = [](void* data) {
auto* c = static_cast<std::atomic<int>*>(data);
(*c)++;
};
celix_bundle_tracking_options_t opts{};
opts.trackerCreatedCallbackData = &count;
opts.trackerCreatedCallback = cb;
long trkId = celix_bundleContext_trackBundlesWithOptionsAsync(ctx, &opts);
EXPECT_GE(trkId, 0);
celix_bundleContext_waitForAsyncTracker(ctx, trkId);
EXPECT_EQ(count.load(), 1); //1x tracker started
celix_bundleContext_stopTrackerAsync(ctx, trkId, &count, cb);
celix_bundleContext_waitForAsyncStopTracker(ctx, trkId);
EXPECT_EQ(2, count.load()); //1x tracker started, 1x tracker stopped
}
TEST_F(CelixBundleContextBundlesTestSuite, TestBundleStateToString) {
const char* result = celix_bundleState_getName(CELIX_BUNDLE_STATE_UNKNOWN);
EXPECT_STREQ(result, "UNKNOWN");
result = celix_bundleState_getName(CELIX_BUNDLE_STATE_UNINSTALLED);
EXPECT_STREQ(result, "UNINSTALLED");
result = celix_bundleState_getName(CELIX_BUNDLE_STATE_INSTALLED);
EXPECT_STREQ(result, "INSTALLED");
result = celix_bundleState_getName(CELIX_BUNDLE_STATE_RESOLVED);
EXPECT_STREQ(result, "RESOLVED");
result = celix_bundleState_getName(CELIX_BUNDLE_STATE_STARTING);
EXPECT_STREQ(result, "STARTING");
result = celix_bundleState_getName(CELIX_BUNDLE_STATE_STOPPING);
EXPECT_STREQ(result, "STOPPING");
result = celix_bundleState_getName(CELIX_BUNDLE_STATE_ACTIVE);
EXPECT_STREQ(result, "ACTIVE");
result = celix_bundleState_getName(OSGI_FRAMEWORK_BUNDLE_UNKNOWN);
EXPECT_STREQ(result, "UNKNOWN");
result = celix_bundleState_getName(OSGI_FRAMEWORK_BUNDLE_UNINSTALLED);
EXPECT_STREQ(result, "UNINSTALLED");
result = celix_bundleState_getName(OSGI_FRAMEWORK_BUNDLE_INSTALLED);
EXPECT_STREQ(result, "INSTALLED");
result = celix_bundleState_getName(OSGI_FRAMEWORK_BUNDLE_RESOLVED);
EXPECT_STREQ(result, "RESOLVED");
result = celix_bundleState_getName(OSGI_FRAMEWORK_BUNDLE_STARTING);
EXPECT_STREQ(result, "STARTING");
result = celix_bundleState_getName(OSGI_FRAMEWORK_BUNDLE_STOPPING);
EXPECT_STREQ(result, "STOPPING");
result = celix_bundleState_getName(OSGI_FRAMEWORK_BUNDLE_ACTIVE);
EXPECT_STREQ(result, "ACTIVE");
result = celix_bundleState_getName((celix_bundle_state_e)444 /*invalid*/);
EXPECT_STREQ(result, "UNKNOWN");
}
TEST_F(CelixBundleContextBundlesTestSuite, UsingDmFunctionsWithInstalledBundlesTest) {
//Given a clean framework and a fw dependency manager
auto* dm = celix_bundleContext_getDependencyManager(ctx);
//Then all components are active (no bundles installed yet)
bool allActive = celix_dependencyManager_allComponentsActive(dm);
EXPECT_TRUE(allActive);
//When installing a bundle
long bndId = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, false);
EXPECT_GE(bndId, 0);
//Then all compnents are still active (bundle not started and no components in bundle)
allActive = celix_dependencyManager_allComponentsActive(dm);
EXPECT_TRUE(allActive);
//And a dm info can be received for the fw bundle
auto* info = celix_dependencyManager_createInfo(dm, CELIX_FRAMEWORK_BUNDLE_ID);
EXPECT_NE(info, nullptr);
celix_dependencyManager_destroyInfo(dm, info);
//But a valid dm info cannot be received for the installed bundle (not started yet)
info = celix_dependencyManager_createInfo(dm, bndId);
EXPECT_EQ(info, nullptr);
celix_dependencyManager_destroyInfo(dm, info); //should be safe
//And infos can be received and only contains dm info for 1 bundle (the framework bundle)
auto* infos = celix_dependencyManager_createInfos(dm);
EXPECT_NE(infos, nullptr);
EXPECT_EQ(1, celix_arrayList_size(infos));
celix_dependencyManager_destroyInfos(dm, infos);
}
class CelixBundleContextTempBundlesTestSuite : public ::testing::Test {
public:
celix_framework_t* fw = nullptr;
celix_bundle_context_t *ctx = nullptr;
celix_properties_t *properties = nullptr;
const char * const TEST_BND1_LOC = "" SIMPLE_TEST_BUNDLE1_LOCATION "";
CelixBundleContextTempBundlesTestSuite() {
properties = celix_properties_create();
celix_properties_setBool(properties, "LOGHELPER_ENABLE_STDOUT_FALLBACK", true);
celix_properties_setBool(properties, CELIX_FRAMEWORK_CACHE_USE_TMP_DIR, true);
fw = celix_frameworkFactory_createFramework(properties);
EXPECT_NE(fw, nullptr);
ctx = framework_getContext(fw);
EXPECT_NE(ctx, nullptr);
}
~CelixBundleContextTempBundlesTestSuite() override {
celix_frameworkFactory_destroyFramework(fw);
}
CelixBundleContextTempBundlesTestSuite(CelixBundleContextTempBundlesTestSuite&&) = delete;
CelixBundleContextTempBundlesTestSuite(const CelixBundleContextTempBundlesTestSuite&) = delete;
CelixBundleContextTempBundlesTestSuite& operator=(CelixBundleContextTempBundlesTestSuite&&) = delete;
CelixBundleContextTempBundlesTestSuite& operator=(const CelixBundleContextTempBundlesTestSuite&) = delete;
};
TEST_F(CelixBundleContextTempBundlesTestSuite, InstallABundleTest) {
long bndId = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
ASSERT_TRUE(bndId >= 0);
}