summary refs log tree commit diff stats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/array/troof1.nim39
-rw-r--r--tests/array/troof2.nim10
-rw-r--r--tests/array/troof3.nim5
-rw-r--r--tests/array/troof4.nim37
-rw-r--r--tests/async/tnewasyncudp.nim6
-rw-r--r--tests/ccgbugs/tbug1081.nim19
-rw-r--r--tests/ccgbugs/tret_arg_init.nim6
-rw-r--r--tests/closure/t1641.nim20
-rw-r--r--tests/concepts/tmapconcept.nim2
-rw-r--r--tests/cpp/tget_subsystem.nim8
-rw-r--r--tests/destructor/tcustomseqs.nim143
-rw-r--r--tests/destructor/tcustomstrings.nim99
-rw-r--r--tests/destructor/topttree.nim104
-rw-r--r--tests/effects/tgcsafe2.nim12
-rw-r--r--tests/exprs/texprstmt.nim2
-rw-r--r--tests/fields/timplicitfieldswithpartial.nim18
-rw-r--r--tests/float/tfloat4.nim2
-rw-r--r--tests/float/tfloatnan.nim16
-rw-r--r--tests/gc/gctest.nim2
-rw-r--r--tests/misc/treadx.nim3
-rw-r--r--tests/misc/tupcomingfeatures.nim8
-rw-r--r--tests/misc/tvarnums.nim2
-rw-r--r--tests/modules/treorder.nim12
-rw-r--r--tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim5
-rw-r--r--tests/niminaction/Chapter8/sfml/sfml_test.nim4
-rw-r--r--tests/overload/tprefer_tygenericinst.nim36
-rw-r--r--tests/package_level_objects/definefoo.nim3
-rw-r--r--tests/package_level_objects/mypackage.nimble0
-rw-r--r--tests/package_level_objects/tusefoo.nim16
-rw-r--r--tests/package_level_objects/tusefoo2.nim19
-rw-r--r--tests/pragmas/treorder.nim75
-rw-r--r--tests/range/tn8vsint16.nim18
-rw-r--r--tests/stdlib/tjsonmacro.nim4
-rw-r--r--tests/stdlib/tstrutil.nim111
-rw-r--r--tests/stdlib/ttimes.nim (renamed from tests/stdlib/ttime.nim)36
-rw-r--r--tests/system/toString.nim77
-rw-r--r--tests/system/tsystem_misc.nim18
-rw-r--r--tests/testament/backend.nim124
-rw-r--r--tests/testament/categories.nim15
-rw-r--r--tests/testament/css/boilerplate.css138
-rw-r--r--tests/testament/css/style.css114
-rw-r--r--tests/testament/htmlgen.nim364
-rw-r--r--tests/testament/specs.nim2
-rw-r--r--tests/testament/testamenthtml.templ318
-rw-r--r--tests/testament/tester.nim45
-rw-r--r--tests/types/tillegaltyperecursion2.nim8
-rw-r--r--tests/untestable/tpostgres.nim19
-rw-r--r--tests/vm/trgba.nim2
48 files changed, 1409 insertions, 737 deletions
diff --git a/tests/array/troof1.nim b/tests/array/troof1.nim
index 96669a121..1ee63826e 100644
--- a/tests/array/troof1.nim
+++ b/tests/array/troof1.nim
@@ -1,7 +1,10 @@
 discard """
   output: '''@[2, 3, 4]321
 9.0 4.0
-(a: 1.0, b: 2.0, c: 8.0)2.0'''
+3
+@[(Field0: 1, Field1: 2), (Field0: 3, Field1: 5)]
+2
+@[a, new one, c]'''
 """
 
 proc foo[T](x, y: T): T = x
@@ -11,26 +14,24 @@ var b: array[3, array[2, float]] = [[1.0,2], [3.0,4], [8.0,9]]
 echo a[1.. ^1], a[^2], a[^3], a[^4]
 echo b[^1][^1], " ", (b[^2]).foo(b[^1])[^1]
 
-type
-  MyArray = object
-    a, b, c: float
+b[^1] = [8.8, 8.9]
 
-var
-  ma = MyArray(a: 1.0, b: 2.0, c: 3.0)
+var c: seq[(int, int)] = @[(1,2), (3,4)]
 
-proc len(x: MyArray): int = 3
+proc takeA(x: ptr int) = echo x[]
 
-proc `[]=`(x: var MyArray; idx: range[0..2]; val: float) =
-  case idx
-  of 0: x.a = val
-  of 1: x.b = val
-  of 2: x.c = val
+takeA(addr c[^1][0])
+c[^1][1] = 5
+echo c
 
-proc `[]`(x: var MyArray; idx: range[0..2]): float =
-  case idx
-  of 0: result = x.a
-  of 1: result = x.b
-  of 2: result = x.c
+proc useOpenarray(x: openArray[int]) =
+  echo x[^2]
 
-ma[^1] = 8.0
-echo ma, ma[^2]
+proc mutOpenarray(x: var openArray[string]) =
+  x[^2] = "new one"
+
+useOpenarray([1, 2, 3])
+
+var z = @["a", "b", "c"]
+mutOpenarray(z)
+echo z
diff --git a/tests/array/troof2.nim b/tests/array/troof2.nim
deleted file mode 100644
index e4b4f4b3c..000000000
--- a/tests/array/troof2.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-discard """
-  errormsg: "invalid context for '^' as 'foo()' has side effects"
-  line: "9"
-"""
-# XXX This needs to be fixed properly!
-proc foo(): seq[int] {.sideEffect.} =
-  echo "ha"
-
-let f = foo()[^1]
-
diff --git a/tests/array/troof3.nim b/tests/array/troof3.nim
index 4b6e22223..efe0eafb8 100644
--- a/tests/array/troof3.nim
+++ b/tests/array/troof3.nim
@@ -1,8 +1,7 @@
 discard """
-  errormsg: "invalid context for '^' as len!=high+1 for 'a'"
-  line: "8"
+  output: '''c'''
 """
 
-var a: array[1..3, string]
+var a: array['a'..'c', string] = ["a", "b", "c"]
 
 echo a[^1]
diff --git a/tests/array/troof4.nim b/tests/array/troof4.nim
deleted file mode 100644
index 7a262d9de..000000000
--- a/tests/array/troof4.nim
+++ /dev/null
@@ -1,37 +0,0 @@
-discard """
-  errormsg: "no surrounding array access context for '^'"
-  line: "37"
-"""
-
-proc foo[T](x, y: T): T = x
-
-var a = @[1, 2, 3, 4]
-var b: array[3, array[2, float]] = [[1.0,2], [3.0,4], [8.0,9]]
-echo a[1.. ^1], a[^2], a[^3], a[^4]
-echo b[^1][^1], " ", (b[^2]).foo(b[^1])[^1]
-
-type
-  MyArray = object
-    a, b, c: float
-
-var
-  ma = MyArray(a: 1.0, b: 2.0, c: 3.0)
-
-proc len(x: MyArray): int = 3
-
-proc `[]=`(x: var MyArray; idx: range[0..2]; val: float) =
-  case idx
-  of 0: x.a = val
-  of 1: x.b = val
-  of 2: x.c = val
-
-proc `[]`(x: var MyArray; idx: range[0..2]): float =
-  case idx
-  of 0: result = x.a
-  of 1: result = x.b
-  of 2: result = x.c
-
-ma[^1] = 8.0
-echo ma, ma[^2]
-
-echo(^1)
diff --git a/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim
index bf54c0d06..b56cdc71b 100644
--- a/tests/async/tnewasyncudp.nim
+++ b/tests/async/tnewasyncudp.nim
@@ -54,7 +54,7 @@ proc launchSwarm(name: ptr SockAddr) {.async.} =
     k = 0
     while k < messagesToSend:
       zeroMem(addr(buffer[0]), 16384)
-      zeroMem(cast[pointer](addr(saddr)), sizeof(Sockaddr_in))      
+      zeroMem(cast[pointer](addr(saddr)), sizeof(Sockaddr_in))
       var message = "Message " & $(i * messagesToSend + k)
       await sendTo(sock, addr message[0], len(message),
                    name, sizeof(Sockaddr_in).SockLen)
@@ -62,7 +62,7 @@ proc launchSwarm(name: ptr SockAddr) {.async.} =
                                     16384, cast[ptr SockAddr](addr saddr),
                                     addr slen)
       size = 0
-      var grammString = $buffer
+      var grammString = $cstring(addr buffer)
       if grammString == message:
         saveSendingPort(sockport)
         inc(recvCount)
@@ -84,7 +84,7 @@ proc readMessages(server: AsyncFD) {.async.} =
                                   16384, cast[ptr SockAddr](addr(saddr)),
                                   addr(slen))
     size = 0
-    var grammString = $buffer
+    var grammString = $cstring(addr buffer)
     if grammString.startswith("Message ") and
        saddr.sin_addr.s_addr == 0x100007F:
       await sendTo(server, addr grammString[0], len(grammString),
diff --git a/tests/ccgbugs/tbug1081.nim b/tests/ccgbugs/tbug1081.nim
index 71628feec..c9a9e6aa4 100644
--- a/tests/ccgbugs/tbug1081.nim
+++ b/tests/ccgbugs/tbug1081.nim
@@ -2,7 +2,8 @@ discard """
   output: '''1
 0
 0
-0'''
+0
+x = [a, b, c, 0, 1, 2, 3, 4, 5, 6] and y = [a, b, c, 0, 1, 2, 3, 4, 5, 6]'''
 """
 
 proc `1/1`() = echo(1 div 1)
@@ -15,3 +16,19 @@ let `1/4` = 1 div 4
 `1/2`()
 echo `1/3`
 echo `1/4`
+
+# bug #6422
+
+proc toCharArray1(N : static[int], s: string): array[N, char] =
+  doAssert s.len <= N
+  let x = cast[ptr array[N, char]](s.cstring)
+  x[]
+
+proc toCharArray2(N : static[int], s: string): array[N, char] =
+  doAssert s.len <= N
+  let x = cast[ptr array[N, char]](s.cstring)
+  result = x[]
+
+let x = toCharArray1(10, "abc0123456")
+let y = toCharArray2(10, "abc0123456")
+echo "x = ", $x, " and y = ", $y
diff --git a/tests/ccgbugs/tret_arg_init.nim b/tests/ccgbugs/tret_arg_init.nim
index 3c80fb061..5cd67de3e 100644
--- a/tests/ccgbugs/tret_arg_init.nim
+++ b/tests/ccgbugs/tret_arg_init.nim
@@ -1,7 +1,7 @@
 discard """
-  output: '''nil
-nil
-nil'''
+  output: '''
+
+'''
 """
 
 type Bar = object
diff --git a/tests/closure/t1641.nim b/tests/closure/t1641.nim
new file mode 100644
index 000000000..a3e4da367
--- /dev/null
+++ b/tests/closure/t1641.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''foo 0
+bar 0
+baz'''
+"""
+
+# bug #1641
+proc baz() =
+  echo "baz"
+
+proc bar(x: int, p: proc()) =
+  echo "bar ", x
+  p()
+
+proc foo(x: int, p: proc(x: int)) =
+  echo "foo ", x
+  p(x)
+
+let x = 0
+x.foo do(x: int): x.bar do(): baz()
diff --git a/tests/concepts/tmapconcept.nim b/tests/concepts/tmapconcept.nim
index 81caed7c6..5082fcb61 100644
--- a/tests/concepts/tmapconcept.nim
+++ b/tests/concepts/tmapconcept.nim
@@ -1,7 +1,7 @@
 discard """
 output: '''10
 10
-nil
+
 1'''
 msg: '''
 K=string V=int
diff --git a/tests/cpp/tget_subsystem.nim b/tests/cpp/tget_subsystem.nim
index 461914739..81009dd39 100644
--- a/tests/cpp/tget_subsystem.nim
+++ b/tests/cpp/tget_subsystem.nim
@@ -21,3 +21,11 @@ proc getSubsystem*[T](): ptr T {.
 
 let input: ptr Input = getSubsystem[Input]()
 
+
+# bug #4910
+
+proc foo() =
+  var ts: array[10, int]
+  for t in mitems(ts):
+     t = 123
+
diff --git a/tests/destructor/tcustomseqs.nim b/tests/destructor/tcustomseqs.nim
new file mode 100644
index 000000000..97d7c07b6
--- /dev/null
+++ b/tests/destructor/tcustomseqs.nim
@@ -0,0 +1,143 @@
+discard """
+  output: '''1
+2
+3
+4
+5
+6
+89
+90
+90
+0 0 1
+0 1 2
+0 2 3
+1 0 4
+1 1 5
+1 2 6
+1 3 7
+after 6 6'''
+  cmd: '''nim c --newruntime $file'''
+"""
+
+import typetraits
+
+type
+  myseq*[T] = object
+    len, cap: int
+    data: ptr UncheckedArray[T]
+
+# XXX make code memory safe for overflows in '*'
+var
+  allocCount, deallocCount: int
+
+proc `=destroy`*[T](x: var myseq[T]) =
+  if x.data != nil:
+    when not supportsCopyMem(T):
+      for i in 0..<x.len: `=destroy`(x[i])
+    dealloc(x.data)
+    inc deallocCount
+    x.data = nil
+    x.len = 0
+    x.cap = 0
+
+proc `=`*[T](a: var myseq[T]; b: myseq[T]) =
+  if a.data == b.data: return
+  if a.data != nil:
+    dealloc(a.data)
+    inc deallocCount
+    a.data = nil
+  a.len = b.len
+  a.cap = b.cap
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
+    inc allocCount
+    when supportsCopyMem(T):
+      copyMem(a.data, b.data, a.cap * sizeof(T))
+    else:
+      for i in 0..<a.len:
+        a.data[i] = b.data[i]
+
+proc `=sink`*[T](a: var myseq[T]; b: myseq[T]) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    inc deallocCount
+  a.len = b.len
+  a.cap = b.cap
+  a.data = b.data
+
+proc resize[T](s: var myseq[T]) =
+  if s.cap == 0: s.cap = 8
+  else: s.cap = (s.cap * 3) shr 1
+  if s.data == nil: inc allocCount
+  s.data = cast[type(s.data)](realloc(s.data, s.cap * sizeof(T)))
+
+proc reserveSlot[T](x: var myseq[T]): ptr T =
+  if x.len >= x.cap: resize(x)
+  result = addr(x.data[x.len])
+  inc x.len
+
+template add*[T](x: var myseq[T]; y: T) =
+  reserveSlot(x)[] = y
+
+proc shrink*[T](x: var myseq[T]; newLen: int) =
+  assert newLen <= x.len
+  assert newLen >= 0
+  when not supportsCopyMem(T):
+    for i in countdown(x.len - 1, newLen - 1):
+      `=destroy`(x.data[i])
+  x.len = newLen
+
+proc grow*[T](x: var myseq[T]; newLen: int; value: T) =
+  if newLen <= x.len: return
+  assert newLen >= 0
+  if x.cap == 0: x.cap = newLen
+  else: x.cap = max(newLen, (x.cap * 3) shr 1)
+  if x.data == nil: inc allocCount
+  x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T)))
+  for i in x.len..<newLen:
+    x.data[i] = value
+  x.len = newLen
+
+template default[T](t: typedesc[T]): T =
+  var v: T
+  v
+
+proc setLen*[T](x: var myseq[T]; newLen: int) {.deprecated.} =
+  if newlen < x.len: shrink(x, newLen)
+  else: grow(x, newLen, default(T))
+
+template `[]`*[T](x: myseq[T]; i: Natural): T =
+  assert i < x.len
+  x.data[i]
+
+template `[]=`*[T](x: myseq[T]; i: Natural; y: T) =
+  assert i < x.len
+  x.data[i] = y
+
+proc createSeq*[T](elems: varargs[T]): myseq[T] =
+  result.cap = elems.len
+  result.len = elems.len
+  result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
+  inc allocCount
+  when supportsCopyMem(T):
+    copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T))
+  else:
+    for i in 0..<result.len:
+      result.data[i] = elems[i]
+
+proc len*[T](x: myseq[T]): int {.inline.} = x.len
+
+proc main =
+  var s = createSeq(1, 2, 3, 4, 5, 6)
+  s.add 89
+  s.grow s.len + 2, 90
+  for i in 0 ..< s.len:
+    echo s[i]
+
+  var nested = createSeq(createSeq(1, 2, 3), createSeq(4, 5, 6, 7))
+  for i in 0 ..< nested.len:
+    for j in 0 ..< nested[i].len:
+      echo i, " ", j, " ", nested[i][j]
+
+main()
+echo "after ", allocCount, " ", deallocCount
diff --git a/tests/destructor/tcustomstrings.nim b/tests/destructor/tcustomstrings.nim
new file mode 100644
index 000000000..1a78df20b
--- /dev/null
+++ b/tests/destructor/tcustomstrings.nim
@@ -0,0 +1,99 @@
+discard """
+  output: '''foo bar to appendmore here
+foo bar to appendmore here
+foo bar to appendmore here
+foo bar to appendmore here
+foo bar to appendmore here
+after 20 20'''
+  cmd: '''nim c --newruntime $file'''
+"""
+
+{.this: self.}
+
+type
+  mystring = object
+    len, cap: int
+    data: ptr UncheckedArray[char]
+
+var
+  allocCount, deallocCount: int
+
+proc `=destroy`*(s: var mystring) =
+  if s.data != nil:
+    dealloc(s.data)
+    inc deallocCount
+    s.data = nil
+    s.len = 0
+    s.cap = 0
+
+proc `=sink`*(a: var mystring, b: mystring) =
+  # we hope this is optimized away for not yet alive objects:
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    inc deallocCount
+  a.len = b.len
+  a.cap = b.cap
+  a.data = b.data
+
+proc `=`*(a: var mystring; b: mystring) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    inc deallocCount
+    a.data = nil
+  a.len = b.len
+  a.cap = b.cap
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(a.cap + 1))
+    inc allocCount
+    copyMem(a.data, b.data, a.cap+1)
+
+proc resize(self: var mystring) =
+  if self.cap == 0: self.cap = 8
+  else: self.cap = (self.cap * 3) shr 1
+  if self.data == nil: inc allocCount
+  self.data = cast[type(data)](realloc(self.data, self.cap + 1))
+
+proc add*(self: var mystring; c: char) =
+  if self.len >= self.cap: resize(self)
+  self.data[self.len] = c
+  self.data[self.len+1] = '\0'
+  inc self.len
+
+proc ensure(self: var mystring; newLen: int) =
+  if newLen >= cap:
+    cap = max((cap * 3) shr 1, newLen)
+    if cap > 0:
+      if data == nil: inc allocCount
+      data = cast[type(data)](realloc(data, cap + 1))
+
+proc add*(self: var mystring; y: mystring) =
+  let newLen = len + y.len
+  ensure(self, newLen)
+  copyMem(addr data[len], y.data, y.data.len + 1)
+  len = newLen
+
+proc create*(lit: string): mystring =
+  let newLen = lit.len
+  ensure(result, newLen)
+  copyMem(addr result.data[result.len], unsafeAddr lit[0], newLen + 1)
+  result.len = newLen
+
+proc `&`*(a, b: mystring): mystring =
+  result = a
+  result.add b
+
+proc main(n: int) =
+  var a: mystring
+  let b = create" to append"
+  for i in 0..<n:
+    if i > 4: break
+    a = create"foo bar"
+    let c = b & create"more here"
+    a.add c
+    echo cstring(a.data)
+
+  var x: array[4, mystring]
+  for i in 0..high(x): x[i] = create"added to array"
+
+main(1000)
+echo "after ", allocCount, " ", deallocCount
diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim
new file mode 100644
index 000000000..924644392
--- /dev/null
+++ b/tests/destructor/topttree.nim
@@ -0,0 +1,104 @@
+discard """
+  output: '''10.0
+60.0
+90.0
+120.0
+10.0
+60.0
+90.0
+120.0
+8 8'''
+  cmd: '''nim c --newruntime $file'''
+"""
+
+import typetraits
+
+type
+  opt[T] = object
+    data: ptr T
+
+var
+  allocCount, deallocCount: int
+
+proc `=destroy`*[T](x: var opt[T]) =
+  if x.data != nil:
+    when not supportsCopyMem(T):
+      `=destroy`(x.data[])
+    dealloc(x.data)
+    inc deallocCount
+    x.data = nil
+
+proc `=`*[T](a: var opt[T]; b: opt[T]) =
+  if a.data == b.data: return
+  if a.data != nil:
+    dealloc(a.data)
+    inc deallocCount
+    a.data = nil
+  if b.data != nil:
+    a.data = cast[type(a.data)](alloc(sizeof(T)))
+    inc allocCount
+    when supportsCopyMem(T):
+      copyMem(a.data, b.data, sizeof(T))
+    else:
+      a.data[] = b.data[]
+
+proc `=sink`*[T](a: var opt[T]; b: opt[T]) =
+  if a.data != nil and a.data != b.data:
+    dealloc(a.data)
+    inc deallocCount
+  a.data = b.data
+
+proc createOpt*[T](x: T): opt[T] =
+  result.data = cast[type(result.data)](alloc(sizeof(T)))
+  inc allocCount
+  result.data[] = x
+
+template `[]`[T](x: opt[T]): T =
+  assert x.p != nil, "attempt to read from moved/destroyed value"
+  x.p[]
+
+template `?=`[T](it: untyped; x: opt[T]): bool =
+  template it: untyped {.inject.} = x.data[]
+  if x.data != nil:
+    true
+  else:
+    false
+
+type
+  Tree = object
+    data: float
+    le, ri: opt[Tree]
+
+proc createTree(data: float): Tree =
+  result.data = data
+
+proc insert(t: var opt[Tree]; newVal: float) =
+  #if it ?= t:
+  if t.data != nil:
+    if newVal < t.data[].data:
+      insert(t.data[].le, newVal)
+    elif t.data[].data < newVal:
+      insert(t.data[].ri, newVal)
+    else:
+      discard "already in the tree"
+  else:
+    t = createOpt(Tree(data: newVal))
+
+proc write(t: opt[Tree]) =
+  if it ?= t:
+    write(it.le)
+    write stdout, it.data, "\n"
+    write(it.ri)
+
+proc main =
+  var t: opt[Tree]
+  insert t, 60.0
+  insert t, 90.0
+  insert t, 10.0
+  insert t, 120.0
+  write t
+  let copy = t
+  write copy
+
+main()
+echo allocCount, " ", deallocCount
diff --git a/tests/effects/tgcsafe2.nim b/tests/effects/tgcsafe2.nim
new file mode 100644
index 000000000..0b2c090a7
--- /dev/null
+++ b/tests/effects/tgcsafe2.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: '''type mismatch: got (proc (s: string){.locks: 0.})'''
+  line: 11
+"""
+#5620
+var res = ""
+
+proc takeCallback(foo: (proc(s: string) {.gcsafe.})) =
+  foo "string"
+
+takeCallback(proc (s: string) =
+  res &= s & "abc")
diff --git a/tests/exprs/texprstmt.nim b/tests/exprs/texprstmt.nim
index 0e92702e8..6c9759cf5 100644
--- a/tests/exprs/texprstmt.nim
+++ b/tests/exprs/texprstmt.nim
@@ -1,6 +1,6 @@
 discard """
   line: 10
-  errormsg: "expression 'result[1 .. -(len(result), 1)]' is of type 'string' and has to be discarded"
+  errormsg: "expression 'result[1 .. BackwardsIndex(1)]' is of type 'string' and has to be discarded"
 """
 
 # bug #578
diff --git a/tests/fields/timplicitfieldswithpartial.nim b/tests/fields/timplicitfieldswithpartial.nim
index 996912a1a..a315cc5d0 100644
--- a/tests/fields/timplicitfieldswithpartial.nim
+++ b/tests/fields/timplicitfieldswithpartial.nim
@@ -1,6 +1,8 @@
 discard """
   output: '''(foo: 38, other: string here)
-43'''
+43
+100
+90'''
 """
 
 type
@@ -17,3 +19,17 @@ proc my(f: Foo) =
 var g: Foo
 new(g)
 my(g)
+
+type
+  FooTask {.partial.} = ref object of RootObj
+
+proc foo(t: FooTask) {.liftLocals: t.} =
+  var x = 90
+  if true:
+    var x = 10
+    while x < 100:
+      inc x
+    echo x
+  echo x
+
+foo(FooTask())
diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim
index d7783ce26..559c8aaca 100644
--- a/tests/float/tfloat4.nim
+++ b/tests/float/tfloat4.nim
@@ -9,7 +9,7 @@ proc c_sprintf(buf, fmt: cstring) {.importc:"sprintf", header: "<stdio.h>", vara
 
 proc floatToStr(f: float64): string =
   var buffer: array[128, char]
-  c_sprintf(buffer, "%.16e", f)
+  c_sprintf(addr buffer, "%.16e", f)
   result = ""
   for ch in buffer:
     if ch == '\0':
diff --git a/tests/float/tfloatnan.nim b/tests/float/tfloatnan.nim
new file mode 100644
index 000000000..aa288d342
--- /dev/null
+++ b/tests/float/tfloatnan.nim
@@ -0,0 +1,16 @@
+discard """
+  file: "tfloatnan.nim"
+  output: '''Nim: nan
+Nim: nan (float)
+Nim: nan (double)
+'''
+"""
+
+let f = NaN
+echo "Nim: ", f
+
+let f32: float32 = NaN
+echo "Nim: ", f32, " (float)"
+
+let f64: float64 = NaN
+echo "Nim: ", f64, " (double)"
diff --git a/tests/gc/gctest.nim b/tests/gc/gctest.nim
index b3b9af608..f5c81f033 100644
--- a/tests/gc/gctest.nim
+++ b/tests/gc/gctest.nim
@@ -31,7 +31,7 @@ type
     of nkList: sons: seq[PCaseNode]
     else: unused: seq[string]
 
-  TIdObj* = object of TObject
+  TIdObj* = object of RootObj
     id*: int  # unique id; use this for comparisons and not the pointers
 
   PIdObj* = ref TIdObj
diff --git a/tests/misc/treadx.nim b/tests/misc/treadx.nim
index 49b6ad691..e68b8933d 100644
--- a/tests/misc/treadx.nim
+++ b/tests/misc/treadx.nim
@@ -6,9 +6,8 @@ when not defined(windows):
   var buf: array[0..10, char]
   while true:
     var r = read(0, addr(buf), sizeof(buf)-1)
-    add inp, $buf
+    add inp, $cstring(addr buf)
     if r != sizeof(buf)-1: break
 
   echo inp
   #dafkladskölklödsaf ölksdakölfölksfklwe4iojr389wr 89uweokf sdlkf jweklr jweflksdj fioewjfsdlfsd
-
diff --git a/tests/misc/tupcomingfeatures.nim b/tests/misc/tupcomingfeatures.nim
index cf07b06df..d37ce85cf 100644
--- a/tests/misc/tupcomingfeatures.nim
+++ b/tests/misc/tupcomingfeatures.nim
@@ -6,12 +6,8 @@ discard """
 {.this: self.}
 
 type
-  Foo {.partial.} = object
-    a, b: int
-
-type
-  tupcomingfeatures.Foo = object
-    x: int
+  Foo = object
+    a, b, x: int
 
 proc yay(self: Foo) =
   echo a, " ", b, " ", x
diff --git a/tests/misc/tvarnums.nim b/tests/misc/tvarnums.nim
index 4cc10d66a..5daa2c4b8 100644
--- a/tests/misc/tvarnums.nim
+++ b/tests/misc/tvarnums.nim
@@ -67,7 +67,7 @@ proc toNum64(b: TBuffer): int64 =
 
 proc toNum(b: TBuffer): int32 =
   # treat first byte different:
-  result = ze(b[0]).int32 and 63i32
+  result = int32 ze(b[0]) and 63
   var
     i = 0
     Shift = 6'i32
diff --git a/tests/modules/treorder.nim b/tests/modules/treorder.nim
index 25280c429..8715e4548 100644
--- a/tests/modules/treorder.nim
+++ b/tests/modules/treorder.nim
@@ -9,7 +9,6 @@ defined
 {.reorder: on.}
 {.experimental.}
 
-{.push callconv: stdcall.}
 proc bar(x: T)
 
 proc foo() =
@@ -22,15 +21,16 @@ proc bar(x: T) =
   echo "works ", x
   foo(x)
 
+when defined(testdef):
+  proc whendep() = echo "defined"
+else:
+  proc whendep() = echo "undefined"
+
 foo()
 
 type
   T = int
 
-when defined(testdef):
-  proc whendep() = echo "defined"
-else:
-  proc whendep() = echo "undefined"
 
 when not declared(goo):
   proc goo(my, omy) = echo my
@@ -42,5 +42,3 @@ using
   my, omy: int
 
 goo(3, 4)
-
-{.pop.}
diff --git a/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim b/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim
index 72e8bff12..502ea61a9 100644
--- a/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim
+++ b/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim
@@ -1,3 +1,8 @@
+discard """
+  errormsg: "unguarded access: counter"
+  line: 14
+"""
+
 import threadpool, locks
 
 var counterLock: Lock
diff --git a/tests/niminaction/Chapter8/sfml/sfml_test.nim b/tests/niminaction/Chapter8/sfml/sfml_test.nim
index 49a8176e5..7d56d0903 100644
--- a/tests/niminaction/Chapter8/sfml/sfml_test.nim
+++ b/tests/niminaction/Chapter8/sfml/sfml_test.nim
@@ -1,3 +1,7 @@
+discard """
+  disabled: "windows"
+"""
+
 import sfml, os
 var window = newRenderWindow(videoMode(800, 600), "SFML works!")
 
diff --git a/tests/overload/tprefer_tygenericinst.nim b/tests/overload/tprefer_tygenericinst.nim
index 56541c7e8..ab461a0f4 100644
--- a/tests/overload/tprefer_tygenericinst.nim
+++ b/tests/overload/tprefer_tygenericinst.nim
@@ -2,7 +2,11 @@ discard """
   output: '''Version 2 was called.
 This has the highest precedence.
 This has the second-highest precedence.
-This has the lowest precedence.'''
+This has the lowest precedence.
+baseobj ==
+true
+even better! ==
+true'''
 """
 
 # bug #2220
@@ -26,13 +30,13 @@ template testPred(a: untyped) =
     type SomeA = A|A # A hack to make "A" a typeclass.
 
     when a >= 3:
-      proc p[X](x: X) =
-        echo "This has the highest precedence."
-    when a >= 2:
       proc p[X: A](x: X) =
+        echo "This has the highest precedence."
+    when a == 2:
+      proc p[X: SomeA](x: X) =
         echo "This has the second-highest precedence."
     when a >= 1:
-      proc p[X: SomeA](x: X) =
+      proc p[X](x: X) =
         echo "This has the lowest precedence."
 
     p(B())
@@ -40,3 +44,25 @@ template testPred(a: untyped) =
 testPred(3)
 testPred(2)
 testPred(1)
+
+# bug #6526
+type
+  BaseObj = ref object of RootObj
+  DerivedObj = ref object of BaseObj
+  OtherDerivate = ref object of BaseObj
+
+proc `==`*[T1, T2: BaseObj](a: T1, b: T2): bool =
+  echo "baseobj =="
+  return true
+
+let a = DerivedObj()
+let b = DerivedObj()
+echo a == b
+
+proc `==`*[T1, T2: OtherDerivate](a: T1, b: T2): bool =
+  echo "even better! =="
+  return true
+
+let a2 = OtherDerivate()
+let b2 = OtherDerivate()
+echo a2 == b2
diff --git a/tests/package_level_objects/definefoo.nim b/tests/package_level_objects/definefoo.nim
new file mode 100644
index 000000000..36576ab59
--- /dev/null
+++ b/tests/package_level_objects/definefoo.nim
@@ -0,0 +1,3 @@
+type
+  Foo* {.package.} = object
+    x, y: int
diff --git a/tests/package_level_objects/mypackage.nimble b/tests/package_level_objects/mypackage.nimble
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/package_level_objects/mypackage.nimble
diff --git a/tests/package_level_objects/tusefoo.nim b/tests/package_level_objects/tusefoo.nim
new file mode 100644
index 000000000..f9bae9545
--- /dev/null
+++ b/tests/package_level_objects/tusefoo.nim
@@ -0,0 +1,16 @@
+discard """
+  output: '''@[(x: 3, y: 4)]'''
+"""
+
+type
+  mypackage.Foo = object
+  Other = proc (inp: Foo)
+
+import definefoo
+
+# after this import, Foo is a completely resolved type, so
+# we can create a sequence of it:
+var s: seq[Foo] = @[]
+
+s.add Foo(x: 3, y: 4)
+echo s
diff --git a/tests/package_level_objects/tusefoo2.nim b/tests/package_level_objects/tusefoo2.nim
new file mode 100644
index 000000000..be6b3fcda
--- /dev/null
+++ b/tests/package_level_objects/tusefoo2.nim
@@ -0,0 +1,19 @@
+discard """
+  output: '''compiles'''
+"""
+
+# Test that the object type does not need to be resolved at all:
+
+type
+  mypackage.Foo = object
+  Other = proc (inp: Foo)
+
+  Node = ref object
+    external: ptr Foo
+    data: string
+
+var x: Node
+new(x)
+x.data = "compiles"
+
+echo x.data
diff --git a/tests/pragmas/treorder.nim b/tests/pragmas/treorder.nim
new file mode 100644
index 000000000..6a6bbff4d
--- /dev/null
+++ b/tests/pragmas/treorder.nim
@@ -0,0 +1,75 @@
+discard """
+output:'''0
+1
+2
+3'''
+"""
+
+import macros
+{.reorder: on .}
+
+echo foo(-1)
+echo callWithFoo(0)
+echo(CA+CD)
+echo useTypes(TA(x:TB(x:1)), 2)
+second(0)
+  
+template callWithFoo(arg: untyped): untyped =
+  foo(arg)
+  
+proc first(i: int): void
+
+proc second(i: int): void =
+  make(first)
+  first(i)
+
+proc useTypes(a: TA, d: TD): int =
+  result = a.x.x+d
+
+type
+  TDoubleCyclic = ref object
+    x: TCyclicA
+    y: TCyclicB
+
+type
+  TCyclicA = ref object
+    x: TDoubleCyclic
+  
+type
+  TCyclicB = ref object
+    x: TDoubleCyclic
+
+const
+  CA = 1
+  CB = CC
+
+type
+  TA = object
+    x: TB
+  TC = type(CC)
+  TD = type(CA)
+
+const
+  CC = 1
+  CD = CB
+
+type
+  TB = object
+    x: TC
+
+proc foo(x: int): int =
+  result = bar(x)
+
+proc bar(x: int): int =
+  result = x+1
+
+macro make(arg: untyped): untyped =
+  ss &= arg.repr
+  ss &= " "
+  discard
+
+proc first(i: int): void =
+  make(second)
+
+static:
+  var ss: string = ""
\ No newline at end of file
diff --git a/tests/range/tn8vsint16.nim b/tests/range/tn8vsint16.nim
new file mode 100644
index 000000000..bf4f78e3b
--- /dev/null
+++ b/tests/range/tn8vsint16.nim
@@ -0,0 +1,18 @@
+discard """
+  output: '''-9'''
+"""
+
+type
+  n32 = range[0..high(int)]
+  n8* = range[0'i8..high(int8)]
+
+proc `+`*(a: n32, b: n32{nkIntLit}): n32 = discard
+
+proc `-`*(a: n8, b: n8): n8 = n8(system.`-`(a, b))
+
+var x, y: n8
+var z: int16
+
+# ensure this doesn't call our '-' but system.`-` for int16:
+echo z - n8(9)
+
diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim
index 32d848e06..153cf8556 100644
--- a/tests/stdlib/tjsonmacro.nim
+++ b/tests/stdlib/tjsonmacro.nim
@@ -87,8 +87,8 @@ when isMainModule:
 
     result = to(node, TestVariant)
     doAssert result.name == node["name"].getStr()
-    doAssert result.age == node["age"].getNum().uint8
-    doAssert result.other == node["other"].getNum()
+    doAssert result.age == node["age"].getInt().uint8
+    doAssert result.other == node["other"].getBiggestInt()
 
   # TODO: Test object variant with set in of branch.
   # TODO: Should we support heterogenous arrays?
diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim
index fef1b38c2..071dae5a7 100644
--- a/tests/stdlib/tstrutil.nim
+++ b/tests/stdlib/tstrutil.nim
@@ -13,15 +13,13 @@ proc testStrip() =
 proc testRemoveSuffix =
   var s = "hello\n\r"
   s.removeSuffix
-  assert s == "hello\n"
-  s.removeSuffix
   assert s == "hello"
   s.removeSuffix
   assert s == "hello"
 
   s = "hello\n\n"
   s.removeSuffix
-  assert s == "hello\n"
+  assert s == "hello"
 
   s = "hello\r"
   s.removeSuffix
@@ -41,7 +39,31 @@ proc testRemoveSuffix =
   s.removeSuffix({'s','z'})
   assert s == "hello"
   s.removeSuffix({'l','o'})
-  assert s == "hell"
+  assert s == "he"
+
+  s = "aeiou"
+  s.removeSuffix("")
+  assert s == "aeiou"
+
+  s = ""
+  s.removeSuffix("")
+  assert s == ""
+
+  s = "  "
+  s.removeSuffix
+  assert s == "  "
+
+  s = "  "
+  s.removeSuffix("")
+  assert s == "  "
+
+  s = "    "
+  s.removeSuffix(" ")
+  assert s == "   "
+
+  s = "    "
+  s.removeSuffix(' ')
+  assert s == ""
 
   # Contrary to Chomp in other languages
   # empty string does not change behaviour
@@ -49,9 +71,71 @@ proc testRemoveSuffix =
   s.removeSuffix("")
   assert s == "hello\r\n\r\n"
 
+proc testRemovePrefix =
+  var s = "\n\rhello"
+  s.removePrefix
+  assert s == "hello"
+  s.removePrefix
+  assert s == "hello"
+
+  s = "\n\nhello"
+  s.removePrefix
+  assert s == "hello"
+
+  s = "\rhello"
+  s.removePrefix
+  assert s == "hello"
+
+  s = "hello \n there"
+  s.removePrefix
+  assert s == "hello \n there"
+
+  s = "hello"
+  s.removePrefix("hel")
+  assert s == "lo"
+  s.removePrefix('l')
+  assert s == "o"
+
+  s = "hellos"
+  s.removePrefix({'h','e'})
+  assert s == "llos"
+  s.removePrefix({'l','o'})
+  assert s == "s"
+
+  s = "aeiou"
+  s.removePrefix("")
+  assert s == "aeiou"
+
+  s = ""
+  s.removePrefix("")
+  assert s == ""
+
+  s = "  "
+  s.removePrefix
+  assert s == "  "
+
+  s = "  "
+  s.removePrefix("")
+  assert s == "  "
+
+  s = "    "
+  s.removePrefix(" ")
+  assert s == "   "
+
+  s = "    "
+  s.removePrefix(' ')
+  assert s == ""
+
+  # Contrary to Chomp in other languages
+  # empty string does not change behaviour
+  s = "\r\n\r\nhello"
+  s.removePrefix("")
+  assert s == "\r\n\r\nhello"
+
 proc main() =
   testStrip()
   testRemoveSuffix()
+  testRemovePrefix()
   for p in split("/home/a1:xyz:/usr/bin", {':'}):
     write(stdout, p)
 
@@ -64,6 +148,25 @@ proc testDelete =
   delete(s, 0, 0)
   assert s == "1236789ABCDEFG"
 
+
+proc testIsAlphaNumeric =
+  assert isAlphaNumeric("abcdABC1234") == true
+  assert isAlphaNumeric("a") == true
+  assert isAlphaNumeric("abcABC?1234") == false
+  assert isAlphaNumeric("abcABC 1234") == false
+  assert isAlphaNumeric(".") == false
+
+testIsAlphaNumeric()
+
+proc testIsDigit =
+  assert isDigit("1") == true
+  assert isDigit("1234") == true
+  assert isDigit("abcABC?1234") == false
+  assert isDigit(".") == false
+  assert isDigit(":") == false
+
+testIsDigit()
+
 proc testFind =
   assert "0123456789ABCDEFGH".find('A') == 10
   assert "0123456789ABCDEFGH".find('A', 5) == 10
diff --git a/tests/stdlib/ttime.nim b/tests/stdlib/ttimes.nim
index b28d8aecd..84e00f8de 100644
--- a/tests/stdlib/ttime.nim
+++ b/tests/stdlib/ttimes.nim
@@ -1,6 +1,7 @@
 # test the new time module
 discard """
