testutils.nim 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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, allowPrefixMatch = false): bool =
  30. ## Returns true if each stripped line in `lhs` appears in rhs, using a greedy matching.
  31. # xxx improve error reporting by showing the last matched pair
  32. iterator splitLinesClosure(): string {.closure.} =
  33. for line in splitLines(rhs.strip):
  34. yield line
  35. template isMatch(lhsi, rhsi): bool =
  36. if allowPrefixMatch:
  37. startsWith(rhsi, lhsi):
  38. else:
  39. lhsi == rhsi
  40. var rhsIter = splitLinesClosure
  41. var currentLine = strip(rhsIter())
  42. for line in lhs.strip.splitLines:
  43. let line = line.strip
  44. if line.len != 0:
  45. while not isMatch(line, currentLine):
  46. currentLine = strip(rhsIter())
  47. if rhsIter.finished:
  48. return false
  49. if rhsIter.finished:
  50. return false
  51. return true
  52. template enableRemoteNetworking*: bool =
  53. ## Allows contolling whether to run some test at a statement-level granularity.
  54. ## Using environment variables simplifies propagating this all the way across
  55. ## process calls, e.g. `testament all` calls itself, which in turns invokes
  56. ## a `nim` invocation (possibly via additional intermediate processes).
  57. getEnv("NIM_TESTAMENT_REMOTE_NETWORKING") == "1"
  58. template whenRuntimeJs*(bodyIf, bodyElse) =
  59. ##[
  60. Behaves as `when defined(js) and not nimvm` (which isn't legal yet).
  61. pending improvements to `nimvm`, this sugar helps; use as follows:
  62. whenRuntimeJs:
  63. doAssert defined(js)
  64. when nimvm: doAssert false
  65. else: discard
  66. do:
  67. discard
  68. ]##
  69. when nimvm: bodyElse
  70. else:
  71. when defined(js): bodyIf
  72. else: bodyElse
  73. template whenVMorJs*(bodyIf, bodyElse) =
  74. ## Behaves as: `when defined(js) or nimvm`
  75. when nimvm: bodyIf
  76. else:
  77. when defined(js): bodyIf
  78. else: bodyElse
  79. template accept*(a) =
  80. doAssert compiles(a)
  81. template reject*(a) =
  82. doAssert not compiles(a)
  83. template disableVm*(body) =
  84. when nimvm: discard
  85. else: body
  86. macro assertAll*(body) =
  87. ## works in VM, unlike `check`, `require`
  88. runnableExamples:
  89. assertAll:
  90. 1+1 == 2
  91. var a = @[1, 2] # statements work
  92. a.len == 2
  93. # remove this once these support VM, pending #10129 (closed but not yet fixed)
  94. result = newStmtList()
  95. for a in body:
  96. result.add genAst(a, a2 = a.repr, info = lineInfo(a)) do:
  97. # D20210421T014713:here
  98. # xxx pending https://github.com/nim-lang/Nim/issues/12030,
  99. # `typeof` should introduce its own scope, so that this
  100. # is sufficient: `typeof(a)` instead of `typeof(block: a)`
  101. when typeof(block: a) is void: a
  102. else:
  103. if not a:
  104. raise newException(AssertionDefect, info & " " & a2)