diff options
Diffstat (limited to 'tests/async/tioselectors.nim')
-rw-r--r-- | tests/async/tioselectors.nim | 351 |
1 files changed, 293 insertions, 58 deletions
diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim index ed2fea84f..f53767408 100644 --- a/tests/async/tioselectors.nim +++ b/tests/async/tioselectors.nim @@ -1,8 +1,7 @@ discard """ - file: "tioselectors.nim" output: "All tests passed!" """ -import ioselectors +import selectors const hasThreadSupport = compileOption("threads") @@ -12,11 +11,10 @@ template processTest(t, x: untyped) = if not x: echo(t & " FAILED\r\n") when not defined(windows): - import os, posix, osproc, nativesockets, times + import os, posix, nativesockets - const supportedPlatform = defined(macosx) or defined(freebsd) or - defined(netbsd) or defined(openbsd) or - defined(linux) + when ioselSupportedPlatform: + import osproc proc socket_notification_test(): bool = proc create_test_socket(): SocketHandle = @@ -38,9 +36,6 @@ when not defined(windows): var client_socket = create_test_socket() var server_socket = create_test_socket() - registerHandle(selector, server_socket, {Event.Read}, 0) - registerHandle(selector, client_socket, {Event.Write}, 0) - var option : int32 = 1 if setsockopt(server_socket, cint(SOL_SOCKET), cint(SO_REUSEADDR), addr(option), sizeof(option).SockLen) < 0: @@ -49,18 +44,25 @@ when not defined(windows): var aiList = getAddrInfo("0.0.0.0", Port(13337)) if bindAddr(server_socket, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: - dealloc(aiList) + freeAddrInfo(aiList) raiseOSError(osLastError()) - discard server_socket.listen() - dealloc(aiList) + if server_socket.listen() == -1: + raiseOSError(osLastError()) + freeAddrInfo(aiList) aiList = getAddrInfo("127.0.0.1", Port(13337)) discard posix.connect(client_socket, aiList.ai_addr, aiList.ai_addrlen.Socklen) - dealloc(aiList) - discard selector.select(100) - var rc1 = selector.select(100) - assert(len(rc1) == 2) + + registerHandle(selector, server_socket, {Event.Read}, 0) + registerHandle(selector, client_socket, {Event.Write}, 0) + + freeAddrInfo(aiList) + + # make sure both sockets are selected + var nevs = 0 + while nevs < 2: + nevs += selector.select(100).len var sockAddress: SockAddr var addrLen = sizeof(sockAddress).Socklen @@ -79,7 +81,7 @@ when not defined(windows): var rc2 = selector.select(100) assert(len(rc2) == 1) - var read_count = posix.recv(server2_socket, addr (buffer[0]), 128, 0) + var read_count = posix.recv(server2_socket, addr buffer[0], 128, 0) if read_count == -1: raiseOSError(osLastError()) @@ -127,15 +129,15 @@ when not defined(windows): var selector = newSelector[int]() var event = newSelectEvent() selector.registerEvent(event, 1) - selector.flush() - event.setEvent() + var rc0 = selector.select(0) + event.trigger() var rc1 = selector.select(0) - event.setEvent() + event.trigger() var rc2 = selector.select(0) var rc3 = selector.select(0) - assert(len(rc1) == 1 and len(rc2) == 1 and len(rc3) == 0) - var ev1 = rc1[0].data - var ev2 = rc2[0].data + assert(len(rc0) == 0 and len(rc1) == 1 and len(rc2) == 1 and len(rc3) == 0) + var ev1 = selector.getData(rc1[0].fd) + var ev2 = selector.getData(rc2[0].fd) assert(ev1 == 1 and ev2 == 1) selector.unregister(event) event.close() @@ -143,28 +145,29 @@ when not defined(windows): selector.close() result = true - when supportedPlatform: + when ioselSupportedPlatform: proc timer_notification_test(): bool = var selector = newSelector[int]() var timer = selector.registerTimer(100, false, 0) - var rc1 = selector.select(140) - var rc2 = selector.select(140) - assert(len(rc1) == 1 and len(rc2) == 1) + var rc1 = selector.select(10000) + var rc2 = selector.select(10000) + # if this flakes, see tests/m14634.nim + assert len(rc1) == 1 and len(rc2) == 1, $(len(rc1), len(rc2)) selector.unregister(timer) - selector.flush() + discard selector.select(0) selector.registerTimer(100, true, 0) - var rc3 = selector.select(120) - var rc4 = selector.select(120) - assert(len(rc3) == 1 and len(rc4) == 0) + var rc4 = selector.select(10000) + var rc5 = selector.select(1000) # this will be an actual wait, keep it small + assert len(rc4) == 1 and len(rc5) == 0, $(len(rc4), len(rc5)) assert(selector.isEmpty()) selector.close() result = true proc process_notification_test(): bool = var selector = newSelector[int]() - var process2 = startProcess("/bin/sleep", "", ["2"], nil, + var process2 = startProcess("sleep", "", ["2"], nil, {poStdErrToStdOut, poUsePath}) - discard startProcess("/bin/sleep", "", ["1"], nil, + discard startProcess("sleep", "", ["1"], nil, {poStdErrToStdOut, poUsePath}) selector.registerProcess(process2.processID, 0) @@ -194,12 +197,14 @@ when not defined(windows): var s1 = selector.registerSignal(SIGUSR1, 1) var s2 = selector.registerSignal(SIGUSR2, 2) var s3 = selector.registerSignal(SIGTERM, 3) - selector.flush() - + discard selector.select(0) discard posix.kill(pid, SIGUSR1) discard posix.kill(pid, SIGUSR2) discard posix.kill(pid, SIGTERM) var rc = selector.select(0) + var cd0 = selector.getData(rc[0].fd) + var cd1 = selector.getData(rc[1].fd) + var cd2 = selector.getData(rc[2].fd) selector.unregister(s1) selector.unregister(s2) selector.unregister(s3) @@ -212,11 +217,234 @@ when not defined(windows): raiseOSError(osLastError()) assert(len(rc) == 3) - assert(rc[0].data + rc[1].data + rc[2].data == 6) # 1 + 2 + 3 + assert(cd0 + cd1 + cd2 == 6, $(cd0 + cd1 + cd2)) # 1 + 2 + 3 assert(equalMem(addr sigset1o, addr sigset2o, sizeof(Sigset))) assert(selector.isEmpty()) result = true + when defined(macosx) or defined(freebsd) or defined(openbsd) or + defined(netbsd): + + proc rename(frompath: cstring, topath: cstring): cint + {.importc: "rename", header: "<stdio.h>".} + + proc createFile(name: string): cint = + result = posix.open(cstring(name), posix.O_CREAT or posix.O_RDWR) + if result == -1: + raiseOsError(osLastError()) + + proc writeFile(name: string, data: string) = + let fd = posix.open(cstring(name), posix.O_APPEND or posix.O_RDWR) + if fd == -1: + raiseOsError(osLastError()) + let length = len(data).cint + if posix.write(fd, cast[pointer](addr data[0]), + len(data).cint) != length: + raiseOsError(osLastError()) + if posix.close(fd) == -1: + raiseOsError(osLastError()) + + proc closeFile(fd: cint) = + if posix.close(fd) == -1: + raiseOsError(osLastError()) + + proc removeFile(name: string) = + let err = posix.unlink(cstring(name)) + if err == -1: + raiseOsError(osLastError()) + + proc createDir(name: string) = + let err = posix.mkdir(cstring(name), 0x1FF) + if err == -1: + raiseOsError(osLastError()) + + proc removeDir(name: string) = + let err = posix.rmdir(cstring(name)) + if err == -1: + raiseOsError(osLastError()) + + proc chmodPath(name: string, mode: cint) = + let err = posix.chmod(cstring(name), Mode(mode)) + if err == -1: + raiseOsError(osLastError()) + + proc renameFile(names: string, named: string) = + let err = rename(cstring(names), cstring(named)) + if err == -1: + raiseOsError(osLastError()) + + proc symlink(names: string, named: string) = + let err = posix.symlink(cstring(names), cstring(named)) + if err == -1: + raiseOsError(osLastError()) + + proc openWatch(name: string): cint = + result = posix.open(cstring(name), posix.O_RDONLY) + if result == -1: + raiseOsError(osLastError()) + + const + testDirectory = "/tmp/kqtest" + + type + valType = object + fd: cint + events: set[Event] + + proc vnode_test(): bool = + proc validate(test: openArray[ReadyKey], + check: openArray[valType]): bool = + result = false + if len(test) == len(check): + for checkItem in check: + result = false + for testItem in test: + if testItem.fd == checkItem.fd and + checkItem.events <= testItem.events: + result = true + break + if not result: + break + + var res: seq[ReadyKey] + var selector = newSelector[int]() + var events = {Event.VnodeWrite, Event.VnodeDelete, Event.VnodeExtend, + Event.VnodeAttrib, Event.VnodeLink, Event.VnodeRename, + Event.VnodeRevoke} + + result = true + discard posix.unlink(testDirectory) + + createDir(testDirectory) + var dirfd = posix.open(cstring(testDirectory), posix.O_RDONLY) + if dirfd == -1: + raiseOsError(osLastError()) + + selector.registerVnode(dirfd, events, 1) + discard selector.select(0) + + # chmod testDirectory to 0777 + chmodPath(testDirectory, 0x1FF) + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeAttrib} <= res[0].events) + + # create subdirectory + createDir(testDirectory & "/test") + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeWrite, + Event.VnodeLink} <= res[0].events) + + # open test directory for watching + var testfd = openWatch(testDirectory & "/test") + selector.registerVnode(testfd, events, 2) + doAssert(len(selector.select(0)) == 0) + + # rename test directory + renameFile(testDirectory & "/test", testDirectory & "/renamed") + res = selector.select(0) + doAssert(len(res) == 2) + doAssert(len(selector.select(0)) == 0) + doAssert(validate(res, + [valType(fd: dirfd, events: {Event.Vnode, Event.VnodeWrite}), + valType(fd: testfd, + events: {Event.Vnode, Event.VnodeRename})]) + ) + + # remove test directory + removeDir(testDirectory & "/renamed") + res = selector.select(0) + doAssert(len(res) == 2) + doAssert(len(selector.select(0)) == 0) + doAssert(validate(res, + [valType(fd: dirfd, events: {Event.Vnode, Event.VnodeWrite, + Event.VnodeLink}), + valType(fd: testfd, + events: {Event.Vnode, Event.VnodeDelete})]) + ) + # create file new test file + testfd = createFile(testDirectory & "/testfile") + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeWrite} <= res[0].events) + + # close new test file + closeFile(testfd) + doAssert(len(selector.select(0)) == 0) + doAssert(len(selector.select(0)) == 0) + + # chmod test file with 0666 + chmodPath(testDirectory & "/testfile", 0x1B6) + doAssert(len(selector.select(0)) == 0) + + testfd = openWatch(testDirectory & "/testfile") + selector.registerVnode(testfd, events, 1) + discard selector.select(0) + + # write data to test file + writeFile(testDirectory & "/testfile", "TESTDATA") + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == testfd and + {Event.Vnode, Event.VnodeWrite, + Event.VnodeExtend} <= res[0].events) + + # symlink test file + symlink(testDirectory & "/testfile", testDirectory & "/testlink") + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeWrite} <= res[0].events) + + # remove test file + removeFile(testDirectory & "/testfile") + res = selector.select(0) + doAssert(len(res) == 2) + doAssert(len(selector.select(0)) == 0) + doAssert(validate(res, + [valType(fd: testfd, events: {Event.Vnode, Event.VnodeDelete}), + valType(fd: dirfd, events: {Event.Vnode, Event.VnodeWrite})]) + ) + + # remove symlink + removeFile(testDirectory & "/testlink") + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeWrite} <= res[0].events) + + # remove testDirectory + removeDir(testDirectory) + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeDelete} <= res[0].events) + + proc pipe_test(): bool = + # closing the read end of a pipe will result in it automatically + # being removed from the kqueue; make sure no exception is raised + var s = newSelector[int]() + var fds: array[2, cint] + discard pipe(fds) + s.registerHandle(fds[1], {Write}, 0) + discard close(fds[0]) + let res = s.select(-1) + doAssert(res.len == 1) + s.unregister(fds[1]) + discard close(fds[1]) + return true + when hasThreadSupport: var counter = 0 @@ -224,7 +452,6 @@ when not defined(windows): proc event_wait_thread(event: SelectEvent) {.thread.} = var selector = newSelector[int]() selector.registerEvent(event, 1) - selector.flush() var rc = selector.select(1000) if len(rc) == 1: inc(counter) @@ -233,16 +460,16 @@ when not defined(windows): proc mt_event_test(): bool = var - thr: array [0..7, Thread[SelectEvent]] + thr: array[0..7, Thread[SelectEvent]] var selector = newSelector[int]() - var sock = newNativeSocket() + var sock = createNativeSocket() var event = newSelectEvent() for i in 0..high(thr): createThread(thr[i], event_wait_thread, event) selector.registerHandle(sock, {Event.Read}, 1) discard selector.select(500) selector.unregister(sock) - event.setEvent() + event.trigger() joinThreads(thr) assert(counter == 1) result = true @@ -252,17 +479,21 @@ when not defined(windows): when hasThreadSupport: processTest("Multithreaded user event notification test...", mt_event_test()) - when supportedPlatform: + when ioselSupportedPlatform: processTest("Timer notification test...", timer_notification_test()) processTest("Process notification test...", process_notification_test()) processTest("Signal notification test...", signal_notification_test()) + when defined(macosx) or defined(freebsd) or defined(openbsd) or + defined(netbsd): + processTest("File notification test...", vnode_test()) + processTest("Pipe test...", pipe_test()) echo("All tests passed!") else: import nativesockets, winlean, os, osproc proc socket_notification_test(): bool = proc create_test_socket(): SocketHandle = - var sock = newNativeSocket() + var sock = createNativeSocket() setBlocking(sock, false) result = sock @@ -285,19 +516,24 @@ else: var aiList = getAddrInfo("0.0.0.0", Port(13337)) if bindAddr(server_socket, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: - dealloc(aiList) + freeAddrInfo(aiList) raiseOSError(osLastError()) discard server_socket.listen() - dealloc(aiList) + freeAddrInfo(aiList) aiList = getAddrInfo("127.0.0.1", Port(13337)) discard connect(client_socket, aiList.ai_addr, aiList.ai_addrlen.Socklen) - dealloc(aiList) + freeAddrInfo(aiList) # for some reason Windows select doesn't return both # descriptors from first call, so we need to make 2 calls - discard selector.select(100) - var rcm = selector.select(100) + var n = 0 + var rcm = selector.select(1000) + while n < 10 and len(rcm) < 2: + sleep(1000) + rcm = selector.select(1000) + inc(n) + assert(len(rcm) == 2) var sockAddress = SockAddr() @@ -314,10 +550,10 @@ else: selector.updateHandle(client_socket, {Event.Read}) - var rc2 = selector.select(100) + var rc2 = selector.select(1000) assert(len(rc2) == 1) - var read_count = recv(server2_socket, addr (buffer[0]), 128, 0) + var read_count = recv(server2_socket, addr buffer[0], 128, 0) if read_count == -1: raiseOSError(osLastError()) @@ -361,15 +597,15 @@ else: var selector = newSelector[int]() var event = newSelectEvent() selector.registerEvent(event, 1) - selector.flush() - event.setEvent() + discard selector.select(0) + event.trigger() var rc1 = selector.select(0) - event.setEvent() + event.trigger() var rc2 = selector.select(0) var rc3 = selector.select(0) assert(len(rc1) == 1 and len(rc2) == 1 and len(rc3) == 0) - var ev1 = rc1[0].data - var ev2 = rc2[0].data + var ev1 = selector.getData(rc1[0].fd) + var ev2 = selector.getData(rc2[0].fd) assert(ev1 == 1 and ev2 == 1) selector.unregister(event) event.close() @@ -383,19 +619,18 @@ else: proc event_wait_thread(event: SelectEvent) {.thread.} = var selector = newSelector[int]() selector.registerEvent(event, 1) - selector.flush() - var rc = selector.select(500) + var rc = selector.select(1500) if len(rc) == 1: inc(counter) selector.unregister(event) assert(selector.isEmpty()) proc mt_event_test(): bool = - var thr: array [0..7, Thread[SelectEvent]] + var thr: array[0..7, Thread[SelectEvent]] var event = newSelectEvent() for i in 0..high(thr): createThread(thr[i], event_wait_thread, event) - event.setEvent() + event.trigger() joinThreads(thr) assert(counter == 1) result = true |