testutils.nim 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import std/private/miscdollars
  2. from std/os import getEnv
  3. import std/[macros, genasts]
  4. template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) =
  5. ## API to deal with flaky or failing tests. This avoids disabling entire tests
  6. ## altogether so that at least the parts that are working are kept being
  7. ## tested. This also avoids making CI fail periodically for tests known to
  8. ## be flaky. Finally, for known failures, passing `notifySuccess = true` will
  9. ## log that the test succeeded, which may indicate that a bug was fixed
  10. ## "by accident" and should be looked into.
  11. const info = instantiationInfo(-1, true)
  12. const expr = astToStr(cond)
  13. if cond and not notifySuccess:
  14. discard # silent success
  15. else:
  16. var msg2 = ""
  17. toLocation(msg2, info.filename, info.line, info.column)
  18. if cond:
  19. # a flaky test is failing, we still report it but we don't fail CI
  20. msg2.add " FLAKY_SUCCESS "
  21. else:
  22. # a previously failing test is now passing, a pre-existing bug might've been
  23. # fixed by accidend
  24. msg2.add " FLAKY_FAILURE "
  25. msg2.add $expr & " " & msg
  26. echo msg2
  27. when not defined(js):
  28. import std/strutils
  29. proc greedyOrderedSubsetLines*(lhs, rhs: string): bool =
  30. ## Returns true if each stripped line in `lhs` appears in rhs, using a greedy matching.
  31. iterator splitLinesClosure(): string {.closure.} =
  32. for line in splitLines(rhs.strip):
  33. yield line
  34. var rhsIter = splitLinesClosure
  35. var currentLine = strip(rhsIter())
  36. for line in lhs.strip.splitLines:
  37. let line = line.strip
  38. if line.len != 0:
  39. while line != currentLine:
  40. currentLine = strip(rhsIter())
  41. if rhsIter.finished:
  42. return false
  43. if rhsIter.finished:
  44. return false
  45. return true
  46. template enableRemoteNetworking*: bool =
  47. ## Allows contolling whether to run some test at a statement-level granularity.
  48. ## Using environment variables simplifies propagating this all the way across
  49. ## process calls, e.g. `testament all` calls itself, which in turns invokes
  50. ## a `nim` invocation (possibly via additional intermediate processes).
  51. getEnv("NIM_TESTAMENT_REMOTE_NETWORKING") == "1"
  52. template whenRuntimeJs*(bodyIf, bodyElse) =
  53. ##[
  54. Behaves as `when defined(js) and not nimvm` (which isn't legal yet).
  55. pending improvements to `nimvm`, this sugar helps; use as follows:
  56. whenRuntimeJs:
  57. doAssert defined(js)
  58. when nimvm: doAssert false
  59. else: discard
  60. do:
  61. discard
  62. ]##
  63. when nimvm: bodyElse
  64. else:
  65. when defined(js): bodyIf
  66. else: bodyElse
  67. template whenVMorJs*(bodyIf, bodyElse) =
  68. ## Behaves as: `when defined(js) or nimvm`
  69. when nimvm: bodyIf
  70. else:
  71. when defined(js): bodyIf
  72. else: bodyElse
  73. template accept*(a) =
  74. doAssert compiles(a)
  75. template reject*(a) =
  76. doAssert not compiles(a)
  77. template disableVm*(body) =
  78. when nimvm: discard
  79. else: body
  80. template typeOrVoid[T](a: T): type =
  81. # FACTOR with asyncjs.typeOrVoid
  82. T
  83. macro assertAll*(body) =
  84. ## works in VM, unlike `check`, `require`
  85. runnableExamples:
  86. assertAll:
  87. 1+1 == 2
  88. var a = @[1, 2] # statements work
  89. a.len == 2
  90. # remove this once these support VM, pending #10129 (closed but not yet fixed)
  91. result = newStmtList()
  92. for a in body:
  93. result.add genAst(a) do:
  94. # better than: `when not compiles(typeof(a)):`
  95. when typeOrVoid(a) is void: a
  96. else: doAssert a