Added a small program for testing unblocking on close and select support. Not quite exhaustive and tests for ECHO mode are missing, since I couldn't figure out how to turn it on.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@11932 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
27a5523990
commit
f8e5453f2b
@ -29,5 +29,6 @@ SEARCH on [ FGristFiles
|
||||
] = [ FDirName $(OBOS_TOP) src kernel core util ] ;
|
||||
|
||||
# SubInclude OBOS_TOP src tests add-ons kernel disk_scanner ;
|
||||
SubInclude OBOS_TOP src tests add-ons kernel drivers ;
|
||||
SubInclude OBOS_TOP src tests add-ons kernel file_systems ;
|
||||
SubInclude OBOS_TOP src tests add-ons kernel network ;
|
||||
|
3
src/tests/add-ons/kernel/drivers/Jamfile
Normal file
3
src/tests/add-ons/kernel/drivers/Jamfile
Normal file
@ -0,0 +1,3 @@
|
||||
SubDir OBOS_TOP src tests add-ons kernel drivers ;
|
||||
|
||||
SubInclude OBOS_TOP src tests add-ons kernel drivers tty ;
|
6
src/tests/add-ons/kernel/drivers/tty/Jamfile
Normal file
6
src/tests/add-ons/kernel/drivers/tty/Jamfile
Normal file
@ -0,0 +1,6 @@
|
||||
SubDir OBOS_TOP src tests add-ons kernel drivers tty ;
|
||||
|
||||
UsePrivateHeaders [ FDirName kernel ] ;
|
||||
|
||||
SimpleTest tty-test : tty-test.cpp : libroot.so ;
|
||||
|
847
src/tests/add-ons/kernel/drivers/tty/tty-test.cpp
Normal file
847
src/tests/add-ons/kernel/drivers/tty/tty-test.cpp
Normal file
@ -0,0 +1,847 @@
|
||||
/*
|
||||
* Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
// Test cases:
|
||||
//
|
||||
// 1. unblock on close:
|
||||
//
|
||||
// * unblock, when the same cookie is closed
|
||||
// - read
|
||||
// - write (with, without ECHO)
|
||||
// * unblock write operations, when the other tty is closed
|
||||
// * unblock slave operations, when the master is closed
|
||||
//
|
||||
//
|
||||
// 2. select:
|
||||
//
|
||||
// * notify read, write, if ready when select()ing
|
||||
// * notify read, write, error on close of the other TTY
|
||||
// (the select() behavior when closing the same TTY is undefined)
|
||||
// * notify read after pending write
|
||||
// - with ECHO
|
||||
// - without ECHO
|
||||
// * notify write after pending read and full buffer
|
||||
// - with ECHO
|
||||
// - without ECHO
|
||||
// * don't notify when there was a pending read/write
|
||||
//
|
||||
//
|
||||
// TODO: There are no ECHO tests yet, since I don't know how to turn it on.
|
||||
|
||||
#define CHK(condition) assert(condition)
|
||||
#define RUN_TEST(test) (test)->Run()
|
||||
#define CHK_ALIVE(thread) CHK((thread)->IsAlive())
|
||||
#define CHK_DEAD(thread) CHK(!(thread)->IsAlive())
|
||||
|
||||
// FDSet
|
||||
struct FDSet : fd_set {
|
||||
FDSet()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
FD_ZERO(this);
|
||||
fCount = 0;
|
||||
}
|
||||
|
||||
void Add(int fd)
|
||||
{
|
||||
if (fd < 0 || fd >= FD_SETSIZE) {
|
||||
fprintf(stderr, "FDSet::Add(%d): Invalid FD.\n", fd);
|
||||
return;
|
||||
}
|
||||
|
||||
FD_SET(fd, this);
|
||||
if (fd >= fCount)
|
||||
fCount = fd + 1;
|
||||
}
|
||||
|
||||
int Count() const
|
||||
{
|
||||
return fCount;
|
||||
}
|
||||
|
||||
void UpdateCount()
|
||||
{
|
||||
if (fCount > 0) {
|
||||
for (int i = fCount - 1; i >= 0; i--) {
|
||||
if (FD_ISSET(i, this)) {
|
||||
fCount = i + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const FDSet &other) const
|
||||
{
|
||||
if (fCount != other.fCount)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < fCount; i++) {
|
||||
if (FD_ISSET(i, this) != FD_ISSET(i, &other))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const FDSet &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
int fCount;
|
||||
};
|
||||
|
||||
// SelectSet
|
||||
class SelectSet {
|
||||
public:
|
||||
SelectSet() {}
|
||||
~SelectSet() {}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
fReadSet.Clear();
|
||||
fWriteSet.Clear();
|
||||
fErrorSet.Clear();
|
||||
}
|
||||
|
||||
void AddReadFD(int fd)
|
||||
{
|
||||
fReadSet.Add(fd);
|
||||
}
|
||||
|
||||
void AddWriteFD(int fd)
|
||||
{
|
||||
fWriteSet.Add(fd);
|
||||
}
|
||||
|
||||
void AddErrorFD(int fd)
|
||||
{
|
||||
fErrorSet.Add(fd);
|
||||
}
|
||||
|
||||
int Select(bigtime_t timeout = -1)
|
||||
{
|
||||
int count = max(max(fReadSet.Count(), fWriteSet.Count()),
|
||||
fErrorSet.Count());
|
||||
fd_set *readSet = (fReadSet.Count() > 0 ? &fReadSet : NULL);
|
||||
fd_set *writeSet = (fWriteSet.Count() > 0 ? &fWriteSet : NULL);
|
||||
fd_set *errorSet = (fErrorSet.Count() > 0 ? &fErrorSet : NULL);
|
||||
|
||||
timeval tv = { timeout / 1000000, timeout % 1000000 };
|
||||
|
||||
int result = select(count, readSet, writeSet, errorSet,
|
||||
(timeout >= 0 ? &tv : NULL));
|
||||
if (result <= 0) {
|
||||
Clear();
|
||||
} else {
|
||||
fReadSet.UpdateCount();
|
||||
fWriteSet.UpdateCount();
|
||||
fErrorSet.UpdateCount();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const SelectSet &other) const
|
||||
{
|
||||
return (fReadSet == other.fReadSet
|
||||
&& fWriteSet == other.fWriteSet
|
||||
&& fErrorSet == other.fErrorSet);
|
||||
}
|
||||
|
||||
bool operator!=(const SelectSet &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
FDSet fReadSet;
|
||||
FDSet fWriteSet;
|
||||
FDSet fErrorSet;
|
||||
};
|
||||
|
||||
// Runnable
|
||||
class Runnable {
|
||||
public:
|
||||
virtual ~Runnable() {}
|
||||
virtual int32 Run() = 0;
|
||||
};
|
||||
|
||||
// Caller
|
||||
template<typename Type>
|
||||
class Caller : public Runnable {
|
||||
public:
|
||||
Caller(Type *object, int32 (Type::*function)())
|
||||
: fObject(object),
|
||||
fFunction(function)
|
||||
{
|
||||
}
|
||||
|
||||
virtual int32 Run()
|
||||
{
|
||||
return (fObject->*fFunction)();
|
||||
}
|
||||
|
||||
private:
|
||||
Type *fObject;
|
||||
int32 (Type::*fFunction)();
|
||||
};
|
||||
|
||||
// create_caller
|
||||
template<typename Type>
|
||||
static inline Runnable *
|
||||
create_caller(Type *object, int32 (Type::*function)())
|
||||
{
|
||||
return new Caller<Type>(object, function);
|
||||
}
|
||||
|
||||
#define CALLER(object, function) create_caller(object, function)
|
||||
|
||||
// Thread
|
||||
struct Thread : public DoublyLinkedListLinkImpl<Thread> {
|
||||
public:
|
||||
Thread(Runnable *runnable, const char *name)
|
||||
: fRunnable(runnable)
|
||||
{
|
||||
fThread = spawn_thread(_Entry, name, B_NORMAL_PRIORITY, this);
|
||||
if (fThread < 0) {
|
||||
sprintf("Failed to spawn thread: %s\n", strerror(fThread));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
~Thread()
|
||||
{
|
||||
Kill();
|
||||
delete fRunnable;
|
||||
}
|
||||
|
||||
void Resume()
|
||||
{
|
||||
resume_thread(fThread);
|
||||
}
|
||||
|
||||
void WaitFor()
|
||||
{
|
||||
status_t result;
|
||||
wait_for_thread(fThread, &result);
|
||||
}
|
||||
|
||||
void Kill()
|
||||
{
|
||||
kill_thread(fThread);
|
||||
}
|
||||
|
||||
bool IsAlive()
|
||||
{
|
||||
thread_info info;
|
||||
return (get_thread_info(fThread, &info) == B_OK);
|
||||
}
|
||||
|
||||
private:
|
||||
static int32 _Entry(void *data)
|
||||
{
|
||||
return ((Thread*)data)->fRunnable->Run();
|
||||
}
|
||||
|
||||
thread_id fThread;
|
||||
Runnable *fRunnable;
|
||||
};
|
||||
|
||||
// TestCase
|
||||
class TestCase {
|
||||
public:
|
||||
TestCase() {}
|
||||
virtual ~TestCase()
|
||||
{
|
||||
while (Thread *thread = fThreads.Head()) {
|
||||
fThreads.Remove(thread);
|
||||
delete thread;
|
||||
}
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
Test();
|
||||
delete this;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Test() = 0;
|
||||
|
||||
Thread *CreateThread(Runnable *runnable, const char *name)
|
||||
{
|
||||
Thread *thread = new Thread(runnable, name);
|
||||
fThreads.Add(thread);
|
||||
return thread;
|
||||
}
|
||||
|
||||
static void WriteUntilBlock(int fd)
|
||||
{
|
||||
// set non-blocking I/O
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
|
||||
fprintf(stderr, "WriteUntilBlock(): Failed to set non-blocking I/O "
|
||||
"mode: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// write till blocking
|
||||
char buffer[1024];
|
||||
memset(buffer, 'A', sizeof(buffer));
|
||||
ssize_t bytesWritten;
|
||||
do {
|
||||
bytesWritten = write(fd, buffer, sizeof(buffer));
|
||||
} while (bytesWritten > 0 || errno == B_INTERRUPTED);
|
||||
|
||||
if (bytesWritten < 0 && errno != B_WOULD_BLOCK) {
|
||||
fprintf(stderr, "WriteUntilBlock(): Writing failed: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// reset to blocking I/O
|
||||
if (fcntl(fd, F_SETFL, 0) < 0) {
|
||||
fprintf(stderr, "WriteUntilBlock(): Failed to set blocking I/O "
|
||||
"mode: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void ReadDontFail(int fd, int32 size)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
while (size > 0) {
|
||||
ssize_t bytesRead;
|
||||
do {
|
||||
int32 toRead = sizeof(buffer);
|
||||
if (toRead > size)
|
||||
toRead = size;
|
||||
|
||||
bytesRead = read(fd, buffer, toRead);
|
||||
} while (bytesRead < 0 && errno == B_INTERRUPTED);
|
||||
|
||||
if (bytesRead < 0) {
|
||||
fprintf(stderr, "ReadDontFail(): Failed to read: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size -= bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteDontFail(int fd, int32 size)
|
||||
{
|
||||
char buffer[1024];
|
||||
memset(buffer, 'A', sizeof(buffer));
|
||||
|
||||
while (size > 0) {
|
||||
ssize_t bytesWritten;
|
||||
do {
|
||||
int32 toWrite = sizeof(buffer);
|
||||
if (toWrite > size)
|
||||
toWrite = size;
|
||||
|
||||
bytesWritten = write(fd, buffer, toWrite);
|
||||
} while (bytesWritten < 0 && errno == B_INTERRUPTED);
|
||||
|
||||
if (bytesWritten < 0) {
|
||||
fprintf(stderr, "WriteDontFail(): Failed to write: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size -= bytesWritten;
|
||||
}
|
||||
}
|
||||
|
||||
void SetEcho(int fd)
|
||||
{
|
||||
// TODO: How to set echo mode?
|
||||
}
|
||||
|
||||
private:
|
||||
typedef DoublyLinkedList<Thread> ThreadList;
|
||||
|
||||
ThreadList fThreads;
|
||||
};
|
||||
|
||||
|
||||
// open_tty
|
||||
static int
|
||||
open_tty(int index, bool master)
|
||||
{
|
||||
if (index < 0 || index >= 16)
|
||||
fprintf(stderr, "open_tty(%d, %d): Bad index!\n", index, master);
|
||||
|
||||
char path[32];
|
||||
sprintf(path, "/dev/%ct/r%x", (master ? 'p' : 't'), index);
|
||||
int fd = open(path, O_RDWR);
|
||||
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Failed to open tty `%s': %s\n", path, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
close_tty(int &fd)
|
||||
{
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
// TestUnblockOnCloseRead
|
||||
class TestUnblockOnCloseRead : public TestCase {
|
||||
public:
|
||||
TestUnblockOnCloseRead(bool master, bool crossOver)
|
||||
: fMaster(-1),
|
||||
fSlave(-1),
|
||||
fTestMaster(master),
|
||||
fCrossOver(crossOver)
|
||||
{
|
||||
printf("TestUnblockOnCloseRead(%d, %d)\n", master, crossOver);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual ~TestUnblockOnCloseRead()
|
||||
{
|
||||
close_tty(fMaster);
|
||||
close_tty(fSlave);
|
||||
}
|
||||
|
||||
virtual void Test()
|
||||
{
|
||||
fMaster = open_tty(0, true);
|
||||
fSlave = open_tty(0, false);
|
||||
|
||||
Thread *thread = CreateThread(
|
||||
CALLER(this, &TestUnblockOnCloseRead::_Reader), "reader");
|
||||
thread->Resume();
|
||||
|
||||
snooze(100000);
|
||||
CHK_ALIVE(thread);
|
||||
|
||||
if (fCrossOver)
|
||||
close_tty(fTestMaster ? fSlave : fMaster);
|
||||
else
|
||||
close_tty(fTestMaster ? fMaster : fSlave);
|
||||
|
||||
snooze(100000);
|
||||
CHK_DEAD(thread);
|
||||
}
|
||||
|
||||
private:
|
||||
int32 _Reader()
|
||||
{
|
||||
char buffer[32];
|
||||
ssize_t bytesRead = read((fTestMaster ? fMaster : fSlave), buffer,
|
||||
sizeof(buffer));
|
||||
|
||||
CHK((bytesRead < 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int fMaster;
|
||||
int fSlave;
|
||||
bool fTestMaster;
|
||||
bool fCrossOver;
|
||||
};
|
||||
|
||||
// TestUnblockOnCloseWrite
|
||||
class TestUnblockOnCloseWrite : public TestCase {
|
||||
public:
|
||||
TestUnblockOnCloseWrite(bool master, bool crossOver, bool echo)
|
||||
: fMaster(-1),
|
||||
fSlave(-1),
|
||||
fTestMaster(master),
|
||||
fCrossOver(crossOver),
|
||||
fEcho(echo)
|
||||
{
|
||||
printf("TestUnblockOnCloseWrite(%d, %d, %d)\n", master, crossOver,
|
||||
echo);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual ~TestUnblockOnCloseWrite()
|
||||
{
|
||||
close_tty(fMaster);
|
||||
close_tty(fSlave);
|
||||
}
|
||||
|
||||
virtual void Test()
|
||||
{
|
||||
fMaster = open_tty(0, true);
|
||||
fSlave = open_tty(0, false);
|
||||
|
||||
if (fEcho)
|
||||
SetEcho((fTestMaster ? fSlave : fMaster));
|
||||
|
||||
WriteUntilBlock((fTestMaster ? fMaster : fSlave));
|
||||
|
||||
Thread *thread = CreateThread(
|
||||
CALLER(this, &TestUnblockOnCloseWrite::_Writer), "writer");
|
||||
thread->Resume();
|
||||
|
||||
snooze(100000);
|
||||
CHK_ALIVE(thread);
|
||||
|
||||
if (fCrossOver)
|
||||
close_tty(fTestMaster ? fSlave : fMaster);
|
||||
else
|
||||
close_tty(fTestMaster ? fMaster : fSlave);
|
||||
|
||||
snooze(100000);
|
||||
CHK_DEAD(thread);
|
||||
}
|
||||
|
||||
private:
|
||||
int32 _Writer()
|
||||
{
|
||||
char buffer[32];
|
||||
memset(buffer, 'A', sizeof(buffer));
|
||||
ssize_t bytesWritten = write((fTestMaster ? fMaster : fSlave), buffer,
|
||||
sizeof(buffer));
|
||||
|
||||
CHK((bytesWritten < 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int fMaster;
|
||||
int fSlave;
|
||||
bool fTestMaster;
|
||||
bool fCrossOver;
|
||||
bool fEcho;
|
||||
};
|
||||
|
||||
// TestSelectAlreadyReady
|
||||
class TestSelectAlreadyReady : public TestCase {
|
||||
public:
|
||||
TestSelectAlreadyReady(bool master, bool write)
|
||||
: fMaster(-1),
|
||||
fSlave(-1),
|
||||
fTestMaster(master),
|
||||
fWrite(write)
|
||||
{
|
||||
printf("TestSelectAlreadyReady(%d, %d)\n", master, write);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual ~TestSelectAlreadyReady()
|
||||
{
|
||||
close_tty(fMaster);
|
||||
close_tty(fSlave);
|
||||
}
|
||||
|
||||
virtual void Test()
|
||||
{
|
||||
fMaster = open_tty(0, true);
|
||||
fSlave = open_tty(0, false);
|
||||
|
||||
if (!fWrite)
|
||||
WriteDontFail((fTestMaster ? fSlave : fMaster), 1);
|
||||
|
||||
Thread *thread = CreateThread(
|
||||
CALLER(this, &TestSelectAlreadyReady::_Selector), "selector");
|
||||
thread->Resume();
|
||||
|
||||
snooze(100000);
|
||||
CHK_DEAD(thread);
|
||||
}
|
||||
|
||||
private:
|
||||
int32 _Selector()
|
||||
{
|
||||
SelectSet selectSet;
|
||||
SelectSet compareSet;
|
||||
if (fWrite) {
|
||||
selectSet.AddWriteFD((fTestMaster ? fMaster : fSlave));
|
||||
compareSet.AddWriteFD((fTestMaster ? fMaster : fSlave));
|
||||
} else {
|
||||
selectSet.AddReadFD((fTestMaster ? fMaster : fSlave));
|
||||
compareSet.AddReadFD((fTestMaster ? fMaster : fSlave));
|
||||
}
|
||||
|
||||
int result = selectSet.Select();
|
||||
CHK(result > 0);
|
||||
CHK(selectSet == compareSet);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int fMaster;
|
||||
int fSlave;
|
||||
bool fTestMaster;
|
||||
bool fWrite;
|
||||
};
|
||||
|
||||
// TestSelectNotifyOnClose
|
||||
class TestSelectNotifyOnClose : public TestCase {
|
||||
public:
|
||||
TestSelectNotifyOnClose(bool master)
|
||||
: fMaster(-1),
|
||||
fSlave(-1),
|
||||
fTestMaster(master)
|
||||
{
|
||||
printf("TestSelectNotifyOnClose(%d)\n", master);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual ~TestSelectNotifyOnClose()
|
||||
{
|
||||
close_tty(fMaster);
|
||||
close_tty(fSlave);
|
||||
}
|
||||
|
||||
virtual void Test()
|
||||
{
|
||||
fMaster = open_tty(0, true);
|
||||
fSlave = open_tty(0, false);
|
||||
|
||||
WriteUntilBlock((fTestMaster ? fMaster : fSlave));
|
||||
|
||||
Thread *thread = CreateThread(
|
||||
CALLER(this, &TestSelectNotifyOnClose::_Selector), "selector");
|
||||
thread->Resume();
|
||||
|
||||
snooze(100000);
|
||||
CHK_ALIVE(thread);
|
||||
|
||||
close_tty((fTestMaster ? fSlave : fMaster));
|
||||
|
||||
snooze(100000);
|
||||
CHK_DEAD(thread);
|
||||
}
|
||||
|
||||
private:
|
||||
int32 _Selector()
|
||||
{
|
||||
int fd = (fTestMaster ? fMaster : fSlave);
|
||||
|
||||
SelectSet selectSet;
|
||||
selectSet.AddReadFD(fd);
|
||||
selectSet.AddWriteFD(fd);
|
||||
selectSet.AddErrorFD(fd);
|
||||
|
||||
// In case the slave is closed while we select() on the master, only a
|
||||
// `write' event will arrive.
|
||||
SelectSet compareSet;
|
||||
if (!fTestMaster) {
|
||||
compareSet.AddReadFD(fd);
|
||||
compareSet.AddErrorFD(fd);
|
||||
}
|
||||
compareSet.AddWriteFD(fd);
|
||||
|
||||
int result = selectSet.Select();
|
||||
CHK(result > 0);
|
||||
CHK(selectSet == compareSet);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int fMaster;
|
||||
int fSlave;
|
||||
bool fTestMaster;
|
||||
};
|
||||
|
||||
// TestSelectNotifyAfterPending
|
||||
class TestSelectNotifyAfterPending : public TestCase {
|
||||
public:
|
||||
TestSelectNotifyAfterPending(bool master, bool write, bool unblock)
|
||||
: fMaster(-1),
|
||||
fSlave(-1),
|
||||
fTestMaster(master),
|
||||
fWrite(write),
|
||||
fUnblock(unblock)
|
||||
{
|
||||
printf("TestSelectNotifyAfterPending(%d, %d, %d)\n", master, write,
|
||||
unblock);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual ~TestSelectNotifyAfterPending()
|
||||
{
|
||||
close_tty(fMaster);
|
||||
close_tty(fSlave);
|
||||
}
|
||||
|
||||
virtual void Test()
|
||||
{
|
||||
fMaster = open_tty(0, true);
|
||||
fSlave = open_tty(0, false);
|
||||
|
||||
if (fWrite)
|
||||
WriteUntilBlock((fTestMaster ? fMaster : fSlave));
|
||||
|
||||
Thread *readWriter = CreateThread(
|
||||
CALLER(this, &TestSelectNotifyAfterPending::_ReadWriter),
|
||||
"read-writer");
|
||||
Thread *selector = CreateThread(
|
||||
CALLER(this, &TestSelectNotifyAfterPending::_Selector), "selector");
|
||||
|
||||
readWriter->Resume();
|
||||
selector->Resume();
|
||||
|
||||
snooze(100000);
|
||||
CHK_ALIVE(readWriter);
|
||||
CHK_ALIVE(selector);
|
||||
|
||||
if (fUnblock) {
|
||||
// unblock the read-writer and the selector
|
||||
if (fWrite)
|
||||
ReadDontFail((fTestMaster ? fSlave : fMaster), 2);
|
||||
else
|
||||
WriteDontFail((fTestMaster ? fSlave : fMaster), 2);
|
||||
|
||||
snooze(100000);
|
||||
CHK_DEAD(readWriter);
|
||||
CHK_DEAD(selector);
|
||||
|
||||
} else {
|
||||
// unblock the read-writer, but not the selector
|
||||
if (fWrite)
|
||||
ReadDontFail((fTestMaster ? fSlave : fMaster), 1);
|
||||
else
|
||||
WriteDontFail((fTestMaster ? fSlave : fMaster), 1);
|
||||
|
||||
snooze(100000);
|
||||
CHK_DEAD(readWriter);
|
||||
CHK_ALIVE(selector);
|
||||
|
||||
close_tty((fTestMaster ? fMaster : fSlave));
|
||||
|
||||
snooze(100000);
|
||||
CHK_DEAD(selector);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int32 _ReadWriter()
|
||||
{
|
||||
int fd = (fTestMaster ? fMaster : fSlave);
|
||||
if (fWrite)
|
||||
WriteDontFail(fd, 1);
|
||||
else
|
||||
ReadDontFail(fd, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 _Selector()
|
||||
{
|
||||
int fd = (fTestMaster ? fMaster : fSlave);
|
||||
|
||||
SelectSet selectSet;
|
||||
SelectSet compareSet;
|
||||
|
||||
if (fWrite) {
|
||||
selectSet.AddWriteFD(fd);
|
||||
compareSet.AddWriteFD(fd);
|
||||
} else {
|
||||
selectSet.AddReadFD(fd);
|
||||
compareSet.AddReadFD(fd);
|
||||
}
|
||||
|
||||
int result = selectSet.Select();
|
||||
CHK(result > 0);
|
||||
CHK(selectSet == compareSet);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int fMaster;
|
||||
int fSlave;
|
||||
bool fTestMaster;
|
||||
bool fWrite;
|
||||
bool fUnblock;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
// unblock tests
|
||||
|
||||
RUN_TEST(new TestUnblockOnCloseRead(true, false));
|
||||
RUN_TEST(new TestUnblockOnCloseRead(false, false));
|
||||
RUN_TEST(new TestUnblockOnCloseRead(false, true));
|
||||
|
||||
RUN_TEST(new TestUnblockOnCloseWrite(true, false, false));
|
||||
RUN_TEST(new TestUnblockOnCloseWrite(false, false, false));
|
||||
RUN_TEST(new TestUnblockOnCloseWrite(true, true, false));
|
||||
RUN_TEST(new TestUnblockOnCloseWrite(false, true, false));
|
||||
// TODO: How to enable echo mode?
|
||||
// RUN_TEST(new TestUnblockOnCloseWrite(true, false, true));
|
||||
// RUN_TEST(new TestUnblockOnCloseWrite(false, false, true));
|
||||
// RUN_TEST(new TestUnblockOnCloseWrite(true, true, true));
|
||||
// RUN_TEST(new TestUnblockOnCloseWrite(false, true, true));
|
||||
|
||||
// select tests
|
||||
|
||||
RUN_TEST(new TestSelectAlreadyReady(true, true));
|
||||
RUN_TEST(new TestSelectAlreadyReady(false, true));
|
||||
RUN_TEST(new TestSelectAlreadyReady(true, false));
|
||||
RUN_TEST(new TestSelectAlreadyReady(false, false));
|
||||
|
||||
RUN_TEST(new TestSelectNotifyOnClose(true));
|
||||
RUN_TEST(new TestSelectNotifyOnClose(false));
|
||||
|
||||
RUN_TEST(new TestSelectNotifyAfterPending(false, false, false));
|
||||
RUN_TEST(new TestSelectNotifyAfterPending(false, false, true));
|
||||
RUN_TEST(new TestSelectNotifyAfterPending(false, true, false));
|
||||
RUN_TEST(new TestSelectNotifyAfterPending(false, true, true));
|
||||
RUN_TEST(new TestSelectNotifyAfterPending(true, false, false));
|
||||
RUN_TEST(new TestSelectNotifyAfterPending(true, false, true));
|
||||
RUN_TEST(new TestSelectNotifyAfterPending(true, true, false));
|
||||
RUN_TEST(new TestSelectNotifyAfterPending(true, true, true));
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user