testutils.nim 3.9 KB

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