1 // AppRunner.cpp 2 3 #include <errno.h> 4 #include <unistd.h> 5 6 #include <Autolock.h> 7 #include <Entry.h> 8 #include <Messenger.h> 9 #include <String.h> 10 11 #include <TestShell.h> 12 #include <TestUtils.h> 13 #include <cppunit/TestAssert.h> 14 15 #include "AppRunner.h" 16 17 static const char *kAppRunnerTeamPort = "app runner team port"; 18 19 // constructor 20 AppRunner::AppRunner(bool requestQuitOnDestruction) 21 : fOutputLock(), 22 fRemotePort(-1), 23 fOutput(), 24 fReader(-1), 25 fTeam(-1), 26 fMessenger(), 27 fRequestQuitOnDestruction(requestQuitOnDestruction) 28 { 29 } 30 31 // destructor 32 AppRunner::~AppRunner() 33 { 34 if (fRequestQuitOnDestruction) 35 WaitFor(true); 36 if (fReader >= 0) { 37 int32 result; 38 wait_for_thread(fReader, &result); 39 } 40 } 41 42 // Run 43 status_t 44 AppRunner::Run(const char *command, const char *args, bool findCommand) 45 { 46 status_t error = (HasQuitted() ? B_OK : B_ERROR); 47 // get the app path 48 BString appPath; 49 if (findCommand) { 50 find_test_app(command, &appPath); 51 command = appPath.String(); 52 } 53 // add args, i.e. compose the command line 54 BString cmdLine(command); 55 if (args) { 56 cmdLine += " "; 57 cmdLine += args; 58 } 59 // lock the team port 60 bool teamPortLocked = false; 61 if (error == B_OK) { 62 teamPortLocked = _LockTeamPort(); 63 if (!teamPortLocked) 64 error = B_ERROR; 65 } 66 // run the command 67 if (error == B_OK) { 68 cmdLine += " &"; 69 if (system(cmdLine.String()) != 0) 70 error = errno; 71 } 72 // read the port ID 73 if (error == B_OK) { 74 fRemotePort = _ReadPortID(fMessenger); 75 if (fRemotePort < 0) 76 error = fRemotePort; 77 } 78 // unlock the team port 79 if (teamPortLocked) 80 _UnlockTeamPort(); 81 // get the team ID 82 if (error == B_OK) { 83 port_info info; 84 error = get_port_info(fRemotePort, &info); 85 fTeam = info.team; 86 } 87 // spawn the reader thread 88 if (error == B_OK) { 89 fReader = spawn_thread(&_ReaderEntry, "AppRunner reader", 90 B_NORMAL_PRIORITY, (void*)this); 91 if (fReader >= 0) 92 error = resume_thread(fReader); 93 else 94 error = fReader; 95 } 96 // cleanup on error 97 if (error != B_OK) { 98 if (fReader >= 0) { 99 kill_thread(fReader); 100 fReader = -1; 101 } 102 } 103 return error; 104 } 105 106 // HasQuitted 107 bool 108 AppRunner::HasQuitted() 109 { 110 port_info info; 111 return (get_port_info(fRemotePort, &info) != B_OK); 112 } 113 114 // WaitFor 115 void 116 AppRunner::WaitFor(bool requestQuit) 117 { 118 if (!HasQuitted() && requestQuit) 119 RequestQuit(); 120 while (!HasQuitted()) 121 snooze(10000); 122 } 123 124 // Team 125 team_id 126 AppRunner::Team() 127 { 128 return fTeam; 129 } 130 131 // RequestQuit 132 status_t 133 AppRunner::RequestQuit() 134 { 135 status_t error = B_OK; 136 if (fTeam >= 0) { 137 BMessenger messenger(fMessenger); 138 error = messenger.SendMessage(B_QUIT_REQUESTED); 139 } else 140 error = fTeam; 141 return error; 142 } 143 144 // GetOutput 145 status_t 146 AppRunner::GetOutput(BString *buffer) 147 { 148 status_t error = (buffer ? B_OK : B_BAD_VALUE); 149 if (error == B_OK) { 150 BAutolock locker(fOutputLock); 151 size_t size = fOutput.BufferLength(); 152 const void *output = fOutput.Buffer(); 153 if (size > 0) 154 buffer->SetTo((const char*)output, size); 155 else 156 *buffer = ""; 157 } 158 return error; 159 } 160 161 // ReadOutput 162 ssize_t 163 AppRunner::ReadOutput(void *buffer, size_t size) 164 { 165 BAutolock locker(fOutputLock); 166 return fOutput.Read(buffer, size); 167 } 168 169 // ReadOutputAt 170 ssize_t 171 AppRunner::ReadOutputAt(off_t position, void *buffer, size_t size) 172 { 173 BAutolock locker(fOutputLock); 174 return fOutput.ReadAt(position, buffer, size); 175 } 176 177 // _ReaderEntry 178 int32 179 AppRunner::_ReaderEntry(void *data) 180 { 181 int32 result = 0; 182 if (AppRunner *me = (AppRunner*)data) 183 result = me->_ReaderLoop(); 184 return result; 185 } 186 187 // _ReaderLoop 188 int32 189 AppRunner::_ReaderLoop() 190 { 191 char buffer[10240]; 192 port_id port = fRemotePort; 193 status_t error = B_OK; 194 while (error == B_OK) { 195 int32 code; 196 ssize_t bytes = read_port(port, &code, buffer, sizeof(buffer) - 1); 197 if (bytes > 0) { 198 // write the buffer 199 BAutolock locker(fOutputLock); 200 off_t oldPosition = fOutput.Seek(0, SEEK_END); 201 fOutput.Write(buffer, bytes); 202 fOutput.Seek(oldPosition, SEEK_SET); 203 } else if (bytes < 0) 204 error = bytes; 205 } 206 fRemotePort = -1; 207 return 0; 208 } 209 210 // _LockTeamPort 211 bool 212 AppRunner::_LockTeamPort() 213 { 214 bool result = fTeamPortLock.Lock(); 215 // lazy port creation 216 if (result && fTeamPort < 0) { 217 fTeamPort = create_port(5, kAppRunnerTeamPort); 218 if (fTeamPort < 0) { 219 fTeamPortLock.Unlock(); 220 result = false; 221 } 222 } 223 return result; 224 } 225 226 // _UnlockTeamPort 227 void 228 AppRunner::_UnlockTeamPort() 229 { 230 fTeamPortLock.Unlock(); 231 } 232 233 // _ReadPortID 234 port_id 235 AppRunner::_ReadPortID(BMessenger &messenger) 236 { 237 port_id port = -1; 238 ssize_t size = read_port(fTeamPort, &port, &messenger, sizeof(BMessenger)); 239 if (size < 0) 240 port = size; 241 return port; 242 } 243 244 245 // fTeamPort 246 port_id AppRunner::fTeamPort = -1; 247 248 // fTeamPortLock 249 BLocker AppRunner::fTeamPortLock; 250 251 252 // find_test_app 253 status_t 254 find_test_app(const char *testApp, BString *path) 255 { 256 status_t error = (testApp && path ? B_OK : B_BAD_VALUE); 257 if (error == B_OK) { 258 *path = BTestShell::GlobalTestDir(); 259 path->CharacterEscape(" \t\n!\"'`$&()?*+{}[]<>|", '\\'); 260 *path += "/kits/app/"; 261 *path += testApp; 262 #ifdef TEST_R5 263 *path += "_r5"; 264 #endif 265 } 266 return error; 267 } 268 269 // find_test_app 270 status_t 271 find_test_app(const char *testApp, entry_ref *ref) 272 { 273 status_t error = (testApp && ref ? B_OK : B_BAD_VALUE); 274 BString path; 275 if (error == B_OK) 276 error = find_test_app(testApp, &path); 277 if (error == B_OK) 278 error = get_ref_for_path(path.String(), ref); 279 return error; 280 } 281 282