1//------------------------------------------------------------------------------
2//	RosterWatchingTester.cpp
3//
4//------------------------------------------------------------------------------
5
6// Standard Includes -----------------------------------------------------------
7#include <stdio.h>
8#include <stdlib.h>
9#include <utime.h>
10
11// System Includes -------------------------------------------------------------
12#include <Message.h>
13#include <OS.h>
14#include <AppFileInfo.h>
15#include <Application.h>
16#include <File.h>
17#include <FindDirectory.h>
18#include <Handler.h>
19#include <Looper.h>
20#include <Message.h>
21#include <MessageQueue.h>
22#include <Path.h>
23#include <Roster.h>
24#include <String.h>
25
26// Project Includes ------------------------------------------------------------
27#include <TestShell.h>
28#include <TestUtils.h>
29#include <cppunit/TestAssert.h>
30
31// Local Includes --------------------------------------------------------------
32#include "AppRunner.h"
33#include "RosterWatchingTester.h"
34#include "LaunchTesterHelper.h"
35#include "RosterTestAppDefs.h"
36
37// Local Defines ---------------------------------------------------------------
38
39// Globals ---------------------------------------------------------------------
40
41//------------------------------------------------------------------------------
42
43static const char *testerSignature
44	= "application/x-vnd.obos-roster-watching-test";
45static const char *appType1	= "application/x-vnd.obos-roster-watching-app1";
46static const char *appType2	= "application/x-vnd.obos-roster-watching-app2";
47static const char *appType3	= "application/x-vnd.obos-roster-watching-app3";
48static const char *appType4	= "application/x-vnd.obos-roster-watching-app4";
49//static const char *appType5	= "application/x-vnd.obos-roster-watching-app5";
50
51static const char *testDir		= "/tmp/testdir";
52static const char *appFile1		= "/tmp/testdir/app1";
53static const char *appFile2		= "/tmp/testdir/app2";
54static const char *appFile3		= "/tmp/testdir/app3";
55static const char *appFile4		= "/tmp/testdir/app4";
56//static const char *appFile5		= "/tmp/testdir/app5";
57
58
59// ref_for_path
60static
61entry_ref
62ref_for_path(const char *filename, bool traverse = true)
63{
64	entry_ref ref;
65	BEntry entry;
66	CHK(entry.SetTo(filename, traverse) == B_OK);
67	CHK(entry.GetRef(&ref) == B_OK);
68	return ref;
69}
70
71// create_app
72static
73entry_ref
74create_app(const char *filename, const char *signature,
75		   bool install = false, bool makeExecutable = true,
76		   uint32 appFlags = B_SINGLE_LAUNCH)
77{
78	BString testApp;
79	CHK(find_test_app("RosterWatchingTestApp1", &testApp) == B_OK);
80	system((string("cp ") + testApp.String() + " " + filename).c_str());
81	if (makeExecutable)
82		system((string("chmod a+x ") + filename).c_str());
83	BFile file;
84	CHK(file.SetTo(filename, B_READ_WRITE) == B_OK);
85	BAppFileInfo appFileInfo;
86	CHK(appFileInfo.SetTo(&file) == B_OK);
87	if (signature)
88		CHK(appFileInfo.SetSignature(signature) == B_OK);
89	CHK(appFileInfo.SetAppFlags(appFlags) == B_OK);
90	if (install && signature)
91		CHK(BMimeType(signature).Install() == B_OK);
92	// We write the signature into a separate attribute, just in case we
93	// decide to also test files without BEOS:APP_SIG attribute.
94	BString signatureString(signature);
95	file.WriteAttrString("signature", &signatureString);
96	return ref_for_path(filename);
97}
98
99// app_info_for_team
100static
101app_info
102app_info_for_team(team_id team)
103{
104	app_info info;
105	CHK(be_roster->GetRunningAppInfo(team, &info) == B_OK);
106	return info;
107}
108
109// check_watching_message
110void
111check_watching_message(LaunchContext &context, team_id team, int32 &cookie,
112					   const app_info &info, uint32 messageCode)
113{
114	// wait for and get the message
115	CHK(context.WaitForMessage(team, MSG_MESSAGE_RECEIVED, false,
116							   B_INFINITE_TIMEOUT, cookie));
117	BMessage *container = context.NextMessageFrom(team, cookie);
118	CHK(container != NULL);
119	CHK(container->what == MSG_MESSAGE_RECEIVED);
120	BMessage message;
121	CHK(container->FindMessage("message", &message) == B_OK);
122	// check the message
123if (message.what != messageCode)
124printf("message.what: %.4s vs messageCode: %.4s\n", (char*)&message.what,
125(char*)&messageCode);
126	CHK(message.what == messageCode);
127	// team
128	team_id foundTeam;
129	CHK(message.FindInt32("be:team", &foundTeam) == B_OK);
130	CHK(foundTeam == info.team);
131	// thread
132	thread_id thread;
133	CHK(message.FindInt32("be:thread", &thread) == B_OK);
134	CHK(thread == info.thread);
135	// signature
136	const char *signature = NULL;
137	CHK(message.FindString("be:signature", &signature) == B_OK);
138	CHK(!strcmp(signature, info.signature));
139	// ref
140	entry_ref ref;
141	CHK(message.FindRef("be:ref", &ref) == B_OK);
142	CHK(ref == info.ref);
143	// flags
144	uint32 flags;
145	CHK(message.FindInt32("be:flags", (int32*)&flags) == B_OK);
146	CHK(flags == info.flags);
147}
148
149
150// setUp
151void
152RosterWatchingTester::setUp()
153{
154	RosterLaunchApp *app = new RosterLaunchApp(testerSignature);
155	fApplication = app;
156	app->SetHandler(new RosterBroadcastHandler);
157	system((string("mkdir ") + testDir).c_str());
158}
159
160// tearDown
161void
162RosterWatchingTester::tearDown()
163{
164	BMimeType(appType1).Delete();
165	BMimeType(appType2).Delete();
166	BMimeType(appType3).Delete();
167	BMimeType(appType4).Delete();
168//	BMimeType(appType5).Delete();
169	delete fApplication;
170	system((string("rm -rf ") + testDir).c_str());
171}
172
173// SimpleAppLauncher
174class SimpleAppLauncher : public LaunchCaller {
175public:
176	SimpleAppLauncher() : fRef() {}
177	SimpleAppLauncher(const entry_ref &ref) : fRef(ref) {}
178	virtual ~SimpleAppLauncher() {}
179	virtual status_t operator()(const char *type, BList *messages, int32 argc,
180								const char **argv, team_id *team)
181	{
182		return be_roster->Launch(&fRef, (BMessage*)NULL, team);
183	}
184	virtual bool SupportsRefs() const { return true; }
185	virtual const entry_ref *Ref() const { return &fRef; }
186
187	virtual LaunchCaller *CloneInternal()
188	{
189		return new SimpleAppLauncher;
190	}
191
192protected:
193	entry_ref fRef;
194};
195
196
197/*
198	status_t StartWatching(BMessenger target, uint32 eventMask) const
199	status_t StopWatching(BMessenger target) const
200	@case 1			{Start,Stop}Watching() with invalid messenger or invalid
201					flags; StopWatching() non-watching messenger =>
202	@results		Should return B_OK; B_BAD_VALUE.
203*/
204void RosterWatchingTester::WatchingTest1()
205{
206	BRoster roster;
207	BMessenger target;
208	// not valid, not watching
209	CHK(roster.StopWatching(target) == B_BAD_VALUE);
210	// not valid
211	CHK(roster.StartWatching(target, B_REQUEST_LAUNCHED | B_REQUEST_QUIT
212							 | B_REQUEST_ACTIVATED) == B_OK);
213	CHK(roster.StopWatching(target) == B_OK);
214	// invalid flags
215	CHK(roster.StartWatching(target, 0) == B_OK);
216	CHK(roster.StopWatching(target) == B_OK);
217	// valid, but not watching
218	CHK(roster.StopWatching(be_app_messenger) == B_BAD_VALUE);
219}
220
221/*
222	status_t StartWatching(BMessenger target, uint32 eventMask) const
223	status_t StopWatching(BMessenger target) const
224	@case 2			several apps, several watchers, different eventMasks
225	@results		Should return B_OK...
226					Watching ends, when target has become invalid and the next watching
227					message is tried to be sent.
228*/
229void RosterWatchingTester::WatchingTest2()
230{
231	LaunchContext context;
232	BRoster roster;
233	// launch app 1
234	entry_ref ref1(create_app(appFile1, appType1));
235	SimpleAppLauncher caller1(ref1);
236	team_id team1;
237	CHK(context(caller1, appType1, &team1) == B_OK);
238	context.WaitForMessage(team1, MSG_READY_TO_RUN);
239	BMessenger target1(NULL, team1);
240	app_info appInfo1(app_info_for_team(team1));
241	CHK(roster.StartWatching(target1, B_REQUEST_LAUNCHED | B_REQUEST_QUIT
242							 | B_REQUEST_ACTIVATED) == B_OK);
243	//   messages: app 1
244	int32 cookie1 = 0;
245	CHK(context.CheckNextMessage(caller1, team1, cookie1, MSG_STARTED));
246	CHK(context.CheckMainArgsMessage(caller1, team1, cookie1, &ref1, false));
247	CHK(context.CheckNextMessage(caller1, team1, cookie1, MSG_READY_TO_RUN));
248	// launch app 2
249	entry_ref ref2(create_app(appFile2, appType2, false, true,
250							  B_SINGLE_LAUNCH | B_ARGV_ONLY));
251	SimpleAppLauncher caller2(ref2);
252	team_id team2;
253	CHK(context(caller2, appType2, &team2) == B_OK);
254	context.WaitForMessage(team2, MSG_READY_TO_RUN);
255	BMessenger target2(context.AppMessengerFor(team2));
256	CHK(target2.IsValid());
257	app_info appInfo2(app_info_for_team(team2));
258	CHK(roster.StartWatching(target2, B_REQUEST_LAUNCHED) == B_OK);
259	//   messages: app 2
260	int32 cookie2 = 0;
261	CHK(context.CheckNextMessage(caller2, team2, cookie2, MSG_STARTED));
262	CHK(context.CheckMainArgsMessage(caller2, team2, cookie2, &ref2, false));
263	CHK(context.CheckNextMessage(caller2, team2, cookie2, MSG_READY_TO_RUN));
264	//   messages: app 1
265	check_watching_message(context, team1, cookie1, appInfo2,
266						   B_SOME_APP_LAUNCHED);
267	// launch app 3
268	entry_ref ref3(create_app(appFile3, appType3));
269	SimpleAppLauncher caller3(ref3);
270	team_id team3;
271	CHK(context(caller3, appType3, &team3) == B_OK);
272	context.WaitForMessage(team3, MSG_READY_TO_RUN);
273	BMessenger target3(NULL, team3);
274	app_info appInfo3(app_info_for_team(team3));
275	CHK(roster.StartWatching(target3, B_REQUEST_QUIT) == B_OK);
276	//   messages: app 3
277	int32 cookie3 = 0;
278	CHK(context.CheckNextMessage(caller3, team3, cookie3, MSG_STARTED));
279	CHK(context.CheckMainArgsMessage(caller3, team3, cookie3, &ref3, false));
280	CHK(context.CheckNextMessage(caller3, team3, cookie3, MSG_READY_TO_RUN));
281	//   messages: app 2
282	check_watching_message(context, team2, cookie2, appInfo3,
283						   B_SOME_APP_LAUNCHED);
284	//   messages: app 1
285	check_watching_message(context, team1, cookie1, appInfo3,
286						   B_SOME_APP_LAUNCHED);
287	// launch app 4
288	entry_ref ref4(create_app(appFile4, appType4));
289	SimpleAppLauncher caller4(ref4);
290	team_id team4;
291	CHK(context(caller4, appType4, &team4) == B_OK);
292	context.WaitForMessage(team4, MSG_READY_TO_RUN);
293	BMessenger target4(NULL, team4);
294	app_info appInfo4(app_info_for_team(team4));
295	//   messages: app 4
296	int32 cookie4 = 0;
297	CHK(context.CheckNextMessage(caller4, team4, cookie4, MSG_STARTED));
298	CHK(context.CheckMainArgsMessage(caller4, team4, cookie4, &ref4, false));
299	CHK(context.CheckNextMessage(caller4, team4, cookie4, MSG_READY_TO_RUN));
300	//   messages: app 3
301	//   none
302	//   messages: app 2
303	check_watching_message(context, team2, cookie2, appInfo4,
304						   B_SOME_APP_LAUNCHED);
305	//   messages: app 1
306	check_watching_message(context, team1, cookie1, appInfo4,
307						   B_SOME_APP_LAUNCHED);
308	// terminate app 4
309	context.TerminateApp(team4);
310	//   messages: app 3
311	check_watching_message(context, team3, cookie3, appInfo4,
312						   B_SOME_APP_QUIT);
313	//   messages: app 2
314	//   none
315	//   messages: app 1
316	check_watching_message(context, team1, cookie1, appInfo4,
317						   B_SOME_APP_QUIT);
318	// stop watching app 1
319	CHK(roster.StopWatching(target1) == B_OK);
320	// terminate app 2
321	context.TerminateApp(team2);
322	CHK(roster.StopWatching(target2) == B_OK);
323	//   messages: app 3
324	check_watching_message(context, team3, cookie3, appInfo2,
325						   B_SOME_APP_QUIT);
326	//   messages: app 1
327	//   none
328	// terminate app 3
329	context.TerminateApp(team3);
330// Haiku handles app termination a bit different. At the point, when the
331// application unregisters itself from the registrar, its port is still
332// valid.
333#ifdef TEST_R5
334	CHK(roster.StopWatching(target3) == B_BAD_VALUE);
335#else
336	CHK(roster.StopWatching(target3) == B_OK);
337#endif
338	//   messages: app 1
339	//   none
340	// remaining messages
341	context.Terminate();
342	// app 1
343	CHK(context.CheckNextMessage(caller1, team1, cookie1, MSG_QUIT_REQUESTED));
344	CHK(context.CheckNextMessage(caller1, team1, cookie1, MSG_TERMINATED));
345	// app 2
346	CHK(context.CheckNextMessage(caller2, team2, cookie2, MSG_QUIT_REQUESTED));
347	CHK(context.CheckNextMessage(caller2, team2, cookie2, MSG_TERMINATED));
348	// app 3
349	CHK(context.CheckNextMessage(caller3, team3, cookie3, MSG_QUIT_REQUESTED));
350	CHK(context.CheckNextMessage(caller3, team3, cookie3, MSG_TERMINATED));
351	// app 4
352	CHK(context.CheckNextMessage(caller4, team4, cookie4, MSG_QUIT_REQUESTED));
353	CHK(context.CheckNextMessage(caller4, team4, cookie4, MSG_TERMINATED));
354}
355
356/*
357	status_t StartWatching(BMessenger target, uint32 eventMask) const
358	status_t StopWatching(BMessenger target) const
359	@case 3			call StartWatching() twice, second time with different
360					masks
361	@results		Should return B_OK. The second call simply overrides the
362					first one.
363*/
364void RosterWatchingTester::WatchingTest3()
365{
366	LaunchContext context;
367	BRoster roster;
368	// launch app 1
369	entry_ref ref1(create_app(appFile1, appType1));
370	SimpleAppLauncher caller1(ref1);
371	team_id team1;
372	CHK(context(caller1, appType1, &team1) == B_OK);
373	context.WaitForMessage(team1, MSG_READY_TO_RUN);
374	BMessenger target1(NULL, team1);
375	app_info appInfo1(app_info_for_team(team1));
376	CHK(roster.StartWatching(target1, B_REQUEST_LAUNCHED | B_REQUEST_QUIT
377							 | B_REQUEST_ACTIVATED) == B_OK);
378	//   messages: app 1
379	int32 cookie1 = 0;
380	CHK(context.CheckNextMessage(caller1, team1, cookie1, MSG_STARTED));
381	CHK(context.CheckMainArgsMessage(caller1, team1, cookie1, &ref1, false));
382	CHK(context.CheckNextMessage(caller1, team1, cookie1, MSG_READY_TO_RUN));
383	// app 1: another StartWatching() with different event mask
384	CHK(roster.StartWatching(target1, B_REQUEST_QUIT) == B_OK);
385	// launch app 2
386	entry_ref ref2(create_app(appFile2, appType2, false, true,
387							  B_SINGLE_LAUNCH | B_ARGV_ONLY));
388	SimpleAppLauncher caller2(ref2);
389	team_id team2;
390	CHK(context(caller2, appType2, &team2) == B_OK);
391	context.WaitForMessage(team2, MSG_READY_TO_RUN);
392	BMessenger target2(context.AppMessengerFor(team2));
393	CHK(target2.IsValid());
394	app_info appInfo2(app_info_for_team(team2));
395	CHK(roster.StartWatching(target2, B_REQUEST_QUIT) == B_OK);
396	//   messages: app 2
397	int32 cookie2 = 0;
398	CHK(context.CheckNextMessage(caller2, team2, cookie2, MSG_STARTED));
399	CHK(context.CheckMainArgsMessage(caller2, team2, cookie2, &ref2, false));
400	CHK(context.CheckNextMessage(caller2, team2, cookie2, MSG_READY_TO_RUN));
401	//   messages: app 1
402	// none
403	// app 2: another StartWatching() with different event mask
404	CHK(roster.StartWatching(target2, B_REQUEST_LAUNCHED) == B_OK);
405	// launch app 3
406	entry_ref ref3(create_app(appFile3, appType3));
407	SimpleAppLauncher caller3(ref3);
408	team_id team3;
409	CHK(context(caller3, appType3, &team3) == B_OK);
410	context.WaitForMessage(team3, MSG_READY_TO_RUN);
411	BMessenger target3(NULL, team3);
412	app_info appInfo3(app_info_for_team(team3));
413	//   messages: app 3
414	int32 cookie3 = 0;
415	CHK(context.CheckNextMessage(caller3, team3, cookie3, MSG_STARTED));
416	CHK(context.CheckMainArgsMessage(caller3, team3, cookie3, &ref3, false));
417	CHK(context.CheckNextMessage(caller3, team3, cookie3, MSG_READY_TO_RUN));
418	//   messages: app 2
419	check_watching_message(context, team2, cookie2, appInfo3,
420						   B_SOME_APP_LAUNCHED);
421	//   messages: app 1
422	//   none
423	// terminate app 3
424	context.TerminateApp(team3);
425	//   messages: app 3
426	//   none
427	//   messages: app 2
428	//   none
429	//   messages: app 1
430	check_watching_message(context, team1, cookie1, appInfo3,
431						   B_SOME_APP_QUIT);
432	// terminate app 2
433	context.TerminateApp(team2);
434	CHK(roster.StopWatching(target2) == B_OK);
435	//   messages: app 1
436	check_watching_message(context, team1, cookie1, appInfo2,
437						   B_SOME_APP_QUIT);
438	// remaining messages
439	context.Terminate();
440	// app 1
441	CHK(context.CheckNextMessage(caller1, team1, cookie1, MSG_QUIT_REQUESTED));
442	CHK(context.CheckNextMessage(caller1, team1, cookie1, MSG_TERMINATED));
443	// app 2
444	CHK(context.CheckNextMessage(caller2, team2, cookie2, MSG_QUIT_REQUESTED));
445	CHK(context.CheckNextMessage(caller2, team2, cookie2, MSG_TERMINATED));
446	// app 3
447	CHK(context.CheckNextMessage(caller3, team3, cookie3, MSG_QUIT_REQUESTED));
448	CHK(context.CheckNextMessage(caller3, team3, cookie3, MSG_TERMINATED));
449}
450
451
452Test* RosterWatchingTester::Suite()
453{
454	TestSuite* SuiteOfTests = new TestSuite;
455
456	ADD_TEST4(BRoster, SuiteOfTests, RosterWatchingTester, WatchingTest1);
457	ADD_TEST4(BRoster, SuiteOfTests, RosterWatchingTester, WatchingTest2);
458	ADD_TEST4(BRoster, SuiteOfTests, RosterWatchingTester, WatchingTest3);
459
460	return SuiteOfTests;
461}
462
463