From 68430a1ea49215860a53b9f60f245da35a33d878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Thu, 16 Feb 2017 23:37:14 +0100 Subject: [PATCH 1/3] Fix file pointer management in FileStream. Fixes rejectedsoftware/vibe.d#1684. --- source/vibe/core/file.d | 3 ++- tests/vibe.core.file.d | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/source/vibe/core/file.d b/source/vibe/core/file.d index e9dedd5..9058d04 100644 --- a/source/vibe/core/file.d +++ b/source/vibe/core/file.d @@ -459,11 +459,12 @@ struct FileStream { } size_t read(ubyte[] dst, IOMode mode) - { + { auto res = asyncAwait!(FileIOCallback, cb => eventDriver.files.read(m_fd, ctx.ptr, dst, mode, cb), cb => eventDriver.files.cancelRead(m_fd) ); + ctx.ptr += res[2]; enforce(res[1] == IOStatus.ok, "Failed to read data from disk."); return res[2]; } diff --git a/tests/vibe.core.file.d b/tests/vibe.core.file.d index 2f212cf..44f8457 100644 --- a/tests/vibe.core.file.d +++ b/tests/vibe.core.file.d @@ -12,16 +12,23 @@ void main() { auto f = openFile("test.dat", FileMode.createTrunc); assert(f.size == 0); + assert(f.tell == 0); f.write(bytes!(1, 2, 3, 4, 5)); assert(f.size == 5); + assert(f.tell == 5); f.seek(0); + assert(f.tell == 0); f.write(bytes!(1, 2, 3, 4, 5)); assert(f.size == 5); + assert(f.tell == 5); f.write(bytes!(6, 7, 8, 9, 10)); assert(f.size == 10); + assert(f.tell == 10); ubyte[5] dst; f.seek(2); + assert(f.tell == 2); f.read(dst); + assert(f.tell == 7); assert(dst[] == bytes!(3, 4, 5, 6, 7)); f.close(); From 99bc332a8190c9e65740adf86bfa622d5422ea38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Fri, 17 Feb 2017 00:49:40 +0100 Subject: [PATCH 2/3] Allow script based tests and fix test package recipes. --- tests/std.concurrency.d | 2 +- tests/vibe.core.file.d | 2 +- tests/vibe.core.net.1429.d | 2 +- tests/vibe.core.net.1441.d | 2 +- tests/vibe.core.net.1452.d | 3 ++- travis-ci.sh | 10 ++++++++-- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/std.concurrency.d b/tests/std.concurrency.d index f7733e5..b7a7a55 100644 --- a/tests/std.concurrency.d +++ b/tests/std.concurrency.d @@ -1,4 +1,4 @@ -/++ dub.sdl: +/+ dub.sdl: name "test" description "Tests vibe.d's std.concurrency integration" dependency "vibe-core" path="../" diff --git a/tests/vibe.core.file.d b/tests/vibe.core.file.d index 44f8457..254b684 100644 --- a/tests/vibe.core.file.d +++ b/tests/vibe.core.file.d @@ -1,4 +1,4 @@ -/++ dub.sdl: +/+ dub.sdl: name "test" dependency "vibe-core" path=".." +/ diff --git a/tests/vibe.core.net.1429.d b/tests/vibe.core.net.1429.d index a81e9f0..81be91f 100644 --- a/tests/vibe.core.net.1429.d +++ b/tests/vibe.core.net.1429.d @@ -1,4 +1,4 @@ -/++ dub.sdl: +/+ dub.sdl: name "test" description "TCP disconnect task issue" dependency "vibe-core" path="../" diff --git a/tests/vibe.core.net.1441.d b/tests/vibe.core.net.1441.d index b5c3d6b..460e4fa 100644 --- a/tests/vibe.core.net.1441.d +++ b/tests/vibe.core.net.1441.d @@ -1,4 +1,4 @@ -/++ dub.sdl: +/+ dub.sdl: name "test" description "TCP disconnect task issue" dependency "vibe-core" path="../" diff --git a/tests/vibe.core.net.1452.d b/tests/vibe.core.net.1452.d index 1a6023c..0ad83bb 100644 --- a/tests/vibe.core.net.1452.d +++ b/tests/vibe.core.net.1452.d @@ -1,7 +1,8 @@ -/++ dub.sdl: +/+ dub.sdl: name "test" description "Invalid memory operation on TCP connection leakage at shutdown" dependency "vibe-core" path="../" +debugVersions "VibeAsyncLog" +/ module test; diff --git a/travis-ci.sh b/travis-ci.sh index d816449..2c8df94 100755 --- a/travis-ci.sh +++ b/travis-ci.sh @@ -21,7 +21,13 @@ if [ ${BUILD_EXAMPLE=1} -eq 1 ]; then fi if [ ${RUN_TEST=1} -eq 1 ]; then for ex in `\ls -1 tests/*.d`; do - echo "[INFO] Running test $ex" - dub --temp-build --compiler=$DC --single $ex # --override-config vibe-core/$CONFIG + script="${ex:0:-2}.sh" + if [ -e "$script" ]; then + echo "[INFO] Running test scipt $script" + (cd tests && "./${script:6}") + else + echo "[INFO] Running test $ex" + dub --temp-build --compiler=$DC --single $ex # --override-config vibe-core/$CONFIG + fi done fi From 5a8d5a2fea53667a5d0d71ef5049f5c7a1df7176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Fri, 17 Feb 2017 00:55:16 +0100 Subject: [PATCH 3/3] Add tests from vibe-d:core. --- tests/args.d | 27 ++++++++ tests/args.sh | 10 +++ tests/dirwatcher.d | 107 +++++++++++++++++++++++++++++ tests/vibe.core.concurrency.1408.d | 58 ++++++++++++++++ tests/vibe.core.core.1590.d | 59 ++++++++++++++++ tests/vibe.core.core.refcount.d | 51 ++++++++++++++ tests/vibe.core.net.1376.d | 42 +++++++++++ 7 files changed, 354 insertions(+) create mode 100644 tests/args.d create mode 100755 tests/args.sh create mode 100644 tests/dirwatcher.d create mode 100644 tests/vibe.core.concurrency.1408.d create mode 100644 tests/vibe.core.core.1590.d create mode 100644 tests/vibe.core.core.refcount.d create mode 100644 tests/vibe.core.net.1376.d diff --git a/tests/args.d b/tests/args.d new file mode 100644 index 0000000..c62d823 --- /dev/null +++ b/tests/args.d @@ -0,0 +1,27 @@ +/+ dub.json: +{ + "name": "tests", + "description": "Command-line argument test", + "dependencies": { + "vibe-core": {"path": "../"} + } +} ++/ +module test; + +import vibe.core.args; +import vibe.core.log; + +import std.stdio; + +shared static this() +{ + string argtest; + readOption("argtest", &argtest, "Test argument"); + writeln("argtest=", argtest); +} + +void main() +{ + finalizeCommandLineOptions(); +} diff --git a/tests/args.sh b/tests/args.sh new file mode 100755 index 0000000..67c66ff --- /dev/null +++ b/tests/args.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -e +die() { echo "$@" 1>&2 ; exit 1; } + +( dub args.d | grep -q '^argtest=$' ) || die "Fail (no argument)" +( dub args.d --argtest=aoeu | grep -q '^argtest=aoeu$' ) || die "Fail (with argument)" +( ( ! dub args.d --inexisting 2>&1 ) | grep -qF 'Unrecognized command line option' ) || die "Fail (unknown argument)" + +echo 'OK' diff --git a/tests/dirwatcher.d b/tests/dirwatcher.d new file mode 100644 index 0000000..bdd20fa --- /dev/null +++ b/tests/dirwatcher.d @@ -0,0 +1,107 @@ +/+ dub.json: +{ + "name": "tests", + "description": "Test for vibe.d's DirectoryWatcher.", + "dependencies": { + "vibe-core": {"path": "../"} + } +} ++/ +import vibe.core.core; +import vibe.core.file; +import vibe.core.log; +import vibe.core.path; +import std.algorithm : canFind, all; +import std.file, std.process; +import std.format : format; +import std.conv : to; +import std.path : buildPath; +import std.typecons : No, Yes; +import core.exception : AssertError; +import core.time : msecs, seconds; + +void runTest() +{ + auto dir = buildPath(tempDir, format("dirwatcher_test_%d", thisProcessID())); + mkdir(dir); + scope(exit) rmdirRecurse(dir); + + DirectoryWatcher watcher; + try watcher = Path(dir).watchDirectory(No.recursive); + catch (AssertError e) { + logInfo("DirectoryWatcher not yet implemented. Skipping test."); + return; + } + DirectoryChange[] changes; + assert(!watcher.readChanges(changes, 500.msecs)); + + auto foo = dir.buildPath("foo"); + + alias Type = DirectoryChangeType; + static DirectoryChange dc(Type t, string p) { return DirectoryChange(t, Path(p)); } + void check(DirectoryChange[] expected) + { + sleep(100.msecs); + assert(watcher.readChanges(changes, 100.msecs), "Could not read changes for " ~ expected.to!string); + assert(expected.all!((a)=> changes.canFind(a))(), "Change is not what was expected, got: " ~ changes.to!string ~ " but expected: " ~ expected.to!string); + assert(!watcher.readChanges(changes, 0.msecs), "Changes were returned when they shouldn't have, for " ~ expected.to!string); + } + + write(foo, null); + check([dc(Type.added, foo)]); + sleep(1.seconds); // OSX has a second resolution on file modification times + write(foo, [0, 1]); + check([dc(Type.modified, foo)]); + remove(foo); + check([dc(Type.removed, foo)]); + write(foo, null); + sleep(1.seconds); + write(foo, [0, 1]); + sleep(100.msecs); + remove(foo); + check([dc(Type.added, foo), dc(Type.modified, foo), dc(Type.removed, foo)]); + + auto subdir = dir.buildPath("subdir"); + mkdir(subdir); + check([dc(Type.added, subdir)]); + auto bar = subdir.buildPath("bar"); + write(bar, null); + assert(!watcher.readChanges(changes, 100.msecs)); + remove(bar); + watcher = Path(dir).watchDirectory(Yes.recursive); + write(foo, null); + sleep(1.seconds); + write(foo, [0, 1]); + sleep(100.msecs); + remove(foo); + + write(bar, null); + sleep(1.seconds); + write(bar, [0, 1]); + sleep(100.msecs); + remove(bar); + check([dc(Type.added, foo), dc(Type.modified, foo), dc(Type.removed, foo), + dc(Type.added, bar), dc(Type.modified, bar), dc(Type.removed, bar)]); + + write(foo, null); + sleep(100.msecs); + rename(foo, bar); + sleep(100.msecs); + remove(bar); + check([dc(Type.added, foo), dc(Type.removed, foo), dc(Type.added, bar), dc(Type.removed, bar)]); + +} + +int main() +{ + int ret = 0; + runTask({ + try runTest(); + catch (Throwable th) { + logError("Test failed: %s", th.toString()); + ret = 1; + } finally exitEventLoop(true); + }); + runEventLoop(); + return ret; +} diff --git a/tests/vibe.core.concurrency.1408.d b/tests/vibe.core.concurrency.1408.d new file mode 100644 index 0000000..3ae0c21 --- /dev/null +++ b/tests/vibe.core.concurrency.1408.d @@ -0,0 +1,58 @@ +/+ dub.sdl: + name "tests" + description "std.concurrency integration issue" + dependency "vibe-core" path="../" + versions "VibeDefaultMain" ++/ +module test; + +import vibe.core.concurrency; +import vibe.core.core; +import vibe.core.log; +import core.time : msecs; +import std.functional : toDelegate; + +void test() +{ + auto t = runTask({ + bool gotit; + receive((int i) { assert(i == 10); gotit = true; }); + assert(gotit); + sleep(10.msecs); + }); + + t.tid.send(10); + t.tid.send(11); // never received + t.join(); + + // ensure that recycled fibers will get a clean message queue + auto t2 = runTask({ + bool gotit; + receive((int i) { assert(i == 12); gotit = true; }); + assert(gotit); + }); + t2.tid.send(12); + t2.join(); + + // test worker tasks + auto t3 = runWorkerTaskH({ + bool gotit; + receive((int i) { assert(i == 13); gotit = true; }); + assert(gotit); + }); + + t3.tid.send(13); + sleep(10.msecs); + + logInfo("Success."); + + exitEventLoop(true); +} + +shared static this() +{ +setLogFormat(FileLogger.Format.threadTime, FileLogger.Format.threadTime); + runTask(toDelegate(&test)); +} + + diff --git a/tests/vibe.core.core.1590.d b/tests/vibe.core.core.1590.d new file mode 100644 index 0000000..b7660fa --- /dev/null +++ b/tests/vibe.core.core.1590.d @@ -0,0 +1,59 @@ +/+ dub.sdl: + name "tests" + description "Semaphore hang" + dependency "vibe-core" path="../" ++/ +module test; +import std.stdio; +import std.socket; +import std.datetime; +import std.functional; +import core.time; +import vibe.core.core; +import vibe.core.log; +import vibe.core.concurrency; +import vibe.core.connectionpool; + +class Conn {} + +void main() +{ + runTask({ + // create pool with 2 max connections + bool[int] results; + auto pool = new ConnectionPool!Conn({ return new Conn; }, 2); + auto task = Task.getThis(); // main task + void worker(int id) { + { + auto conn = pool.lockConnection(); // <-- worker(4) hangs here + sleep(1.msecs); // <-- important, without sleep everything works fine + } + task.send(id); // send signal to the main task + } + // run 4 tasks (2 * pool max connections) + runTask(&worker, 1); + runTask(&worker, 2); + runTask(&worker, 3); + runTask(&worker, 4); + + // wait for first signal and run one more task + results[receiveOnly!int] = true; + runTask(&worker, 5); + + // wait for other signals + results[receiveOnly!int] = true; + results[receiveOnly!int] = true; + results[receiveOnly!int] = true; + results[receiveOnly!int] = true; + + foreach (r; results.byKey) + assert(r >= 1 && r <= 5); + + exitEventLoop(); + }); + + setTimer(1.seconds, { assert(false, "Test has hung."); }); + + runEventLoop(); +} + diff --git a/tests/vibe.core.core.refcount.d b/tests/vibe.core.core.refcount.d new file mode 100644 index 0000000..c372cd9 --- /dev/null +++ b/tests/vibe.core.core.refcount.d @@ -0,0 +1,51 @@ +/+ dub.sdl: + name "tests" + description "Invalid ref count after runTask" + dependency "vibe-core" path="../" ++/ +module test; +import vibe.core.core; +import std.stdio; + +struct RC { + int* rc; + this(int* rc) { this.rc = rc; } + this(this) { + if (rc) { + (*rc)++; + writefln("addref %s", *rc); + } + } + ~this() { + if (rc) { + (*rc)--; + writefln("release %s", *rc); + } + } +} + +void main() +{ + int rc = 1; + bool done = false; + + { + auto s = RC(&rc); + assert(rc == 1); + runTask((RC st) { + assert(rc == 2); + st = RC.init; + assert(rc == 1); + exitEventLoop(); + done = true; + }, s); + assert(rc == 2); + } + + assert(rc == 1); + + runEventLoop(); + + assert(rc == 0); + assert(done); +} diff --git a/tests/vibe.core.net.1376.d b/tests/vibe.core.net.1376.d new file mode 100644 index 0000000..4e01fd9 --- /dev/null +++ b/tests/vibe.core.net.1376.d @@ -0,0 +1,42 @@ +/+ dub.sdl: + name "tests" + description "TCP disconnect task issue" + dependency "vibe-core" path="../" + versions "VibeDefaultMain" ++/ +module test; + +import vibe.core.core; +import vibe.core.net; +import core.time : msecs; + +shared static this() +{ + listenTCP(11375,(conn){ + auto td = runTask!TCPConnection((conn) { + ubyte [3] buf; + try { + conn.read(buf); + assert(false, "Expected read() to throw an exception."); + } catch (Exception) {} // expected + }, conn); + sleep(10.msecs); + conn.close(); + }); + + runTask({ + try { + auto conn = connectTCP("127.0.0.1", 11375); + conn.write("a"); + conn.close(); + } catch (Exception e) assert(false, e.msg); + + try { + auto conn = connectTCP("127.0.0.1", 11375); + conn.close(); + } catch (Exception e) assert(false, e.msg); + + sleep(50.msecs); + exitEventLoop(); + }); +}