-  file: "ttime.nim"
+  file: "ttimes.nim"
+  action: "run"
 """
 
 import
@@ -31,14 +32,6 @@ t2.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
   " ss t tt y yy yyy yyyy yyyyy z zz zzz",
   "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 +0 +00 +00:00")
 
-when not defined(JS):
-  when sizeof(Time) == 8:
-    var t3 = getGMTime(fromSeconds(889067643645)) # Fri  7 Jun 19:20:45 BST 30143
-    t3.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
-      " ss t tt y yy yyy yyyy yyyyy z zz zzz",
-      "7 07 Fri Friday 6 06 18 18 20 20 6 06 Jun June 45 45 P PM 3 43 143 0143 30143 +0 +00 +00:00")
-    t3.checkFormat(":,[]()-/", ":,[]()-/")
-
 var t4 = getGMTime(fromSeconds(876124714)) # Mon  6 Oct 08:58:34 BST 1997
 t4.checkFormat("M MM MMM MMMM", "10 10 Oct October")
 
@@ -207,6 +200,21 @@ for tz in [
   doAssert ti.format("zz") == tz[2]
   doAssert ti.format("zzz") == tz[3]
 
+block formatDst:
+  var ti = TimeInfo(monthday: 1, isDst: true)
+
+  # BST
+  ti.timezone = 0
+  doAssert ti.format("z") == "+1"
+  doAssert ti.format("zz") == "+01"
+  doAssert ti.format("zzz") == "+01:00"
+
+  # EDT
+  ti.timezone = 5 * 60 * 60 
+  doAssert ti.format("z") == "-4"
+  doAssert ti.format("zz") == "-04"
+  doAssert ti.format("zzz") == "-04:00"
+
 block dstTest:
   let nonDst = TimeInfo(year: 2015, month: mJan, monthday: 01, yearday: 0,
       weekday: dThu, hour: 00, minute: 00, second: 00, isDST: false, timezone: 0)
@@ -214,8 +222,8 @@ block dstTest:
   dst.isDst = true
   # note that both isDST == true and isDST == false are valid here because
   # DST is in effect on January 1st in some southern parts of Australia.
-
-  doAssert nonDst.toTime() - dst.toTime() == 3600
+  # FIXME: Fails in UTC
+  # doAssert nonDst.toTime() - dst.toTime() == 3600
   doAssert nonDst.format("z") == "+0"
   doAssert dst.format("z") == "+1"
 
@@ -227,3 +235,9 @@ block dstTest:
     parsedJul = parse("2016-07-01 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz")
   doAssert toTime(parsedJan) == fromSeconds(1451962800)
   doAssert toTime(parsedJul) == fromSeconds(1467342000)
+
+block countLeapYears:
+  # 1920, 2004 and 2020 are leap years, and should be counted starting at the following year
+  doAssert countLeapYears(1920) + 1 == countLeapYears(1921)
+  doAssert countLeapYears(2004) + 1 == countLeapYears(2005)
+  doAssert countLeapYears(2020) + 1 == countLeapYears(2021)
\ No newline at end of file
diff --git a/tests/system/toString.nim b/tests/system/toString.nim
index a2337f5dd..3e7fc7ddb 100644
--- a/tests/system/toString.nim
+++ b/tests/system/toString.nim
@@ -1,42 +1,53 @@
 discard """
-  output:'''@[23, 45]
-@[, foo, bar]
-{a, b, c}
-2.3242
-2.982
-123912.1
-123912.1823
-5.0
-1e+100
-inf
--inf
-nan
-nil
-nil'''
+  output:""
 """
 
-echo($(@[23, 45]))
-echo($(@["", "foo", "bar"]))
-#echo($(["", "foo", "bar"]))
-#echo($([23, 45]))
+doAssert "@[23, 45]" == $(@[23, 45])
+doAssert "[32, 45]" == $([32, 45])
+doAssert "@[, foo, bar]" == $(@["", "foo", "bar"])
+doAssert "[, foo, bar]" ==  $(["", "foo", "bar"])
 
 # bug #2395
-
 let alphaSet: set[char] = {'a'..'c'}
-echo alphaSet
-
-echo($(2.3242))
-echo($(2.982))
-echo($(123912.1))
-echo($(123912.1823))
-echo($(5.0))
-echo($(1e100))
-echo($(1e1000000))
-echo($(-1e1000000))
-echo($(0.0/0.0))
+doAssert "{a, b, c}" == $alphaSet
+doAssert "2.3242" == $(2.3242)
+doAssert "2.982" == $(2.982)
+doAssert "123912.1" == $(123912.1)
+doAssert "123912.1823" == $(123912.1823)
+doAssert "5.0" == $(5.0)
+doAssert "1e+100" == $(1e100)
+doAssert "inf" == $(1e1000000)
+doAssert "-inf" == $(-1e1000000)
+doAssert "nan" == $(0.0/0.0)
 
 # nil tests
+# maybe a bit inconsistent in types
 var x: seq[string]
-var y: string
-echo(x)
-echo(y)
+doAssert "nil" == $(x)
+
+var y: string = nil
+doAssert nil == $(y)
+
+type
+  Foo = object
+    a: int
+    b: string
+
+var foo1: Foo
+
+# nil string should be an some point in time equal to the empty string
+doAssert(($foo1)[0..9] == "(a: 0, b: ")
+
+const
+  data = @['a','b', '\0', 'c','d']
+  dataStr = $data
+
+# ensure same result when on VM or when at program execution
+doAssert dataStr == $data
+
+import strutils
+# array test
+
+let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0']
+doAssert $arr == "[H, e, l, l, o,  , W, o, r, l, d, !, \0]"
+doAssert $cstring(unsafeAddr arr) == "Hello World!"
diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim
new file mode 100644
index 000000000..66b789ee9
--- /dev/null
+++ b/tests/system/tsystem_misc.nim
@@ -0,0 +1,18 @@
+discard """
+  output:""
+"""
+
+# check high/low implementations
+doAssert high(int) > low(int)
+doAssert high(int8) > low(int8)
+doAssert high(int16) > low(int16)
+doAssert high(int32) > low(int32)
+doAssert high(int64) > low(int64)
+# doAssert high(uint) > low(uint) # reconsider depending on issue #6620
+doAssert high(uint8) > low(uint8)
+doAssert high(uint16) > low(uint16)
+doAssert high(uint32) > low(uint32)
+# doAssert high(uint64) > low(uint64) # reconsider depending on issue #6620
+doAssert high(float) > low(float)
+doAssert high(float32) > low(float32)
+doAssert high(float64) > low(float64)
diff --git a/tests/testament/backend.nim b/tests/testament/backend.nim
index 8f0961566..4acef9ca4 100644
--- a/tests/testament/backend.nim
+++ b/tests/testament/backend.nim
@@ -1,56 +1,16 @@
 #
 #
 #              The Nim Tester
-#        (c) Copyright 2015 Andreas Rumpf
+#        (c) Copyright 2017 Andreas Rumpf
 #
 #    Look at license.txt for more info.
 #    All rights reserved.
 
-import strutils, db_sqlite, os, osproc
-
-var db: DbConn
-
-proc createDb() =
-  db.exec(sql"""
-    create table if not exists Machine(
-      id integer primary key,
-      name varchar(100) not null,
-      os varchar(20) not null,
-      cpu varchar(20) not null
-    );""")
-
-  db.exec(sql"""
-    create table if not exists [Commit](
-      id integer primary key,
-      hash varchar(256) not null,
-      branch varchar(50) not null
-    );""")
-
-  db.exec(sql"""
-    create table if not exists TestResult(
-      id integer primary key,
-      name varchar(100) not null,
-      category varchar(100) not null,
-      target varchar(20) not null,
-      action varchar(10) not null,
-      result varchar(30) not null,
-      [commit] int not null,
-      machine int not null,
-      expected varchar(10000) not null,
-      given varchar(10000) not null,
-      created timestamp not null default (DATETIME('now')),
-
-      foreign key ([commit]) references [commit](id),
-      foreign key (machine) references machine(id)
-    );""")
-
-  #db.exec(sql"""
-  #  --create unique index if not exists TsstNameIx on TestResult(name);
-  #  """, [])
+import strutils, os, osproc, json
 
 type
-  MachineId* = distinct int64
-  CommitId = distinct int64
+  MachineId* = distinct string
+  CommitId = distinct string
 
 proc `$`*(id: MachineId): string {.borrow.}
 proc `$`(id: CommitId): string {.borrow.}
@@ -58,11 +18,12 @@ proc `$`(id: CommitId): string {.borrow.}
 var
   thisMachine: MachineId
   thisCommit: CommitId
+  thisBranch: string
 
 {.experimental.}
 proc `()`(cmd: string{lit}): string = cmd.execProcess.string.strip
 
-proc getMachine*(db: DbConn): MachineId =
+proc getMachine*(): MachineId =
   var name = "hostname"()
   if name.len == 0:
     name = when defined(posix): getenv"HOSTNAME".string
