1 /*  $Id: test_ncbiexec.cpp 604961 2020-04-05 17:25:42Z lavr $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Vladimir Ivanov
27  *
28  * File Description:   Test program for portable exec functions
29  *
30  * Note:  On mS-Windows Cygwin should be installed and added to PATH before
31  *        run this test program, because we use 'ls' command.
32  *
33  */
34 
35 #include <ncbi_pch.hpp>
36 #include <corelib/ncbiapp.hpp>
37 #include <corelib/ncbiargs.hpp>
38 #include <corelib/ncbienv.hpp>
39 #include <corelib/ncbiexec.hpp>
40 
41 #if defined(HAVE_UNISTD_H)
42 #  include <unistd.h>         // for _exit()
43 #endif
44 
45 #include <common/test_assert.h> // This header must go last
46 
47 #if NCBI_COMPILER_MSVC && (_MSC_VER >= 1400)
48  /* Microsoft does not want to use POSIX name, not to accept POSIX compliance */
49 #  define sys_strdup  _strdup
50 #else
51 #  define sys_strdup   strdup
52 #endif /*NCBI_COMPILER_MSVC && _MSC_VER>=1400*/
53 
54 
55 USING_NCBI_SCOPE;
56 
57 
58 #define TEST_RESULT_C    99   // Test exit code for current test
59 #define TEST_RESULT_P     0   // Test exit code for test from PATH
60 
61 // Array of arguments to test argument quoting in SpawnL/SpawnV
62 const char* s_QuoteArgsTest[] =
63 {
64     "",             // reserved:  Will be overwritten with application name
65     "SpawnV_Quote", // reserved:  Test name
66     "",
67     " ",
68     "\\",
69     " \\",
70     "\\ ",
71     "dir\\",
72     "dir\\path",
73     "d i r\\p a t h",
74     "d i r\\p a t h\\",
75     "a b",
76     "\"a",          // "a
77     "a\"b",         // a"b
78     "\"a b\"",      // "a b"
79     "a\\\\b",       // a\\b
80     "a\\\\\\b",     // a\\\b
81     "a\\\"b",       // a\"b
82     "a\\\\\"b",     // a\\"b
83     "a\\\\\\\"b",   // a\\\"b
84     "a\\\\\\\\\"b", // a\\\\"b
85     "\"",           // "
86     "\"\"",         // ""
87     "\"\"\"",       // """
88     "\"\"\"\"",     // """"
89     "!{} \t\r\n[|&;<>()$`'*?#~=%",
90     NULL
91 };
92 
93 
94 ////////////////////////////////
95 // Test application
96 //
97 
98 class CTest : public CNcbiApplication
99 {
100 public:
101     void Init(void);
102     int Run(void);
103 };
104 
105 
Init(void)106 void CTest::Init(void)
107 {
108     SetDiagPostLevel(eDiag_Warning);
109     unique_ptr<CArgDescriptions> d(new CArgDescriptions);
110     d->SetUsageContext("test_files",
111                        "test file's accessory functions");
112     SetupArgDescriptions(d.release());
113 }
114 
115 
Run(void)116 int CTest::Run(void)
117 {
118     TExitCode code;
119     TProcessHandle handle;
120 
121     string app = GetArguments().GetProgramName();
122     LOG_POST("Application path: " << app);
123 
124     // Initialization of variables and structures
125 
126     char* app_c = sys_strdup(app.c_str());
127     assert( app_c != 0 );
128 
129     const char* app_p  = "ls";
130     const char* app_pp = "..";
131 
132     const char* my_env[] =   // Environment for Spawn*E
133     {
134         "THIS=environment will be",
135         "PASSED=to new process by the",
136         "EXEC=functions",
137         "TEST_NCBI_EXEC=yes",
138         NULL
139     };
140 
141     {{
142 #if defined(NCBI_OS_CYGWIN)
143         string path_setting("PATH=");
144         const string& path = GetEnvironment().Get("PATH");
145 #elif defined(NCBI_OS_DARWIN)
146         string path_setting("DYLD_LIBRARY_PATH=");
147         const string& path = GetEnvironment().Get("DYLD_LIBRARY_PATH");
148 #else
149         string path_setting("LD_LIBRARY_PATH=");
150         const string& path = GetEnvironment().Get("LD_LIBRARY_PATH");
151 #endif
152         if (path.size()) {
153             path_setting += path;
154             my_env[0] = sys_strdup(path_setting.c_str());
155         }
156     }}
157 
158     const char* args_c[3];   // Arguments for SpawnV[E]
159     args_c[1] = "SpawnV[E]";
160     args_c[2] = NULL;
161 
162     const char* args_p[3];   // Arguments for SpawnVP[E]
163     args_p[1] = app_pp;
164     args_p[2] = NULL;
165 
166 
167     // ResolvePath() test
168 
169     assert( CExec::IsExecutable(app_c) );
170     string res_path;
171     res_path = CExec::ResolvePath(app_c);
172     LOG_POST("Resolve path: " << app_c << " -> " << res_path);
173     assert( !res_path.empty() );
174     res_path = CExec::ResolvePath(app_p);
175     LOG_POST("Resolve path: " << app_p << " -> " << res_path);
176     assert( !res_path.empty() );
177 #if defined(NCBI_OS_MSWIN)
178     res_path = CExec::ResolvePath("winver.exe");
179     LOG_POST("Resolve path: " << "winver.exe" << " -> " << res_path);
180     assert( !res_path.empty() );
181 #endif
182 
183 
184     // System
185 
186 #if !defined(NCBI_OS_CYGWIN)
187     // This test doesn't work on GCC/Cygwin
188     assert( CExec::System(0) > 0 );
189 #endif
190     string cmd = app + " System";
191     assert( CExec::System(cmd.c_str()) == TEST_RESULT_C );
192     cmd = string(app_p) + " " + app_pp;
193     assert( CExec::System(cmd.c_str()) == TEST_RESULT_P );
194 
195     // Spawn with eWait
196     {{
197         code = CExec::SpawnL  (CExec::eWait, app_c, "SpawnL_eWait", NULL).GetExitCode();
198         assert( code == TEST_RESULT_C );
199         code = CExec::SpawnLP (CExec::eWait, app_p, app_pp, NULL).GetExitCode();
200         assert( code == TEST_RESULT_P );
201         code = CExec::SpawnLE (CExec::eWait, app_c, "SpawnLE_eWait", NULL, my_env).GetExitCode();
202         assert( code == TEST_RESULT_C );
203         code = CExec::SpawnLPE(CExec::eWait, app_c, "SpawnLPE_eWait", NULL, my_env).GetExitCode();
204         assert( code == TEST_RESULT_C );
205         code = CExec::SpawnV  (CExec::eWait, app_c, args_c).GetExitCode();
206         assert( code == TEST_RESULT_C );
207         code = CExec::SpawnVP (CExec::eWait, app_p, args_p).GetExitCode();
208         assert( code == TEST_RESULT_P );
209         code = CExec::SpawnVE (CExec::eWait, app_c, args_c, my_env).GetExitCode();
210         assert( code == TEST_RESULT_C );
211         code = CExec::SpawnVPE(CExec::eWait, app_c, args_c, my_env).GetExitCode();
212         assert( code == TEST_RESULT_C );
213     }}
214 
215     // Spawn with eNoWait, waiting self
216     {{
217         handle = CExec::SpawnL  (CExec::eNoWait, app_c, "SpawnL_eNoWait", NULL).GetProcessHandle();
218         assert(CExec::Wait(handle) == TEST_RESULT_C );
219         handle = CExec::SpawnLP (CExec::eNoWait, app_p, app_pp, NULL).GetProcessHandle();
220         assert(CExec::Wait(handle) == TEST_RESULT_P);
221         handle = CExec::SpawnLE (CExec::eNoWait, app_c, "SpawnLE_eNoWait", NULL, my_env).GetProcessHandle();
222         assert(CExec::Wait(handle) == TEST_RESULT_C);
223         handle = CExec::SpawnLPE(CExec::eNoWait, app_c, "SpawnLPE_eNoWait", NULL, my_env).GetProcessHandle();
224         assert(CExec::Wait(handle) == TEST_RESULT_C);
225     }}
226 
227     // Spawn with eDetach
228     {{
229         CExec::SpawnL  (CExec::eDetach, app_c, "SpawnL_eDetach", NULL);
230         CExec::SpawnLP (CExec::eDetach, app_p, app_pp, NULL);
231         CExec::SpawnLE (CExec::eDetach, app_c, "SpawnLE_eDetach", NULL, my_env);
232         CExec::SpawnLPE(CExec::eDetach, app_c, "SpawnLPE_eDetach",NULL, my_env);
233         CExec::SpawnV  (CExec::eDetach, app_c, args_c);
234         CExec::SpawnVP (CExec::eDetach, app_p, args_p);
235         CExec::SpawnVE (CExec::eDetach, app_c, args_c, my_env);
236         CExec::SpawnVPE(CExec::eDetach, app_c, args_c, my_env);
237     }}
238 
239     // Spawn with eWaitGroup
240     {{
241         code = CExec::SpawnL(CExec::eWaitGroup, app_c, "SpawnL_eWaitGroup", NULL).GetExitCode();
242         assert( code == TEST_RESULT_C );
243     }}
244 
245     // Spawn with eNoWaitGroup, waiting self
246     {{
247         handle = CExec::SpawnL(CExec::eNoWaitGroup, app_c, "SpawnL_eNoWaitGroup", NULL).GetProcessHandle();
248         assert( CExec::Wait(handle) == TEST_RESULT_C );
249     }}
250 
251     // Argument quoting test
252     {{
253         code = CExec::SpawnL(CExec::eWait, app_c,
254                              "SpawnL_Quote",
255                              s_QuoteArgsTest[ 2],
256                              s_QuoteArgsTest[ 3],
257                              s_QuoteArgsTest[ 4],
258                              s_QuoteArgsTest[ 5],
259                              s_QuoteArgsTest[ 6],
260                              s_QuoteArgsTest[ 7],
261                              s_QuoteArgsTest[ 8],
262                              s_QuoteArgsTest[ 9],
263                              s_QuoteArgsTest[10],
264                              s_QuoteArgsTest[11],
265                              s_QuoteArgsTest[12],
266                              s_QuoteArgsTest[13],
267                              s_QuoteArgsTest[14],
268                              s_QuoteArgsTest[15],
269                              s_QuoteArgsTest[16],
270                              s_QuoteArgsTest[17],
271                              s_QuoteArgsTest[18],
272                              s_QuoteArgsTest[19],
273                              s_QuoteArgsTest[20],
274                              s_QuoteArgsTest[21],
275                              s_QuoteArgsTest[22],
276                              s_QuoteArgsTest[23],
277                              s_QuoteArgsTest[24],
278                              s_QuoteArgsTest[25],
279                              NULL).GetExitCode();
280         assert( code == TEST_RESULT_C );
281         code = CExec::SpawnV(CExec::eWait, app_c, s_QuoteArgsTest).GetExitCode();
282         assert( code == TEST_RESULT_C );
283     }}
284 
285     // Spawn with eOverlay
286     {{
287         code = CExec::SpawnL(CExec::eOverlay, app_c, "SpawnL_eOverlay", NULL).GetExitCode();
288         assert( code == TEST_RESULT_C );
289     }}
290 
291     // At success code below never been executed
292     LOG_POST("\nTEST execution fails!\n");
293 
294     return 77;
295 }
296 
297 
298 ///////////////////////////////////
299 // MAIN
300 //
301 
main(int argc,const char * argv[],const char * [])302 int main(int argc, const char* argv[], const char* /*envp*/[])
303 {
304     // Exec from test?
305     if ( argc > 1) {
306         assert(argv[1] && *argv[1]);
307         cout << endl << "Exec: " << argv[1] << endl;
308 
309         if ( strstr(argv[1],"Quote")) {
310             // Check arguments
311             const size_t n = sizeof(s_QuoteArgsTest) / sizeof(s_QuoteArgsTest[0]);
312             assert(argc == (n-1));
313             for (int i=2; i<argc; i++) {
314                 //cout << i << " = '" << argv[i] << "'" << endl;
315                 //cout << i << " = '" << s_QuoteArgsTest[i] << "'" << endl;
316                 //cout << i << " = '" << NStr::PrintableString(argv[i]) << "'" << endl;
317                 //cout << i << " = '" << NStr::PrintableString(s_QuoteArgsTest[i]) << "'" << endl;
318                 cout.flush();
319                 assert( NStr::CompareCase(s_QuoteArgsTest[i], argv[i]) == 0 );
320             }
321 
322         } else {
323             assert(argc == 2);
324         }
325 
326         // View environment
327         /*
328         const char** env_var = &envp[0];
329         while (*env_var) {
330             cout << *env_var << endl;
331             env_var++;
332         }
333         */
334         // Check environment
335         if ( strstr(argv[1],"E_e")) {
336             char* ptr = getenv("TEST_NCBI_EXEC");
337             if (!ptr || !*ptr) {
338                 cout << "Environment variable TEST_NCBI_EXEC not found" <<endl;
339                 cout.flush();
340                 _exit(88);
341             } else {
342                 cout << "TEST_NCBI_EXEC=" << ptr << endl;
343             }
344         }
345         cout.flush();
346         _exit(TEST_RESULT_C);
347     }
348     LOG_POST("Start tests:\n");
349 
350     // Execute main application function
351     return CTest().AppMain(argc, argv);
352 }
353