summary refs log tree commit diff stats
path: root/tests/stdlib
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2019-02-08 17:18:17 +0100
committerAndreas Rumpf <rumpf_a@web.de>2019-02-08 17:18:17 +0100
commit0841c64a3217594d0d260a0f24616b469447c1b9 (patch)
treed16995b64b4cd42b7e3d9adf94dd7f7ff57b600c /tests/stdlib
parentcb9110c43d4ae9c29a0a1e0d54f7735712d4ba62 (diff)
parent444f2231c9b48c34f9bec2ce6cfa3de5ae2560b1 (diff)
downloadNim-0841c64a3217594d0d260a0f24616b469447c1b9.tar.gz
Merge branch 'devel' into araq-quirky-exceptions
Diffstat (limited to 'tests/stdlib')
-rw-r--r--tests/stdlib/t10231.nim15
-rw-r--r--tests/stdlib/tbitops.nim100
-rw-r--r--tests/stdlib/tgetaddrinfo.nim36
-rw-r--r--tests/stdlib/tjsonmacro.nim4
-rw-r--r--tests/stdlib/tmath.nim81
-rw-r--r--tests/stdlib/tmget.nim8
-rw-r--r--tests/stdlib/tos.nim156
-rw-r--r--tests/stdlib/tospaths.nim99
-rw-r--r--tests/stdlib/tosproc.nim112
-rw-r--r--tests/stdlib/trepr.nim11
-rw-r--r--tests/stdlib/ttimes.nim175
-rw-r--r--tests/stdlib/tunittest.nim1
12 files changed, 536 insertions, 262 deletions
diff --git a/tests/stdlib/t10231.nim b/tests/stdlib/t10231.nim
new file mode 100644
index 000000000..2bb64b475
--- /dev/null
+++ b/tests/stdlib/t10231.nim
@@ -0,0 +1,15 @@
+discard """
+  target: cpp
+  action: run
+  exitcode: 0
+"""
+
+import os
+
+# consider moving this inside tosproc (taking care that it's for cpp mode)
+
+if paramCount() == 0:
+  # main process
+  doAssert execShellCmd(getAppFilename().quoteShell & " test") == 1
+else:
+  quit 1
diff --git a/tests/stdlib/tbitops.nim b/tests/stdlib/tbitops.nim
index d8c6da1d4..1cbab4870 100644
--- a/tests/stdlib/tbitops.nim
+++ b/tests/stdlib/tbitops.nim
@@ -1,9 +1,9 @@
 discard """
+  nimout: "OK"
   output: "OK"
 """
 import bitops
 
-
 proc main() =
   const U8 = 0b0011_0010'u8
   const I8 = 0b0011_0010'i8
@@ -79,25 +79,6 @@ proc main() =
   doAssert( U8.rotateLeftBits(3) == 0b10010001'u8)
   doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8)
 