@@ -70,54 +31,45 @@ proc getMachine*(db: DbConn): MachineId =
   if name.len == 0:
     quit "cannot determine the machine name"
 
-  let id = db.getValue(sql"select id from Machine where name = ?", name)
-  if id.len > 0:
-    result = id.parseInt.MachineId
-  else:
-    result = db.insertId(sql"insert into Machine(name, os, cpu) values (?,?,?)",
-                         name, system.hostOS, system.hostCPU).MachineId
+  result = MachineId(name)
 
-proc getCommit(db: DbConn): CommitId =
+proc getCommit(): CommitId =
   const commLen = "commit ".len
   let hash = "git log -n 1"()[commLen..commLen+10]
-  let branch = "git symbolic-ref --short HEAD"()
-  if hash.len == 0 or branch.len == 0: quit "cannot determine git HEAD"
+  thisBranch = "git symbolic-ref --short HEAD"()
+  if hash.len == 0 or thisBranch.len == 0: quit "cannot determine git HEAD"
+  result = CommitId(hash)
 
-  let id = db.getValue(sql"select id from [Commit] where hash = ? and branch = ?",
-                       hash, branch)
-  if id.len > 0:
-    result = id.parseInt.CommitId
-  else:
-    result = db.insertId(sql"insert into [Commit](hash, branch) values (?, ?)",
-                         hash, branch).CommitId
+var
+  results: File
+  currentCategory: string
+  entries: int
 
 proc writeTestResult*(name, category, target,
                       action, result, expected, given: string) =
-  let id = db.getValue(sql"""select id from TestResult
-                             where name = ? and category = ? and target = ? and
-                                machine = ? and [commit] = ?""",
-                                name, category, target,
-                                thisMachine, thisCommit)
-  if id.len > 0:
-    db.exec(sql"""update TestResult
-                  set action = ?, result = ?, expected = ?, given = ?
-                  where id = ?""", action, result, expected, given, id)
-  else:
-    db.exec(sql"""insert into TestResult(name, category, target,
-                                         action,
-                                         result, expected, given,
-                                         [commit], machine)
-                  values (?,?,?,?,?,?,?,?,?) """, name, category, target,
-                                        action,
-                                        result, expected, given,
-                                        thisCommit, thisMachine)
+  createDir("testresults")
+  if currentCategory != category:
+    if currentCategory.len > 0:
+      results.writeLine("]")
+      close(results)
+    currentCategory = category
+    results = open("testresults" / category.addFileExt"json", fmWrite)
+    results.writeLine("[")
+    entries = 0
+
+  let jentry = %*{"name": name, "category": category, "target": target,
+    "action": action, "result": result, "expected": expected, "given": given,
+    "machine": thisMachine.string, "commit": thisCommit.string, "branch": thisBranch}
+  if entries > 0:
+    results.writeLine(",")
+  results.write($jentry)
+  inc entries
 
 proc open*() =
-  let dbFile = if existsEnv("TRAVIS") or existsEnv("APPVEYOR"): ":memory:" else: "testament.db"
-  db = open(connection=dbFile, user="testament", password="",
-            database="testament")
-  createDb()
-  thisMachine = getMachine(db)
-  thisCommit = getCommit(db)
+  thisMachine = getMachine()
+  thisCommit = getCommit()
 
-proc close*() = close(db)
+proc close*() =
+  if currentCategory.len > 0:
+    results.writeLine("]")
+    close(results)
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index f71a4a1e7..675ff946f 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -83,9 +83,9 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
       ""
 
   testSpec c, makeTest("lib/nimrtl.nim",
-    options & " --app:lib -d:createNimRtl", cat)
+    options & " --app:lib -d:createNimRtl --threads:on", cat)
   testSpec c, makeTest("tests/dll/server.nim",
-    options & " --app:lib -d:useNimRtl" & rpath, cat)
+    options & " --app:lib -d:useNimRtl --threads:on" & rpath, cat)
 
 
   when defined(Windows):
@@ -101,7 +101,7 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
     var nimrtlDll = DynlibFormat % "nimrtl"
     safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll)
 
-  testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl" & rpath,
+  testSpec r, makeTest("tests/dll/client.nim", options & " -d:useNimRtl --threads:on" & rpath,
                        cat, actionRun)
 
 proc dllTests(r: var TResults, cat: Category, options: string) =
@@ -259,6 +259,7 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
     "niminaction/Chapter6/WikipediaStats/parallel_counts",
     "niminaction/Chapter6/WikipediaStats/race_condition",
     "niminaction/Chapter6/WikipediaStats/sequential_counts",
+    "niminaction/Chapter6/WikipediaStats/unguarded_access",
     "niminaction/Chapter7/Tweeter/src/tweeter",
     "niminaction/Chapter7/Tweeter/src/createDatabase",
     "niminaction/Chapter7/Tweeter/tests/database_test",
@@ -267,11 +268,6 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) =
   for testfile in tests:
     test "tests/" & testfile & ".nim", actionCompile
 
-  # TODO: This doesn't work for some reason ;\
-  # let reject = "tests/niminaction/Chapter6/WikipediaStats" &
-  #              "/unguarded_access.nim"
-  # test reject, actionReject
-
   let jsFile = "tests/niminaction/Chapter8/canvas/canvas_test.nim"
   testJS jsFile
 
@@ -328,9 +324,10 @@ type PackageFilter = enum
   pfExtraOnly
   pfAll
 
+var nimbleDir = getEnv("NIMBLE_DIR").string
+if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
 let
   nimbleExe = findExe("nimble")
-  nimbleDir = getHomeDir() / ".nimble"
   packageDir = nimbleDir / "pkgs"
   packageIndex = nimbleDir / "packages.json"
 
