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 WNT ) // Windows
62 # include <crt_externs.h>
63 # define environ (*_NSGetEnviron())
65 extern char** environ
;
70 const rtl::OUString
EXECUTABLE_NAME ("osl_process_child.exe");
72 const rtl::OUString
EXECUTABLE_NAME ("osl_process_child");
77 using ::rtl::OUString
;
78 using ::rtl::OUStringToOString
;
83 inline ::rtl::OUString
getExecutablePath()
85 ::rtl::OUString dirPath
;
86 osl::Module::getUrlFromAddress(
87 reinterpret_cast<oslGenericFunction
>(&getExecutablePath
), dirPath
);
88 dirPath
= dirPath
.copy( 0, dirPath
.lastIndexOf('/') );
89 dirPath
= dirPath
.copy( 0, dirPath
.lastIndexOf('/') + 1);
90 dirPath
+= rtl::OUString("Executable");
94 //rtl::OUString CWD = getExecutablePath();
96 typedef std::vector
<OString
> string_container_t
;
97 typedef string_container_t::const_iterator string_container_const_iter_t
;
98 typedef string_container_t::iterator string_container_iter_t
;
100 class exclude
: public std::unary_function
<OString
, bool>
104 exclude(const string_container_t
& exclude_list
)
106 string_container_const_iter_t iter
= exclude_list
.begin();
107 string_container_const_iter_t iter_end
= exclude_list
.end();
108 for (/**/; iter
!= iter_end
; ++iter
)
109 exclude_list_
.push_back(env_var_name(*iter
));
112 bool operator() (const OString
& env_var
) const
114 return (exclude_list_
.end() !=
116 exclude_list_
.begin(),
118 env_var_name(env_var
)));
123 // extract the name from an environment variable
124 // that is given in the form "NAME=VALUE"
125 static OString
env_var_name(const OString
& env_var
)
127 sal_Int32 pos_equal_sign
=
128 env_var
.indexOf('=');
130 if (-1 != pos_equal_sign
)
131 return env_var
.copy(0, pos_equal_sign
);
137 string_container_t exclude_list_
;
142 void tidy_container(string_container_t
&env_container
)
144 //sort them because there are no guarantees to ordering
145 std::sort(env_container
.begin(), env_container
.end());
146 if (RUNNING_ON_VALGRIND
)
150 env_container
.begin(), env_container
.end(),
151 [](OString
const & s
) {
152 return s
.startsWith("LD_PRELOAD=")
153 || s
.startsWith("VALGRIND_LIB="); }),
154 env_container
.end());
160 void read_parent_environment(string_container_t
* env_container
)
162 LPTSTR env
= reinterpret_cast<LPTSTR
>(GetEnvironmentStrings());
165 while (size_t l
= _tcslen(p
))
167 env_container
->push_back(OString(p
));
170 FreeEnvironmentStrings(env
);
171 tidy_container(*env_container
);
174 void read_parent_environment(string_container_t
* env_container
)
176 for (int i
= 0; NULL
!= environ
[i
]; i
++)
177 env_container
->push_back(OString(environ
[i
]));
178 tidy_container(*env_container
);
182 class Test_osl_executeProcess
: public CppUnit::TestFixture
184 const OUString env_param_
;
186 OUString temp_file_url_
;
187 OUString temp_file_path_
;
188 rtl_uString
* parameters_
[2];
189 int parameters_count_
;
191 OUString suExecutableFileURL
;
196 Test_osl_executeProcess() :
197 env_param_(OUString("-env")),
200 parameters_
[0] = env_param_
.pData
;
201 suCWD
= getExecutablePath();
202 suExecutableFileURL
= suCWD
;
203 suExecutableFileURL
+= rtl::OUString("/");
204 suExecutableFileURL
+= EXECUTABLE_NAME
;
207 virtual void setUp() SAL_OVERRIDE
209 temp_file_path_
= create_temp_file(temp_file_url_
);
210 parameters_
[1] = temp_file_path_
.pData
;
213 virtual void tearDown() SAL_OVERRIDE
215 osl::File::remove(temp_file_url_
);
218 OUString
create_temp_file(OUString
&temp_file_url
)
220 FileBase::RC rc
= FileBase::createTempFile(0, 0, &temp_file_url
);
221 CPPUNIT_ASSERT_EQUAL_MESSAGE("createTempFile failed", FileBase::E_None
, rc
);
223 OUString temp_file_path
;
224 rc
= FileBase::getSystemPathFromFileURL(temp_file_url
, temp_file_path
);
225 CPPUNIT_ASSERT_EQUAL_MESSAGE("getSystemPathFromFileURL failed", FileBase::E_None
, rc
);
227 return temp_file_path
;
230 void read_child_environment(string_container_t
* env_container
)
232 OString temp_file_name
= OUStringToOString(OUString(
233 parameters_
[1]), osl_getThreadTextEncoding());
234 std::ifstream
file(temp_file_name
.getStr());
236 CPPUNIT_ASSERT_MESSAGE
238 "I/O error, cannot open child environment file",
244 while (std::getline(file
, line
, '\0'))
245 env_container
->push_back(OString(line
.c_str()));
246 tidy_container(*env_container
);
249 // environment of the child process that was
250 // started. The child process writes his
251 // environment into a file
252 void compare_environments()
254 string_container_t parent_env
;
255 read_parent_environment(&parent_env
);
257 string_container_t child_env
;
258 read_child_environment(&child_env
);
261 OString::number(parent_env
.size()) + "/"
262 + OString::number(child_env
.size()));
263 auto min
= std::min(parent_env
.size(), child_env
.size());
264 for (decltype(min
) i
= 0; i
!= min
; ++i
) {
265 CPPUNIT_ASSERT_EQUAL_MESSAGE(
266 msg
.getStr(), parent_env
[i
], child_env
[i
]);
268 if (parent_env
.size() != child_env
.size()) {
269 CPPUNIT_ASSERT_EQUAL_MESSAGE(
270 (parent_env
.size() >= child_env
.size()
271 ? parent_env
.back() : child_env
.back()).getStr(),
272 parent_env
.size(), child_env
.size());
276 // compare the equal environment parts and the
277 // different part of the child environment
278 bool compare_merged_environments(const string_container_t
& different_env_vars
)
280 string_container_t parent_env
;
281 read_parent_environment(&parent_env
);
283 #if OSL_DEBUG_LEVEL > 1
284 for (string_container_t::const_iterator iter
= parent_env
.begin(), end
= parent_env
.end(); iter
!= end
; ++iter
)
285 std::cerr
<< "initially parent env: " << *iter
<< std::endl
;
288 //remove the environment variables that we have changed
289 //in the child environment from the read parent environment
291 std::remove_if(parent_env
.begin(), parent_env
.end(), exclude(different_env_vars
)),
294 #if OSL_DEBUG_LEVEL > 1
295 for (string_container_t::const_iterator iter
= parent_env
.begin(), end
= parent_env
.end(); iter
!= end
; ++iter
)
296 std::cerr
<< "stripped parent env: " << *iter
<< std::endl
;
299 //read the child environment and exclude the variables that
301 string_container_t child_env
;
302 read_child_environment(&child_env
);
304 #if OSL_DEBUG_LEVEL > 1
305 for (string_container_t::const_iterator iter
= child_env
.begin(), end
= child_env
.end(); iter
!= end
; ++iter
)
306 std::cerr
<< "initial child env: " << *iter
<< std::endl
;
308 //partition the child environment into the variables that
309 //are different to the parent environment (they come first)
310 //and the variables that should be equal between parent
311 //and child environment
312 string_container_iter_t iter_logical_end
=
313 std::stable_partition(child_env
.begin(), child_env
.end(), exclude(different_env_vars
));
315 string_container_t
different_child_env_vars(child_env
.begin(), iter_logical_end
);
316 child_env
.erase(child_env
.begin(), iter_logical_end
);
318 #if OSL_DEBUG_LEVEL > 1
319 for (string_container_t::const_iterator iter
= child_env
.begin(), end
= child_env
.end(); iter
!= end
; ++iter
)
320 std::cerr
<< "stripped child env: " << *iter
<< std::endl
;
323 bool common_env_size_equals
= (parent_env
.size() == child_env
.size());
324 bool common_env_content_equals
= std::equal(child_env
.begin(), child_env
.end(), parent_env
.begin());
326 #if OSL_DEBUG_LEVEL > 1
327 for (string_container_t::const_iterator iter
= different_env_vars
.begin(), end
= different_env_vars
.end(); iter
!= end
; ++iter
)
328 std::cerr
<< "different should be: " << *iter
<< std::endl
;
331 #if OSL_DEBUG_LEVEL > 1
332 for (string_container_t::const_iterator iter
= different_child_env_vars
.begin(), end
= different_child_env_vars
.end(); iter
!= end
; ++iter
)
333 std::cerr
<< "different are: " << *iter
<< std::endl
;
336 bool different_env_size_equals
= (different_child_env_vars
.size() == different_env_vars
.size());
337 bool different_env_content_equals
=
338 std::equal(different_env_vars
.begin(), different_env_vars
.end(), different_child_env_vars
.begin());
340 return (common_env_size_equals
&& common_env_content_equals
&&
341 different_env_size_equals
&& different_env_content_equals
);
344 // test that parent and child process have the
345 // same environment when osl_executeProcess will
346 // be called with out setting new environment
348 void osl_execProc_parent_equals_child_environment()
351 oslProcessError osl_error
= osl_executeProcess(
352 suExecutableFileURL
.pData
,
362 CPPUNIT_ASSERT_EQUAL_MESSAGE
364 "osl_createProcess failed",
365 osl_Process_E_None
, osl_error
368 osl_error
= ::osl_joinProcess(process
);
370 CPPUNIT_ASSERT_EQUAL_MESSAGE
372 "osl_joinProcess returned with failure",
373 osl_Process_E_None
, osl_error
376 osl_freeProcessHandle(process
);
378 compare_environments();
381 #define ENV1 "PAT=a:\\"
382 #define ENV2 "PATHb=b:\\"
383 #define ENV3 "Patha=c:\\"
384 #define ENV4 "Patha=d:\\"
386 void osl_execProc_merged_child_environment()
388 rtl_uString
* child_env
[4];
394 child_env
[0] = env1
.pData
;
395 child_env
[1] = env2
.pData
;
396 child_env
[2] = env3
.pData
;
397 child_env
[3] = env4
.pData
;
400 oslProcessError osl_error
= osl_executeProcess(
401 suExecutableFileURL
.pData
,
408 SAL_N_ELEMENTS(child_env
),
411 CPPUNIT_ASSERT_EQUAL_MESSAGE
413 "osl_createProcess failed",
414 osl_Process_E_None
, osl_error
417 osl_error
= ::osl_joinProcess(process
);
419 CPPUNIT_ASSERT_EQUAL_MESSAGE
421 "osl_joinProcess returned with failure",
422 osl_Process_E_None
, osl_error
425 osl_freeProcessHandle(process
);
427 string_container_t different_child_env_vars
;
428 different_child_env_vars
.push_back(ENV1
);
429 different_child_env_vars
.push_back(ENV2
);
430 different_child_env_vars
.push_back(ENV4
);
432 CPPUNIT_ASSERT_MESSAGE
434 "osl_execProc_merged_child_environment",
435 compare_merged_environments(different_child_env_vars
)
439 void osl_execProc_test_batch()
443 rtl::OUString suBatch
= suCWD
+ "/batch.bat";
445 rtl::OUString suBatch
= suCWD
+ "/batch.sh";
447 oslProcessError osl_error
= osl_executeProcess(
458 CPPUNIT_ASSERT_EQUAL_MESSAGE
460 "osl_createProcess failed",
461 osl_Process_E_None
, osl_error
464 osl_error
= ::osl_joinProcess(process
);
466 CPPUNIT_ASSERT_EQUAL_MESSAGE
468 "osl_joinProcess returned with failure",
469 osl_Process_E_None
, osl_error
472 osl_freeProcessHandle(process
);
475 CPPUNIT_TEST_SUITE(Test_osl_executeProcess
);
476 //TODO: Repair these (at least under Windows)
478 CPPUNIT_TEST(osl_execProc_parent_equals_child_environment
);
479 CPPUNIT_TEST(osl_execProc_merged_child_environment
);
481 CPPUNIT_TEST(osl_execProc_test_batch
);
482 ///TODO: Repair test (or tested function ;-) - test fails.
483 CPPUNIT_TEST_SUITE_END();
486 CPPUNIT_TEST_SUITE_REGISTRATION(Test_osl_executeProcess
);
488 CPPUNIT_PLUGIN_IMPLEMENT();
490 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */