1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #define CPPUNIT_PLUGIN_EXPORTED_NAME cppunitTest_osl_process
24 #include <sal/types.h>
25 #include <cppunit/TestFixture.h>
26 #include <cppunit/extensions/HelperMacros.h>
27 #include <cppunit/plugin/TestPlugIn.h>
29 #include <osl/process.h>
30 #include <osl/file.hxx>
31 #include <osl/thread.h>
32 #include <rtl/ustring.hxx>
37 #include <osl/module.hxx>
38 #include <sal/macros.h>
40 #if defined HAVE_VALGRIND_HEADERS
41 #include <valgrind/valgrind.h>
43 #define RUNNING_ON_VALGRIND false
46 #if !defined(_WIN32) // Windows
59 # include <crt_externs.h>
60 # define environ (*_NSGetEnviron())
62 extern char** environ
;
70 static OUString
getExecutablePath()
73 osl::Module::getUrlFromAddress(
74 reinterpret_cast<oslGenericFunction
>(&getExecutablePath
), dirPath
);
75 dirPath
= dirPath
.copy( 0, dirPath
.lastIndexOf('/') );
76 dirPath
= OUString::Concat(dirPath
.subView( 0, dirPath
.lastIndexOf('/') + 1)) +
89 explicit exclude(const std::vector
<OString
>& exclude_list
)
91 for (auto& exclude_list_item
: exclude_list
)
92 exclude_list_
.push_back(env_var_name(exclude_list_item
));
95 bool operator() (const OString
& env_var
) const
97 return (exclude_list_
.end() !=
99 exclude_list_
.begin(),
101 env_var_name(env_var
)));
106 // extract the name from an environment variable
107 // that is given in the form "NAME=VALUE"
108 static OString
env_var_name(const OString
& env_var
)
110 sal_Int32 pos_equal_sign
=
111 env_var
.indexOf('=');
113 if (pos_equal_sign
!= -1)
114 return env_var
.copy(0, pos_equal_sign
);
120 std::vector
<OString
> exclude_list_
;
123 void tidy_container(std::vector
<OString
> &env_container
)
125 //sort them because there are no guarantees to ordering
126 std::sort(env_container
.begin(), env_container
.end());
127 if (RUNNING_ON_VALGRIND
)
131 env_container
.begin(), env_container
.end(),
132 [](OString
const & s
) {
133 return s
.startsWith("LD_PRELOAD=")
134 || s
.startsWith("VALGRIND_LIB="); }),
135 env_container
.end());
140 static void read_parent_environment(std::vector
<OString
>* env_container
)
142 for (int i
= 0; environ
[i
] != nullptr; i
++)
143 env_container
->push_back(OString(environ
[i
]));
144 tidy_container(*env_container
);
149 class Test_osl_executeProcess
: public CppUnit::TestFixture
151 const OUString env_param_
;
153 OUString temp_file_url_
;
154 OUString temp_file_path_
;
155 rtl_uString
* parameters_
[2];
156 static const int parameters_count_
= 2;
158 OUString suExecutableFileURL
;
163 Test_osl_executeProcess() :
164 env_param_(OUString("-env")), suCWD(getExecutablePath())
166 parameters_
[0] = env_param_
.pData
;
169 suExecutableFileURL
= suCWD
+ "/" "osl_process_child.exe";
171 suExecutableFileURL
= suCWD
+ "/" "osl_process_child";
175 virtual void setUp() override
177 temp_file_path_
= create_temp_file(temp_file_url_
);
178 parameters_
[1] = temp_file_path_
.pData
;
181 virtual void tearDown() override
183 osl::File::remove(temp_file_url_
);
186 OUString
create_temp_file(OUString
&temp_file_url
)
188 FileBase::RC rc
= FileBase::createTempFile(nullptr, nullptr, &temp_file_url
);
189 CPPUNIT_ASSERT_EQUAL_MESSAGE("createTempFile failed", FileBase::E_None
, rc
);
191 OUString temp_file_path
;
192 rc
= FileBase::getSystemPathFromFileURL(temp_file_url
, temp_file_path
);
193 CPPUNIT_ASSERT_EQUAL_MESSAGE("getSystemPathFromFileURL failed", FileBase::E_None
, rc
);
195 return temp_file_path
;
200 void read_child_environment(std::vector
<OString
>* env_container
)
202 OString temp_file_name
= OUStringToOString(OUString(
203 parameters_
[1]), osl_getThreadTextEncoding());
204 std::ifstream
file(temp_file_name
.getStr());
206 CPPUNIT_ASSERT_MESSAGE
208 "I/O error, cannot open child environment file",
214 while (std::getline(file
, line
, '\0'))
215 env_container
->push_back(OString(line
.c_str()));
216 tidy_container(*env_container
);
219 // environment of the child process that was
220 // started. The child process writes his
221 // environment into a file
222 void compare_environments()
224 std::vector
<OString
> parent_env
;
225 read_parent_environment(&parent_env
);
227 std::vector
<OString
> child_env
;
228 read_child_environment(&child_env
);
231 OString::number(parent_env
.size()) + "/"
232 + OString::number(child_env
.size()));
233 auto min
= std::min(parent_env
.size(), child_env
.size());
234 for (decltype(min
) i
= 0; i
!= min
; ++i
) {
235 CPPUNIT_ASSERT_EQUAL_MESSAGE(
236 msg
.getStr(), parent_env
[i
], child_env
[i
]);
238 if (parent_env
.size() != child_env
.size()) {
239 CPPUNIT_ASSERT_EQUAL_MESSAGE(
240 (parent_env
.size() >= child_env
.size()
241 ? parent_env
.back() : child_env
.back()).getStr(),
242 parent_env
.size(), child_env
.size());
246 // compare the equal environment parts and the
247 // different part of the child environment
248 bool compare_merged_environments(const std::vector
<OString
>& different_env_vars
)
250 std::vector
<OString
> parent_env
;
251 read_parent_environment(&parent_env
);
253 for (auto& env
: parent_env
)
254 std::cout
<< "initially parent env: " << env
<< "\n";
256 //remove the environment variables that we have changed
257 //in the child environment from the read parent environment
259 std::remove_if(parent_env
.begin(), parent_env
.end(), exclude(different_env_vars
)),
262 for (auto& env
: parent_env
)
263 std::cout
<< "stripped parent env: " << env
<< "\n";
265 //read the child environment and exclude the variables that
267 std::vector
<OString
> child_env
;
268 read_child_environment(&child_env
);
270 for (auto& env
: child_env
)
271 std::cout
<< "initial child env: " << env
<< "\n";
272 //partition the child environment into the variables that
273 //are different to the parent environment (they come first)
274 //and the variables that should be equal between parent
275 //and child environment
276 auto iter_logical_end
=
277 std::stable_partition(child_env
.begin(), child_env
.end(), exclude(different_env_vars
));
279 std::vector
<OString
> different_child_env_vars(child_env
.begin(), iter_logical_end
);
280 child_env
.erase(child_env
.begin(), iter_logical_end
);
282 for (auto& env
: child_env
)
283 std::cout
<< "stripped child env: " << env
<< "\n";
285 bool common_env_size_equals
= (parent_env
.size() == child_env
.size());
286 bool common_env_content_equals
= std::equal(child_env
.begin(), child_env
.end(), parent_env
.begin());
288 for (auto& env_var
: different_env_vars
)
289 std::cout
<< "different should be: " << env_var
<< "\n";
291 for (auto& env_var
: different_child_env_vars
)
292 std::cout
<< "different are: " << env_var
<< "\n";
294 bool different_env_size_equals
= (different_child_env_vars
.size() == different_env_vars
.size());
295 bool different_env_content_equals
=
296 std::equal(different_env_vars
.begin(), different_env_vars
.end(), different_child_env_vars
.begin());
298 return (common_env_size_equals
&& common_env_content_equals
&&
299 different_env_size_equals
&& different_env_content_equals
);
302 // test that parent and child process have the
303 // same environment when osl_executeProcess will
304 // be called without setting new environment
306 void osl_execProc_parent_equals_child_environment()
309 oslProcessError osl_error
= osl_executeProcess(
310 suExecutableFileURL
.pData
,
320 CPPUNIT_ASSERT_EQUAL_MESSAGE
322 "osl_createProcess failed",
323 osl_Process_E_None
, osl_error
326 osl_error
= ::osl_joinProcess(process
);
328 CPPUNIT_ASSERT_EQUAL_MESSAGE
330 "osl_joinProcess returned with failure",
331 osl_Process_E_None
, osl_error
334 osl_freeProcessHandle(process
);
336 compare_environments();
339 #define ENV1 "PAT=a:\\"
340 #define ENV2 "PATHb=b:\\"
341 #define ENV3 "Patha=c:\\"
342 #define ENV4 "Patha=d:\\"
344 void osl_execProc_merged_child_environment()
346 rtl_uString
* child_env
[4];
352 child_env
[0] = env1
.pData
;
353 child_env
[1] = env2
.pData
;
354 child_env
[2] = env3
.pData
;
355 child_env
[3] = env4
.pData
;
358 oslProcessError osl_error
= osl_executeProcess(
359 suExecutableFileURL
.pData
,
366 SAL_N_ELEMENTS(child_env
),
369 CPPUNIT_ASSERT_EQUAL_MESSAGE
371 "osl_createProcess failed",
372 osl_Process_E_None
, osl_error
375 osl_error
= ::osl_joinProcess(process
);
377 CPPUNIT_ASSERT_EQUAL_MESSAGE
379 "osl_joinProcess returned with failure",
380 osl_Process_E_None
, osl_error
383 osl_freeProcessHandle(process
);
385 std::vector
<OString
> different_child_env_vars
392 CPPUNIT_ASSERT_MESSAGE
394 "osl_execProc_merged_child_environment",
395 compare_merged_environments(different_child_env_vars
)
401 void osl_execProc_test_batch()
405 OUString suBatch
= suCWD
+ "/batch.bat";
407 OUString suBatch
= suCWD
+ "/batch.sh";
409 oslProcessError osl_error
= osl_executeProcess(
420 CPPUNIT_ASSERT_EQUAL_MESSAGE
422 "osl_createProcess failed",
423 osl_Process_E_None
, osl_error
426 osl_error
= ::osl_joinProcess(process
);
428 CPPUNIT_ASSERT_EQUAL_MESSAGE
430 "osl_joinProcess returned with failure",
431 osl_Process_E_None
, osl_error
434 osl_freeProcessHandle(process
);
437 CPPUNIT_TEST_SUITE(Test_osl_executeProcess
);
438 //TODO: Repair these (at least under Windows)
440 CPPUNIT_TEST(osl_execProc_parent_equals_child_environment
);
441 CPPUNIT_TEST(osl_execProc_merged_child_environment
);
443 CPPUNIT_TEST(osl_execProc_test_batch
);
444 ///TODO: Repair test (or tested function ;-) - test fails.
445 CPPUNIT_TEST_SUITE_END();
448 CPPUNIT_TEST_SUITE_REGISTRATION(Test_osl_executeProcess
);
450 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */