xref: /haiku/src/tests/kits/support/barchivable/InstantiateObjectTester.cpp (revision df8b692ac0ba4c71a968a22593d2195450466d27)
1 //------------------------------------------------------------------------------
2 //	InstantiateObjectTester.cpp
3 //
4 /**
5 	Testing of instantiate_object(BMessage* archive, image_id* id)
6 	@note	No cases are currently defined for NULL 'id' parameter, since NULL
7 			is a valid value for it.  Perhaps there should be to ensure that the
8 			instantiate_object is, in fact, dealing with that case correctly.
9 			There are also no tests against instantiate_object(BMessage*) as it
10 			simply calls instantiate_object(BMessage*, image_id*) with NULL for
11 			the image_id parameter.
12  */
13 //------------------------------------------------------------------------------
14 
15 #include "InstantiateObjectTester.h"
16 
17 // Standard Includes -----------------------------------------------------------
18 #include <errno.h>
19 #include <stdexcept>
20 #include <iostream>
21 
22 // System Includes -------------------------------------------------------------
23 #include <Roster.h>
24 #include <Entry.h>
25 #include <Path.h>
26 
27 // Project Includes ------------------------------------------------------------
28 #include <cppunit/Exception.h>
29 #include <TestShell.h>
30 
31 // Local Includes --------------------------------------------------------------
32 #include "remoteobjectdef/RemoteTestObject.h"
33 #include "LocalTestObject.h"
34 
35 using namespace std;
36 
37 // Local Defines ---------------------------------------------------------------
38 #define FORMAT_AND_THROW(MSG, ERR)	\
39 	FormatAndThrow(__LINE__, __FILE__, MSG, ERR)
40 
41 // Globals ---------------------------------------------------------------------
42 const char* gInvalidClassName	= "TInvalidClassName";
43 const char* gInvalidSig			= "application/x-vnd.InvalidSignature";
44 const char* gLocalClassName		= "TIOTest";
45 const char* gLocalSig			= "application/x-vnd.LocalSignature";
46 const char* gRemoteClassName	= "TRemoteTestObject";
47 const char* gRemoteSig			= "application/x-vnd.RemoteObjectDef";
48 const char* gValidSig			= gRemoteSig;
49 #if !TEST_R5
50 const char* gRemoteLib			= "/lib/libsupporttest_RemoteTestObject.so";
51 #else
52 const char* gRemoteLib			= "/lib/libsupporttest_RemoteTestObject_r5.so";
53 #endif
54 
55 void FormatAndThrow(int line, const char* file, const char* msg, int err);
56 
57 //------------------------------------------------------------------------------
TInstantiateObjectTester(string name)58 TInstantiateObjectTester::TInstantiateObjectTester(string name)
59 	:	BTestCase(name), fAddonId(B_ERROR)
60 {
61 	;
62 }
63 //------------------------------------------------------------------------------
64 /**
65 	instantiate_object(BMessage* archive, image_id* id)
66 	@case			Invalid archive
67 	@param archive	NULL
68 	@param id		Valid image_id pointer
69 	@results		Returns NULL.
70 					*id is set to B_BAD_VALUE.
71 					errno is set to B_BAD_VALUE.
72  */
Case1()73 void TInstantiateObjectTester::Case1()
74 {
75 	errno = B_OK;
76 	image_id id = B_OK;
77 	TIOTest* Test = (TIOTest*)instantiate_object(NULL, &id);
78 	CPPUNIT_ASSERT(Test == NULL);
79 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
80 	CPPUNIT_ASSERT(errno == B_BAD_VALUE);
81 }
82 //------------------------------------------------------------------------------
83 /**
84 	instantiate_object(BMessage* archive, image_id* id)
85 	@case			No class name
86 	@param archive	Valid BMessage pointer without string field "class"
87 	@param id		Valid image_id pointer
88 	@results		Returns NULL.
89 					*id is set to B_BAD_VALUE.
90 					errno is set to B_OK.
91  */
Case2()92 void TInstantiateObjectTester::Case2()
93 {
94 	errno = B_OK;
95 	BMessage Archive;
96 	image_id id = B_OK;
97 	TIOTest* Test = (TIOTest*)instantiate_object(&Archive, &id);
98 	CPPUNIT_ASSERT(Test == NULL);
99 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
100 	CPPUNIT_ASSERT(errno == B_OK);
101 }
102 //------------------------------------------------------------------------------
103 
104 //------------------------------------------------------------------------------
105 //	Invalid class name tests
106 //------------------------------------------------------------------------------
107 /**
108 	instantiate_object(BMessage* archive, image_id* id)
109 	@case			Invalid class name
110 	@param archive	Valid BMessage pointer, with string field labeled "class"
111 					containing an invalid class name
112 	@param id		Valid image_id pointer
113 	@results		Returns NULL.
114 					*id is set to B_BAD_VALUE.
115 					errno is set to B_BAD_VALUE.
116  */
Case3()117 void TInstantiateObjectTester::Case3()
118 {
119 	errno = B_OK;
120 	BMessage Archive;
121 	Archive.AddString("class", gInvalidClassName);
122 	image_id id = B_OK;
123 	TIOTest* Test = (TIOTest*)instantiate_object(&Archive, &id);
124 	CPPUNIT_ASSERT(Test == NULL);
125 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
126 	CPPUNIT_ASSERT(errno == B_BAD_VALUE);
127 }
128 //------------------------------------------------------------------------------
129 /**
130 	instantiate_object(BMessage* archive, image_id* id)
131 	@case			Invalid class name and signature
132 	@param archive	Valid BMessage pointer, with string fields labeled "class"
133 					and "add_on", containing invalid class name and signature,
134 					respectively
135 	@param id		Valid image_id pointer
136 	@results		Returns NULL.
137 					*id is set to B_BAD_VALUE.
138 					errno is set to B_LAUNCH_FAILED_APP_NOT_FOUND.
139  */
Case4()140 void TInstantiateObjectTester::Case4()
141 {
142 	errno = B_OK;
143 	BMessage Archive;
144 	Archive.AddString("class", gInvalidClassName);
145 	Archive.AddString("add_on", gInvalidSig);
146 	image_id id = B_OK;
147 	TIOTest* Test = (TIOTest*)instantiate_object(&Archive, &id);
148 	CPPUNIT_ASSERT(Test == NULL);
149 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
150 	CPPUNIT_ASSERT(errno == B_LAUNCH_FAILED_APP_NOT_FOUND);
151 }
152 //------------------------------------------------------------------------------
153 /**
154 	instantiate_object(BMessage* archive, image_id* id)
155 	@case			Invalid class name, valid signature
156 	@param archive	Valid BMessage pointer with string fields labeled "class"
157 					and "add_on", containing invalid class name and valid
158 					signature, respectively
159 	@param id		Valid image_id pointer
160 	@requires		RemoteObjectDef add-on must be built and accessible
161 	@results		Returns NULL.
162 					*id is > 0 (add-on was loaded)
163 					errno is set to B_BAD_VALUE.
164  */
Case5()165 void TInstantiateObjectTester::Case5()
166 {
167 	errno = B_OK;
168 	BMessage Archive;
169 	Archive.AddString("class", gInvalidClassName);
170 	Archive.AddString("add_on", gValidSig);
171 	image_id id = B_OK;
172 	TIOTest* Test = (TIOTest*)instantiate_object(&Archive, &id);
173 	CPPUNIT_ASSERT(Test == NULL);
174 	// The system implementation returns the image_id of the last addon searched
175 	// Implies the addon is not unloaded.  How to verify this behaviour?  Should
176 	// the addon be unloaded if it doesn't contain our function?  Addons do,
177 	// after all, eat into our allowable memory.
178 
179 	// Verified that addon is *not* unloaded in the Be implementation.  If Case8
180 	// runs after this case without explicitely unloaded the addon here, it
181 	// fails because it depends on the addon image not being available within
182 	// the team.
183 	CPPUNIT_ASSERT(id > 0);
184 	unload_add_on(id);
185 	CPPUNIT_ASSERT(errno == B_BAD_VALUE);
186 }
187 //------------------------------------------------------------------------------
188 
189 
190 //------------------------------------------------------------------------------
191 //	Valid class name tests
192 //------------------------------------------------------------------------------
193 /**
194 	instantiate_object(BMessage* archive, image_id* id)
195 	@case			Valid archive of class defined in local image
196 	@param archive	Valid BMessage pointer with string field "class" containing
197 					name of locally defined class which can be instantiated via
198 					archiving mechanism
199 	@param id		Valid image_id pointer
200 	@requires		locally defined class which can be instantiated via
201 					archiving mechanism
202 	@results		Returns valid TIOTest instance.
203 					*id is set to B_BAD_VALUE (no image was loaded).
204 					errno is set to B_OK.
205  */
206 //	No sig
207 //		Local app -- local class
Case6()208 void TInstantiateObjectTester::Case6()
209 {
210 	errno = B_OK;
211 	BMessage Archive;
212 	Archive.AddString("class", gLocalClassName);
213 	image_id id = B_OK;
214 	TIOTest* Test = (TIOTest*)instantiate_object(&Archive, &id);
215 	CPPUNIT_ASSERT(Test != NULL);
216 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
217 	CPPUNIT_ASSERT(errno == B_OK);
218 }
219 //------------------------------------------------------------------------------
220 /**
221 	instantiate_object(BMessage* archive, image_id* id)
222 	@case			Valid archive of class defined in add-on explicitely loaded
223 					by this team
224 	@param archive	Valid BMessage pointer with string field "class" containing
225 					name of remotely defined class which can be instantiated via
226 					archiving mechanism
227 	@param id		Valid image_id pointer
228 	@requires		RemoteObjectDef add-on must be built and accessible
229 	@results		Returns valid TRemoteTestObject instance.
230 					*id is set to B_BAD_VALUE (no image was loaded).
231 					errno is set to B_OK.
232  */
Case7()233 void TInstantiateObjectTester::Case7()
234 {
235 	errno = B_OK;
236 	LoadAddon();
237 
238 	BMessage Archive;
239 	Archive.AddString("class", gRemoteClassName);
240 	image_id id = B_OK;
241 	TRemoteTestObject* Test = (TRemoteTestObject*)instantiate_object(&Archive,
242 																	 &id);
243 	CPPUNIT_ASSERT(Test != NULL);
244 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
245 	CPPUNIT_ASSERT(errno == B_OK);
246 
247 	UnloadAddon();
248 }
249 //------------------------------------------------------------------------------
250 /**
251 	instantiate_object(BMessage* archive, image_id* id)
252 	@case			Valid archive of remotely-defined class, without required
253 					signature of the defining add-on
254 	@param archive	Valid BMessage pointer with string field "class" containing
255 					name of remotely-defined class; no "add-on" field
256 	@param id		Valid image_id pointer
257 	@results		Returns NULL.
258 					*id is set to B_BAD_VALUE (no image loaded).
259 					errno is set to B_BAD_VALUE.
260  */
Case8()261 void TInstantiateObjectTester::Case8()
262 {
263 	errno = B_OK;
264 	BMessage Archive;
265 	CPPUNIT_ASSERT(Archive.AddString("class", gRemoteClassName) == B_OK);
266 	image_id id = B_OK;
267 	TRemoteTestObject* Test = (TRemoteTestObject*)instantiate_object(&Archive,
268 																	 &id);
269 	CPPUNIT_ASSERT(Test == NULL);
270 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
271 	CPPUNIT_ASSERT(errno == B_BAD_VALUE);
272 }
273 //------------------------------------------------------------------------------
274 /**
275 	instantiate_object(BMessage* archive, image_id* id)
276 	@case			Valid archive naming locally defined class with invalid
277 					signature
278 	@param archive	Valid BMessage pointer with string field "class" containing
279 					name of locally defined class and string field "add_on"
280 					containing invalid signature
281 	@param id		Valid image_id pointer
282 	@results		Returns NULL.
283 					*id is set to B_BAD_VALUE (no image loaded).
284 					errno is set to B_LAUNCH_FAILED_APP_NOT_FOUND.
285  */
Case9()286 void TInstantiateObjectTester::Case9()
287 {
288 	errno = B_OK;
289 	BMessage Archive;
290 	CPPUNIT_ASSERT(Archive.AddString("class", gLocalClassName) == B_OK);
291 	CPPUNIT_ASSERT(Archive.AddString("add_on", gInvalidSig) == B_OK);
292 	image_id id = B_OK;
293 	TIOTest* Test = (TIOTest*)instantiate_object(&Archive, &id);
294 	CPPUNIT_ASSERT(Test == NULL);
295 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
296 	CPPUNIT_ASSERT(errno == B_LAUNCH_FAILED_APP_NOT_FOUND);
297 }
298 //------------------------------------------------------------------------------
299 /**
300 	instantiate_object(BMessage* archive, image_id* id)
301 	@case			Valid archive of class defined in add-on explicitely loaded
302 					by this team, but with an invalid signature
303 	@param archive	Valid BMessage pointer with string field "class" containing
304 					name of remotely-defined class and string field "add_on"
305 					containing invalid signature
306 	@param id		Valid image_id pointer
307 	@requires		RemoteObjectDef add-on must be built and accessible
308 	@results		Returns NULL.
309 					*id is set to B_BAD_VALUE (no image loaded).
310 					errno is set to B_LAUNCH_FAILED_APP_NOT_FOUND.
311  */
Case10()312 void TInstantiateObjectTester::Case10()
313 {
314 	errno = B_OK;
315 	LoadAddon();
316 
317 	BMessage Archive;
318 	Archive.AddString("class", gRemoteClassName);
319 	Archive.AddString("add_on", gInvalidSig);
320 	image_id id = B_OK;
321 	TIOTest* Test = (TIOTest*)instantiate_object(&Archive, &id);
322 	CPPUNIT_ASSERT(Test == NULL);
323 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
324 	CPPUNIT_ASSERT(errno == B_LAUNCH_FAILED_APP_NOT_FOUND);
325 
326 	UnloadAddon();
327 }
328 //------------------------------------------------------------------------------
329 /**
330 	instantiate_object(BMessage* archive, image_id* id)
331 	@case			Valid archive of remotely-defined class, with invalid
332 					signature
333 	@param archive	Valid BMessage pointer with string field "class" containing
334 					name of remotely-defined class and string field add-on
335 					containing invalid signature
336 	@param id		Valid image_id pointer
337 	@results		Returns NULL.
338 					*id is set to B_BAD_VALUE.
339 					errno is set to B_LAUNCH_FAILED_APP_NOT_FOUND
340  */
Case11()341 void TInstantiateObjectTester::Case11()
342 {
343 	errno = B_OK;
344 	BMessage Archive;
345 	Archive.AddString("class", gRemoteClassName);
346 	Archive.AddString("add_on", gInvalidSig);
347 	image_id id = B_OK;
348 	TIOTest* Test = (TIOTest*)instantiate_object(&Archive, &id);
349 	CPPUNIT_ASSERT(Test == NULL);
350 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
351 	CPPUNIT_ASSERT(errno == B_LAUNCH_FAILED_APP_NOT_FOUND);
352 }
353 //------------------------------------------------------------------------------
354 /**
355 	instantiate_object(BMessage* archive, image_id* id)
356 	@case			Valid archive of locally-defined class with correct
357 					signature
358 	@param archive	Valid BMessage pointer with string field "class" containing
359 					name of locally-defined class and string field "add_on"
360 					containing signature of current team
361 	@param id		Valid image_id pointer
362 	@requires		locally defined class which can be instantiated via
363 					archiving mechanism
364 	@results		Returns valid TIOTest instance.
365 					*id is set to B_BAD_VALUE (no image loaded).
366 					errno is set to B_OK.
367 	@note			This test is not currently used; GetLocalSignature() doesn't
368 					seem to work without a BApplication instance constructed.
369 					See GetLocalSignature() for more info.
370  */
Case12()371 void TInstantiateObjectTester::Case12()
372 {
373 	errno = B_OK;
374 	BMessage Archive;
375 	Archive.AddString("class", gLocalClassName);
376 	Archive.AddString("add_on", GetLocalSignature().c_str());
377 	image_id id = B_OK;
378 	TIOTest* Test = (TIOTest*)instantiate_object(&Archive, &id);
379 	CPPUNIT_ASSERT(Test != NULL);
380 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
381 	CPPUNIT_ASSERT(errno == B_OK);
382 }
383 //------------------------------------------------------------------------------
384 /**
385 	instantiate_object(BMessage* archive, image_id* id)
386 	@case			Valid archive of class defined in add-on explicitely loaded
387 					by this team with signature of add-on
388 	@param archive	Valid BMessage pointer with string field "class" containing
389 					name of remotely-defined class and string field "add_on"
390 					containing signature of loaded add-on
391 	@param id		Valid image_id pointer
392 	@requires		RemoteObjectDef add-on must be built and accessible
393 	@results		Returns valid instance of TRemoteTestObject.
394 					*id is set to B_BAD_VALUE (image load not necessary).
395 					errno is set to B_OK.
396  */
Case13()397 void TInstantiateObjectTester::Case13()
398 {
399 	errno = B_OK;
400 	LoadAddon();
401 
402 	BMessage Archive;
403 	Archive.AddString("class", gRemoteClassName);
404 	Archive.AddString("add_on", gRemoteSig);
405 	image_id id = B_OK;
406 	TRemoteTestObject* Test = (TRemoteTestObject*)instantiate_object(&Archive, &id);
407 	CPPUNIT_ASSERT(Test != NULL);
408 	CPPUNIT_ASSERT(id == B_BAD_VALUE);
409 	CPPUNIT_ASSERT(errno == B_OK);
410 
411 	UnloadAddon();
412 }
413 //------------------------------------------------------------------------------
414 /**
415 	instantiate_object(BMessage* archive, image_id* id)
416 	@case			Valid archive of remotely-defined class with correct
417 					signature
418 	@param archive	Valid BMessage pointer with string field "class" containing
419 					name of remotely-defined class and string field "add_on"
420 					containing signature of defining add-on
421 	@param id		Valid image_id pointer
422 	@requires		RemoteObjectDef must be built and accessible
423 	@results		Returns valid instance of TRemoteTestObject.
424 					*id > 0 (image was loaded).
425 					errno is set to B_OK.
426  */
Case14()427 void TInstantiateObjectTester::Case14()
428 {
429 	errno = B_OK;
430 	BMessage Archive;
431 	Archive.AddString("class", gRemoteClassName);
432 	Archive.AddString("add_on", gRemoteSig);
433 	image_id id = B_OK;
434 	TRemoteTestObject* Test = (TRemoteTestObject*)instantiate_object(&Archive, &id);
435 	CPPUNIT_ASSERT(Test != NULL);
436 	CPPUNIT_ASSERT(id > 0);
437 	unload_add_on(id);
438 	CPPUNIT_ASSERT(errno == B_OK);
439 }
440 //------------------------------------------------------------------------------
Suite()441 CppUnit::Test* TInstantiateObjectTester::Suite()
442 {
443 	CppUnit::TestSuite* SuiteOfTests = new CppUnit::TestSuite;
444 
445 //	SuiteOfTests->addTest(
446 //		new CppUnit::TestCaller<TInstantiateObjectTester>("BArchivable::instantiate_object() Test",
447 //			&TInstantiateObjectTester::RunTests));
448 
449 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case1);
450 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case2);
451 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case3);
452 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case4);
453 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case5);
454 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case6);
455 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case8);
456 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case7);
457 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case9);
458 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case10);
459 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case11);
460 //	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case12);
461 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case13);
462 	ADD_TEST(SuiteOfTests, TInstantiateObjectTester, Case14);
463 
464 	return SuiteOfTests;
465 }
466 //------------------------------------------------------------------------------
LoadAddon()467 void TInstantiateObjectTester::LoadAddon()
468 {
469 	if (fAddonId > 0)
470 		return;
471 
472 	// We're not testing the roster, so I'm going to just
473 	// find the add-on manually.
474 	std::string libPath = std::string(BTestShell::GlobalTestDir()) + gRemoteLib;
475 	cout << "dir == '" << libPath << "'" << endl;
476 	fAddonId = load_add_on(libPath.c_str());
477 
478 	RES(fAddonId);
479 	if (fAddonId <= 0)
480 	{
481 		FORMAT_AND_THROW(" failed to load addon: ", fAddonId);
482 	}
483 }
484 //------------------------------------------------------------------------------
UnloadAddon()485 void TInstantiateObjectTester::UnloadAddon()
486 {
487 	if (fAddonId > 0)
488 	{
489 		status_t err = unload_add_on(fAddonId);
490 		fAddonId = B_ERROR;
491 		if (err)
492 		{
493 			FORMAT_AND_THROW(" failed to unload addon: ", err);
494 		}
495 	}
496 }
497 //------------------------------------------------------------------------------
GetLocalSignature()498 std::string TInstantiateObjectTester::GetLocalSignature()
499 {
500 	BRoster Roster;
501 	app_info ai;
502 	team_id team;
503 
504 	// Get the team_id of this app
505 	thread_id tid = find_thread(NULL);
506 	thread_info ti;
507 	status_t err = get_thread_info(tid, &ti);
508 	if (err)
509 	{
510 		FORMAT_AND_THROW(" failed to get thread_info: ", err);
511 	}
512 
513 	// Get the app_info via the team_id
514 	team = ti.team;
515 	team_info info;
516 	err = get_team_info(team, &info);
517 	if (err)
518 	{
519 		FORMAT_AND_THROW(" failed to get team_info: ", err);
520 	}
521 
522 	team = info.team;
523 
524 	// It seems that this call to GetRunningAppInfo() is not working because we
525 	// don't have an instance of BApplication somewhere -- the roster, therefore,
526 	// doesn't know about us.
527 	err = Roster.GetRunningAppInfo(team, &ai);
528 	if (err)
529 	{
530 		FORMAT_AND_THROW(" failed to get app_info: ", err);
531 	}
532 
533 	// Return the signature from the app_info
534 	return ai.signature;
535 }
536 //------------------------------------------------------------------------------
537 
538 //------------------------------------------------------------------------------
FormatAndThrow(int line,const char * file,const char * msg,int err)539 void FormatAndThrow(int line, const char *file, const char *msg, int err)
540 {
541 	std::string s("line: ");
542 	s += IntToStr(line);
543 	s += " ";
544 	s += file;
545 	s += msg;
546 	s += strerror(err);
547 	s += "(";
548 	s += IntToStr(err);
549 	s += ")";
550 	CppUnit::Exception re(s.c_str());
551 	throw re;
552 }
553 //------------------------------------------------------------------------------
554 
555 void
RunTests()556 TInstantiateObjectTester::RunTests() {
557 	NextSubTest();
558 	Case1();
559 	NextSubTest();
560 	Case2();
561 	NextSubTest();
562 	Case3();
563 	NextSubTest();
564 	Case4();
565 	NextSubTest();
566 	Case5();
567 	NextSubTest();
568 	Case6();
569 	NextSubTest();
570 	Case7();
571 	NextSubTest();
572 	Case8();
573 	NextSubTest();
574 	Case9();
575 	NextSubTest();
576 	Case10();
577 	NextSubTest();
578 	Case11();
579 	NextSubTest();
580 	Case12();
581 	NextSubTest();
582 	Case13();
583 	NextSubTest();
584 	Case14();
585 }
586 
587 /*
588  * $Log $
589  *
590  * $Id  $
591  *
592  */
593 
594 
595