diff options
Diffstat (limited to '')
-rw-r--r-- | sal/qa/osl/process/osl_process.cxx | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/sal/qa/osl/process/osl_process.cxx b/sal/qa/osl/process/osl_process.cxx new file mode 100644 index 000000000..4555a0d83 --- /dev/null +++ b/sal/qa/osl/process/osl_process.cxx @@ -0,0 +1,448 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#ifdef IOS +#define CPPUNIT_PLUGIN_EXPORTED_NAME cppunitTest_osl_process +#endif + +#include <sal/types.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> + +#include <osl/process.h> +#include <osl/file.hxx> +#include <osl/thread.h> +#include <rtl/ustring.hxx> +#include <signal.h> + +#include <stdio.h> +#include <stdlib.h> +#include <osl/module.hxx> +#include <sal/macros.h> + +#if defined HAVE_VALGRIND_HEADERS +#include <valgrind/valgrind.h> +#elif !defined _WIN32 +#define RUNNING_ON_VALGRIND false +#endif + +#if !defined(_WIN32) // Windows +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include <vector> +#include <algorithm> +#include <iterator> +#include <string> + +#ifdef UNX +#if defined( MACOSX ) +# include <crt_externs.h> +# define environ (*_NSGetEnviron()) +# else + extern char** environ; +# endif +#endif + +using namespace osl; + +/** get binary Path. +*/ +static OUString getExecutablePath() +{ + OUString dirPath; + osl::Module::getUrlFromAddress( + reinterpret_cast<oslGenericFunction>(&getExecutablePath), dirPath); + dirPath = dirPath.copy( 0, dirPath.lastIndexOf('/') ); + dirPath = dirPath.copy( 0, dirPath.lastIndexOf('/') + 1) + + "Executable"; + return dirPath; +} + +#if !defined _WIN32 + +namespace { + +class exclude +{ +public: + + explicit exclude(const std::vector<OString>& exclude_list) + { + for (auto& exclude_list_item : exclude_list) + exclude_list_.push_back(env_var_name(exclude_list_item)); + } + + bool operator() (const OString& env_var) const + { + return (exclude_list_.end() != + std::find( + exclude_list_.begin(), + exclude_list_.end(), + env_var_name(env_var))); + } + +private: + + // extract the name from an environment variable + // that is given in the form "NAME=VALUE" + static OString env_var_name(const OString& env_var) + { + sal_Int32 pos_equal_sign = + env_var.indexOf('='); + + if (pos_equal_sign != -1) + return env_var.copy(0, pos_equal_sign); + + return OString(); + } + +private: + std::vector<OString> exclude_list_; +}; + + void tidy_container(std::vector<OString> &env_container) + { + //sort them because there are no guarantees to ordering + std::sort(env_container.begin(), env_container.end()); + if (RUNNING_ON_VALGRIND) + { + env_container.erase( + std::remove_if( + env_container.begin(), env_container.end(), + [](OString const & s) { + return s.startsWith("LD_PRELOAD=") + || s.startsWith("VALGRIND_LIB="); }), + env_container.end()); + } + } +} + + static void read_parent_environment(std::vector<OString>* env_container) + { + for (int i = 0; environ[i] != nullptr; i++) + env_container->push_back(OString(environ[i])); + tidy_container(*env_container); + } + +#endif + +class Test_osl_executeProcess : public CppUnit::TestFixture +{ + const OUString env_param_; + + OUString temp_file_url_; + OUString temp_file_path_; + rtl_uString* parameters_[2]; + static const int parameters_count_ = 2; + OUString suCWD; + OUString suExecutableFileURL; + +public: + + // ctor + Test_osl_executeProcess() : + env_param_(OUString("-env")), suCWD(getExecutablePath()) + { + parameters_[0] = env_param_.pData; + +#if defined(_WIN32) + suExecutableFileURL = suCWD + "/" "osl_process_child.exe"; +#else + suExecutableFileURL = suCWD + "/" "osl_process_child"; +#endif + } + + virtual void setUp() override + { + temp_file_path_ = create_temp_file(temp_file_url_); + parameters_[1] = temp_file_path_.pData; + } + + virtual void tearDown() override + { + osl::File::remove(temp_file_url_); + } + + OUString create_temp_file(OUString &temp_file_url) + { + FileBase::RC rc = FileBase::createTempFile(nullptr, nullptr, &temp_file_url); + CPPUNIT_ASSERT_EQUAL_MESSAGE("createTempFile failed", FileBase::E_None, rc); + + OUString temp_file_path; + rc = FileBase::getSystemPathFromFileURL(temp_file_url, temp_file_path); + CPPUNIT_ASSERT_EQUAL_MESSAGE("getSystemPathFromFileURL failed", FileBase::E_None, rc); + + return temp_file_path; + } + +#if !defined _WIN32 + + void read_child_environment(std::vector<OString>* env_container) + { + OString temp_file_name = OUStringToOString(OUString( + parameters_[1]), osl_getThreadTextEncoding()); + std::ifstream file(temp_file_name.getStr()); + + CPPUNIT_ASSERT_MESSAGE + ( + "I/O error, cannot open child environment file", + file.is_open() + ); + + std::string line; + line.reserve(1024); + while (std::getline(file, line, '\0')) + env_container->push_back(OString(line.c_str())); + tidy_container(*env_container); + } + + // environment of the child process that was + // started. The child process writes his + // environment into a file + void compare_environments() + { + std::vector<OString> parent_env; + read_parent_environment(&parent_env); + + std::vector<OString> child_env; + read_child_environment(&child_env); + + OString msg( + OString::number(parent_env.size()) + "/" + + OString::number(child_env.size())); + auto min = std::min(parent_env.size(), child_env.size()); + for (decltype(min) i = 0; i != min; ++i) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + msg.getStr(), parent_env[i], child_env[i]); + } + if (parent_env.size() != child_env.size()) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + (parent_env.size() >= child_env.size() + ? parent_env.back() : child_env.back()).getStr(), + parent_env.size(), child_env.size()); + } + } + + // compare the equal environment parts and the + // different part of the child environment + bool compare_merged_environments(const std::vector<OString>& different_env_vars) + { + std::vector<OString> parent_env; + read_parent_environment(&parent_env); + + for (auto& env : parent_env) + std::cout << "initially parent env: " << env << "\n"; + + //remove the environment variables that we have changed + //in the child environment from the read parent environment + parent_env.erase( + std::remove_if(parent_env.begin(), parent_env.end(), exclude(different_env_vars)), + parent_env.end()); + + for (auto& env : parent_env) + std::cout << "stripped parent env: " << env << "\n"; + + //read the child environment and exclude the variables that + //are different + std::vector<OString> child_env; + read_child_environment(&child_env); + + for (auto& env : child_env) + std::cout << "initial child env: " << env << "\n"; + //partition the child environment into the variables that + //are different to the parent environment (they come first) + //and the variables that should be equal between parent + //and child environment + auto iter_logical_end = + std::stable_partition(child_env.begin(), child_env.end(), exclude(different_env_vars)); + + std::vector<OString> different_child_env_vars(child_env.begin(), iter_logical_end); + child_env.erase(child_env.begin(), iter_logical_end); + + for (auto& env : child_env) + std::cout << "stripped child env: " << env << "\n"; + + bool common_env_size_equals = (parent_env.size() == child_env.size()); + bool common_env_content_equals = std::equal(child_env.begin(), child_env.end(), parent_env.begin()); + + for (auto& env_var : different_env_vars) + std::cout << "different should be: " << env_var << "\n"; + + for (auto& env_var : different_child_env_vars) + std::cout << "different are: " << env_var << "\n"; + + bool different_env_size_equals = (different_child_env_vars.size() == different_env_vars.size()); + bool different_env_content_equals = + std::equal(different_env_vars.begin(), different_env_vars.end(), different_child_env_vars.begin()); + + return (common_env_size_equals && common_env_content_equals && + different_env_size_equals && different_env_content_equals); + } + + // test that parent and child process have the + // same environment when osl_executeProcess will + // be called without setting new environment + // variables + void osl_execProc_parent_equals_child_environment() + { + oslProcess process; + oslProcessError osl_error = osl_executeProcess( + suExecutableFileURL.pData, + parameters_, + parameters_count_, + osl_Process_NORMAL, + nullptr, + suCWD.pData, + nullptr, + 0, + &process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_createProcess failed", + osl_Process_E_None, osl_error + ); + + osl_error = ::osl_joinProcess(process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_joinProcess returned with failure", + osl_Process_E_None, osl_error + ); + + osl_freeProcessHandle(process); + + compare_environments(); + } + + #define ENV1 "PAT=a:\\" + #define ENV2 "PATHb=b:\\" + #define ENV3 "Patha=c:\\" + #define ENV4 "Patha=d:\\" + + void osl_execProc_merged_child_environment() + { + rtl_uString* child_env[4]; + OUString env1(ENV1); + OUString env2(ENV2); + OUString env3(ENV3); + OUString env4(ENV4); + + child_env[0] = env1.pData; + child_env[1] = env2.pData; + child_env[2] = env3.pData; + child_env[3] = env4.pData; + + oslProcess process; + oslProcessError osl_error = osl_executeProcess( + suExecutableFileURL.pData, + parameters_, + parameters_count_, + osl_Process_NORMAL, + nullptr, + suCWD.pData, + child_env, + SAL_N_ELEMENTS(child_env), + &process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_createProcess failed", + osl_Process_E_None, osl_error + ); + + osl_error = ::osl_joinProcess(process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_joinProcess returned with failure", + osl_Process_E_None, osl_error + ); + + osl_freeProcessHandle(process); + + std::vector<OString> different_child_env_vars; + different_child_env_vars.push_back(ENV1); + different_child_env_vars.push_back(ENV2); + different_child_env_vars.push_back(ENV4); + + CPPUNIT_ASSERT_MESSAGE + ( + "osl_execProc_merged_child_environment", + compare_merged_environments(different_child_env_vars) + ); + } + +#endif + + void osl_execProc_test_batch() + { + oslProcess process; +#if defined(_WIN32) + OUString suBatch = suCWD + "/batch.bat"; +#else + OUString suBatch = suCWD + "/batch.sh"; +#endif + oslProcessError osl_error = osl_executeProcess( + suBatch.pData, + nullptr, + 0, + osl_Process_NORMAL, + nullptr, + suCWD.pData, + nullptr, + 0, + &process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_createProcess failed", + osl_Process_E_None, osl_error + ); + + osl_error = ::osl_joinProcess(process); + + CPPUNIT_ASSERT_EQUAL_MESSAGE + ( + "osl_joinProcess returned with failure", + osl_Process_E_None, osl_error + ); + + osl_freeProcessHandle(process); + } + + CPPUNIT_TEST_SUITE(Test_osl_executeProcess); + //TODO: Repair these (at least under Windows) +#if !defined(_WIN32) + CPPUNIT_TEST(osl_execProc_parent_equals_child_environment); + CPPUNIT_TEST(osl_execProc_merged_child_environment); +#endif + CPPUNIT_TEST(osl_execProc_test_batch); + ///TODO: Repair test (or tested function ;-) - test fails. + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(Test_osl_executeProcess); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |