xref: /haiku/src/tests/add-ons/kernel/drivers/tty/tty-test.cpp (revision b55a57da7173b9af0432bd3e148d03f06161d036)
1 /*
2  * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <algorithm>
7 
8 #include <assert.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <sys/select.h>
16 
17 #include <OS.h>
18 
19 #include <util/DoublyLinkedList.h>
20 
21 //	Test cases:
22 //
23 //	1. unblock on close:
24 //
25 //	* unblock, when the same cookie is closed
26 //	  - read
27 //	  - write (with, without ECHO)
28 //	* unblock write operations, when the other tty is closed
29 //	* unblock slave operations, when the master is closed
30 //
31 //
32 //	2. select:
33 //
34 //	* notify read, write, if ready when select()ing
35 //	* notify read, write, error on close of the other TTY
36 //    (the select() behavior when closing the same TTY is undefined)
37 //	* notify read after pending write
38 //	  - with ECHO
39 //	  - without ECHO
40 //	* notify write after pending read and full buffer
41 //	  - with ECHO
42 //	  - without ECHO
43 //	* don't notify when there was a pending read/write
44 //
45 //
46 // TODO: There are no ECHO tests yet, since I don't know how to turn it on.
47 
48 #define CHK(condition)		assert(condition)
49 #define RUN_TEST(test)		(test)->Run()
50 #define CHK_ALIVE(thread)	CHK((thread)->IsAlive())
51 #define CHK_DEAD(thread)	CHK(!(thread)->IsAlive())
52 
53 // FDSet
54 struct FDSet : fd_set {
55 	FDSet()
56 	{
57 		Clear();
58 	}
59 
60 	void Clear()
61 	{
62 		FD_ZERO(this);
63 		fCount = 0;
64 	}
65 
66 	void Add(int fd)
67 	{
68 		if (fd < 0 || fd >= FD_SETSIZE) {
69 			fprintf(stderr, "FDSet::Add(%d): Invalid FD.\n", fd);
70 			return;
71 		}
72 
73 		FD_SET(fd, this);
74 		if (fd >= fCount)
75 			fCount = fd + 1;
76 	}
77 
78 	int Count() const
79 	{
80 		return fCount;
81 	}
82 
83 	void UpdateCount()
84 	{
85 		if (fCount > 0) {
86 			for (int i = fCount - 1; i >= 0; i--) {
87 				if (FD_ISSET(i, this)) {
88 					fCount = i + 1;
89 					return;
90 				}
91 			}
92 			fCount = 0;
93 		}
94 	}
95 
96 	bool operator==(const FDSet &other) const
97 	{
98 		if (fCount != other.fCount)
99 			return false;
100 
101 		for (int i = 0; i < fCount; i++) {
102 			if (FD_ISSET(i, this) != FD_ISSET(i, &other))
103 				return false;
104 		}
105 
106 		return true;
107 	}
108 
109 	bool operator!=(const FDSet &other) const
110 	{
111 		return !(*this == other);
112 	}
113 
114 private:
115 	int	fCount;
116 };
117 
118 // SelectSet
119 class SelectSet {
120 public:
121 	SelectSet() {}
122 	~SelectSet() {}
123 
124 	void Clear()
125 	{
126 		fReadSet.Clear();
127 		fWriteSet.Clear();
128 		fErrorSet.Clear();
129 	}
130 
131 	void AddReadFD(int fd)
132 	{
133 		fReadSet.Add(fd);
134 	}
135 
136 	void AddWriteFD(int fd)
137 	{
138 		fWriteSet.Add(fd);
139 	}
140 
141 	void AddErrorFD(int fd)
142 	{
143 		fErrorSet.Add(fd);
144 	}
145 
146 	int Select(bigtime_t timeout = -1)
147 	{
148 		int count = max(max(fReadSet.Count(), fWriteSet.Count()),
149 			fErrorSet.Count());
150 		fd_set *readSet = (fReadSet.Count() > 0 ? &fReadSet : NULL);
151 		fd_set *writeSet = (fWriteSet.Count() > 0 ? &fWriteSet : NULL);
152 		fd_set *errorSet = (fErrorSet.Count() > 0 ? &fErrorSet : NULL);
153 
154 		timeval tv = { timeout / 1000000, timeout % 1000000 };
155 
156 		int result = select(count, readSet, writeSet, errorSet,
157 			(timeout >= 0 ? &tv : NULL));
158 		if (result <= 0) {
159 			Clear();
160 		} else {
161 			fReadSet.UpdateCount();
162 			fWriteSet.UpdateCount();
163 			fErrorSet.UpdateCount();
164 		}
165 
166 		return result;
167 	}
168 
169 	bool operator==(const SelectSet &other) const
170 	{
171 		return (fReadSet == other.fReadSet
172 			&& fWriteSet == other.fWriteSet
173 			&& fErrorSet == other.fErrorSet);
174 	}
175 
176 	bool operator!=(const SelectSet &other) const
177 	{
178 		return !(*this == other);
179 	}
180 
181 private:
182 	FDSet	fReadSet;
183 	FDSet	fWriteSet;
184 	FDSet	fErrorSet;
185 };
186 
187 // Runnable
188 class Runnable {
189 public:
190 	virtual ~Runnable() {}
191 	virtual int32 Run() = 0;
192 };
193 
194 // Caller
195 template<typename Type>
196 class Caller : public Runnable {
197 public:
198 	Caller(Type *object, int32 (Type::*function)())
199 		: fObject(object),
200 		  fFunction(function)
201 	{
202 	}
203 
204 	virtual int32 Run()
205 	{
206 		return (fObject->*fFunction)();
207 	}
208 
209 private:
210 	Type	*fObject;
211 	int32	(Type::*fFunction)();
212 };
213 
214 // create_caller
215 template<typename Type>
216 static inline Runnable *
217 create_caller(Type *object, int32 (Type::*function)())
218 {
219 	return new Caller<Type>(object, function);
220 }
221 
222 #define CALLER(object, function)	create_caller(object, function)
223 
224 // Thread
225 struct Thread : public DoublyLinkedListLinkImpl<Thread> {
226 public:
227 	Thread(Runnable *runnable, const char *name)
228 		: fRunnable(runnable)
229 	{
230 		fThread = spawn_thread(_Entry, name, B_NORMAL_PRIORITY, this);
231 		if (fThread < 0) {
232 			sprintf("Failed to spawn thread: %s\n", strerror(fThread));
233 			exit(1);
234 		}
235 	}
236 
237 	~Thread()
238 	{
239 		Kill();
240 		delete fRunnable;
241 	}
242 
243 	void Resume()
244 	{
245 		resume_thread(fThread);
246 	}
247 
248 	void WaitFor()
249 	{
250 		status_t result;
251 		wait_for_thread(fThread, &result);
252 	}
253 
254 	void Kill()
255 	{
256 		kill_thread(fThread);
257 	}
258 
259 	bool IsAlive()
260 	{
261 		thread_info info;
262 		return (get_thread_info(fThread, &info) == B_OK);
263 	}
264 
265 private:
266 	static int32 _Entry(void *data)
267 	{
268 		return ((Thread*)data)->fRunnable->Run();
269 	}
270 
271 	thread_id	fThread;
272 	Runnable	*fRunnable;
273 };
274 
275 // TestCase
276 class TestCase {
277 public:
278 	TestCase() {}
279 	virtual ~TestCase()
280 	{
281 		while (Thread *thread = fThreads.Head()) {
282 			fThreads.Remove(thread);
283 			delete thread;
284 		}
285 	}
286 
287 	void Run()
288 	{
289 		Test();
290 		delete this;
291 	}
292 
293 protected:
294 	virtual void Test() = 0;
295 
296 	Thread *CreateThread(Runnable *runnable, const char *name)
297 	{
298 		Thread *thread = new Thread(runnable, name);
299 		fThreads.Add(thread);
300 		return thread;
301 	}
302 
303 	static void WriteUntilBlock(int fd)
304 	{
305 		// set non-blocking I/O
306 		if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
307 			fprintf(stderr, "WriteUntilBlock(): Failed to set non-blocking I/O "
308 				"mode: %s\n", strerror(errno));
309 			exit(1);
310 		}
311 
312 		// write till blocking
313 		char buffer[1024];
314 		memset(buffer, 'A', sizeof(buffer));
315 		ssize_t bytesWritten;
316 		do {
317 			bytesWritten = write(fd, buffer, sizeof(buffer));
318 		} while (bytesWritten > 0 || errno == B_INTERRUPTED);
319 
320 		if (bytesWritten < 0 && errno != B_WOULD_BLOCK) {
321 			fprintf(stderr, "WriteUntilBlock(): Writing failed: %s\n",
322 				strerror(errno));
323 			exit(1);
324 		}
325 
326 		// reset to blocking I/O
327 		if (fcntl(fd, F_SETFL, 0) < 0) {
328 			fprintf(stderr, "WriteUntilBlock(): Failed to set blocking I/O "
329 				"mode: %s\n", strerror(errno));
330 			exit(1);
331 		}
332 	}
333 
334 	static void ReadDontFail(int fd, int32 size)
335 	{
336 		char buffer[1024];
337 
338 		while (size > 0) {
339 			ssize_t bytesRead;
340 			do {
341 				int32 toRead = sizeof(buffer);
342 				if (toRead > size)
343 					toRead = size;
344 
345 				bytesRead = read(fd, buffer, toRead);
346 			} while (bytesRead < 0 && errno == B_INTERRUPTED);
347 
348 			if (bytesRead < 0) {
349 				fprintf(stderr, "ReadDontFail(): Failed to read: %s\n",
350 					strerror(errno));
351 				exit(1);
352 			}
353 
354 			size -= bytesRead;
355 		}
356 	}
357 
358 	static void WriteDontFail(int fd, int32 size)
359 	{
360 		char buffer[1024];
361 		memset(buffer, 'A', sizeof(buffer));
362 
363 		while (size > 0) {
364 			ssize_t bytesWritten;
365 			do {
366 				int32 toWrite = sizeof(buffer);
367 				if (toWrite > size)
368 					toWrite = size;
369 
370 				bytesWritten = write(fd, buffer, toWrite);
371 			} while (bytesWritten < 0 && errno == B_INTERRUPTED);
372 
373 			if (bytesWritten < 0) {
374 				fprintf(stderr, "WriteDontFail(): Failed to write: %s\n",
375 					strerror(errno));
376 				exit(1);
377 			}
378 
379 			size -= bytesWritten;
380 		}
381 	}
382 
383 	void SetEcho(int fd)
384 	{
385 		// TODO: How to set echo mode?
386 	}
387 
388 private:
389 	typedef DoublyLinkedList<Thread> ThreadList;
390 
391 	ThreadList	fThreads;
392 };
393 
394 
395 // open_tty
396 static int
397 open_tty(int index, bool master)
398 {
399 	if (index < 0 || index >= 16)
400 		fprintf(stderr, "open_tty(%d, %d): Bad index!\n", index, master);
401 
402 	char path[32];
403 	sprintf(path, "/dev/%ct/r%x", (master ? 'p' : 't'), index);
404 	int fd = open(path, O_RDWR);
405 
406 	if (fd < 0) {
407 		fprintf(stderr, "Failed to open tty `%s': %s\n", path, strerror(errno));
408 		exit(1);
409 	}
410 
411 	return fd;
412 }
413 
414 
415 static void
416 close_tty(int &fd)
417 {
418 	if (fd >= 0) {
419 		close(fd);
420 		fd = -1;
421 	}
422 }
423 
424 
425 // #pragma mark -
426 
427 // TestUnblockOnCloseRead
428 class TestUnblockOnCloseRead : public TestCase {
429 public:
430 	TestUnblockOnCloseRead(bool master, bool crossOver)
431 		: fMaster(-1),
432 		  fSlave(-1),
433 		  fTestMaster(master),
434 		  fCrossOver(crossOver)
435 	{
436 		printf("TestUnblockOnCloseRead(%d, %d)\n", master, crossOver);
437 	}
438 
439 
440 protected:
441 	virtual ~TestUnblockOnCloseRead()
442 	{
443 		close_tty(fMaster);
444 		close_tty(fSlave);
445 	}
446 
447 	virtual void Test()
448 	{
449 		fMaster = open_tty(0, true);
450 		fSlave = open_tty(0, false);
451 
452 		Thread *thread = CreateThread(
453 			CALLER(this, &TestUnblockOnCloseRead::_Reader), "reader");
454 		thread->Resume();
455 
456 		snooze(100000);
457 		CHK_ALIVE(thread);
458 
459 		if (fCrossOver)
460 			close_tty(fTestMaster ? fSlave : fMaster);
461 		else
462 			close_tty(fTestMaster ? fMaster : fSlave);
463 
464 		snooze(100000);
465 		CHK_DEAD(thread);
466 	}
467 
468 private:
469 	int32 _Reader()
470 	{
471 		char buffer[32];
472 		ssize_t bytesRead = read((fTestMaster ? fMaster : fSlave), buffer,
473 			sizeof(buffer));
474 
475 		CHK((bytesRead < 0));
476 
477 		return 0;
478 	}
479 
480 private:
481 	int		fMaster;
482 	int		fSlave;
483 	bool	fTestMaster;
484 	bool	fCrossOver;
485 };
486 
487 // TestUnblockOnCloseWrite
488 class TestUnblockOnCloseWrite : public TestCase {
489 public:
490 	TestUnblockOnCloseWrite(bool master, bool crossOver, bool echo)
491 		: fMaster(-1),
492 		  fSlave(-1),
493 		  fTestMaster(master),
494 		  fCrossOver(crossOver),
495 		  fEcho(echo)
496 	{
497 		printf("TestUnblockOnCloseWrite(%d, %d, %d)\n", master, crossOver,
498 			echo);
499 	}
500 
501 
502 protected:
503 	virtual ~TestUnblockOnCloseWrite()
504 	{
505 		close_tty(fMaster);
506 		close_tty(fSlave);
507 	}
508 
509 	virtual void Test()
510 	{
511 		fMaster = open_tty(0, true);
512 		fSlave = open_tty(0, false);
513 
514 		if (fEcho)
515 			SetEcho((fTestMaster ? fSlave : fMaster));
516 
517 		WriteUntilBlock((fTestMaster ? fMaster : fSlave));
518 
519 		Thread *thread = CreateThread(
520 			CALLER(this, &TestUnblockOnCloseWrite::_Writer), "writer");
521 		thread->Resume();
522 
523 		snooze(100000);
524 		CHK_ALIVE(thread);
525 
526 		if (fCrossOver)
527 			close_tty(fTestMaster ? fSlave : fMaster);
528 		else
529 			close_tty(fTestMaster ? fMaster : fSlave);
530 
531 		snooze(100000);
532 		CHK_DEAD(thread);
533 	}
534 
535 private:
536 	int32 _Writer()
537 	{
538 		char buffer[32];
539 		memset(buffer, 'A', sizeof(buffer));
540 		ssize_t bytesWritten = write((fTestMaster ? fMaster : fSlave), buffer,
541 			sizeof(buffer));
542 
543 		CHK((bytesWritten < 0));
544 
545 		return 0;
546 	}
547 
548 private:
549 	int		fMaster;
550 	int		fSlave;
551 	bool	fTestMaster;
552 	bool	fCrossOver;
553 	bool	fEcho;
554 };
555 
556 // TestSelectAlreadyReady
557 class TestSelectAlreadyReady : public TestCase {
558 public:
559 	TestSelectAlreadyReady(bool master, bool write)
560 		: fMaster(-1),
561 		  fSlave(-1),
562 		  fTestMaster(master),
563 		  fWrite(write)
564 	{
565 		printf("TestSelectAlreadyReady(%d, %d)\n", master, write);
566 	}
567 
568 
569 protected:
570 	virtual ~TestSelectAlreadyReady()
571 	{
572 		close_tty(fMaster);
573 		close_tty(fSlave);
574 	}
575 
576 	virtual void Test()
577 	{
578 		fMaster = open_tty(0, true);
579 		fSlave = open_tty(0, false);
580 
581 		if (!fWrite)
582 			WriteDontFail((fTestMaster ? fSlave : fMaster), 1);
583 
584 		Thread *thread = CreateThread(
585 			CALLER(this, &TestSelectAlreadyReady::_Selector), "selector");
586 		thread->Resume();
587 
588 		snooze(100000);
589 		CHK_DEAD(thread);
590 	}
591 
592 private:
593 	int32 _Selector()
594 	{
595 		SelectSet selectSet;
596 		SelectSet compareSet;
597 		if (fWrite) {
598 			selectSet.AddWriteFD((fTestMaster ? fMaster : fSlave));
599 			compareSet.AddWriteFD((fTestMaster ? fMaster : fSlave));
600 		} else {
601 			selectSet.AddReadFD((fTestMaster ? fMaster : fSlave));
602 			compareSet.AddReadFD((fTestMaster ? fMaster : fSlave));
603 		}
604 
605 		int result = selectSet.Select();
606 		CHK(result > 0);
607 		CHK(selectSet == compareSet);
608 
609 		return 0;
610 	}
611 
612 private:
613 	int		fMaster;
614 	int		fSlave;
615 	bool	fTestMaster;
616 	bool	fWrite;
617 };
618 
619 // TestSelectNotifyOnClose
620 class TestSelectNotifyOnClose : public TestCase {
621 public:
622 	TestSelectNotifyOnClose(bool master)
623 		: fMaster(-1),
624 		  fSlave(-1),
625 		  fTestMaster(master)
626 	{
627 		printf("TestSelectNotifyOnClose(%d)\n", master);
628 	}
629 
630 
631 protected:
632 	virtual ~TestSelectNotifyOnClose()
633 	{
634 		close_tty(fMaster);
635 		close_tty(fSlave);
636 	}
637 
638 	virtual void Test()
639 	{
640 		fMaster = open_tty(0, true);
641 		fSlave = open_tty(0, false);
642 
643 		WriteUntilBlock((fTestMaster ? fMaster : fSlave));
644 
645 		Thread *thread = CreateThread(
646 			CALLER(this, &TestSelectNotifyOnClose::_Selector), "selector");
647 		thread->Resume();
648 
649 		snooze(100000);
650 		CHK_ALIVE(thread);
651 
652 		close_tty((fTestMaster ? fSlave : fMaster));
653 
654 		snooze(100000);
655 		CHK_DEAD(thread);
656 	}
657 
658 private:
659 	int32 _Selector()
660 	{
661 		int fd = (fTestMaster ? fMaster : fSlave);
662 
663 		SelectSet selectSet;
664 		selectSet.AddReadFD(fd);
665 		selectSet.AddWriteFD(fd);
666 		selectSet.AddErrorFD(fd);
667 
668 		// In case the slave is closed while we select() on the master, only a
669 		// `write' event will arrive.
670 		SelectSet compareSet;
671 		if (!fTestMaster) {
672 			compareSet.AddReadFD(fd);
673 			compareSet.AddErrorFD(fd);
674 		}
675 		compareSet.AddWriteFD(fd);
676 
677 		int result = selectSet.Select();
678 		CHK(result > 0);
679 		CHK(selectSet == compareSet);
680 
681 		return 0;
682 	}
683 
684 private:
685 	int		fMaster;
686 	int		fSlave;
687 	bool	fTestMaster;
688 };
689 
690 // TestSelectNotifyAfterPending
691 class TestSelectNotifyAfterPending : public TestCase {
692 public:
693 	TestSelectNotifyAfterPending(bool master, bool write, bool unblock)
694 		: fMaster(-1),
695 		  fSlave(-1),
696 		  fTestMaster(master),
697 		  fWrite(write),
698 		  fUnblock(unblock)
699 	{
700 		printf("TestSelectNotifyAfterPending(%d, %d, %d)\n", master, write,
701 			unblock);
702 	}
703 
704 
705 protected:
706 	virtual ~TestSelectNotifyAfterPending()
707 	{
708 		close_tty(fMaster);
709 		close_tty(fSlave);
710 	}
711 
712 	virtual void Test()
713 	{
714 		fMaster = open_tty(0, true);
715 		fSlave = open_tty(0, false);
716 
717 		if (fWrite)
718 			WriteUntilBlock((fTestMaster ? fMaster : fSlave));
719 
720 		Thread *readWriter = CreateThread(
721 			CALLER(this, &TestSelectNotifyAfterPending::_ReadWriter),
722 			"read-writer");
723 		Thread *selector = CreateThread(
724 			CALLER(this, &TestSelectNotifyAfterPending::_Selector), "selector");
725 
726 		readWriter->Resume();
727 		selector->Resume();
728 
729 		snooze(100000);
730 		CHK_ALIVE(readWriter);
731 		CHK_ALIVE(selector);
732 
733 		if (fUnblock) {
734 			// unblock the read-writer and the selector
735 			if (fWrite)
736 				ReadDontFail((fTestMaster ? fSlave : fMaster), 2);
737 			else
738 				WriteDontFail((fTestMaster ? fSlave : fMaster), 2);
739 
740 			snooze(100000);
741 			CHK_DEAD(readWriter);
742 			CHK_DEAD(selector);
743 
744 		} else {
745 			// unblock the read-writer, but not the selector
746 			if (fWrite)
747 				ReadDontFail((fTestMaster ? fSlave : fMaster), 1);
748 			else
749 				WriteDontFail((fTestMaster ? fSlave : fMaster), 1);
750 
751 			snooze(100000);
752 			CHK_DEAD(readWriter);
753 			CHK_ALIVE(selector);
754 
755 			close_tty((fTestMaster ? fMaster : fSlave));
756 
757 			snooze(100000);
758 			CHK_DEAD(selector);
759 		}
760 	}
761 
762 private:
763 	int32 _ReadWriter()
764 	{
765 		int fd = (fTestMaster ? fMaster : fSlave);
766 		if (fWrite)
767 			WriteDontFail(fd, 1);
768 		else
769 			ReadDontFail(fd, 1);
770 
771 		return 0;
772 	}
773 
774 	int32 _Selector()
775 	{
776 		int fd = (fTestMaster ? fMaster : fSlave);
777 
778 		SelectSet selectSet;
779 		SelectSet compareSet;
780 
781 		if (fWrite) {
782 			selectSet.AddWriteFD(fd);
783 			compareSet.AddWriteFD(fd);
784 		} else {
785 			selectSet.AddReadFD(fd);
786 			compareSet.AddReadFD(fd);
787 		}
788 
789 		int result = selectSet.Select();
790 		CHK(result > 0);
791 		CHK(selectSet == compareSet);
792 
793 		return 0;
794 	}
795 
796 private:
797 	int		fMaster;
798 	int		fSlave;
799 	bool	fTestMaster;
800 	bool	fWrite;
801 	bool	fUnblock;
802 };
803 
804 
805 
806 // #pragma mark -
807 
808 int
809 main()
810 {
811 	// unblock tests
812 
813 	RUN_TEST(new TestUnblockOnCloseRead(true,  false));
814 	RUN_TEST(new TestUnblockOnCloseRead(false, false));
815 	RUN_TEST(new TestUnblockOnCloseRead(false, true));
816 
817 	RUN_TEST(new TestUnblockOnCloseWrite(true,  false, false));
818 	RUN_TEST(new TestUnblockOnCloseWrite(false, false, false));
819 	RUN_TEST(new TestUnblockOnCloseWrite(true,  true,  false));
820 	RUN_TEST(new TestUnblockOnCloseWrite(false, true,  false));
821 // TODO: How to enable echo mode?
822 //	RUN_TEST(new TestUnblockOnCloseWrite(true,  false, true));
823 //	RUN_TEST(new TestUnblockOnCloseWrite(false, false, true));
824 //	RUN_TEST(new TestUnblockOnCloseWrite(true,  true,  true));
825 //	RUN_TEST(new TestUnblockOnCloseWrite(false, true,  true));
826 
827 	// select tests
828 
829 	RUN_TEST(new TestSelectAlreadyReady(true,  true));
830 	RUN_TEST(new TestSelectAlreadyReady(false, true));
831 	RUN_TEST(new TestSelectAlreadyReady(true,  false));
832 	RUN_TEST(new TestSelectAlreadyReady(false, false));
833 
834 	RUN_TEST(new TestSelectNotifyOnClose(true));
835 	RUN_TEST(new TestSelectNotifyOnClose(false));
836 
837 	RUN_TEST(new TestSelectNotifyAfterPending(false, false, false));
838 	RUN_TEST(new TestSelectNotifyAfterPending(false, false, true));
839 	RUN_TEST(new TestSelectNotifyAfterPending(false, true,  false));
840 	RUN_TEST(new TestSelectNotifyAfterPending(false, true,  true));
841 	RUN_TEST(new TestSelectNotifyAfterPending(true,  false, false));
842 	RUN_TEST(new TestSelectNotifyAfterPending(true,  false, true));
843 	RUN_TEST(new TestSelectNotifyAfterPending(true,  true,  false));
844 	RUN_TEST(new TestSelectNotifyAfterPending(true,  true,  true));
845 
846 	return 0;
847 }
848