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