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