diff --git a/tests/testament/css/boilerplate.css b/tests/testament/css/boilerplate.css
deleted file mode 100644
index b209b5aa1..000000000
--- a/tests/testament/css/boilerplate.css
+++ /dev/null
@@ -1,138 +0,0 @@
-/* ==== Scroll down to find where to put your styles :) ==== */
-
-/*  HTML5 ✰ Boilerplate  */
-
-html, body, div, span, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
-small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-article, aside, canvas, details, figcaption, figure,
-footer, header, hgroup, menu, nav, section, summary,
-time, mark, audio, video {
-  margin: 0;
-  padding: 0;
-  border: 0;
-  font-size: 100%;
-  font: inherit;
-  vertical-align: baseline;
-}
-
-article, aside, details, figcaption, figure,
-footer, header, hgroup, menu, nav, section {
-  display: block;
-}
-
-blockquote, q { quotes: none; }
-blockquote:before, blockquote:after,
-q:before, q:after { content: ''; content: none; }
-ins { background-color: #ff9; color: #000; text-decoration: none; }
-mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
-del { text-decoration: line-through; }
-abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
-table { border-collapse: collapse; border-spacing: 0; }
-hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
-input, select { vertical-align: middle; }
-
-body { font:13px/1.231 sans-serif; *font-size:small; } 
-select, input, textarea, button { font:99% sans-serif; }
-pre, code, kbd, samp { font-family: monospace, sans-serif; }
-
-html { overflow-y: scroll; }
-a:hover, a:active { outline: none; }
-ul, ol { margin-left: 2em; }
-ol { list-style-type: decimal; }
-nav ul, nav li { margin: 0; list-style:none; list-style-image: none; }
-small { font-size: 85%; }
-strong, th { font-weight: bold; }
-td { vertical-align: top; }
-
-sub, sup { font-size: 75%; line-height: 0; position: relative; }
-sup { top: -0.5em; }
-sub { bottom: -0.25em; }
-
-pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; padding: 15px; }
-textarea { overflow: auto; }
-.ie6 legend, .ie7 legend { margin-left: -7px; } 
-input[type="radio"] { vertical-align: text-bottom; }
-input[type="checkbox"] { vertical-align: bottom; }
-.ie7 input[type="checkbox"] { vertical-align: baseline; }
-.ie6 input { vertical-align: text-bottom; }
-label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; }
-button, input, select, textarea { margin: 0; }
-input:valid, textarea:valid   {  }
-input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; }
-.no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; }
-
-a:link { -webkit-tap-highlight-color: #FF5E99; }
-
-button {  width: auto; overflow: visible; }
-.ie7 img { -ms-interpolation-mode: bicubic; }
-
-body, select, input, textarea {  color: #444; }
-h1, h2, h3, h4, h5, h6 { font-weight: bold; }
-a, a:active, a:visited { color: #607890; }
-a:hover { color: #036; }
-
-/*
-    // ========================================== \\
-   ||                                              ||
-   ||               Your styles !                  ||
-   ||                                              ||
-    \\ ========================================== //
-*/
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-.ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; }
-.hidden { display: none; visibility: hidden; }
-.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
-.visuallyhidden.focusable:active,
-.visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; }
-.invisible { visibility: hidden; }
-.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
-.clearfix:after { clear: both; }
-.clearfix { zoom: 1; }
-
-
-@media all and (orientation:portrait) {
-
-}
-
-@media all and (orientation:landscape) {
-
-}
-
-@media screen and (max-device-width: 480px) {
-
-  /* html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */
-}
-
-
-@media print {
-  * { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important;
-  -ms-filter: none !important; } 
-  a, a:visited { color: #444 !important; text-decoration: underline; }
-  a[href]:after { content: " (" attr(href) ")"; }
-  abbr[title]:after { content: " (" attr(title) ")"; }
-  .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }  
-  pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
-  thead { display: table-header-group; }
-  tr, img { page-break-inside: avoid; }
-  @page { margin: 0.5cm; }
-  p, h2, h3 { orphans: 3; widows: 3; }
-  h2, h3{ page-break-after: avoid; }
-}
diff --git a/tests/testament/css/style.css b/tests/testament/css/style.css
deleted file mode 100644
index 43a8add68..000000000
--- a/tests/testament/css/style.css
+++ /dev/null
@@ -1,114 +0,0 @@
-body {
-  font-size: medium;
-}
-
-div#header {
-  font-size: 2em;
-  background-color: #3d3d3d;
-  border-bottom: solid 2px #000000;
-  padding: 0.25em;
-  color: #ffffff;
-}
-
-div#content {
-  margin: 0.5em;
-}
-
-table {
-  text-align: left;
-  margin-bottom: 0.5em;
-}
-
-table td, table th {
-  padding: 0.15em 0.5em;
-}
-
-tr:nth-child(even) {
-  background-color: #eee;
-}
-
-/* Awesome buttons :P */
-
-a.button {
-  border-radius: 2px 2px 2px 2px;
-  background: -moz-linear-gradient(top, #f7f7f7, #ebebeb);
-  background: -webkit-linear-gradient(top, #f7f7f7, #ebebeb);
-  background: -o-linear-gradient(top, #f7f7f7, #ebebeb);
-  text-decoration: none;
-  color: #3d3d3d;
-  padding: 5px;
-  border: solid 1px #9d9d9d;
-  display: inline-block;
-  position: relative;
-  text-align: center;
-  font-size: small;
-}
-
-a.button.active {
-  background: -moz-linear-gradient(top, #00B40C, #03A90E);
-  background: -webkit-linear-gradient(top, #00B40C, #03A90E);
-  background: -o-linear-gradient(top, #00B40C, #03A90E);
-  border: solid 1px #148420;
-  color: #ffffff;
-}
-
-a.button.left {
-  border-top-right-radius: 0;
-  border-bottom-right-radius: 0;
-}
-
-a.button.middle {
-  border-radius: 0;
-  border-left: 0;
-}
-
-a.button.right {
-  border-top-left-radius: 0;
-  border-bottom-left-radius: 0;
-  border-left: 0;
-}
-
-a.button:hover {
-  background: -moz-linear-gradient(top, #0099c7, #0294C1);
-  background: -webkit-linear-gradient(top, #0099c7, #0294C1);
-  background: -o-linear-gradient(top, #0099c7, #0294C1);
-  border: solid 1px #077A9C;
-  color: #ffffff;
-}
-
-a.button.middle:hover, a.button.right:hover {
-  border-left: 0;
-}
-
-a.button span.download {
-  background-image: url("../images/icons.png");
-  background-repeat: no-repeat;
-  display: inline-block;
-  margin: auto 3px auto auto;
-  height: 15px;
-  width: 14px;
-  position: relative;
-  background-position: 0 -30px;
-  top: 3px;
-}
-
-a.button span.book {
-  background-image: url("../images/icons.png");
-  background-repeat: no-repeat;
-  display: inline-block;
-  margin: auto 3px auto auto;
-  height: 15px;
-  width: 14px;
-  position: relative;
-  background-position: 0 0;
-  top: 3px;
-}
-
-a.button.active span.download, a.button:hover span.download {
-  background-position: 0 -45px;
-}
-
-a.button.active span.book, a.button:hover span.book {
-  background-position: 0 -15px;
-}
-
diff --git a/tests/testament/htmlgen.nim b/tests/testament/htmlgen.nim
index 15960f09a..05c24b2b5 100644
--- a/tests/testament/htmlgen.nim
+++ b/tests/testament/htmlgen.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim Tester
-#        (c) Copyright 2015 Andreas Rumpf
+#        (c) Copyright 2017 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -9,220 +9,158 @@
 
 ## HTML generator for the tester.
 
-import db_sqlite, cgi, backend, strutils, json
-
-const
-  TableHeader = """<table border="1">
-                      <tr><td>Test</td><td>Category</td><td>Target</td>
-                          <td>Action</td>
-                          <td>Expected</td>
-                          <td>Given</td>
-                          <td>Success</td></tr>"""
-  TableFooter = "</table>"
-  HtmlBegin = """<html>
-    <head>
-      <title>Test results</title>
-      <style type="text/css">
-      <!--""" & slurp("css/boilerplate.css") & "\n" &
-                slurp("css/style.css") &
-      """
-ul#tabs { list-style-type: none; margin: 30px 0 0 0; padding: 0 0 0.3em 0; }
-ul#tabs li { display: inline; }
-ul#tabs li a { color: #42454a; background-color: #dedbde;
-               border: 1px solid #c9c3ba; border-bottom: none;
-               padding: 0.3em; text-decoration: none; }
-ul#tabs li a:hover { background-color: #f1f0ee; }
-ul#tabs li a.selected { color: #000; background-color: #f1f0ee;
-                        font-weight: bold; padding: 0.7em 0.3em 0.38em 0.3em; }
-div.tabContent { border: 1px solid #c9c3ba;
-                 padding: 0.5em; background-color: #f1f0ee; }
-div.tabContent.hide { display: none; }
-      -->
-    </style>
-    <script>
-
-    var tabLinks = new Array();
-    var contentDivs = new Array();
-
-    function init() {
-      // Grab the tab links and content divs from the page
-      var tabListItems = document.getElementById('tabs').childNodes;
-      for (var i = 0; i < tabListItems.length; i++) {
-        if (tabListItems[i].nodeName == "LI") {
-          var tabLink = getFirstChildWithTagName(tabListItems[i], 'A');
-          var id = getHash(tabLink.getAttribute('href'));
-          tabLinks[id] = tabLink;
-          contentDivs[id] = document.getElementById(id);
-        }
-      }
-      // Assign onclick events to the tab links, and
-      // highlight the first tab
-      var i = 0;
-      for (var id in tabLinks) {
-        tabLinks[id].onclick = showTab;
-        tabLinks[id].onfocus = function() { this.blur() };
-        if (i == 0) tabLinks[id].className = 'selected';
-        i++;
-      }
-      // Hide all content divs except the first
-      var i = 0;
-      for (var id in contentDivs) {
-        if (i != 0) contentDivs[id].className = 'tabContent hide';
-        i++;
-      }
-    }
-
-    function showTab() {
-      var selectedId = getHash(this.getAttribute('href'));
-
-      // Highlight the selected tab, and dim all others.
-      // Also show the selected content div, and hide all others.
-      for (var id in contentDivs) {
-        if (id == selectedId) {
-          tabLinks[id].className = 'selected';
-          contentDivs[id].className = 'tabContent';
-        } else {
-          tabLinks[id].className = '';
-          contentDivs[id].className = 'tabContent hide';
-        }
-      }
-      // Stop the browser following the link
-      return false;
-    }
-
-    function getFirstChildWithTagName(element, tagName) {
-      for (var i = 0; i < element.childNodes.length; i++) {
-        if (element.childNodes[i].nodeName == tagName) return element.childNodes[i];
-      }
-    }
-    function getHash(url) {
-      var hashPos = url.lastIndexOf('#');
-      return url.substring(hashPos + 1);
-    }
-    </script>
-
-    </head>
-    <body onload="init()">"""
-
-  HtmlEnd = "</body></html>"
-
-proc td(s: string): string =
-  result = "<td>" & s.substr(0, 200).xmlEncode & "</td>"
-
-proc getCommit(db: DbConn, c: int): string =
-  var commit = c
-  for thisCommit in db.rows(sql"select id from [Commit] order by id desc"):
-    if commit == 0: result = thisCommit[0]
-    inc commit
-
-proc generateHtml*(filename: string, commit: int; onlyFailing: bool) =
-  const selRow = """select name, category, target, action,
-                           expected, given, result
-                    from TestResult
-                    where [commit] = ? and machine = ?
-                    order by category"""
-  var db = open(connection="testament.db", user="testament", password="",
-                database="testament")
-  # search for proper commit:
-  let lastCommit = db.getCommit(commit)
-
+import cgi, backend, strutils, json, os
+
+import "testamenthtml.templ"
+
+proc generateTestRunTabListItemPartial(outfile: File, testRunRow: JsonNode, firstRow = false) =
+  let
+    # The first tab gets the bootstrap class for a selected tab
+    firstTabActiveClass = if firstRow: "active"
+                          else: ""
+    commitId = htmlQuote testRunRow["commit"].str
+    hash = htmlQuote(testRunRow["commit"].str)
+    branch = htmlQuote(testRunRow["branch"].str)
+    machineId = htmlQuote testRunRow["machine"].str
+    machineName = htmlQuote(testRunRow["machine"].str)
+
+  outfile.generateHtmlTabListItem(
+      firstTabActiveClass,
+      commitId,
+      machineId,
+      branch,
+      hash,
+      machineName
+    )
+
+proc generateTestResultPanelPartial(outfile: File, testResultRow: JsonNode, onlyFailing = false) =
+  let
+    trId = htmlQuote(testResultRow["category"].str & "_" & testResultRow["name"].str)
+    name = testResultRow["name"].str.htmlQuote()
+    category = testResultRow["category"].str.htmlQuote()
+    target = testResultRow["target"].str.htmlQuote()
+    action = testResultRow["action"].str.htmlQuote()
+    result = htmlQuote testResultRow["result"].str
+    expected = htmlQuote testResultRow["expected"].str
+    gotten = htmlQuote testResultRow["given"].str
+    timestamp = "unknown"
+  var panelCtxClass, textCtxClass, bgCtxClass, resultSign, resultDescription: string
+  case result
+  of "reSuccess":
+    if onlyFailing:
+      return
+    panelCtxClass = "success"
+    textCtxClass = "success"
+    bgCtxClass = "success"
+    resultSign = "ok"
+    resultDescription = "PASS"
+  of "reIgnored":
+    if onlyFailing:
+      return
+    panelCtxClass = "info"
+    textCtxClass = "info"
+    bgCtxClass = "info"
+    resultSign = "question"
+    resultDescription = "SKIP"
+  else:
+    panelCtxClass = "danger"
+    textCtxClass = "danger"
+    bgCtxClass = "danger"
+    resultSign = "exclamation"
+    resultDescription = "FAIL"
+
+  outfile.generateHtmlTestresultPanelBegin(
+    trId, name, target, category, action, resultDescription,
+    timestamp,
+    result, resultSign,
+    panelCtxClass, textCtxClass, bgCtxClass
+  )
+  if expected.isNilOrWhitespace() and gotten.isNilOrWhitespace():
+    outfile.generateHtmlTestresultOutputNone()
+  else:
+    outfile.generateHtmlTestresultOutputDetails(
+      expected.strip().htmlQuote,
+      gotten.strip().htmlQuote
+    )
+  outfile.generateHtmlTestresultPanelEnd()
+
+type
+  AllTests = object
+    data: JSonNode
+    totalCount, successCount, ignoredCount, failedCount: int
+    successPercentage, ignoredPercentage, failedPercentage: BiggestFloat
+
+proc allTestResults(): AllTests =
+  result.data = newJArray()
+  for file in os.walkFiles("testresults/*.json"):
+    let data = parseFile(file)
+    if data.kind != JArray:
+      echo "[ERROR] ignoring json file that is not an array: ", file
+    else:
+      for elem in data:
+        result.data.add elem
+        let state = elem["result"].str
+        if state.contains("reSuccess"): inc result.successCount
+        elif state.contains("reIgnored"): inc result.ignoredCount
+
+  result.totalCount = result.data.len
+  result.successPercentage = 100 * (result.successCount.toBiggestFloat() / result.totalCount.toBiggestFloat())
+  result.ignoredPercentage = 100 * (result.ignoredCount.toBiggestFloat() / result.totalCount.toBiggestFloat())
+  result.failedCount = result.totalCount - result.successCount - result.ignoredCount
+  result.failedPercentage = 100 * (result.failedCount.toBiggestFloat() / result.totalCount.toBiggestFloat())
+
+
+proc generateTestResultsPanelGroupPartial(outfile: File, allResults: JsonNode, onlyFailing = false) =
+  for testresultRow in allResults:
+    generateTestResultPanelPartial(outfile, testresultRow, onlyFailing)
+
+proc generateTestRunTabContentPartial(outfile: File, allResults: AllTests, testRunRow: JsonNode, onlyFailing = false, firstRow = false) =
+  let
+    # The first tab gets the bootstrap classes for a selected and displaying tab content
+    firstTabActiveClass = if firstRow: " in active"
+                          else: ""
+    commitId = htmlQuote testRunRow["commit"].str
+    hash = htmlQuote(testRunRow["commit"].str)
+    branch = htmlQuote(testRunRow["branch"].str)
+    machineId = htmlQuote testRunRow["machine"].str
+    machineName = htmlQuote(testRunRow["machine"].str)
+    os = htmlQuote("unknown_os")
+    cpu = htmlQuote("unknown_cpu")
+
+  outfile.generateHtmlTabPageBegin(
+    firstTabActiveClass, commitId,
+    machineId, branch, hash, machineName, os, cpu,
+    allResults.totalCount,
+    allResults.successCount, formatBiggestFloat(allResults.successPercentage, ffDecimal, 2) & "%",
+    allResults.ignoredCount, formatBiggestFloat(allResults.ignoredPercentage, ffDecimal, 2) & "%",
+    allResults.failedCount, formatBiggestFloat(allResults.failedPercentage, ffDecimal, 2) & "%"
+  )
+  generateTestResultsPanelGroupPartial(outfile, allResults.data, onlyFailing)
+  outfile.generateHtmlTabPageEnd()
+
+proc generateTestRunsHtmlPartial(outfile: File, allResults: AllTests, onlyFailing = false) =
+  # Iterating the results twice, get entire result set in one go
+  outfile.generateHtmlTabListBegin()
+  if allResults.data.len > 0:
+    generateTestRunTabListItemPartial(outfile, allResults.data[0], true)
+  outfile.generateHtmlTabListEnd()
+
+  outfile.generateHtmlTabContentsBegin()
+  var firstRow = true
+  for testRunRow in allResults.data:
+    generateTestRunTabContentPartial(outfile, allResults, testRunRow, onlyFailing, firstRow)
+    if firstRow:
+      firstRow = false
+  outfile.generateHtmlTabContentsEnd()
+
+proc generateHtml*(filename: string, onlyFailing: bool) =
   var outfile = open(filename, fmWrite)
-  outfile.write(HtmlBegin)
-
-  let commit = db.getValue(sql"select hash from [Commit] where id = ?",
-                            lastCommit)
-  let branch = db.getValue(sql"select branch from [Commit] where id = ?",
-                            lastCommit)
-  outfile.write("<p><b>$# $#</b></p>" % [branch, commit])
-
-  # generate navigation:
-  outfile.write("""<ul id="tabs">""")
-  for m in db.rows(sql"select id, name, os, cpu from Machine order by id"):
-    outfile.writeLine """<li><a href="#$#">$#: $#, $#</a></li>""" % m
-  outfile.write("</ul>")
-
-  for currentMachine in db.rows(sql"select id from Machine order by id"):
-    let m = currentMachine[0]
-    outfile.write("""<div class="tabContent" id="$#">""" % m)
-
-    outfile.write(TableHeader)
-    for row in db.rows(sql(selRow), lastCommit, m):
-      if onlyFailing and row.len > 0 and row[row.high] == "reSuccess":
-        discard
-      else:
-        outfile.write("<tr>")
-        for x in row:
-          outfile.write(x.td)
-        outfile.write("</tr>")
-
-    outfile.write(TableFooter)
-    outfile.write("</div>")
-  outfile.write(HtmlEnd)
-  close(db)
-  close(outfile)
 
-proc generateJson*(filename: string, commit: int) =
-  const
-    selRow = """select count(*),
-                           sum(result = 'reSuccess'),
-                           sum(result = 'reIgnored')
-                from TestResult
-                where [commit] = ? and machine = ?
-                order by category"""
-    selDiff = """select A.category || '/' || A.target || '/' || A.name,
-                        A.result,
-                        B.result
-                from TestResult A
-                inner join TestResult B
-                on A.name = B.name and A.category = B.category
-                where A.[commit] = ? and B.[commit] = ? and A.machine = ?
-                   and A.result != B.result"""
-    selResults = """select
-                      category || '/' || target || '/' || name,
-                      category, target, action, result, expected, given
-                    from TestResult
-                    where [commit] = ?"""
-  var db = open(connection="testament.db", user="testament", password="",
-                database="testament")
-  let lastCommit = db.getCommit(commit)
-  if lastCommit.isNil:
-    quit "cannot determine commit " & $commit
-
-  let previousCommit = db.getCommit(commit-1)
+  outfile.generateHtmlBegin()
 
-  var outfile = open(filename, fmWrite)
+  generateTestRunsHtmlPartial(outfile, allTestResults(), onlyFailing)
+
+  outfile.generateHtmlEnd()
 
-  let machine = $backend.getMachine(db)
-  let data = db.getRow(sql(selRow), lastCommit, machine)
-
-  outfile.writeLine("""{"total": $#, "passed": $#, "skipped": $#""" % data)
-
-  let results = newJArray()
-  for row in db.rows(sql(selResults), lastCommit):
-    var obj = newJObject()
-    obj["name"] = %row[0]
-    obj["category"] = %row[1]
-    obj["target"] = %row[2]
-    obj["action"] = %row[3]
-    obj["result"] = %row[4]
-    obj["expected"] = %row[5]
-    obj["given"] = %row[6]
-    results.add(obj)
-  outfile.writeLine(""", "results": """)
-  outfile.write(results.pretty)
-
-  if not previousCommit.isNil:
-    let diff = newJArray()
-
-    for row in db.rows(sql(selDiff), previousCommit, lastCommit, machine):
-      var obj = newJObject()
-      obj["name"] = %row[0]
-      obj["old"] = %row[1]
-      obj["new"] = %row[2]
-      diff.add obj
-    outfile.writeLine(""", "diff": """)
-    outfile.writeLine(diff.pretty)
-
-  outfile.writeLine "}"
-  close(db)
+  outfile.flushFile()
   close(outfile)
diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim
index ed435465a..89e786d48 100644
--- a/tests/testament/specs.nim
+++ b/tests/testament/specs.nim
@@ -153,7 +153,7 @@ proc parseSpec*(filename: string): TSpec =
       result.msg = e.value
       if result.action != actionRun:
         result.action = actionCompile
-    of "errormsg":
+    of "errormsg", "errmsg":
       result.msg = e.value
       result.action = actionReject
     of "nimout":
diff --git a/tests/testament/testamenthtml.templ b/tests/testament/testamenthtml.templ
new file mode 100644
index 000000000..f7477f3aa
--- /dev/null
+++ b/tests/testament/testamenthtml.templ
@@ -0,0 +1,318 @@
+#? stdtmpl(subsChar = '%', metaChar = '#', emit = "outfile.write")
+#import strutils
+#
+#proc htmlQuote*(raw: string): string =
+#  result = raw.multiReplace(
+#    ("&", "&amp;"),
+#    ("\"", "&quot;"),
+#    ("'", "&apos;"),
+#    ("<", "&lt;"),
+#    (">", "&gt;")
+#  )
+#
+#end proc
+#proc generateHtmlBegin*(outfile: File) =
+<!DOCTYPE html>
+<html>
+<head>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Testament Test Results</title>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js" integrity="sha256-ihAoc6M/JPfrIiIeayPE9xjin4UWjsx2mjW/rtmxLM4=" crossorigin="anonymous"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" crossorigin="anonymous"></script>
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha256-ZT4HPpdCOt2lvDkXokHuhJfdOKSPFLzeAJik5U/Q+l4=" crossorigin="anonymous" />
+    <script>
+        /**
+        * Callback function that is executed for each Element in an array.
+        * @callback executeForElement
+        * @param {Element} elem Element to operate on
+        */
+
+        /**
+        * 
+        * @param {number} index
+        * @param {Element[]} elemArray
+        * @param {executeForElement} executeOnItem
+        */
+        function executeAllAsync(elemArray, index, executeOnItem) {
+            for (var i = 0; index < elemArray.length && i < 100; i++ , index++) {
+                var item = elemArray[index];
+                executeOnItem(item);
+            }
+            if (index < elemArray.length) {
+                setTimeout(executeAllAsync, 0, elemArray, index, executeOnItem);
+            }
+        }
+
+        /** @param {Element} elem */
+        function executeShowOnElement(elem) {
+            while (elem.classList.contains("hidden")) {
+                elem.classList.remove("hidden");
+            }
+        }
+
+        /** @param {Element} elem */
+        function executeHideOnElement(elem) {
+            if (!elem.classList.contains("hidden")) {
+                elem.classList.add("hidden");
+            }
+        }
+
+        /** @param {Element} elem */
+        function executeExpandOnElement(elem) {
+            $(elem).collapse("show");
+        }
+
+        /** @param {Element} elem */
+        function executeCollapseOnElement(elem) {
+            $(elem).collapse("hide");
+        }
+
+        /**
+        * @param {string} tabId The id of the tabpanel div to search.
+        * @param {string} [category] Optional bootstrap panel context class (danger, warning, info, success)
+        * @param {executeForElement} executeOnEachPanel
+        */
+        function wholePanelAll(tabId, category, executeOnEachPanel) {
+            var selector = "div.panel";
+            if (typeof category === "string" && category) {
+                selector += "-" + category;
+            }
+
+            var jqPanels = $(selector, $("#" + tabId));
+            /** @type {Element[]} */
+            var elemArray = jqPanels.toArray();
+
+            setTimeout(executeAllAsync, 0, elemArray, 0, executeOnEachPanel);
+        }
+
+        /**
+        * @param {string} tabId The id of the tabpanel div to search.
+        * @param {string} [category] Optional bootstrap panel context class (danger, warning, info, success)
+        * @param {executeForElement} executeOnEachPanel
+        */
+        function panelBodyAll(tabId, category, executeOnEachPanelBody) {
+            var selector = "div.panel";
+            if (typeof category === "string" && category) {
+                selector += "-" + category;
+            }
+
+            var jqPanels = $(selector, $("#" + tabId));
+
+            var jqPanelBodies = $("div.panel-body", jqPanels);
+            /** @type {Element[]} */
+            var elemArray = jqPanelBodies.toArray();
+
+            setTimeout(executeAllAsync, 0, elemArray, 0, executeOnEachPanelBody);
+        }
+
+        /**
+        * @param {string} tabId The id of the tabpanel div to search.
+        * @param {string} [category] Optional bootstrap panel context class (danger, warning, info, success)
+        */
+        function showAll(tabId, category) {
+            wholePanelAll(tabId, category, executeShowOnElement);
+        }
+
+        /**
+        * @param {string} tabId The id of the tabpanel div to search.
+        * @param {string} [category] Optional bootstrap panel context class (danger, warning, info, success)
+        */
+        function hideAll(tabId, category) {
+            wholePanelAll(tabId, category, executeHideOnElement);
+        }
+
+        /**
+        * @param {string} tabId The id of the tabpanel div to search.
+        * @param {string} [category] Optional bootstrap panel context class (danger, warning, info, success)
+        */
+        function expandAll(tabId, category) {
+            panelBodyAll(tabId, category, executeExpandOnElement);
+        }
+
+        /**
+        * @param {string} tabId The id of the tabpanel div to search.
+        * @param {string} [category] Optional bootstrap panel context class (danger, warning, info, success)
+        */
+        function collapseAll(tabId, category) {
+            panelBodyAll(tabId, category, executeCollapseOnElement);
+        }
+    </script>
+</head>
+<body>
+    <div class="container">
+        <h1>Testament Test Results <small>Nim Tester</small></h1>
+#end proc
+#proc generateHtmlTabListBegin*(outfile: File) =
+        <ul class="nav nav-tabs" role="tablist">
+#end proc
+#proc generateHtmlTabListItem*(outfile: File, firstTabActiveClass, commitId, 
+#  machineId, branch, hash, machineName: string) =
+            <li role="presentation" class="%firstTabActiveClass">
+                <a href="#tab-commit-%commitId-machine-%machineId" aria-controls="tab-commit-%commitId-machine-%machineId" role="tab" data-toggle="tab">
+                    %branch#%hash@%machineName
+                </a>
+            </li>
+#end proc
+#proc generateHtmlTabListEnd*(outfile: File) =
+        </ul>
+#end proc
+#proc generateHtmlTabContentsBegin*(outfile: File) =
+        <div class="tab-content">
+#end proc
+#proc generateHtmlTabPageBegin*(outfile: File, firstTabActiveClass, commitId,
+#  machineId, branch, hash, machineName, os, cpu: string, totalCount: BiggestInt,
+#  successCount: BiggestInt, successPercentage: string,
+#  ignoredCount: BiggestInt, ignoredPercentage: string,
+#  failedCount: BiggestInt, failedPercentage: string) =
+            <div id="tab-commit-%commitId-machine-%machineId" class="tab-pane fade%firstTabActiveClass" role="tabpanel">
+                <h2>%branch#%hash@%machineName</h2>
+                <dl class="dl-horizontal">
+                    <dt>Branch</dt>
+                    <dd>%branch</dd>
+                    <dt>Commit Hash</dt>
+                    <dd><code>%hash</code></dd>
+                    <dt>Machine Name</dt>
+                    <dd>%machineName</dd>
+                    <dt>OS</dt>
+                    <dd>%os</dd>
+                    <dt title="CPU Architecture">CPU</dt>
+                    <dd>%cpu</dd>
+                    <dt>All Tests</dt>
+                    <dd>
+                        <span class="glyphicon glyphicon-th-list"></span>
+                        %totalCount
+                    </dd>
+                    <dt>Successful Tests</dt>
+                    <dd>
+                        <span class="glyphicon glyphicon-ok-sign"></span>
+                        %successCount (%successPercentage)
+                    </dd>
+                    <dt>Skipped Tests</dt>
+                    <dd>
+                        <span class="glyphicon glyphicon-question-sign"></span>
+                        %ignoredCount (%ignoredPercentage)
+                    </dd>
+                    <dt>Failed Tests</dt>
+                    <dd>
+                        <span class="glyphicon glyphicon-exclamation-sign"></span>
+                        %failedCount (%failedPercentage)
+                    </dd>
+                </dl>
+                <div class="table-responsive">
+                    <table class="table table-condensed">
+                        <tr>
+                            <th class="text-right" style="vertical-align:middle">All Tests</th>
+                            <td>
+                                <div class="btn-group">
+                                    <button class="btn btn-default" type="button" onclick="showAll('tab-commit-%commitId-machine-%machineId');">Show All</button>
+                                    <button class="btn btn-default" type="button" onclick="hideAll('tab-commit-%commitId-machine-%machineId');">Hide All</button>
+                                    <button class="btn btn-default" type="button" onclick="expandAll('tab-commit-%commitId-machine-%machineId');">Expand All</button>
+                                    <button class="btn btn-default" type="button" onclick="collapseAll('tab-commit-%commitId-machine-%machineId');">Collapse All</button>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <th class="text-right" style="vertical-align:middle">Successful Tests</th>
+                            <td>
+                                <div class="btn-group">
+                                    <button class="btn btn-default" type="button" onclick="showAll('tab-commit-%commitId-machine-%machineId', 'success');">Show All</button>
+                                    <button class="btn btn-default" type="button" onclick="hideAll('tab-commit-%commitId-machine-%machineId', 'success');">Hide All</button>
+                                    <button class="btn btn-default" type="button" onclick="expandAll('tab-commit-%commitId-machine-%machineId', 'success');">Expand All</button>
+                                    <button class="btn btn-default" type="button" onclick="collapseAll('tab-commit-%commitId-machine-%machineId', 'success');">Collapse All</button>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <th class="text-right" style="vertical-align:middle">Skipped Tests</th>
+                            <td>
+                                <div class="btn-group">
+                                    <button class="btn btn-default" type="button" onclick="showAll('tab-commit-%commitId-machine-%machineId', 'info');">Show All</button>
+                                    <button class="btn btn-default" type="button" onclick="hideAll('tab-commit-%commitId-machine-%machineId', 'info');">Hide All</button>
+                                    <button class="btn btn-default" type="button" onclick="expandAll('tab-commit-%commitId-machine-%machineId', 'info');">Expand All</button>
+                                    <button class="btn btn-default" type="button" onclick="collapseAll('tab-commit-%commitId-machine-%machineId', 'info');">Collapse All</button>
+                                </div>
+                            </td>
+                        </tr>
+                        <tr>
+                            <th class="text-right" style="vertical-align:middle">Failed Tests</th>
+                            <td>
+                                <div class="btn-group">
+                                    <button class="btn btn-default" type="button" onclick="showAll('tab-commit-%commitId-machine-%machineId', 'danger');">Show All</button>
+                                    <button class="btn btn-default" type="button" onclick="hideAll('tab-commit-%commitId-machine-%machineId', 'danger');">Hide All</button>
+                                    <button class="btn btn-default" type="button" onclick="expandAll('tab-commit-%commitId-machine-%machineId', 'danger');">Expand All</button>
+                                    <button class="btn btn-default" type="button" onclick="collapseAll('tab-commit-%commitId-machine-%machineId', 'danger');">Collapse All</button>
+                                </div>
+                            </td>
+                        </tr>
+                    </table>
+                </div>
+                <div class="panel-group">
+#end proc
+#proc generateHtmlTestresultPanelBegin*(outfile: File, trId, name, target, category,
+#  action, resultDescription, timestamp, result, resultSign, 
+#  panelCtxClass, textCtxClass, bgCtxClass: string) =
+                    <div id="panel-testResult-%trId" class="panel panel-%panelCtxClass">
+                        <div class="panel-heading" style="cursor:pointer" data-toggle="collapse" data-target="#panel-body-testResult-%trId" aria-controls="panel-body-testResult-%trId" aria-expanded="false">
+                            <div class="row">
+                                <h4 class="col-xs-3 col-sm-1 panel-title">
+                                    <span class="glyphicon glyphicon-%resultSign-sign"></span>
+                                    <strong>%resultDescription</strong>
+                                </h4>
+                                <h4 class="col-xs-1 panel-title"><span class="badge">%target</span></h4>
+                                <h4 class="col-xs-5 col-sm-7 panel-title" title="%name"><code class="text-%textCtxClass">%name</code></h4>
+                                <h4 class="col-xs-3 col-sm-3 panel-title text-right"><span class="badge">%category</span></h4>
+                            </div>
+                        </div>
+                        <div id="panel-body-testResult-%trId" class="panel-body collapse bg-%bgCtxClass">
+                            <dl class="dl-horizontal">
+                                <dt>Name</dt>
+                                <dd><code class="text-%textCtxClass">%name</code></dd>
+                                <dt>Category</dt>
+                                <dd><span class="badge">%category</span></dd>
+                                <dt>Timestamp</dt>
+                                <dd>%timestamp</dd>
+                                <dt>Nim Action</dt>
+                                <dd><code class="text-%textCtxClass">%action</code></dd>
+                                <dt>Nim Backend Target</dt>
+                                <dd><span class="badge">%target</span></dd>
+                                <dt>Code</dt>
+                                <dd><code class="text-%textCtxClass">%result</code></dd>
+                            </dl>
+#end proc
+#proc generateHtmlTestresultOutputDetails*(outfile: File, expected, gotten: string) =
+                            <div class="table-responsive">
+                                <table class="table table-condensed">
+                                    <thead>
+                                        <tr>
+                                            <th>Expected</th>
+                                            <th>Actual</th>
+                                        </tr>
+                                    </thead>
+                                    <tbody>
+                                        <tr>
+                                            <td><pre>%expected</pre></td>
+                                            <td><pre>%gotten</pre></td>
+                                        </tr>
+                                    </tbody>
+                                </table>
+                            </div>
+#end proc
+#proc generateHtmlTestresultOutputNone*(outfile: File) =
+                            <p class="sr-only">No output details</p>
+#end proc
+#proc generateHtmlTestresultPanelEnd*(outfile: File) =
+                        </div>
+                    </div>
+#end proc
+#proc generateHtmlTabPageEnd*(outfile: File) =
+                </div>
+            </div>
+#end proc
+#proc generateHtmlTabContentsEnd*(outfile: File) =
+        </div>
+#end proc
+#proc generateHtmlEnd*(outfile: File) =
+    </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim
index e4bbc3a00..0daf4089e 100644
--- a/tests/testament/tester.nim
+++ b/tests/testament/tester.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim Tester
-#        (c) Copyright 2015 Andreas Rumpf
+#        (c) Copyright 2017 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -12,7 +12,7 @@
 import
   parseutils, strutils, pegs, os, osproc, streams, parsecfg, json,
   marshal, backend, parseopt, specs, htmlgen, browsers, terminal,
-  algorithm, compiler/nodejs, re, times, sets
+  algorithm, compiler/nodejs, times, sets
 
 const
   resultsFile = "testresults.html"
@@ -24,15 +24,12 @@ Command:
   all                         run all tests
   c|category <category>       run all the tests of a certain category
   r|run <test>                run single test file
-  html [commit]               generate $1 from the database; uses the latest
-                              commit or a specific one (use -1 for the commit
-                              before latest etc)
+  html                        generate $1 from the database
 Arguments:
   arguments are passed to the compiler
 Options:
   --print                   also print results to the console
   --failing                 only show failing/ignored tests
-  --pedantic                return non-zero status code if there are failures
   --targets:"c c++ js objc" run tests for specified targets (default: all)
   --nim:path                use a particular nim executable (default: compiler/nim)
 """ % resultsFile
@@ -191,7 +188,12 @@ proc addResult(r: var TResults, test: TTest,
         ("Skipped", "")
       else:
         ("Failed", "Failure: " & $success & "\nExpected:\n" & expected & "\n\n" & "Gotten:\n" & given)
-    var p = startProcess("appveyor", args=["AddTest", test.name.replace("\\", "/") & test.options, "-Framework", "nim-testament", "-FileName", test.cat.string, "-Outcome", outcome, "-ErrorMessage", msg, "-Duration", $(duration*1000).int], options={poStdErrToStdOut, poUsePath, poParentStreams})
+    var p = startProcess("appveyor", args=["AddTest", test.name.replace("\\", "/") & test.options,
+                         "-Framework", "nim-testament", "-FileName",
+                         test.cat.string,
+                         "-Outcome", outcome, "-ErrorMessage", msg,
+                         "-Duration", $(duration*1000).int],
+                         options={poStdErrToStdOut, poUsePath, poParentStreams})
     discard waitForExit(p)
     close(p)
 
@@ -290,7 +292,7 @@ proc analyzeAndConsolidateOutput(s: string): string =
       result = substr(rows[i], pos) & "\n"
       for i in i+1 ..< rows.len:
         result.add rows[i] & "\n"
-        if not (rows[i] =~ re"^[^(]+\(\d+\)\s+"):
+        if not (rows[i] =~ peg"['(']+ '(' \d+ ')' \s+"):
           return
     elif (let pos = find(rows[i], "SIGSEGV: Illegal storage access."); pos != -1):
       result = substr(rows[i], pos)
@@ -436,7 +438,6 @@ proc main() =
   backend.open()
   var optPrintResults = false
   var optFailing = false
-  var optPedantic = false
 
   var p = initOptParser()
   p.next()
@@ -444,7 +445,7 @@ proc main() =
     case p.key.string.normalize
     of "print", "verbose": optPrintResults = true
     of "failing": optFailing = true
-    of "pedantic": optPedantic = true
+    of "pedantic": discard "now always enabled"
     of "targets": targets = parseTargets(p.val.string)
     of "nim": compilerPrefix = p.val.string
     else: quit Usage
@@ -456,13 +457,17 @@ proc main() =
   case action
   of "all":
     let testsDir = "tests" & DirSep
+    let myself = quoteShell(findExe("tests" / "testament" / "tester"))
+    var cmds: seq[string] = @[]
+    let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: ""
     for kind, dir in walkDir(testsDir):
       assert testsDir.startsWith(testsDir)
       let cat = dir[testsDir.len .. ^1]
       if kind == pcDir and cat notin ["testament", "testdata", "nimcache"]:
-        processCategory(r, Category(cat), p.cmdLineRest.string)
-    for a in AdditionalCategories:
-      processCategory(r, Category(a), p.cmdLineRest.string)
+        cmds.add(myself & " cat " & cat & rest)
+    for cat in AdditionalCategories:
+      cmds.add(myself & " cat " & cat & rest)
+    quit osproc.execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams})
   of "c", "cat", "category":
     var cat = Category(p.key)
     p.next
@@ -473,10 +478,7 @@ proc main() =
     var cat = Category(subdir)
     processSingleTest(r, cat, p.cmdLineRest.string, file)
   of "html":
-    var commit = 0
-    discard parseInt(p.cmdLineRest.string, commit)
-    generateHtml(resultsFile, commit, optFailing)
-    generateJson(jsonFile, commit)
+    generateHtml(resultsFile, optFailing)
   else:
     quit Usage
 
@@ -484,11 +486,10 @@ proc main() =
     if action == "html": openDefaultBrowser(resultsFile)
     else: echo r, r.data
   backend.close()
-  if optPedantic:
-    var failed = r.total - r.passed - r.skipped
-    if failed > 0:
-      echo "FAILURE! total: ", r.total, " passed: ", r.passed, " skipped: ", r.skipped
-      quit(QuitFailure)
+  var failed = r.total - r.passed - r.skipped
+  if failed > 0:
+    echo "FAILURE! total: ", r.total, " passed: ", r.passed, " skipped: ", r.skipped
+    quit(QuitFailure)
 
 if paramCount() == 0:
   quit Usage
diff --git a/tests/types/tillegaltyperecursion2.nim b/tests/types/tillegaltyperecursion2.nim
new file mode 100644
index 000000000..b5ffdda72
--- /dev/null
+++ b/tests/types/tillegaltyperecursion2.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "invalid recursion in type 'Executor'"
+  line: 8
+"""
+# bug reported by PR #5637
+type
+  Executor[N] = Executor[N]
+var e: Executor[int]
diff --git a/tests/untestable/tpostgres.nim b/tests/untestable/tpostgres.nim
index 486d0d703..d3397e53a 100644
--- a/tests/untestable/tpostgres.nim
+++ b/tests/untestable/tpostgres.nim
@@ -304,7 +304,24 @@ doAssert parseInt(dbCols[40].typ.name) > 0
 doAssert dbCols[41].name == "range_col"
 doAssert dbCols[41].typ.kind == DbTypeKind.dbComposite
 doAssert dbCols[41].typ.name == "int4range"
-    
+
+# issue 6571
+db.exec(sql"DROP TABLE IF EXISTS DICTIONARY")
+db.exec(sql("""CREATE TABLE DICTIONARY(
+               id             SERIAL PRIMARY KEY,
+               entry      VARCHAR(1000) NOT NULL,
+               definition VARCHAR(4000) NOT NULL
+            );"""))
+var entry = "あっそ"
+var definition = "(int) (See ああそうそう) oh, really (uninterested)/oh yeah?/hmmmmm"
+discard db.getRow(
+  SqlQuery("INSERT INTO DICTIONARY(entry, definition) VALUES(\'$1\', \'$2\') RETURNING id" % [entry, definition]))
+doAssert db.getValue(sql"SELECT definition FROM DICTIONARY WHERE entry = ?", entry) == definition
+entry = "Format string entry"
+definition = "Format string definition"
+db.exec(sql"INSERT INTO DICTIONARY(entry, definition) VALUES (?, ?)", entry, definition)
+doAssert db.getValue(sql"SELECT definition FROM DICTIONARY WHERE entry = ?", entry) == definition
+
 echo("All tests succeeded!")
 
 db.close()
diff --git a/tests/vm/trgba.nim b/tests/vm/trgba.nim
index a270df267..923ea1b2e 100644
--- a/tests/vm/trgba.nim
+++ b/tests/vm/trgba.nim
@@ -22,7 +22,7 @@ template `B=`*(self: TAggRgba8, val: byte) =
 template `A=`*(self: TAggRgba8, val: byte) =
   self[3] = val
 
-proc ABGR* (val: int| int64): TAggRgba8 =
+proc ABGR*(val: int| int64): TAggRgba8 =
   var V = val
   result.R = byte(V and 0xFF)
   V = V shr 8