/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #if defined HAVE_VALGRIND_HEADERS #include #elif !defined _WIN32 #define RUNNING_ON_VALGRIND false #endif #if !defined(_WIN32) // Windows #include #endif #include #include #include #include #include #include #ifdef UNX #if defined( MACOSX ) # include # 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(&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& 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 exclude_list_; }; void tidy_container(std::vector &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* 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* 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 parent_env; read_parent_environment(&parent_env); std::vector 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& different_env_vars) { std::vector 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 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 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 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: */