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