blob: de57a0211dec777df3c6767d19c55306a9ed601f [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 <csignal>
#include <thread>
#include <future>
#include <vector>
#include <string>
#include <pthread.h>
#include "celix_constants.h"
#include "celix_launcher.h"
#include "celix_launcher_private.h"
#include "celix_stdlib_cleanup.h"
#include "celix_utils.h"
#define LAUNCH_WAIT_TIMEOUT 200
class CelixLauncherTestSuite : public ::testing::Test {
public:
static std::future<void> launchInThread(const std::vector<std::string>& args, celix_properties_t* props, int expectedRc) {
std::string propsStr{};
if (props) {
celix_autofree char* str;
EXPECT_EQ(CELIX_SUCCESS, celix_properties_saveToString(props, 0, &str));
propsStr = str;
celix_properties_destroy(props);
}
return std::async(std::launch::async, [args, propsStr, expectedRc] {
char** argv = new char*[args.size()];
for (size_t i = 0; i < args.size(); ++i) {
argv[i] = celix_utils_strdup(args[i].c_str());
}
const char* str = propsStr.empty() ? nullptr : propsStr.c_str();
auto rc = celix_launcher_launchAndWait((int)args.size(), argv, str);
EXPECT_EQ(rc, expectedRc);
for (size_t i = 0; i < args.size(); ++i) {
free(argv[i]);
}
delete [] argv;
});
}
};
TEST_F(CelixLauncherTestSuite, PrintPropertiesTest) {
//launch framework with bundle configured to start
//Given a properties set with 1 bundle configured for start and 1 bundle configured for install
auto* props = celix_properties_create();
ASSERT_TRUE(props != nullptr);
celix_properties_set(props, CELIX_AUTO_START_0, SIMPLE_TEST_BUNDLE1_LOCATION);
celix_properties_set(props, CELIX_AUTO_INSTALL, SIMPLE_TEST_BUNDLE2_LOCATION);
//When I run the celixLauncher with "-p" argument and expect a 0 return code
auto future = launchInThread({"programName", "-p"}, props, 0);
//Then the launch will print the properties and exit
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}
TEST_F(CelixLauncherTestSuite, PrintEmptyPropertiesTest) {
//launch framework with bundle configured to start
//Given an empty properties
auto* props = celix_properties_create();
ASSERT_TRUE(props != nullptr);
//When I run the celixLauncher with "-p" argument and expect a 0 return code
auto future = launchInThread({"programName", "-p", "empty.properties"}, props, 0);
//Then the launch will print the properties and exit
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}
TEST_F(CelixLauncherTestSuite, PrintHelpTest) {
// props should be released by launcher
auto* props = celix_properties_create();
ASSERT_TRUE(props != nullptr);
celix_properties_set(props, CELIX_AUTO_START_0, SIMPLE_TEST_BUNDLE1_LOCATION);
celix_properties_set(props, CELIX_AUTO_INSTALL, SIMPLE_TEST_BUNDLE2_LOCATION);
//When I run the celixLauncher with "-h" argument and expect a 0 return code
auto future = launchInThread({"programName", "-h"}, props, 0);
//Then the launch will print the help info and exit
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}
TEST_F(CelixLauncherTestSuite, ExtractBundlesTest) {
//launch framework with bundle configured to start
//Given a properties set with 2 bundles configured for start
auto* props = celix_properties_create();
ASSERT_TRUE(props != nullptr);
celix_properties_set(props, CELIX_AUTO_START_0, SIMPLE_TEST_BUNDLE1_LOCATION);
celix_properties_set(props, CELIX_AUTO_INSTALL, SIMPLE_TEST_BUNDLE2_LOCATION);
celix_autofree char* propsStr;
ASSERT_EQ(CELIX_SUCCESS, celix_properties_saveToString(props, 0, &propsStr));
//When I run the celixLauncher with "-c" argument and expect a 0 return code
auto future = launchInThread({"programName", "-c"}, props, 0);
//Then the launch print the result of the extracted bundles and exit
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}
TEST_F(CelixLauncherTestSuite, ExtractNonexistentBundlesTest) {
//launch framework with bundle configured to start
//Given a properties set with a nonexistent bundle configured for start
auto* props = celix_properties_create();
ASSERT_TRUE(props != nullptr);
auto start = std::string{} + "nonexistent";
celix_properties_set(props, CELIX_AUTO_START_0, start.c_str());
celix_autofree char* propsStr;
ASSERT_EQ(CELIX_SUCCESS, celix_properties_saveToString(props, 0, &propsStr));
//When I run the celixLauncher with "-c" argument and expect a 1 return code
auto future = launchInThread({"programName", "-c"}, props, 1);
//Then the launch will exit
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}
TEST_F(CelixLauncherTestSuite, ExtractBundlesIntoTempTest) {
//launch framework with bundle configured to start
//Given a properties set with 2 bundles configured for start
//And the CELIX_FRAMEWORK_CACHE_USE_TMP_DIR is set to true
auto* props = celix_properties_create();
ASSERT_TRUE(props != nullptr);
auto start = std::string{} + SIMPLE_TEST_BUNDLE1_LOCATION + "," + SIMPLE_TEST_BUNDLE2_LOCATION;
celix_properties_set(props, CELIX_AUTO_START_0, start.c_str());
celix_properties_setBool(props, CELIX_FRAMEWORK_CACHE_USE_TMP_DIR, true);
//When I run the celixLauncher with "-c" argument and expect a 1 return code, because
//extracting bundles to a temp dir is not supported.
auto future = launchInThread({"programName", "-c"}, props, 1);
//Then the launch print the result of the extracted bundles and exit
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}
TEST_F(CelixLauncherTestSuite, LaunchCelixTest) {
//launch framework with bundle configured to start
//Given a properties set with 2 bundles configured for start
auto* props = celix_properties_create();
ASSERT_TRUE(props != nullptr);
auto start = std::string{} + SIMPLE_TEST_BUNDLE1_LOCATION + " " + SIMPLE_TEST_BUNDLE2_LOCATION;
celix_properties_set(props, CELIX_AUTO_START_0, start.c_str());
celix_autofree char* propsStr;
ASSERT_EQ(CELIX_SUCCESS, celix_properties_saveToString(props, 0, &propsStr));
//When I run the celixLauncher with "-c" argument
auto future = launchInThread({"programName", "config.properties"}, props, 0);
//Then the wait for the launch to finish will time out (framework is running)
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::timeout);
//When I stop the framework
celix_launcher_triggerStop();
//Then the launch will exit
status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}
TEST_F(CelixLauncherTestSuite, LaunchWithInvalidConfigPropertiesTest) {
//When launching the framework with an invalid config properties file
auto future = launchInThread({"programName", "invalid.properties"}, nullptr, 1);
//Then the launch will exit
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}
TEST_F(CelixLauncherTestSuite, LaunchWithInvalidConfigProperties2Test) {
//When launching the framework with a properties set with a negative shutdown period
auto* props = celix_properties_create();
ASSERT_TRUE(props != nullptr);
celix_properties_set(props, CELIX_AUTO_START_0, IMMEDIATE_STOP_BUNDLE_LOCATION);
celix_properties_setDouble(props, CELIX_LAUNCHER_SHUTDOWN_PERIOD_IN_SECONDS, -1.0);
auto future = launchInThread({"programName"}, props, 0);
//Then launch will exit
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}
TEST_F(CelixLauncherTestSuite, StopLauncherWithSignalTest) {
auto* props = celix_properties_create();
// When launching the framework
celix_properties_setDouble(props, CELIX_LAUNCHER_SHUTDOWN_PERIOD_IN_SECONDS, 0.01);
auto future = launchInThread({"programName"}, props, 0);
// Then the launch will not exit, because the framework is running
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::timeout);
// When I stop the framework by sending a SIGINT signal
int signal = SIGINT;
EXPECT_EQ(0, pthread_kill(pthread_self(), signal));
// Then the launch will exit
status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}
TEST_F(CelixLauncherTestSuite, DoubleStartAndStopLauncherTest) {
// When launching the framework
auto future = launchInThread({"programName"}, nullptr, 0);
// Then the launch will not exit, because the framework is running
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::timeout);
//When I try start the framework again and expect a 1 return code (framework already running)
auto future2 = launchInThread({"programName"}, nullptr, 1);
// The launch will exit
status = future2.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
// When I stop the framework
celix_launcher_triggerStop();
// Then the launch will exit
status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
// When I try to stop the framework again
celix_launcher_triggerStop();
// Then nothing happens
}
TEST_F(CelixLauncherTestSuite, StartWithInvalidEmbeddedPropertiesTest) {
//When launching the framework with an invalid embedded properties
auto rc = celix_launcher_launchAndWait(0, nullptr, "invalid props");
//Then the launch will exit with a return code of 1
EXPECT_EQ(1, rc);
}
TEST_F(CelixLauncherTestSuite, StartWithInvalidArgumentsTest) {
//When launching the framework with invalid arguments and expect a 1 return code
auto future = launchInThread({"programName", "config1.properties", "config2.properties"}, nullptr, 1);
// The launch will exit
auto status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
//When launching the framework with invalid arguments and expect a 1 return code
future = launchInThread({"programName", "-c", "-p"}, nullptr, 1);
// The launch will exit
status = future.wait_for(std::chrono::milliseconds{LAUNCH_WAIT_TIMEOUT});
EXPECT_EQ(status, std::future_status::ready);
}