bump product version to 5.0.4.1
[LibreOffice.git] / sal / qa / osl / process / osl_process.cxx
blob4a9cd425658cdba11c5894945300488aecd3d925
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #ifdef IOS
21 #define CPPUNIT_PLUGIN_EXPORTED_NAME cppunitTest_osl_process
22 #endif
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>
33 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <osl/module.hxx>
38 #include <sal/macros.h>
40 #if defined HAVE_VALGRIND_HEADERS
41 #include <valgrind/valgrind.h>
42 #else
43 #define RUNNING_ON_VALGRIND false
44 #endif
46 #if ( defined WNT ) // Windows
47 # include <windows.h>
48 # include <tchar.h>
49 #else
50 #include <unistd.h>
51 #endif
53 #include <iostream>
54 #include <fstream>
55 #include <vector>
56 #include <algorithm>
57 #include <iterator>
58 #include <string>
60 #ifdef UNX
61 #if defined( MACOSX )
62 # include <crt_externs.h>
63 # define environ (*_NSGetEnviron())
64 # else
65 extern char** environ;
66 # endif
67 #endif
69 #if defined(WNT)
70 const rtl::OUString EXECUTABLE_NAME ("osl_process_child.exe");
71 #else
72 const rtl::OUString EXECUTABLE_NAME ("osl_process_child");
73 #endif
75 using namespace osl;
77 using ::rtl::OUString;
78 using ::rtl::OUStringToOString;
79 using ::rtl::OString;
81 /** get binary Path.
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");
91 return dirPath;
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>
102 public:
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() !=
115 std::find(
116 exclude_list_.begin(),
117 exclude_list_.end(),
118 env_var_name(env_var)));
121 private:
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);
133 return OString();
136 private:
137 string_container_t exclude_list_;
140 namespace
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)
148 env_container.erase(
149 std::remove_if(
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());
159 #ifdef WNT
160 void read_parent_environment(string_container_t* env_container)
162 LPTSTR env = reinterpret_cast<LPTSTR>(GetEnvironmentStrings());
163 LPTSTR p = env;
165 while (size_t l = _tcslen(p))
167 env_container->push_back(OString(p));
168 p += l + 1;
170 FreeEnvironmentStrings(env);
171 tidy_container(*env_container);
173 #else
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);
180 #endif
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_;
190 OUString suCWD;
191 OUString suExecutableFileURL;
193 public:
195 // ctor
196 Test_osl_executeProcess() :
197 env_param_(OUString("-env")),
198 parameters_count_(2)
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",
239 file.is_open()
242 std::string line;
243 line.reserve(1024);
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);
260 OString msg(
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;
286 #endif
288 //remove the environment variables that we have changed
289 //in the child environment from the read parent environment
290 parent_env.erase(
291 std::remove_if(parent_env.begin(), parent_env.end(), exclude(different_env_vars)),
292 parent_env.end());
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;
297 #endif
299 //read the child environment and exclude the variables that
300 //are different
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;
307 #endif
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;
321 #endif
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;
329 #endif
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;
334 #endif
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
347 // variables
348 void osl_execProc_parent_equals_child_environment()
350 oslProcess process;
351 oslProcessError osl_error = osl_executeProcess(
352 suExecutableFileURL.pData,
353 parameters_,
354 parameters_count_,
355 osl_Process_NORMAL,
356 NULL,
357 suCWD.pData,
358 NULL,
360 &process);
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];
389 OUString env1(ENV1);
390 OUString env2(ENV2);
391 OUString env3(ENV3);
392 OUString env4(ENV4);
394 child_env[0] = env1.pData;
395 child_env[1] = env2.pData;
396 child_env[2] = env3.pData;
397 child_env[3] = env4.pData;
399 oslProcess process;
400 oslProcessError osl_error = osl_executeProcess(
401 suExecutableFileURL.pData,
402 parameters_,
403 parameters_count_,
404 osl_Process_NORMAL,
405 NULL,
406 suCWD.pData,
407 child_env,
408 SAL_N_ELEMENTS(child_env),
409 &process);
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()
441 oslProcess process;
442 #if defined(WNT)
443 rtl::OUString suBatch = suCWD + "/batch.bat";
444 #else
445 rtl::OUString suBatch = suCWD + "/batch.sh";
446 #endif
447 oslProcessError osl_error = osl_executeProcess(
448 suBatch.pData,
449 NULL,
451 osl_Process_NORMAL,
452 NULL,
453 suCWD.pData,
454 NULL,
456 &process);
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)
477 #if !defined(WNT)
478 CPPUNIT_TEST(osl_execProc_parent_equals_child_environment);
479 CPPUNIT_TEST(osl_execProc_merged_child_environment);
480 #endif
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: */