1 /*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2  *
3  *  Data Differential YATL (i.e. libtest)  library
4  *
5  *  Copyright (C) 2012 Data Differential, http://datadifferential.com/
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions are
9  *  met:
10  *
11  *      * Redistributions of source code must retain the above copyright
12  *  notice, this list of conditions and the following disclaimer.
13  *
14  *      * Redistributions in binary form must reproduce the above
15  *  copyright notice, this list of conditions and the following disclaimer
16  *  in the documentation and/or other materials provided with the
17  *  distribution.
18  *
19  *      * The names of its contributors may not be used to endorse or
20  *  promote products derived from this software without specific prior
21  *  written permission.
22  *
23  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36 
37 #pragma once
38 
39 #include <spawn.h>
40 
41 // http://www.gnu.org/software/automake/manual/automake.html#Using-the-TAP-test-protocol
42 #ifndef EXIT_SKIP
43 # define EXIT_SKIP 77
44 #endif
45 
46 #ifndef EXIT_FATAL
47 # define EXIT_FATAL 99
48 #endif
49 
50 #ifndef EX_NOEXEC
51 # define EX_NOEXEC 126
52 #endif
53 
54 #ifndef EX_NOTFOUND
55 # define EX_NOTFOUND 127
56 #endif
57 
58 namespace libtest {
59 
60 class Application {
61 private:
62   typedef std::vector< std::pair<std::string, std::string> > Options;
63 
64 public:
65 
66   enum error_t {
67     SUCCESS= EXIT_SUCCESS,
68     FAILURE= EXIT_FAILURE,
69     UNINITIALIZED,
70     SIGTERM_KILLED,
71     UNKNOWN,
72     UNKNOWN_SIGNAL,
73     INVALID_POSIX_SPAWN= 127
74   };
75 
toString(error_t arg)76   static const char* toString(error_t arg)
77   {
78     switch (arg)
79     {
80     case Application::SUCCESS:
81       return "EXIT_SUCCESS";
82 
83     case Application::UNINITIALIZED:
84       return "UNINITIALIZED";
85 
86     case Application::SIGTERM_KILLED:
87       return "Exit happened via SIGTERM";
88 
89     case Application::FAILURE:
90       return "EXIT_FAILURE";
91 
92     case Application::UNKNOWN_SIGNAL:
93       return "Exit happened via a signal which was not SIGTERM";
94 
95     case Application::INVALID_POSIX_SPAWN:
96       return "127: Invalid call to posix_spawn()";
97 
98     case Application::UNKNOWN:
99     default:
100       break;
101     }
102 
103     return "EXIT_UNKNOWN";
104   }
105 
106   class Pipe {
107   public:
108     Pipe(int);
109     ~Pipe();
110 
111     int fd();
112 
113     enum close_t {
114       READ= 0,
115       WRITE= 1
116     };
117 
118     void reset();
119     void close(const close_t& arg);
120     void dup_for_spawn(posix_spawn_file_actions_t& file_actions);
121 
122     void nonblock();
123     void cloexec();
124     bool read(libtest::vchar_t&);
125 
126   private:
127     const int _std_fd;
128     int _pipe_fd[2];
129     bool _open[2];
130   };
131 
132 public:
133   Application(const std::string& arg, const bool _use_libtool_arg= false);
134 
135   virtual ~Application();
136 
137   void add_option(const std::string&);
138   void add_option(const std::string&, const std::string&);
139   void add_long_option(const std::string& option_name, const std::string& option_value);
140   error_t run(const char *args[]= NULL);
141   Application::error_t join();
142 
stdout_result()143   libtest::vchar_t stdout_result() const
144   {
145     return _stdout_buffer;
146   }
147 
stdout_result_length()148   size_t stdout_result_length() const
149   {
150     return _stdout_buffer.size();
151   }
152 
stdout_c_str()153   const char* stdout_c_str() const
154   {
155     return &_stdout_buffer[0];
156   }
157 
stderr_result()158   libtest::vchar_t stderr_result() const
159   {
160     return _stderr_buffer;
161   }
162 
stderr_c_str()163   const char* stderr_c_str() const
164   {
165     return &_stderr_buffer[0];
166   }
167 
stderr_result_length()168   size_t stderr_result_length() const
169   {
170     return _stderr_buffer.size();
171   }
172 
173   std::string print();
174 
use_valgrind(bool arg)175   void use_valgrind(bool arg)
176   {
177     _use_valgrind= arg;
178   }
179 
180   bool check() const;
181 
182   bool slurp();
183   void murder();
184 
use_gdb(bool arg)185   void use_gdb(bool arg)
186   {
187     _use_gdb= arg;
188   }
189 
use_ptrcheck(bool arg)190   void use_ptrcheck(bool arg)
191   {
192     _use_ptrcheck= arg;
193   }
194 
195   std::string arguments();
196 
gdb_filename()197   std::string gdb_filename()
198   {
199     return  _gdb_filename;
200   }
201 
pid()202   pid_t pid() const
203   {
204     return _pid;
205   }
206 
will_fail()207   void will_fail()
208   {
209     _will_fail= true;
210   }
211 
212 private:
213   void create_argv(const char *args[]);
214   void delete_argv();
215   void add_to_build_argv(const char*);
216 
217 private:
218   const bool _use_libtool;
219   bool _use_valgrind;
220   bool _use_gdb;
221   bool _use_ptrcheck;
222   bool _will_fail;
223   size_t _argc;
224   std::string _exectuble_name;
225   std::string _exectuble;
226   std::string _exectuble_with_path;
227   std::string _gdb_filename;
228   Options _options;
229   Pipe stdin_fd;
230   Pipe stdout_fd;
231   Pipe stderr_fd;
232   libtest::vchar_ptr_t built_argv;
233   pid_t _pid;
234   libtest::vchar_t _stdout_buffer;
235   libtest::vchar_t _stderr_buffer;
236   int _status;
237   pthread_t _thread;
238   error_t _app_exit_state;
239 };
240 
241 static inline std::ostream& operator<<(std::ostream& output, const enum Application::error_t &arg)
242 {
243   return output << Application::toString(arg);
244 }
245 
246 int exec_cmdline(const std::string& executable, const char *args[], bool use_libtool= false);
247 
248 }
249