discard """ output: "All tests passed!" """ import selectors const hasThreadSupport = compileOption("threads") template processTest(t, x: untyped) = #stdout.write(t) #stdout.flushFile() if not x: echo(t & " FAILED\r\n") when not defined(windows): import os, posix, nativesockets when ioselSupportedPlatform: import osproc proc socket_notification_test(): bool = proc create_test_socket(): SocketHandle = var sock = posix.socket(posix.AF_INET, posix.SOCK_STREAM, posix.IPPROTO_TCP) var x: int = fcntl(sock, F_GETFL, 0) if x == -1: raiseOSError(osLastError()) else: var mode = x or O_NONBLOCK if fcntl(sock, F_SETFL, mode) == -1: raiseOSError(osLastError()) result = sock var client_message = "SERVER HELLO =>" var server_message = "CLIENT HELLO" var buffer : array[128, char] var selector = newSelector[int]() var client_socket = create_test_socket() var server_socket = create_test_socket() var option : int32 = 1 if setsockopt(server_socket, cint(SOL_SOCKET), cint(SO_REUSEADDR), addr(option), sizeof(option).SockLen) < 0: raiseOSError(osLastError()) var aiList = getAddrInfo("0.0.0.0", Port(13337)) if bindAddr(server_socket, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: freeAddrInfo(aiList) raiseOSError(osLastError()) 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) registerHandle(selector, server_socket, {Event.Read}, 0) registerHandle(selector, client_socket, {Event.Write}, 0) freeAddrInfo(aiList) discard selector.select(100) var sockAddress: SockAddr var addrLen = sizeof(sockAddress).Socklen var server2_socket = accept(server_socket, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) assert(server2_socket != osInvalidSocket) selector.registerHandle(server2_socket, {Event.Read}, 0) if posix.send(client_socket, addr(client_message[0]), len(client_message), 0) == -1: raiseOSError(osLastError()) selector.updateHandle(client_socket, {Event.Read}) var rc2 = selector.select(100) assert(len(rc2) == 1) var read_count = posix.recv(server2_socket, addr buffer[0], 128, 0) if read_count == -1: raiseOSError(osLastError()) assert(read_count == len(client_message)) var test1 = true for i in 0..".} 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](unsafeAddr 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) when hasThreadSupport: var counter = 0 proc event_wait_thread(event: SelectEvent) {.thread.} = var selector = newSelector[int]() selector.registerEvent(event, 1) var rc = selector.select(1000) 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 selector = newSelector[int]() 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.trigger() joinThreads(thr) assert(counter == 1) result = true processTest("Socket notification test...", socket_notification_test()) processTest("User event notification test...", event_notification_test()) when hasThreadSupport: processTest("Multithreaded user event notification test...", mt_event_test()) 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()) echo("All tests passed!") else: import nativesockets, winlean, os, osproc proc socket_notification_test(): bool = proc create_test_socket(): SocketHandle = var sock = createNativeSocket() setBlocking(sock, false) result = sock var client_message = "SERVER HELLO =>" var server_message = "CLIENT HELLO" var buffer : array[128, char] var selector = newSelector[int]() var client_socket = create_test_socket() var server_socket = create_test_socket() selector.registerHandle(server_socket, {Event.Read}, 0) selector.registerHandle(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: raiseOSError(osLastError()) var aiList = getAddrInfo("0.0.0.0", Port(13337)) if bindAddr(server_socket, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: freeAddrInfo(aiList) raiseOSError(osLastError()) discard server_socket.listen() freeAddrInfo(aiList) aiList = getAddrInfo("127.0.0.1", Port(13337)) discard connect(client_socket, aiList.ai_addr, aiList.ai_addrlen.Socklen) freeAddrInfo(aiList) # for some reason Windows select doesn't return both # descriptors from first call, so we need to make 2 calls 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() var addrLen = sizeof(sockAddress).Socklen var server2_socket = accept(server_socket, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) assert(server2_socket != osInvalidSocket) selector.registerHandle(server2_socket, {Event.Read}, 0) if send(client_socket, cast[pointer](addr(client_message[0])), cint(len(client_message)), 0) == -1: raiseOSError(osLastError()) selector.updateHandle(client_socket, {Event.Read}) var rc2 = selector.select(1000) assert(len(rc2) == 1) var read_count = recv(server2_socket, addr buffer[0], 128, 0) if read_count == -1: raiseOSError(osLastError()) assert(read_count == len(client_message)) var test1 = true for i in 0..