xref: /haiku/src/tests/add-ons/kernel/drivers/tty/tty-test.cpp (revision aa3083e086e5a929c061c72983e09d916c548a38)
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 {
FDSetFDSet57 	FDSet()
58 	{
59 		Clear();
60 	}
61 
ClearFDSet62 	void Clear()
63 	{
64 		FD_ZERO(this);
65 		fCount = 0;
66 	}
67 
AddFDSet68 	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 
CountFDSet80 	int Count() const
81 	{
82 		return fCount;
83 	}
84 
UpdateCountFDSet85 	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 
operator ==FDSet98 	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 
operator !=FDSet111 	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:
SelectSet()123 	SelectSet() {}
~SelectSet()124 	~SelectSet() {}
125 
Clear()126 	void Clear()
127 	{
128 		fReadSet.Clear();
129 		fWriteSet.Clear();
130 		fErrorSet.Clear();
131 	}
132 
AddReadFD(int fd)133 	void AddReadFD(int fd)
134 	{
135 		fReadSet.Add(fd);
136 	}
137 
AddWriteFD(int fd)138 	void AddWriteFD(int fd)
139 	{
140 		fWriteSet.Add(fd);
141 	}
142 
AddErrorFD(int fd)143 	void AddErrorFD(int fd)
144 	{
145 		fErrorSet.Add(fd);
146 	}
147 
Select(bigtime_t timeout=-1)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 
operator ==(const SelectSet & other) const171 	bool operator==(const SelectSet &other) const
172 	{
173 		return (fReadSet == other.fReadSet
174 			&& fWriteSet == other.fWriteSet
175 			&& fErrorSet == other.fErrorSet);
176 	}
177 
operator !=(const SelectSet & other) const178 	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:
~Runnable()192 	virtual ~Runnable() {}
193 	virtual int32 Run() = 0;
194 };
195 
196 // Caller
197 template<typename Type>
198 class Caller : public Runnable {
199 public:
Caller(Type * object,int32 (Type::* function)())200 	Caller(Type *object, int32 (Type::*function)())
201 		: fObject(object),
202 		  fFunction(function)
203 	{
204 	}
205 
Run()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 *
create_caller(Type * object,int32 (Type::* function)())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:
ThreadThread229 	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 
~ThreadThread239 	~Thread()
240 	{
241 		Kill();
242 		delete fRunnable;
243 	}
244 
ResumeThread245 	void Resume()
246 	{
247 		resume_thread(fThread);
248 	}
249 
WaitForThread250 	void WaitFor()
251 	{
252 		status_t result;
253 		wait_for_thread(fThread, &result);
254 	}
255 
KillThread256 	void Kill()
257 	{
258 		kill_thread(fThread);
259 	}
260 
IsAliveThread261 	bool IsAlive()
262 	{
263 		thread_info info;
264 		return (get_thread_info(fThread, &info) == B_OK);
265 	}
266 
267 private:
_EntryThread268 	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:
TestCase()280 	TestCase() {}
~TestCase()281 	virtual ~TestCase()
282 	{
283 		while (Thread *thread = fThreads.Head()) {
284 			fThreads.Remove(thread);
285 			delete thread;
286 		}
287 	}
288 
Run()289 	void Run()
290 	{
291 		Test();
292 		delete this;
293 	}
294 
295 protected:
296 	virtual void Test() = 0;
297 
CreateThread(Runnable * runnable,const char * name)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 
WriteUntilBlock(int fd)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 
ReadDontFail(int fd,int32 size)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 
WriteDontFail(int fd,int32 size)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 
SetEcho(int fd)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
open_tty(int index,bool master)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,
411 			strerror(errno));
412 		exit(1);
413 	}
414 
415 	return fd;
416 }
417 
418 
419 static void
close_tty(int & fd)420 close_tty(int &fd)
421 {
422 	if (fd >= 0) {
423 		close(fd);
424 		fd = -1;
425 	}
426 }
427 
428 
429 // #pragma mark -
430 
431 // TestUnblockOnCloseRead
432 class TestUnblockOnCloseRead : public TestCase {
433 public:
TestUnblockOnCloseRead(bool master,bool crossOver)434 	TestUnblockOnCloseRead(bool master, bool crossOver)
435 		:
436 		fMaster(-1),
437 		fSlave(-1),
438 		fTestMaster(master),
439 		fCrossOver(crossOver)
440 	{
441 		printf("TestUnblockOnCloseRead(%d, %d)\n", master, crossOver);
442 	}
443 
444 
445 protected:
~TestUnblockOnCloseRead()446 	virtual ~TestUnblockOnCloseRead()
447 	{
448 		close_tty(fMaster);
449 		close_tty(fSlave);
450 	}
451 
Test()452 	virtual void Test()
453 	{
454 		fMaster = open_tty(0, true);
455 		fSlave = open_tty(0, false);
456 
457 		Thread *thread = CreateThread(
458 			CALLER(this, &TestUnblockOnCloseRead::_Reader), "reader");
459 		thread->Resume();
460 
461 		snooze(100000);
462 		CHK_ALIVE(thread);
463 
464 		if (fCrossOver)
465 			close_tty(fTestMaster ? fSlave : fMaster);
466 		else
467 			close_tty(fTestMaster ? fMaster : fSlave);
468 
469 		snooze(100000);
470 		CHK_DEAD(thread);
471 	}
472 
473 private:
_Reader()474 	int32 _Reader()
475 	{
476 		char buffer[32];
477 		ssize_t bytesRead = read((fTestMaster ? fMaster : fSlave), buffer,
478 			sizeof(buffer));
479 
480 		CHK((bytesRead < 0));
481 
482 		return 0;
483 	}
484 
485 private:
486 	int		fMaster;
487 	int		fSlave;
488 	bool	fTestMaster;
489 	bool	fCrossOver;
490 };
491 
492 // TestUnblockOnCloseWrite
493 class TestUnblockOnCloseWrite : public TestCase {
494 public:
TestUnblockOnCloseWrite(bool master,bool crossOver,bool echo)495 	TestUnblockOnCloseWrite(bool master, bool crossOver, bool echo)
496 		:
497 		fMaster(-1),
498 		fSlave(-1),
499 		fTestMaster(master),
500 		fCrossOver(crossOver),
501 		fEcho(echo)
502 	{
503 		printf("TestUnblockOnCloseWrite(%d, %d, %d)\n", master, crossOver,
504 			echo);
505 	}
506 
507 
508 protected:
~TestUnblockOnCloseWrite()509 	virtual ~TestUnblockOnCloseWrite()
510 	{
511 		close_tty(fMaster);
512 		close_tty(fSlave);
513 	}
514 
Test()515 	virtual void Test()
516 	{
517 		fMaster = open_tty(0, true);
518 		fSlave = open_tty(0, false);
519 
520 		if (fEcho)
521 			SetEcho((fTestMaster ? fSlave : fMaster));
522 
523 		WriteUntilBlock((fTestMaster ? fMaster : fSlave));
524 
525 		Thread *thread = CreateThread(
526 			CALLER(this, &TestUnblockOnCloseWrite::_Writer), "writer");
527 		thread->Resume();
528 
529 		snooze(100000);
530 		CHK_ALIVE(thread);
531 
532 		if (fCrossOver)
533 			close_tty(fTestMaster ? fSlave : fMaster);
534 		else
535 			close_tty(fTestMaster ? fMaster : fSlave);
536 
537 		snooze(100000);
538 		CHK_DEAD(thread);
539 	}
540 
541 private:
_Writer()542 	int32 _Writer()
543 	{
544 		char buffer[32];
545 		memset(buffer, 'A', sizeof(buffer));
546 		ssize_t bytesWritten = write((fTestMaster ? fMaster : fSlave), buffer,
547 			sizeof(buffer));
548 
549 		CHK((bytesWritten < 0));
550 
551 		return 0;
552 	}
553 
554 private:
555 	int		fMaster;
556 	int		fSlave;
557 	bool	fTestMaster;
558 	bool	fCrossOver;
559 	bool	fEcho;
560 };
561 
562 // TestSelectAlreadyReady
563 class TestSelectAlreadyReady : public TestCase {
564 public:
TestSelectAlreadyReady(bool master,bool write)565 	TestSelectAlreadyReady(bool master, bool write)
566 		:
567 		fMaster(-1),
568 		fSlave(-1),
569 		fTestMaster(master),
570 		fWrite(write)
571 	{
572 		printf("TestSelectAlreadyReady(%d, %d)\n", master, write);
573 	}
574 
575 
576 protected:
~TestSelectAlreadyReady()577 	virtual ~TestSelectAlreadyReady()
578 	{
579 		close_tty(fMaster);
580 		close_tty(fSlave);
581 	}
582 
Test()583 	virtual void Test()
584 	{
585 		fMaster = open_tty(0, true);
586 		fSlave = open_tty(0, false);
587 
588 		if (!fWrite)
589 			WriteDontFail((fTestMaster ? fSlave : fMaster), 1);
590 
591 		Thread *thread = CreateThread(
592 			CALLER(this, &TestSelectAlreadyReady::_Selector), "selector");
593 		thread->Resume();
594 
595 		snooze(100000);
596 		CHK_DEAD(thread);
597 	}
598 
599 private:
_Selector()600 	int32 _Selector()
601 	{
602 		SelectSet selectSet;
603 		SelectSet compareSet;
604 		if (fWrite) {
605 			selectSet.AddWriteFD((fTestMaster ? fMaster : fSlave));
606 			compareSet.AddWriteFD((fTestMaster ? fMaster : fSlave));
607 		} else {
608 			selectSet.AddReadFD((fTestMaster ? fMaster : fSlave));
609 			compareSet.AddReadFD((fTestMaster ? fMaster : fSlave));
610 		}
611 
612 		int result = selectSet.Select();
613 		CHK(result > 0);
614 		CHK(selectSet == compareSet);
615 
616 		return 0;
617 	}
618 
619 private:
620 	int		fMaster;
621 	int		fSlave;
622 	bool	fTestMaster;
623 	bool	fWrite;
624 };
625 
626 // TestSelectNotifyOnClose
627 class TestSelectNotifyOnClose : public TestCase {
628 public:
TestSelectNotifyOnClose(bool master)629 	TestSelectNotifyOnClose(bool master)
630 		: fMaster(-1),
631 		  fSlave(-1),
632 		  fTestMaster(master)
633 	{
634 		printf("TestSelectNotifyOnClose(%d)\n", master);
635 	}
636 
637 
638 protected:
~TestSelectNotifyOnClose()639 	virtual ~TestSelectNotifyOnClose()
640 	{
641 		close_tty(fMaster);
642 		close_tty(fSlave);
643 	}
644 
Test()645 	virtual void Test()
646 	{
647 		fMaster = open_tty(0, true);
648 		fSlave = open_tty(0, false);
649 
650 		WriteUntilBlock((fTestMaster ? fMaster : fSlave));
651 
652 		Thread *thread = CreateThread(
653 			CALLER(this, &TestSelectNotifyOnClose::_Selector), "selector");
654 		thread->Resume();
655 
656 		snooze(100000);
657 		CHK_ALIVE(thread);
658 
659 		close_tty((fTestMaster ? fSlave : fMaster));
660 
661 		snooze(100000);
662 		CHK_DEAD(thread);
663 	}
664 
665 private:
_Selector()666 	int32 _Selector()
667 	{
668 		int fd = (fTestMaster ? fMaster : fSlave);
669 
670 		SelectSet selectSet;
671 		selectSet.AddReadFD(fd);
672 		selectSet.AddWriteFD(fd);
673 		selectSet.AddErrorFD(fd);
674 
675 		// In case the slave is closed while we select() on the master, only a
676 		// `write' event will arrive.
677 		SelectSet compareSet;
678 		if (!fTestMaster) {
679 			compareSet.AddReadFD(fd);
680 			compareSet.AddErrorFD(fd);
681 		}
682 		compareSet.AddWriteFD(fd);
683 
684 		int result = selectSet.Select();
685 		CHK(result > 0);
686 		CHK(selectSet == compareSet);
687 
688 		return 0;
689 	}
690 
691 private:
692 	int		fMaster;
693 	int		fSlave;
694 	bool	fTestMaster;
695 };
696 
697 // TestSelectNotifyAfterPending
698 class TestSelectNotifyAfterPending : public TestCase {
699 public:
TestSelectNotifyAfterPending(bool master,bool write,bool unblock)700 	TestSelectNotifyAfterPending(bool master, bool write, bool unblock)
701 		:
702 		fMaster(-1),
703 		fSlave(-1),
704 		fTestMaster(master),
705 		fWrite(write),
706 		fUnblock(unblock)
707 	{
708 		printf("TestSelectNotifyAfterPending(%d, %d, %d)\n", master, write,
709 			unblock);
710 	}
711 
712 
713 protected:
~TestSelectNotifyAfterPending()714 	virtual ~TestSelectNotifyAfterPending()
715 	{
716 		close_tty(fMaster);
717 		close_tty(fSlave);
718 	}
719 
Test()720 	virtual void Test()
721 	{
722 		fMaster = open_tty(0, true);
723 		fSlave = open_tty(0, false);
724 
725 		if (fWrite)
726 			WriteUntilBlock((fTestMaster ? fMaster : fSlave));
727 
728 		Thread *readWriter = CreateThread(
729 			CALLER(this, &TestSelectNotifyAfterPending::_ReadWriter),
730 			"read-writer");
731 		Thread *selector = CreateThread(
732 			CALLER(this, &TestSelectNotifyAfterPending::_Selector), "selector");
733 
734 		readWriter->Resume();
735 		selector->Resume();
736 
737 		snooze(100000);
738 		CHK_ALIVE(readWriter);
739 		CHK_ALIVE(selector);
740 
741 		if (fUnblock) {
742 			// unblock the read-writer and the selector
743 			if (fWrite)
744 				ReadDontFail((fTestMaster ? fSlave : fMaster), 2);
745 			else
746 				WriteDontFail((fTestMaster ? fSlave : fMaster), 2);
747 
748 			snooze(100000);
749 			CHK_DEAD(readWriter);
750 			CHK_DEAD(selector);
751 
752 		} else {
753 			// unblock the read-writer, but not the selector
754 			if (fWrite)
755 				ReadDontFail((fTestMaster ? fSlave : fMaster), 1);
756 			else
757 				WriteDontFail((fTestMaster ? fSlave : fMaster), 1);
758 
759 			snooze(100000);
760 			CHK_DEAD(readWriter);
761 			CHK_ALIVE(selector);
762 
763 			close_tty((fTestMaster ? fMaster : fSlave));
764 
765 			snooze(100000);
766 			CHK_DEAD(selector);
767 		}
768 	}
769 
770 private:
_ReadWriter()771 	int32 _ReadWriter()
772 	{
773 		int fd = (fTestMaster ? fMaster : fSlave);
774 		if (fWrite)
775 			WriteDontFail(fd, 1);
776 		else
777 			ReadDontFail(fd, 1);
778 
779 		return 0;
780 	}
781 
_Selector()782 	int32 _Selector()
783 	{
784 		int fd = (fTestMaster ? fMaster : fSlave);
785 
786 		SelectSet selectSet;
787 		SelectSet compareSet;
788 
789 		if (fWrite) {
790 			selectSet.AddWriteFD(fd);
791 			compareSet.AddWriteFD(fd);
792 		} else {
793 			selectSet.AddReadFD(fd);
794 			compareSet.AddReadFD(fd);
795 		}
796 
797 		int result = selectSet.Select();
798 		CHK(result > 0);
799 		CHK(selectSet == compareSet);
800 
801 		return 0;
802 	}
803 
804 private:
805 	int		fMaster;
806 	int		fSlave;
807 	bool	fTestMaster;
808 	bool	fWrite;
809 	bool	fUnblock;
810 };
811 
812 // TestIoctlFIONRead
813 class TestIoctlFIONRead : public TestCase {
814 public:
TestIoctlFIONRead(bool master)815 	TestIoctlFIONRead(bool master)
816 		:
817 		fMaster(-1),
818 		fSlave(-1),
819 		fTestMaster(master)
820 	{
821 		printf("TestIoctlFIONRead(%d)\n", master);
822 	}
823 
824 
825 protected:
~TestIoctlFIONRead()826 	virtual ~TestIoctlFIONRead()
827 	{
828 		close_tty(fMaster);
829 		close_tty(fSlave);
830 	}
831 
Test()832 	virtual void Test()
833 	{
834 		fMaster = open_tty(0, true);
835 		fSlave = open_tty(0, false);
836 
837 		int fd = (fTestMaster ? fMaster : fSlave);
838 		status_t err;
839 		int toRead = -1;
840 
841 		errno = 0;
842 		err = ioctl(fd, FIONREAD, NULL);
843 		CHK(err == -1);
844 		printf("e: %s\n", strerror(errno));
845 		// should be CHK(errno == EINVAL); !!
846 		CHK(errno == EFAULT);
847 
848 		errno = 0;
849 		err = ioctl(fd, FIONREAD, (void *)1);
850 		CHK(err == -1);
851 		printf("e: %s\n", strerror(errno));
852 		CHK(errno == EFAULT);
853 
854 		errno = 0;
855 
856 		err = ioctl(fd, FIONREAD, &toRead);
857 		printf("e: %s\n", strerror(errno));
858 		//CHK(err == 0);
859 		//CHK(toRead == 0);
860 
861 		WriteDontFail((fTestMaster ? fSlave : fMaster), 1);
862 
863 		errno = 0;
864 
865 		err = ioctl(fd, FIONREAD, &toRead);
866 		printf("e: %d\n", err);
867 		CHK(err == 0);
868 		CHK(toRead == 1);
869 
870 		WriteUntilBlock((fTestMaster ? fSlave : fMaster));
871 
872 		err = ioctl(fd, FIONREAD, &toRead);
873 		CHK(err == 0);
874 		CHK(toRead > 1);
875 
876 		close_tty((fTestMaster ? fSlave : fMaster));
877 
878 		err = ioctl(fd, FIONREAD, &toRead);
879 		CHK(err == 0);
880 		CHK(toRead > 1);
881 
882 		ReadDontFail(fd, toRead);
883 
884 		err = ioctl(fd, FIONREAD, &toRead);
885 		CHK(err == 0);
886 		CHK(toRead == 0);
887 
888 	}
889 
890 private:
891 	int		fMaster;
892 	int		fSlave;
893 	bool	fTestMaster;
894 };
895 
896 
897 
898 // #pragma mark -
899 
900 int
main()901 main()
902 {
903 	// unblock tests
904 
905 	RUN_TEST(new TestUnblockOnCloseRead(true,  false));
906 	RUN_TEST(new TestUnblockOnCloseRead(false, false));
907 	RUN_TEST(new TestUnblockOnCloseRead(false, true));
908 
909 	RUN_TEST(new TestUnblockOnCloseWrite(true,  false, false));
910 	RUN_TEST(new TestUnblockOnCloseWrite(false, false, false));
911 	RUN_TEST(new TestUnblockOnCloseWrite(true,  true,  false));
912 	RUN_TEST(new TestUnblockOnCloseWrite(false, true,  false));
913 // TODO: How to enable echo mode?
914 //	RUN_TEST(new TestUnblockOnCloseWrite(true,  false, true));
915 //	RUN_TEST(new TestUnblockOnCloseWrite(false, false, true));
916 //	RUN_TEST(new TestUnblockOnCloseWrite(true,  true,  true));
917 //	RUN_TEST(new TestUnblockOnCloseWrite(false, true,  true));
918 
919 	// select tests
920 
921 	RUN_TEST(new TestSelectAlreadyReady(true,  true));
922 	RUN_TEST(new TestSelectAlreadyReady(false, true));
923 	RUN_TEST(new TestSelectAlreadyReady(true,  false));
924 	RUN_TEST(new TestSelectAlreadyReady(false, false));
925 
926 	RUN_TEST(new TestSelectNotifyOnClose(true));
927 	RUN_TEST(new TestSelectNotifyOnClose(false));
928 
929 	RUN_TEST(new TestSelectNotifyAfterPending(false, false, false));
930 	RUN_TEST(new TestSelectNotifyAfterPending(false, false, true));
931 	RUN_TEST(new TestSelectNotifyAfterPending(false, true,  false));
932 	RUN_TEST(new TestSelectNotifyAfterPending(false, true,  true));
933 	RUN_TEST(new TestSelectNotifyAfterPending(true,  false, false));
934 	RUN_TEST(new TestSelectNotifyAfterPending(true,  false, true));
935 	RUN_TEST(new TestSelectNotifyAfterPending(true,  true,  false));
936 	RUN_TEST(new TestSelectNotifyAfterPending(true,  true,  true));
937 
938 	RUN_TEST(new TestIoctlFIONRead(true));
939 	//RUN_TEST(new TestIoctlFIONRead(false));
940 
941 	return 0;
942 }
943