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