-  static :
-    # test bitopts at compile time with vm
-    doAssert( U8.fastLog2 == 5)
-    doAssert( I8.fastLog2 == 5)
-    doAssert( U8.countLeadingZeroBits == 2)
-    doAssert( I8.countLeadingZeroBits == 2)
-    doAssert( U8.countTrailingZeroBits == 1)
-    doAssert( I8.countTrailingZeroBits == 1)
-    doAssert( U8.firstSetBit == 2)
-    doAssert( I8.firstSetBit == 2)
-    doAssert( U8.parityBits == 1)
-    doAssert( I8.parityBits == 1)
-    doAssert( U8.countSetBits == 3)
-    doAssert( I8.countSetBits == 3)
-    doAssert( U8.rotateLeftBits(3) == 0b10010001'u8)
-    doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8)
-
-
-
   template test_undefined_impl(ffunc: untyped; expected: int; is_static: bool) =
     doAssert( ffunc(0'u8) == expected)
     doAssert( ffunc(0'i8) == expected)
@@ -142,26 +123,67 @@ proc main() =
     doAssert( U64A.rotateLeftBits(64) == U64A)
     doAssert( U64A.rotateRightBits(64) == U64A)
 
-    static:    # check for undefined behavior with rotate by zero.
-      doAssert( U8.rotateLeftBits(0) == U8)
-      doAssert( U8.rotateRightBits(0) == U8)
-      doAssert( U16.rotateLeftBits(0) == U16)
-      doAssert( U16.rotateRightBits(0) == U16)
-      doAssert( U32.rotateLeftBits(0) == U32)
-      doAssert( U32.rotateRightBits(0) == U32)
-      doAssert( U64A.rotateLeftBits(0) == U64A)
-      doAssert( U64A.rotateRightBits(0) == U64A)
-
-      # check for undefined behavior with rotate by integer width.
-      doAssert( U8.rotateLeftBits(8) == U8)
-      doAssert( U8.rotateRightBits(8) == U8)
-      doAssert( U16.rotateLeftBits(16) == U16)
-      doAssert( U16.rotateRightBits(16) == U16)
-      doAssert( U32.rotateLeftBits(32) == U32)
-      doAssert( U32.rotateRightBits(32) == U32)
-      doAssert( U64A.rotateLeftBits(64) == U64A)
-      doAssert( U64A.rotateRightBits(64) == U64A)
+  block:
+    # mask operations
+    var v: uint8
+    v.setMask(0b1100_0000)
+    v.setMask(0b0000_1100)
+    doAssert(v == 0b1100_1100)
+    v.flipMask(0b0101_0101)
+    doAssert(v == 0b1001_1001)
+    v.clearMask(0b1000_1000)
+    doAssert(v == 0b0001_0001)
+    v.clearMask(0b0001_0001)
+    doAssert(v == 0b0000_0000)
+  block:
+    # single bit operations
+    var v: uint8
+    v.setBit(0)
+    doAssert v == 0x0000_0001
+    v.setBit(1)
+    doAssert v == 0b0000_0011
+    v.flipBit(7)
+    doAssert v == 0b1000_0011
+    v.clearBit(0)
+    doAssert v == 0b1000_0010
+    v.flipBit(1)
+    doAssert v == 0b1000_0000
+    doAssert v.testbit(7)
+    doAssert not v.testbit(6)
+  block:
+    # multi bit operations
+    var v: uint8
+    v.setBits(0, 1, 7)
+    doAssert v == 0b1000_0011
+    v.flipBits(2, 3)
+    doAssert v == 0b1000_1111
+    v.clearBits(7, 0, 1)
+    doAssert v == 0b0000_1100
+  block:
+    # signed
+    var v: int8
+    v.setBit(7)
+    doAssert v == -128
+  block:
+    var v: uint64
+    v.setBit(63)
+    doAssert v == 0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000'u64
 
   echo "OK"
 
+block: # not ready for vm because exception is compile error
+  try:
+    var v: uint32
+    var i = 32
+    v.setBit(i)
+    doAssert false
+  except RangeError:
+    discard
+  except:
+    doAssert false
+
+
 main()
+static:
+  # test everything on vm as well
+  main()
diff --git a/tests/stdlib/tgetaddrinfo.nim b/tests/stdlib/tgetaddrinfo.nim
new file mode 100644
index 000000000..39102e131
--- /dev/null
+++ b/tests/stdlib/tgetaddrinfo.nim
@@ -0,0 +1,36 @@
+discard """
+  exitcode: 0
+  output: ""
+"""
+
+# bug: https://github.com/nim-lang/Nim/issues/10198
+
+import nativesockets
+
+block DGRAM_UDP:
+  let aiList = getAddrInfo("127.0.0.1", 999.Port, AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+  doAssert aiList != nil
+  doAssert aiList.ai_addr != nil
+  doAssert aiList.ai_addrlen == 16
+  doAssert aiList.ai_next == nil
+  freeAddrInfo aiList
+
+when defined(posix):
+
+  block RAW_ICMP:
+    # the port will be ignored
+    let aiList = getAddrInfo("127.0.0.1", 999.Port, AF_INET, SOCK_RAW, IPPROTO_ICMP)
+    doAssert aiList != nil
+    doAssert aiList.ai_addr != nil
+    doAssert aiList.ai_addrlen == 16
+    doAssert aiList.ai_next == nil
+    freeAddrInfo aiList
+
+  block RAW_ICMPV6:
+    # the port will be ignored
+    let aiList = getAddrInfo("::1", 999.Port, AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)
+    doAssert aiList != nil
+    doAssert aiList.ai_addr != nil
+    doAssert aiList.ai_addrlen == 28
+    doAssert aiList.ai_next == nil
+    freeAddrInfo aiList
diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim
index 33332447b..2e95b4833 100644
--- a/tests/stdlib/tjsonmacro.nim
+++ b/tests/stdlib/tjsonmacro.nim
@@ -516,3 +516,7 @@ when true:
       var w = u.to(MyDistRef)
       doAssert v.name == "smith"
       doAssert MyRef(w).name == "smith"
+
+    block test_tuple:
+      doAssert $(%* (a1: 10, a2: "foo")) == """{"a1":10,"a2":"foo"}"""
+      doAssert $(%* (10, "foo")) == """[10,"foo"]"""
diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim
index 7c1851e7a..bdb5aa332 100644
--- a/tests/stdlib/tmath.nim
+++ b/tests/stdlib/tmath.nim
@@ -4,6 +4,8 @@ discard """
 
 [Suite] random float
 
+[Suite] cumsum
+
 [Suite] random sample
 
 [Suite] ^
@@ -18,29 +20,29 @@ import sets, tables
 suite "random int":
   test "there might be some randomness":
     var set = initSet[int](128)
-    randomize()
+
     for i in 1..1000:
       incl(set, random(high(int)))
     check len(set) == 1000
   test "single number bounds work":
-    randomize()
+
     var rand: int
     for i in 1..1000:
       rand = random(1000)
       check rand < 1000
       check rand > -1
   test "slice bounds work":
-    randomize()
+
     var rand: int
     for i in 1..1000:
       rand = random(100..1000)
       check rand < 1000
       check rand >= 100
-  test "randomize() again gives new numbers":
-    randomize()
+  test " again gives new numbers":
+
     var rand1 = random(1000000)
     os.sleep(200)
-    randomize()
+
     var rand2 = random(1000000)
     check rand1 != rand2
 
@@ -48,55 +50,92 @@ suite "random int":
 suite "random float":
   test "there might be some randomness":
     var set = initSet[float](128)
-    randomize()
+
     for i in 1..100:
       incl(set, random(1.0))
     check len(set) == 100
   test "single number bounds work":
-    randomize()
+
     var rand: float
     for i in 1..1000:
       rand = random(1000.0)
       check rand < 1000.0
       check rand > -1.0
   test "slice bounds work":
-    randomize()
+
     var rand: float
     for i in 1..1000:
       rand = random(100.0..1000.0)
       check rand < 1000.0
       check rand >= 100.0
-  test "randomize() again gives new numbers":
-    randomize()
+  test " again gives new numbers":
+
     var rand1:float = random(1000000.0)
     os.sleep(200)
-    randomize()
+
     var rand2:float = random(1000000.0)
     check rand1 != rand2
 
+suite "cumsum":
+  test "cumsum int seq return":
+    let counts = [ 1, 2, 3, 4 ]
+    check counts.cumsummed == [ 1, 3, 6, 10 ]
+
+  test "cumsum float seq return":
+    let counts = [ 1.0, 2.0, 3.0, 4.0 ]
+    check counts.cumsummed == [ 1.0, 3.0, 6.0, 10.0 ]
+
+  test "cumsum int in-place":
+    var counts = [ 1, 2, 3, 4 ]
+    counts.cumsum
+    check counts == [ 1, 3, 6, 10 ]
+
+  test "cumsum float in-place":
+    var counts = [ 1.0, 2.0, 3.0, 4.0 ]
+    counts.cumsum
+    check counts == [ 1.0, 3.0, 6.0, 10.0 ]
+
 suite "random sample":
-  test "non-uniform array sample":
+  test "non-uniform array sample unnormalized int CDF":
     let values = [ 10, 20, 30, 40, 50 ] # values
-    let weight = [ 4, 3, 2, 1, 0 ]      # weights aka unnormalized probabilities
-    let weightSum = 10.0                # sum of weights
+    let counts = [ 4, 3, 2, 1, 0 ]      # weights aka unnormalized probabilities
     var histo = initCountTable[int]()
-    for v in sample(values, weight, 5000):
-      histo.inc(v)
-    check histo.len == 4                # number of non-zero in `weight`
+    let cdf = counts.cumsummed          # unnormalized CDF
+    for i in 0 ..< 5000:
+      histo.inc(sample(values, cdf))
+    check histo.len == 4                # number of non-zero in `counts`
     # Any one bin is a binomial random var for n samples, each with prob p of
     # adding a count to k; E[k]=p*n, Var k=p*(1-p)*n, approximately Normal for
     # big n.  So, P(abs(k - p*n)/sqrt(p*(1-p)*n))>3.0) =~ 0.0027, while
     # P(wholeTestFails) =~ 1 - P(binPasses)^4 =~ 1 - (1-0.0027)^4 =~ 0.01.
-    for i, w in weight:
-      if w == 0:
+    for i, c in counts:
+      if c == 0:
         check values[i] notin histo
         continue
-      let p = float(w) / float(weightSum)
+      let p = float(c) / float(cdf[^1])
       let n = 5000.0
       let expected = p * n
       let stdDev = sqrt(n * p * (1.0 - p))
       check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
 
+  test "non-uniform array sample normalized float CDF":
+    let values = [ 10, 20, 30, 40, 50 ]     # values
+    let counts = [ 0.4, 0.3, 0.2, 0.1, 0 ]  # probabilities
+    var histo = initCountTable[int]()
+    let cdf = counts.cumsummed              # normalized CDF
+    for i in 0 ..< 5000:
+      histo.inc(sample(values, cdf))
+    check histo.len == 4                    # number of non-zero in ``counts``
+    for i, c in counts:
+      if c == 0:
+        check values[i] notin histo
+        continue
+      let p = float(c) / float(cdf[^1])
+      let n = 5000.0
+      let expected = p * n
+      let stdDev = sqrt(n * p * (1.0 - p))
+      # NOTE: like unnormalized int CDF test, P(wholeTestFails) =~ 0.01.
+      check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
 
 suite "^":
   test "compiles for valid types":
diff --git a/tests/stdlib/tmget.nim b/tests/stdlib/tmget.nim
index 5792b6282..5e2e327f4 100644
--- a/tests/stdlib/tmget.nim
+++ b/tests/stdlib/tmget.nim
@@ -11,10 +11,10 @@ Can't access 6
 Can't access 6
 10
 11
-Can't access 6
+0
 10
 11
-Can't access 6
+0
 10
 11
 Can't access 6
@@ -85,7 +85,7 @@ block:
   except KeyError:
     echo "Can't access 6"
   echo x[5]
-  x[5] += 1
+  x.inc 5, 1
   var c = x[5]
   echo c
 
@@ -97,7 +97,7 @@ block:
   except KeyError:
     echo "Can't access 6"
   echo x[5]
-  x[5] += 1
+  x.inc 5, 1
   var c = x[5]
   echo c
 
diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim
index a7cf5d5b6..23fa4d098 100644
--- a/tests/stdlib/tos.nim
+++ b/tests/stdlib/tos.nim
@@ -1,13 +1,5 @@
 discard """
-  output: '''true
-true
-true
-true
-true
-true
-true
-true
-true
+  output: '''
 All:
 __really_obscure_dir_name/are.x
 __really_obscure_dir_name/created
@@ -27,31 +19,13 @@ __really_obscure_dir_name/created
 __really_obscure_dir_name/dirs
 __really_obscure_dir_name/some
 __really_obscure_dir_name/test
-false
-false
-false
-false
-false
-false
-false
-false
-false
-true
-true
 Raises
 Raises
-true
-true
-true
-true
-true
-true
-
 '''
 """
 # test os path creation, iteration, and deletion
 
-import os, strutils
+import os, strutils, pathnorm
 
 block fileOperations:
   let files = @["these.txt", "are.x", "testing.r", "files.q"]
@@ -60,17 +34,17 @@ block fileOperations:
   let dname = "__really_obscure_dir_name"
 
   createDir(dname)
-  echo dirExists(dname)
+  doAssert dirExists(dname)
 
   # Test creating files and dirs
   for dir in dirs:
     createDir(dname/dir)
-    echo dirExists(dname/dir)
+    doAssert dirExists(dname/dir)
 
   for file in files:
     let fh = open(dname/file, fmReadWrite)
     fh.close()
-    echo fileExists(dname/file)
+    doAssert fileExists(dname/file)
 
   echo "All:"
 
@@ -93,23 +67,23 @@ block fileOperations:
   # Test removal of files dirs
   for dir in dirs:
     removeDir(dname/dir)
-    echo dirExists(dname/dir)
+    doAssert: not dirExists(dname/dir)
 
   for file in files:
     removeFile(dname/file)
-    echo fileExists(dname/file)
+    doAssert: not fileExists(dname/file)
 
   removeDir(dname)
-  echo dirExists(dname)
+  doAssert: not dirExists(dname)
 
   # createDir should create recursive directories
   createDir(dirs[0] / dirs[1])
-  echo dirExists(dirs[0] / dirs[1]) # true
+  doAssert dirExists(dirs[0] / dirs[1]) # true
   removeDir(dirs[0])
 
   # createDir should properly handle trailing separator
   createDir(dname / "")
-  echo dirExists(dname) # true
+  doAssert dirExists(dname) # true
   removeDir(dname)
 
   # createDir should raise IOError if the path exists
@@ -138,10 +112,10 @@ block fileOperations:
   copyDir("a", "../dest/a")
   removeDir("a")
 
-  echo dirExists("../dest/a/b")
-  echo fileExists("../dest/a/b/file.txt")
+  doAssert dirExists("../dest/a/b")
+  doAssert fileExists("../dest/a/b/file.txt")
 
-  echo fileExists("../dest/a/b/c/fileC.txt")
+  doAssert fileExists("../dest/a/b/c/fileC.txt")
   removeDir("../dest")
 
   # test copyDir:
@@ -152,8 +126,8 @@ block fileOperations:
   copyDir("a/", "../dest/a/")
   removeDir("a")
 
-  echo dirExists("../dest/a/b")
-  echo fileExists("../dest/a/file.txt")
+  doAssert dirExists("../dest/a/b")
+  doAssert fileExists("../dest/a/file.txt")
   removeDir("../dest")
 
 import times
@@ -165,9 +139,9 @@ block modificationTime:
   setLastModificationTime("a", tm)
 
   when defined(macosx):
-    echo "true"
+    doAssert true
   else:
-    echo getLastModificationTime("a") == tm
+    doAssert getLastModificationTime("a") == tm
   removeFile("a")
 
 block walkDirRec:
@@ -263,3 +237,99 @@ block splitFile:
   doAssert splitFile("abc/.") == ("abc", ".", "")
   doAssert splitFile("..") == ("", "..", "")
   doAssert splitFile("a/..") == ("a", "..", "")
+
+# execShellCmd is tested in tosproc
+
+block ospaths:
+  doAssert unixToNativePath("") == ""
+  doAssert unixToNativePath(".") == $CurDir
+  doAssert unixToNativePath("..") == $ParDir
+  doAssert isAbsolute(unixToNativePath("/"))
+  doAssert isAbsolute(unixToNativePath("/", "a"))
+  doAssert isAbsolute(unixToNativePath("/a"))
+  doAssert isAbsolute(unixToNativePath("/a", "a"))
+  doAssert isAbsolute(unixToNativePath("/a/b"))
+  doAssert isAbsolute(unixToNativePath("/a/b", "a"))
+  doAssert unixToNativePath("a/b") == joinPath("a", "b")
+
+  when defined(macos):
+    doAssert unixToNativePath("./") == ":"
+    doAssert unixToNativePath("./abc") == ":abc"
+    doAssert unixToNativePath("../abc") == "::abc"
+    doAssert unixToNativePath("../../abc") == ":::abc"
+    doAssert unixToNativePath("/abc", "a") == "abc"
+    doAssert unixToNativePath("/abc/def", "a") == "abc:def"
+  elif doslikeFileSystem:
+    doAssert unixToNativePath("./") == ".\\"
+    doAssert unixToNativePath("./abc") == ".\\abc"
+    doAssert unixToNativePath("../abc") == "..\\abc"
+    doAssert unixToNativePath("../../abc") == "..\\..\\abc"
+    doAssert unixToNativePath("/abc", "a") == "a:\\abc"
+    doAssert unixToNativePath("/abc/def", "a") == "a:\\abc\\def"
+  else:
+    #Tests for unix
+    doAssert unixToNativePath("./") == "./"
+    doAssert unixToNativePath("./abc") == "./abc"
+    doAssert unixToNativePath("../abc") == "../abc"
+    doAssert unixToNativePath("../../abc") == "../../abc"
+    doAssert unixToNativePath("/abc", "a") == "/abc"
+    doAssert unixToNativePath("/abc/def", "a") == "/abc/def"
+
+  block extractFilenameTest:
+    doAssert extractFilename("") == ""
+    when defined(posix):
+      doAssert extractFilename("foo/bar") == "bar"
+      doAssert extractFilename("foo/bar.txt") == "bar.txt"
+      doAssert extractFilename("foo/") == ""
+      doAssert extractFilename("/") == ""
+    when doslikeFileSystem:
+      doAssert extractFilename(r"foo\bar") == "bar"
+      doAssert extractFilename(r"foo\bar.txt") == "bar.txt"
+      doAssert extractFilename(r"foo\") == ""
+      doAssert extractFilename(r"C:\") == ""
+
+  block lastPathPartTest:
+    doAssert lastPathPart("") == ""
+    when defined(posix):
+      doAssert lastPathPart("foo/bar.txt") == "bar.txt"
+      doAssert lastPathPart("foo/") == "foo"
+      doAssert lastPathPart("/") == ""
+    when doslikeFileSystem:
+      doAssert lastPathPart(r"foo\bar.txt") == "bar.txt"
+      doAssert lastPathPart(r"foo\") == "foo"
+
+  template canon(x): untyped = normalizePath(x, '/')
+  doAssert canon"/foo/../bar" == "/bar"
+  doAssert canon"foo/../bar" == "bar"
+
+  doAssert canon"/f/../bar///" == "/bar"
+  doAssert canon"f/..////bar" == "bar"
+
+  doAssert canon"../bar" == "../bar"
+  doAssert canon"/../bar" == "/../bar"
+
+  doAssert canon("foo/../../bar/") == "../bar"
+  doAssert canon("./bla/blob/") == "bla/blob"
+  doAssert canon(".hiddenFile") == ".hiddenFile"
+  doAssert canon("./bla/../../blob/./zoo.nim") == "../blob/zoo.nim"
+
+  doAssert canon("C:/file/to/this/long") == "C:/file/to/this/long"
+  doAssert canon("") == ""
+  doAssert canon("foobar") == "foobar"
+  doAssert canon("f/////////") == "f"
+
+  doAssert relativePath("/foo/bar//baz.nim", "/foo", '/') == "bar/baz.nim"
+  doAssert normalizePath("./foo//bar/../baz", '/') == "foo/baz"
+
+  doAssert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim"
+
+  doAssert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim"
+  doAssert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim"
+  doAssert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
+  doAssert relativePath("", "/users/moo", '/') == ""
+  doAssert relativePath("foo", "", '/') == "foo"
+
+  doAssert joinPath("usr", "") == unixToNativePath"usr/"
+  doAssert joinPath("", "lib") == "lib"
+  doAssert joinPath("", "/lib") == unixToNativePath"/lib"
+  doAssert joinPath("usr/", "/lib") == unixToNativePath"usr/lib"
diff --git a/tests/stdlib/tospaths.nim b/tests/stdlib/tospaths.nim
deleted file mode 100644
index ce00b5a95..000000000
--- a/tests/stdlib/tospaths.nim
+++ /dev/null
@@ -1,99 +0,0 @@
-discard """
-  output: ""
-"""
-# test the ospaths module
-
-import os, pathnorm
-
-doAssert unixToNativePath("") == ""
-doAssert unixToNativePath(".") == $CurDir
-doAssert unixToNativePath("..") == $ParDir
-doAssert isAbsolute(unixToNativePath("/"))
-doAssert isAbsolute(unixToNativePath("/", "a"))
-doAssert isAbsolute(unixToNativePath("/a"))
-doAssert isAbsolute(unixToNativePath("/a", "a"))
-doAssert isAbsolute(unixToNativePath("/a/b"))
-doAssert isAbsolute(unixToNativePath("/a/b", "a"))
-doAssert unixToNativePath("a/b") == joinPath("a", "b")
-
-when defined(macos):
-  doAssert unixToNativePath("./") == ":"
-  doAssert unixToNativePath("./abc") == ":abc"
-  doAssert unixToNativePath("../abc") == "::abc"
-  doAssert unixToNativePath("../../abc") == ":::abc"
-  doAssert unixToNativePath("/abc", "a") == "abc"
-  doAssert unixToNativePath("/abc/def", "a") == "abc:def"
-elif doslikeFileSystem:
-  doAssert unixToNativePath("./") == ".\\"
-  doAssert unixToNativePath("./abc") == ".\\abc"
-  doAssert unixToNativePath("../abc") == "..\\abc"
-  doAssert unixToNativePath("../../abc") == "..\\..\\abc"
-  doAssert unixToNativePath("/abc", "a") == "a:\\abc"
-  doAssert unixToNativePath("/abc/def", "a") == "a:\\abc\\def"
-else:
-  #Tests for unix
-  doAssert unixToNativePath("./") == "./"
-  doAssert unixToNativePath("./abc") == "./abc"
-  doAssert unixToNativePath("../abc") == "../abc"
-  doAssert unixToNativePath("../../abc") == "../../abc"
-  doAssert unixToNativePath("/abc", "a") == "/abc"
-  doAssert unixToNativePath("/abc/def", "a") == "/abc/def"
-
-block extractFilenameTest:
-  doAssert extractFilename("") == ""
-  when defined(posix):
-    doAssert extractFilename("foo/bar") == "bar"
-    doAssert extractFilename("foo/bar.txt") == "bar.txt"
-    doAssert extractFilename("foo/") == ""
-    doAssert extractFilename("/") == ""
-  when doslikeFileSystem:
-    doAssert extractFilename(r"foo\bar") == "bar"
-    doAssert extractFilename(r"foo\bar.txt") == "bar.txt"
-    doAssert extractFilename(r"foo\") == ""
-    doAssert extractFilename(r"C:\") == ""
-
-block lastPathPartTest:
-  doAssert lastPathPart("") == ""
-  when defined(posix):
-    doAssert lastPathPart("foo/bar.txt") == "bar.txt"
-    doAssert lastPathPart("foo/") == "foo"
-    doAssert lastPathPart("/") == ""
-  when doslikeFileSystem:
-    doAssert lastPathPart(r"foo\bar.txt") == "bar.txt"
-    doAssert lastPathPart(r"foo\") == "foo"
-
-template canon(x): untyped = normalizePath(x, '/')
-doAssert canon"/foo/../bar" == "/bar"
-doAssert canon"foo/../bar" == "bar"
-
-doAssert canon"/f/../bar///" == "/bar"
-doAssert canon"f/..////bar" == "bar"
-
-doAssert canon"../bar" == "../bar"
-doAssert canon"/../bar" == "/../bar"
-
-doAssert canon("foo/../../bar/") == "../bar"
-doAssert canon("./bla/blob/") == "bla/blob"
-doAssert canon(".hiddenFile") == ".hiddenFile"
-doAssert canon("./bla/../../blob/./zoo.nim") == "../blob/zoo.nim"
-
-doAssert canon("C:/file/to/this/long") == "C:/file/to/this/long"
-doAssert canon("") == ""
-doAssert canon("foobar") == "foobar"
-doAssert canon("f/////////") == "f"
-
-doAssert relativePath("/foo/bar//baz.nim", "/foo", '/') == "bar/baz.nim"
-doAssert normalizePath("./foo//bar/../baz", '/') == "foo/baz"
-
-doAssert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim"
-
-doAssert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim"
-doAssert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim"
-doAssert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
-doAssert relativePath("", "/users/moo", '/') == ""
-doAssert relativePath("foo", "", '/') == "foo"
-
-doAssert joinPath("usr", "") == unixToNativePath"usr/"
-doAssert joinPath("", "lib") == "lib"
-doAssert joinPath("", "/lib") == unixToNativePath"/lib"
-doAssert joinPath("usr/", "/lib") == unixToNativePath"usr/lib"
diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim
index 9d57d4574..b8d3be9bb 100644
--- a/tests/stdlib/tosproc.nim
+++ b/tests/stdlib/tosproc.nim
@@ -1,23 +1,99 @@
-discard """
-  output: ""
-"""
 # test the osproc module
 
-import os, osproc
+import stdtest/specialpaths
+import "../.." / compiler/unittest_light
 
-block execProcessTest:
-  let dir = parentDir(currentSourcePath())
-  let (outp, err) = execCmdEx("nim c " & quoteShell(dir / "osproctest.nim"))
-  doAssert err == 0
-  let exePath = dir / addFileExt("osproctest", ExeExt)
-  let outStr1 = execProcess(exePath, workingDir=dir, args=["foo", "b A r"], options={})
-  doAssert outStr1 == dir & "\nfoo\nb A r\n"
+when defined(case_testfile): # compiled test file for child process
+  from posix import exitnow
+  proc c_exit2(code: c_int): void {.importc: "_exit", header: "<unistd.h>".}
+  import os
+  var a = 0
+  proc fun(b = 0) =
+    a.inc
+    if a mod 10000000 == 0: # prevents optimizing it away
+      echo a
+    fun(b+1)
 
-  const testDir = "t e st"
-  createDir(testDir)
-  doAssert dirExists(testDir)
-  let outStr2 = execProcess(exePath, workingDir=testDir, args=["x yz"], options={})
-  doAssert outStr2 == absolutePath(testDir) & "\nx yz\n"
+  proc main() =
+    let args = commandLineParams()
+    echo (msg: "child binary", pid: getCurrentProcessId())
+    let arg = args[0]
+    echo (arg: arg)
+    case arg
+    of "exit_0":
+      if true: quit(0)
+    of "exitnow_139":
+      if true: exitnow(139)
+    of "c_exit2_139":
+      if true: c_exit2(139)
+    of "quit_139":
+      # `exitStatusLikeShell` doesn't distinguish between a process that
+      # exit(139) and a process that gets killed with `SIGSEGV` because
+      # 139 = 11 + 128 = SIGSEGV + 128.
+      # However, as #10249 shows, this leads to bad debugging experience
+      # when a child process dies with SIGSEGV, leaving no trace of why it
+      # failed. The shell (and lldb debugger) solves that by inserting a
+      # helpful msg: `segmentation fault` when it detects a signal killed
+      # the child.
+      # todo: expose an API that will show more diagnostic, returing
+      # (exitCode, signal) instead of just `shellExitCode`.
+      if true: quit(139)
+    of "exit_recursion": # stack overflow by infinite recursion
+      fun()
+      echo a
+    of "exit_array": # bad array access
+      echo args[1]
+  main()
 
-  removeDir(testDir)
-  removeFile(exePath)
+else:
+
+  import os, osproc, strutils, posix
+
+  block execShellCmdTest:
+    ## first, compile child program
+    const nim = getCurrentCompilerExe()
+    const sourcePath = currentSourcePath()
+    let output = buildDir / "D20190111T024543".addFileExt(ExeExt)
+    let cmd = "$# c -o:$# -d:release -d:case_testfile $#" % [nim, output,
+        sourcePath]
+    # we're testing `execShellCmd` so don't rely on it to compile test file
+    # note: this should be exported in posix.nim
+    proc c_system(cmd: cstring): cint {.importc: "system",
+      header: "<stdlib.h>".}
+    assertEquals c_system(cmd), 0
+
+    ## use it
+    template runTest(arg: string, expected: int) =
+      echo (arg2: arg, expected2: expected)
+      assertEquals execShellCmd(output & " " & arg), expected
+
+    runTest("exit_0", 0)
+    runTest("exitnow_139", 139)
+    runTest("c_exit2_139", 139)
+    runTest("quit_139", 139)
+    runTest("exit_array", 1)
+    when defined(posix): # on windows, -1073741571
+      runTest("exit_recursion", SIGSEGV.int + 128) # bug #10273: was returning 0
+      assertEquals exitStatusLikeShell(SIGSEGV), SIGSEGV + 128.cint
+
+  block execProcessTest:
+    let dir = parentDir(currentSourcePath())
+    let (outp, err) = execCmdEx("nim c " & quoteShell(dir / "osproctest.nim"))
+    doAssert err == 0
+    let exePath = dir / addFileExt("osproctest", ExeExt)
+    let outStr1 = execProcess(exePath, workingDir = dir, args = ["foo",
+        "b A r"], options = {})
+    doAssert outStr1 == dir & "\nfoo\nb A r\n"
+
+    const testDir = "t e st"
+    createDir(testDir)
+    doAssert dirExists(testDir)
+    let outStr2 = execProcess(exePath, workingDir = testDir, args = ["x yz"],
+        options = {})
+    doAssert outStr2 == absolutePath(testDir) & "\nx yz\n"
+
+    removeDir(testDir)
+    try:
+      removeFile(exePath)
+    except OSError:
+      discard
diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim
index 33cb581ef..c1941bd38 100644
--- a/tests/stdlib/trepr.nim
+++ b/tests/stdlib/trepr.nim
@@ -1,5 +1,6 @@
 discard """
-  output: "{a, b}{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}"
+  output: '''{a, b}{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}
+[1, 2, 3, 4, 5, 6]'''
 """
 
 type
@@ -25,3 +26,11 @@ when false:
 #  "a", "b", "c", "d", "e"
 #]
 #echo(repr(testseq))
+
+# bug #7878
+proc test(variable: var openarray[int]) =
+  echo repr(variable)
+
+var arr = [1, 2, 3, 4, 5, 6]
+
+test(arr)
diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim
index 32f39953d..456ff6315 100644
--- a/tests/stdlib/ttimes.nim
+++ b/tests/stdlib/ttimes.nim
@@ -115,6 +115,13 @@ template runTimezoneTests() =
     check toTime(parsedJan).toUnix == 1451962800
     check toTime(parsedJul).toUnix == 1467342000
 
+template usingTimezone(tz: string, body: untyped) =
+  when defined(linux) or defined(macosx):
+    let oldZone = getEnv("TZ")
+    putEnv("TZ", tz)
+    body
+    putEnv("TZ", oldZone)
+
 suite "ttimes":
 
   # Generate tests for multiple timezone files where available
@@ -123,37 +130,47 @@ suite "ttimes":
     let tz_dir = getEnv("TZDIR", "/usr/share/zoneinfo")
     const f = "yyyy-MM-dd HH:mm zzz"
 
-    let orig_tz = getEnv("TZ")
     var tz_cnt = 0
-    for tz_fn in walkFiles(tz_dir & "/**/*"):
-      if symlinkExists(tz_fn) or tz_fn.endsWith(".tab") or
-          tz_fn.endsWith(".list"):
+    for timezone in walkFiles(tz_dir & "/**/*"):
+      if symlinkExists(timezone) or timezone.endsWith(".tab") or
+          timezone.endsWith(".list"):
         continue
 
-      test "test for " & tz_fn:
-        tz_cnt.inc
-        putEnv("TZ", tz_fn)
-        runTimezoneTests()
+      usingTimezone(timezone):
+        test "test for " & timezone:
+          tz_cnt.inc
+          runTimezoneTests()
 
     test "enough timezone files tested":
       check tz_cnt > 10
 
-    test "dst handling":
-      putEnv("TZ", "Europe/Stockholm")
-      # In case of an impossible time, the time is moved to after the impossible time period
-      check initDateTime(26, mMar, 2017, 02, 30, 00).format(f) == "2017-03-26 03:30 +02:00"
+  else:
+    # not on Linux or macosx: run in the local timezone only
+    test "parseTest":
+      runTimezoneTests()
+
+  test "dst handling":
+    usingTimezone("Europe/Stockholm"):
+      # In case of an impossible time, the time is moved to after the
+      # impossible time period
+      check initDateTime(26, mMar, 2017, 02, 30, 00).format(f) ==
+        "2017-03-26 03:30 +02:00"
       # In case of an ambiguous time, the earlier time is choosen
-      check initDateTime(29, mOct, 2017, 02, 00, 00).format(f) == "2017-10-29 02:00 +02:00"
+      check initDateTime(29, mOct, 2017, 02, 00, 00).format(f) ==
+        "2017-10-29 02:00 +02:00"
       # These are just dates on either side of the dst switch
-      check initDateTime(29, mOct, 2017, 01, 00, 00).format(f) == "2017-10-29 01:00 +02:00"
+      check initDateTime(29, mOct, 2017, 01, 00, 00).format(f) ==
+        "2017-10-29 01:00 +02:00"
       check initDateTime(29, mOct, 2017, 01, 00, 00).isDst
-      check initDateTime(29, mOct, 2017, 03, 01, 00).format(f) == "2017-10-29 03:01 +01:00"
+      check initDateTime(29, mOct, 2017, 03, 01, 00).format(f) ==
+        "2017-10-29 03:01 +01:00"
       check (not initDateTime(29, mOct, 2017, 03, 01, 00).isDst)
 
-      check initDateTime(21, mOct, 2017, 01, 00, 00).format(f) == "2017-10-21 01:00 +02:00"
+      check initDateTime(21, mOct, 2017, 01, 00, 00).format(f) ==
+        "2017-10-21 01:00 +02:00"
 
-    test "issue #6520":
-      putEnv("TZ", "Europe/Stockholm")
+  test "issue #6520":
+    usingTimezone("Europe/Stockholm"):
       var local = fromUnix(1469275200).local
       var utc = fromUnix(1469275200).utc
 
@@ -161,35 +178,28 @@ suite "ttimes":
       local.utcOffset = 0
       check claimedOffset == utc.toTime - local.toTime
 
-    test "issue #5704":
-      putEnv("TZ", "Asia/Seoul")
-      let diff = parse("19700101-000000", "yyyyMMdd-hhmmss").toTime - parse("19000101-000000", "yyyyMMdd-hhmmss").toTime
+  test "issue #5704":
+    usingTimezone("Asia/Seoul"):
+      let diff = parse("19700101-000000", "yyyyMMdd-hhmmss").toTime -
+        parse("19000101-000000", "yyyyMMdd-hhmmss").toTime
       check diff == initDuration(seconds = 2208986872)
 
-    test "issue #6465":
-      putEnv("TZ", "Europe/Stockholm")
+  test "issue #6465":
+    usingTimezone("Europe/Stockholm"):
       let dt = parse("2017-03-25 12:00", "yyyy-MM-dd hh:mm")
       check $(dt + initTimeInterval(days = 1)) == "2017-03-26T12:00:00+02:00"
       check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00"
 
-    test "datetime before epoch":
-      check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52Z"
-
-    test "adding/subtracting time across dst":
-      putenv("TZ", "Europe/Stockholm")
-
+  test "adding/subtracting time across dst":
+    usingTimezone("Europe/Stockholm"):
       let dt1 = initDateTime(26, mMar, 2017, 03, 00, 00)
       check $(dt1 - 1.seconds) == "2017-03-26T01:59:59+01:00"
 
       var dt2 = initDateTime(29, mOct, 2017, 02, 59, 59)
       check  $(dt2 + 1.seconds) == "2017-10-29T02:00:00+01:00"
 
-    putEnv("TZ", orig_tz)
-
-  else:
-    # not on Linux or macosx: run in the local timezone only
-    test "parseTest":
-      runTimezoneTests()
+  test "datetime before epoch":
+    check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52Z"
 
   test "incorrect inputs: empty string":
     parseTestExcp("", "yyyy-MM-dd")
@@ -253,7 +263,7 @@ suite "ttimes":
     parseTestExcp("+1", "mm")
     parseTestExcp("+1", "ss")
 
-  test "_ as a seperator":
+  test "_ as a separator":
     discard parse("2000_01_01", "YYYY'_'MM'_'dd")
 
   test "dynamic timezone":
@@ -485,3 +495,96 @@ suite "ttimes":
     check getDayOfWeek(21, mSep, 1970) == dMon
     check getDayOfWeek(01, mJan, 2000) == dSat
     check getDayOfWeek(01, mJan, 2021) == dFri
+
+  test "between - simple":
+    let x = initDateTime(10, mJan, 2018, 13, 00, 00)
+    let y = initDateTime(11, mJan, 2018, 12, 00, 00)
+    doAssert x + between(x, y) == y
+
+  test "between - dst start":
+    usingTimezone("Europe/Stockholm"):
+      let x = initDateTime(25, mMar, 2018, 00, 00, 00)
+      let y = initDateTime(25, mMar, 2018, 04, 00, 00)
+      doAssert x + between(x, y) == y
+
+  test "between - empty interval":
+    let x = now()
+    let y = x
+    doAssert x + between(x, y) == y
+
+  test "between - dst end":
+    usingTimezone("Europe/Stockholm"):
+      let x = initDateTime(27, mOct, 2018, 02, 00, 00)
+      let y = initDateTime(28, mOct, 2018, 01, 00, 00)
+      doAssert x + between(x, y) == y
+
+  test "between - long day":
+    usingTimezone("Europe/Stockholm"):
+      # This day is 25 hours long in Europe/Stockholm
+      let x = initDateTime(28, mOct, 2018, 00, 30, 00)
+      let y = initDateTime(29, mOct, 2018, 00, 00, 00)
+      doAssert between(x, y) == 24.hours + 30.minutes
+      doAssert x + between(x, y) == y
+
+  test "between - offset change edge case":
+    # This test case is important because in this case
+    # `x + between(x.utc, y.utc) == y` is not true, which is very rare.
+    usingTimezone("America/Belem"):
+      let x = initDateTime(24, mOct, 1987, 00, 00, 00)
+      let y = initDateTime(26, mOct, 1987, 23, 00, 00)
+      doAssert x + between(x, y) == y
+      doAssert y + between(y, x) == x
+
+  test "between - all units":
+    let x = initDateTime(1, mJan, 2000, 00, 00, 00, utc())
+    let ti = initTimeInterval(1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
+    let y = x + ti
+    doAssert between(x, y) == ti
+    doAssert between(y, x) == -ti
+
+  test "between - monthday overflow":
+      let x = initDateTime(31, mJan, 2001, 00, 00, 00, utc())
+      let y = initDateTime(1, mMar, 2001, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+
+  test "between - misc":
+    block:
+      let x = initDateTime(31, mDec, 2000, 12, 00, 00, utc())
+      let y = initDateTime(01, mJan, 2001, 00, 00, 00, utc())
+      doAssert between(x, y) == 12.hours
+
+    block:
+      let x = initDateTime(31, mDec, 2000, 12, 00, 00, utc())
+      let y = initDateTime(02, mJan, 2001, 00, 00, 00, utc())
+      doAssert between(x, y) == 1.days + 12.hours
+
+    block:
+      let x = initDateTime(31, mDec, 1995, 00, 00, 00, utc())
+      let y = initDateTime(01, mFeb, 2000, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+
+    block:
+      let x = initDateTime(01, mDec, 1995, 00, 00, 00, utc())
+      let y = initDateTime(31, mJan, 2000, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+
+    block:
+      let x = initDateTime(31, mJan, 2000, 00, 00, 00, utc())
+      let y = initDateTime(01, mFeb, 2000, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+
+    block:
+      let x = initDateTime(01, mJan, 1995, 12, 00, 00, utc())
+      let y = initDateTime(01, mFeb, 1995, 00, 00, 00, utc())
+      doAssert between(x, y) == 4.weeks + 2.days + 12.hours
+
+    block:
+      let x = initDateTime(31, mJan, 1995, 00, 00, 00, utc())
+      let y = initDateTime(10, mFeb, 1995, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+
+    block:
+      let x = initDateTime(31, mJan, 1995, 00, 00, 00, utc())
+      let y = initDateTime(10, mMar, 1995, 00, 00, 00, utc())
+      doAssert x + between(x, y) == y
+      doAssert between(x, y) == 1.months + 1.weeks
diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim
index c8656bbff..65baefef0 100644
--- a/tests/stdlib/tunittest.nim
+++ b/tests/stdlib/tunittest.nim
@@ -50,7 +50,6 @@ test "unittest multiple requires":
 import math, random
 from strutils import parseInt
 proc defectiveRobot() =
-  randomize()
   case random(1..4)
   of 1: raise newException(OSError, "CANNOT COMPUTE!")
   of 2: discard parseInt("Hello World!")