mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-03 03:32:32 +00:00
Testament now retries a test by a specified amount if it fails in any way other than an invalid spec. This is to deal with the flaky GC tests on Windows CI that fail in many different ways, from the linker randomly erroring, segfaults, etc. Unfortunately I couldn't do this cleanly in testament's current code. The proc `addResult`, which is the "final" proc called in a test run's lifetime, is now wrapped in a proc `finishTest` that returns a bool `true` if the test failed and has to be retried. This result is propagated up from `cmpMsgs` and `compilerOutputTests` until it reaches `testSpecHelper`, which handles these results by recursing if the test has to be retried. Since calling `testSpecHelper` means "run this test with one given configuration", this means every single matrix option/target etc. receive an equal amount of retries each. The result of `finishTest` is ignored in cases where it's known that it won't be retried due to passing, being skipped, having an invalid spec etc. It's also ignored in `testNimblePackages` because it's not necessary for those specific tests yet and similar retry behavior is already implemented for part of it. This was a last resort for the flaky GC tests but they've been a problem for years at this point, they give us more work to do and turn off contributors. Ideally GC tests failing should mark as "needs review" in the CI rather than "failed" but I don't know if Github supports something like this.
212 lines
4.9 KiB
Nim
212 lines
4.9 KiB
Nim
discard """
|
|
outputsub: "finished"
|
|
retries: 2
|
|
"""
|
|
|
|
# Test the garbage collector.
|
|
|
|
import
|
|
strutils
|
|
|
|
type
|
|
PNode = ref TNode
|
|
TNode {.final.} = object
|
|
le, ri: PNode
|
|
data: string
|
|
|
|
TTable {.final.} = object
|
|
counter, max: int
|
|
data: seq[string]
|
|
|
|
TBNode {.final.} = object
|
|
other: PNode # a completely different tree
|
|
data: string
|
|
sons: seq[TBNode] # directly embedded!
|
|
t: TTable
|
|
|
|
TCaseKind = enum nkStr, nkWhole, nkList
|
|
PCaseNode = ref TCaseNode
|
|
TCaseNode {.final.} = object
|
|
case kind: TCaseKind
|
|
of nkStr: data: string
|
|
of nkList: sons: seq[PCaseNode]
|
|
else: unused: seq[string]
|
|
|
|
TIdObj* = object of RootObj
|
|
id*: int # unique id; use this for comparisons and not the pointers
|
|
|
|
PIdObj* = ref TIdObj
|
|
PIdent* = ref TIdent
|
|
TIdent*{.acyclic.} = object of TIdObj
|
|
s*: string
|
|
next*: PIdent # for hash-table chaining
|
|
h*: int # hash value of s
|
|
|
|
var
|
|
flip: int
|
|
|
|
proc newCaseNode(data: string): PCaseNode =
|
|
if flip == 0:
|
|
result = PCaseNode(kind: nkStr, data: data)
|
|
else:
|
|
result = PCaseNode(kind: nkWhole, unused: @["", "abc", "abdc"])
|
|
flip = 1 - flip
|
|
|
|
proc newCaseNode(a, b: PCaseNode): PCaseNode =
|
|
result = PCaseNode(kind: nkList, sons: @[a, b])
|
|
|
|
proc caseTree(lvl: int = 0): PCaseNode =
|
|
if lvl == 3: result = newCaseNode("data item")
|
|
else: result = newCaseNode(caseTree(lvl+1), caseTree(lvl+1))
|
|
|
|
proc finalizeNode(n: PNode) =
|
|
assert(n != nil)
|
|
write(stdout, "finalizing: ")
|
|
writeLine(stdout, "not nil")
|
|
|
|
var
|
|
id: int = 1
|
|
|
|
proc buildTree(depth = 1): PNode =
|
|
if depth == 7: return nil
|
|
new(result, finalizeNode)
|
|
result.le = buildTree(depth+1)
|
|
result.ri = buildTree(depth+1)
|
|
result.data = $id
|
|
inc(id)
|
|
|
|
proc returnTree(): PNode =
|
|
writeLine(stdout, "creating id: " & $id)
|
|
new(result, finalizeNode)
|
|
result.data = $id
|
|
new(result.le, finalizeNode)
|
|
result.le.data = $id & ".1"
|
|
new(result.ri, finalizeNode)
|
|
result.ri.data = $id & ".2"
|
|
inc(id)
|
|
|
|
# now create a cycle:
|
|
writeLine(stdout, "creating id (cyclic): " & $id)
|
|
var cycle: PNode
|
|
new(cycle, finalizeNode)
|
|
cycle.data = $id
|
|
cycle.le = cycle
|
|
cycle.ri = cycle
|
|
inc(id)
|
|
#writeLine(stdout, "refcount: " & $refcount(cycle))
|
|
#writeLine(stdout, "refcount le: " & $refcount(cycle.le))
|
|
#writeLine(stdout, "refcount ri: " & $refcount(cycle.ri))
|
|
|
|
proc printTree(t: PNode) =
|
|
if t == nil: return
|
|
writeLine(stdout, "printing")
|
|
writeLine(stdout, t.data)
|
|
printTree(t.le)
|
|
printTree(t.ri)
|
|
|
|
proc unsureNew(result: var PNode) =
|
|
writeLine(stdout, "creating unsure id: " & $id)
|
|
new(result, finalizeNode)
|
|
result.data = $id
|
|
new(result.le, finalizeNode)
|
|
result.le.data = $id & ".a"
|
|
new(result.ri, finalizeNode)
|
|
result.ri.data = $id & ".b"
|
|
inc(id)
|
|
|
|
proc setSons(n: var TBNode) =
|
|
n.sons = @[] # free memory of the sons
|
|
n.t.data = @[]
|
|
var
|
|
m: seq[string]
|
|
m = @[]
|
|
setLen(m, len(n.t.data) * 2)
|
|
for i in 0..high(m):
|
|
m[i] = "..."
|
|
n.t.data = m
|
|
|
|
proc buildBTree(father: var TBNode) =
|
|
father.data = "father"
|
|
father.other = nil
|
|
father.sons = @[]
|
|
for i in 1..10:
|
|
write(stdout, "next iteration!\n")
|
|
var n: TBNode
|
|
n.other = returnTree()
|
|
n.data = "B node: " & $i
|
|
if i mod 2 == 0: n.sons = @[] # nil and [] need to be handled correctly!
|
|
add father.sons, n
|
|
father.t.counter = 0
|
|
father.t.max = 3
|
|
father.t.data = @["ha", "lets", "stress", "it"]
|
|
setSons(father)
|
|
|
|
proc getIdent(identifier: cstring, length: int, h: int): PIdent =
|
|
new(result)
|
|
result.h = h
|
|
result.s = newString(length)
|
|
|
|
proc main() =
|
|
discard getIdent("addr", 4, 0)
|
|
discard getIdent("hall", 4, 0)
|
|
discard getIdent("echo", 4, 0)
|
|
discard getIdent("huch", 4, 0)
|
|
|
|
var
|
|
father: TBNode
|
|
for i in 1..1_00:
|
|
buildBTree(father)
|
|
|
|
for i in 1..1_00:
|
|
var t = returnTree()
|
|
var t2: PNode
|
|
unsureNew(t2)
|
|
write(stdout, "now building bigger trees: ")
|
|
var t2: PNode
|
|
for i in 1..100:
|
|
t2 = buildTree()
|
|
printTree(t2)
|
|
write(stdout, "now test sequences of strings:")
|
|
var s: seq[string] = @[]
|
|
for i in 1..100:
|
|
add s, "hohoho" # test reallocation
|
|
writeLine(stdout, s[89])
|
|
write(stdout, "done!\n")
|
|
|
|
var
|
|
father {.threadvar.}: TBNode
|
|
s {.threadvar.}: string
|
|
|
|
fatherAsGlobal: TBNode
|
|
|
|
proc start =
|
|
s = ""
|
|
s = ""
|
|
writeLine(stdout, repr(caseTree()))
|
|
father.t.data = @["ha", "lets", "stress", "it"]
|
|
father.t.data = @["ha", "lets", "stress", "it"]
|
|
var t = buildTree()
|
|
write(stdout, repr(t[]))
|
|
buildBTree(father)
|
|
write(stdout, repr(father))
|
|
|
|
write(stdout, "starting main...\n")
|
|
main()
|
|
|
|
GC_fullCollect()
|
|
# the M&S GC fails with this call and it's unclear why. Definitely something
|
|
# we need to fix!
|
|
#GC_fullCollect()
|
|
writeLine(stdout, GC_getStatistics())
|
|
write(stdout, "finished\n")
|
|
|
|
fatherAsGlobal.t.data = @["ha", "lets", "stress", "it"]
|
|
var tg = buildTree()
|
|
buildBTree(fatherAsGlobal)
|
|
|
|
var thr: array[8, Thread[void]]
|
|
for i in low(thr)..high(thr):
|
|
createThread(thr[i], start)
|
|
joinThreads(thr)
|
|
start()
|