summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/js/asyncjs.nim43
-rw-r--r--lib/pure/asyncdispatch.nim2
-rw-r--r--lib/pure/math.nim17
-rw-r--r--lib/pure/unittest.nim91
-rw-r--r--lib/system.nim5
-rw-r--r--lib/system/chcks.nim1
-rw-r--r--lib/system/sysstr.nim8
7 files changed, 139 insertions, 28 deletions
diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim
index bde3d787f..ec410ee39 100644
--- a/lib/js/asyncjs.nim
+++ b/lib/js/asyncjs.nim
@@ -44,10 +44,10 @@
 ##         resolve(game)
 ##     return promise
 ##
-## Forward definitions work properly, you just don't need to add the ``{.async.}`` pragma:
+## Forward definitions work properly, you just need to always add the ``{.async.}`` pragma:
 ##
 ## .. code-block:: nim
-##   proc loadGame(name: string): Future[Game]
+##   proc loadGame(name: string): Future[Game] {.async.}
 ##
 ## JavaScript compatibility
 ## ~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -83,23 +83,54 @@ proc replaceReturn(node: var NimNode) =
       replaceReturn(son)
     inc z
 
+proc isFutureVoid(node: NimNode): bool =
+  result = node.kind == nnkBracketExpr and
+           node[0].kind == nnkIdent and $node[0] == "Future" and
+           node[1].kind == nnkIdent and $node[1] == "void"
+
 proc generateJsasync(arg: NimNode): NimNode =
   assert arg.kind == nnkProcDef
   result = arg
+  var isVoid = false
+  var jsResolveNode = ident("jsResolve")
+
   if arg.params[0].kind == nnkEmpty:
     result.params[0] = nnkBracketExpr.newTree(ident("Future"), ident("void"))
+    isVoid = true
+  elif isFutureVoid(arg.params[0]):
+    isVoid = true
+
   var code = result.body
   replaceReturn(code)
   result.body = nnkStmtList.newTree()
-  var q = quote:
-    proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
-    proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
-  result.body.add(q)
+
+  if len(code) > 0:
+    var awaitFunction = quote:
+      proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
+    result.body.add(awaitFunction)
+
+    var resolve: NimNode
+    if isVoid:
+      resolve = quote:
+        var `jsResolveNode` {.importcpp: "undefined".}: Future[void]
+    else:
+      resolve = quote:
+        proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
+    result.body.add(resolve)
+  else:
+    result.body = newEmptyNode()
   for child in code:
     result.body.add(child)
+
+  if len(code) > 0 and isVoid:
+    var voidFix = quote:
+      return `jsResolveNode`
+    result.body.add(voidFix)
+
   result.pragma = quote:
     {.codegenDecl: "async function $2($3)".}
 
+
 macro async*(arg: untyped): untyped =
   ## Macro which converts normal procedures into
   ## javascript-compatible async procedures
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 675e8fc5e..23eb80b37 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -1234,7 +1234,7 @@ else:
           processBasicCallbacks(fd, writeList)
           result = true
 
-        if Event.User in events or events == {Event.Error}:
+        if Event.User in events:
           processBasicCallbacks(fd, readList)
           custom = true
           if rLength == 0:
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index a9dabfa48..cbd04a145 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -351,15 +351,19 @@ proc round*[T: float32|float64](x: T, places: int = 0): T =
     result = round0(x*mult)/mult
 
 when not defined(JS):
-  proc frexp*(x: float32, exponent: var int): float32 {.
+  proc c_frexp*(x: float32, exponent: var int32): float32 {.
     importc: "frexp", header: "<math.h>".}
-  proc frexp*(x: float64, exponent: var int): float64 {.
+  proc c_frexp*(x: float64, exponent: var int32): float64 {.
     importc: "frexp", header: "<math.h>".}
+  proc frexp*[T, U](x: T, exponent: var U): T =
     ## Split a number into mantissa and exponent.
     ## `frexp` calculates the mantissa m (a float greater than or equal to 0.5
     ## and less than 1) and the integer value n such that `x` (the original
     ## float value) equals m * 2**n. frexp stores n in `exponent` and returns
     ## m.
+    var exp: int32
+    result = c_frexp(x, exp)
+    exponent = exp
 else:
   proc frexp*[T: float32|float64](x: T, exponent: var int): T =
     if x == 0.0:
@@ -368,9 +372,14 @@ else:
     elif x < 0.0:
       result = -frexp(-x, exponent)
     else:
-      var ex = floor(log2(x))
-      exponent = round(ex)
+      var ex = trunc(log2(x))
+      exponent = int(ex)
       result = x / pow(2.0, ex)
+      if abs(result) >= 1:
+        inc(exponent)
+        result = result / 2
+      if exponent == 1024 and result == 0.0:
+        result = 0.99999999999999988898
 
 proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
   ## Breaks `x` into an integral and a fractional part.
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 7a8d1dad0..fbce087ff 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -21,13 +21,41 @@
 ## ``nim c -r <testfile.nim>`` exits with 0 or 1
 ##
 ## Running a single test
-## ---------------------
+## =====================
 ##
-## Simply specify the test name as a command line argument.
+## Specify the test name as a command line argument.
 ##
 ## .. code::
 ##
-##   nim c -r test "my super awesome test name"
+##   nim c -r test "my test name" "another test"
+##
+## Multiple arguments can be used.
+##
+## Running a single test suite
+## ===========================
+##
+## Specify the suite name delimited by ``"::"``.
+##
+## .. code::
+##
+##   nim c -r test "my test name::"
+##
+## Selecting tests by pattern
+## ==========================
+##
+## A single ``"*"`` can be used for globbing.
+##
+## Delimit the end of a suite name with ``"::"``.
+##
+## Tests matching **any** of the arguments are executed.
+##
+## .. code::
+##
+##   nim c -r test fast_suite::mytest1 fast_suite::mytest2
+##   nim c -r test "fast_suite::mytest*"
+##   nim c -r test "auth*::" "crypto::hashing*"
+##   # Run suites starting with 'bug #' and standalone tests starting with '#'
+##   nim c -r test 'bug #*::' '::#*'
 ##
 ## Example
 ## -------
@@ -121,7 +149,7 @@ var
 
   checkpoints {.threadvar.}: seq[string]
   formatters {.threadvar.}: seq[OutputFormatter]
-  testsToRun {.threadvar.}: HashSet[string]
+  testsFilters {.threadvar.}: HashSet[string]
 
 when declared(stdout):
   abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
@@ -300,22 +328,63 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
 method suiteEnded*(formatter: JUnitOutputFormatter) =
   formatter.stream.writeLine("\t</testsuite>")
 
-proc shouldRun(testName: string): bool =
-  if testsToRun.len == 0:
+proc glob(matcher, filter: string): bool =
+  ## Globbing using a single `*`. Empty `filter` matches everything.
+  if filter.len == 0:
     return true
 
-  result = testName in testsToRun
+  if not filter.contains('*'):
+    return matcher == filter
+
+  let beforeAndAfter = filter.split('*', maxsplit=1)
+  if beforeAndAfter.len == 1:
+    # "foo*"
+    return matcher.startswith(beforeAndAfter[0])
+
+  if matcher.len < filter.len - 1:
+    return false  # "12345" should not match "123*345"
+
+  return matcher.startsWith(beforeAndAfter[0]) and matcher.endsWith(beforeAndAfter[1])
+
+proc matchFilter(suiteName, testName, filter: string): bool =
+  if filter == "":
+    return true
+  if testName == filter:
+    # corner case for tests containing "::" in their name
+    return true
+  let suiteAndTestFilters = filter.split("::", maxsplit=1)
+
+  if suiteAndTestFilters.len == 1:
+    # no suite specified
+    let test_f = suiteAndTestFilters[0]
+    return glob(testName, test_f)
+
+  return glob(suiteName, suiteAndTestFilters[0]) and glob(testName, suiteAndTestFilters[1])
+
+when defined(testing): export matchFilter
+
+proc shouldRun(currentSuiteName, testName: string): bool =
+  ## Check if a test should be run by matching suiteName and testName against
+  ## test filters.
+  if testsFilters.len == 0:
+    return true
+
+  for f in testsFilters:
+    if matchFilter(currentSuiteName, testName, f):
+      return true
+
+  return false
 
 proc ensureInitialized() =
   if formatters == nil:
     formatters = @[OutputFormatter(defaultConsoleFormatter())]
 
-  if not testsToRun.isValid:
-    testsToRun.init()
+  if not testsFilters.isValid:
+    testsFilters.init()
     when declared(paramCount):
       # Read tests to run from the command line.
       for i in 1 .. paramCount():
-        testsToRun.incl(paramStr(i))
+        testsFilters.incl(paramStr(i))
 
 # These two procs are added as workarounds for
 # https://github.com/nim-lang/Nim/issues/5549
@@ -395,7 +464,7 @@ template test*(name, body) {.dirty.} =
 
   ensureInitialized()
 
-  if shouldRun(name):
+  if shouldRun(when declared(testSuiteName): testSuiteName else: "", name):
     checkpoints = @[]
     var testStatusIMPL {.inject.} = OK
 
diff --git a/lib/system.nim b/lib/system.nim
index 83e87683a..85643891b 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2916,7 +2916,10 @@ when not defined(JS): #and not defined(nimscript):
       elif x > y: result = 1
       else: result = 0
     else:
-      result = int(c_strcmp(x, y))
+      let minlen = min(x.len, y.len)
+      result = int(c_memcmp(x.cstring, y.cstring, minlen.csize))
+      if result == 0:
+        result = x.len - y.len
 
   when defined(nimscript):
     proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.}
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index 1520f231e..69b680dbd 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -63,7 +63,6 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
   while x != subclass:
     if x == nil:
       sysFatal(ObjectConversionError, "invalid object conversion")
-      break
     x = x.base
 
 proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 56b8ade97..4c5f3d9a1 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -24,10 +24,10 @@ proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
   if a == b: return 0
   if a == nil: return -1
   if b == nil: return 1
-  when defined(nimNoArrayToCstringConversion):
-    return c_strcmp(addr a.data, addr b.data)
-  else:
-    return c_strcmp(a.data, b.data)
+  let minlen = min(a.len, b.len)
+  result = c_memcmp(addr a.data, addr b.data, minlen.csize)
+  if result == 0:
+    result = a.len - b.len
 
 proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
   if a == b: return true