diff options
Diffstat (limited to 'tests/stdlib')
218 files changed, 19545 insertions, 2626 deletions
diff --git a/tests/stdlib/concurrency/atomicSample.nim b/tests/stdlib/concurrency/atomicSample.nim new file mode 100644 index 000000000..d56d867df --- /dev/null +++ b/tests/stdlib/concurrency/atomicSample.nim @@ -0,0 +1,9 @@ +import atomics + +type + AtomicWithGeneric*[T] = object + value: Atomic[T] + +proc initAtomicWithGeneric*[T](value: T): AtomicWithGeneric[T] = + result.value.store(value) + diff --git a/tests/stdlib/concurrency/tatomic_import.nim b/tests/stdlib/concurrency/tatomic_import.nim new file mode 100644 index 000000000..e8faaae20 --- /dev/null +++ b/tests/stdlib/concurrency/tatomic_import.nim @@ -0,0 +1,11 @@ +import atomicSample + +block crossFileObjectContainingAGenericWithAComplexObject: + discard initAtomicWithGeneric[string]("foo") + +block crossFileObjectContainingAGenericWithAnInteger: + discard initAtomicWithGeneric[int](1) + discard initAtomicWithGeneric[int8](1) + discard initAtomicWithGeneric[int16](1) + discard initAtomicWithGeneric[int32](1) + discard initAtomicWithGeneric[int64](1) diff --git a/tests/stdlib/concurrency/tatomics.nim b/tests/stdlib/concurrency/tatomics.nim new file mode 100644 index 000000000..08f2e7d3e --- /dev/null +++ b/tests/stdlib/concurrency/tatomics.nim @@ -0,0 +1,637 @@ +discard """ + # test C with -d:nimUseCppAtomics as well to check nothing breaks + matrix: "--mm:refc; --mm:orc; --mm:refc -d:nimUseCppAtomics; --mm:orc -d:nimUseCppAtomics" + targets: "c cpp" +""" + +# test atomic operations + +import std/[atomics, bitops] +import std/assertions + + +type + Object = object + val: int + + +# Atomic operations for trivial objects + + +block trivialLoad: + var location: Atomic[int] + location.store(1) + doAssert location.load == 1 + location.store(2) + doAssert location.load(moRelaxed) == 2 + location.store(3) + doAssert location.load(moAcquire) == 3 + + +block trivialStore: + var location: Atomic[int] + location.store(1) + doAssert location.load == 1 + location.store(2, moRelaxed) + doAssert location.load == 2 + location.store(3, moRelease) + doAssert location.load == 3 + + +block trivialExchange: + var location: Atomic[int] + location.store(1) + doAssert location.exchange(2) == 1 + doAssert location.exchange(3, moRelaxed) == 2 + doAssert location.exchange(4, moAcquire) == 3 + doAssert location.exchange(5, moRelease) == 4 + doAssert location.exchange(6, moAcquireRelease) == 5 + doAssert location.load == 6 + + +block trivialCompareExchangeDoesExchange: + var location: Atomic[int] + var expected = 1 + location.store(1) + doAssert location.compareExchange(expected, 2) + doAssert expected == 1 + doAssert location.load == 2 + expected = 2 + doAssert location.compareExchange(expected, 3, moRelaxed) + doAssert expected == 2 + doAssert location.load == 3 + expected = 3 + doAssert location.compareExchange(expected, 4, moAcquire) + doAssert expected == 3 + doAssert location.load == 4 + expected = 4 + doAssert location.compareExchange(expected, 5, moRelease) + doAssert expected == 4 + doAssert location.load == 5 + expected = 5 + doAssert location.compareExchange(expected, 6, moAcquireRelease) + doAssert expected == 5 + doAssert location.load == 6 + + +block trivialCompareExchangeDoesNotExchange: + var location: Atomic[int] + var expected = 10 + location.store(1) + doAssert not location.compareExchange(expected, 2) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 3, moRelaxed) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 4, moAcquire) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 5, moRelease) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 6, moAcquireRelease) + doAssert expected == 1 + doAssert location.load == 1 + + +block trivialCompareExchangeSuccessFailureDoesExchange: + var location: Atomic[int] + var expected = 1 + location.store(1) + doAssert location.compareExchange(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == 1 + doAssert location.load == 2 + expected = 2 + doAssert location.compareExchange(expected, 3, moRelaxed, moRelaxed) + doAssert expected == 2 + doAssert location.load == 3 + expected = 3 + doAssert location.compareExchange(expected, 4, moAcquire, moAcquire) + doAssert expected == 3 + doAssert location.load == 4 + expected = 4 + doAssert location.compareExchange(expected, 5, moRelease, moRelease) + doAssert expected == 4 + doAssert location.load == 5 + expected = 5 + doAssert location.compareExchange(expected, 6, moAcquireRelease, moAcquireRelease) + doAssert expected == 5 + doAssert location.load == 6 + + +block trivialCompareExchangeSuccessFailureDoesNotExchange: + var location: Atomic[int] + var expected = 10 + location.store(1) + doAssert not location.compareExchange(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 3, moRelaxed, moRelaxed) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 4, moAcquire, moAcquire) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 5, moRelease, moRelease) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 6, moAcquireRelease, moAcquireRelease) + doAssert expected == 1 + doAssert location.load == 1 + + +block trivialCompareExchangeWeakDoesExchange: + var location: Atomic[int] + var expected = 1 + location.store(1) + doAssert location.compareExchangeWeak(expected, 2) + doAssert expected == 1 + doAssert location.load == 2 + expected = 2 + doAssert location.compareExchangeWeak(expected, 3, moRelaxed) + doAssert expected == 2 + doAssert location.load == 3 + expected = 3 + doAssert location.compareExchangeWeak(expected, 4, moAcquire) + doAssert expected == 3 + doAssert location.load == 4 + expected = 4 + doAssert location.compareExchangeWeak(expected, 5, moRelease) + doAssert expected == 4 + doAssert location.load == 5 + expected = 5 + doAssert location.compareExchangeWeak(expected, 6, moAcquireRelease) + doAssert expected == 5 + doAssert location.load == 6 + + +block trivialCompareExchangeWeakDoesNotExchange: + var location: Atomic[int] + var expected = 10 + location.store(1) + doAssert not location.compareExchangeWeak(expected, 2) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 3, moRelaxed) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 4, moAcquire) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 5, moRelease) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 6, moAcquireRelease) + doAssert expected == 1 + doAssert location.load == 1 + + +block trivialCompareExchangeWeakSuccessFailureDoesExchange: + var location: Atomic[int] + var expected = 1 + location.store(1) + doAssert location.compareExchangeWeak(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == 1 + doAssert location.load == 2 + expected = 2 + doAssert location.compareExchangeWeak(expected, 3, moRelaxed, moRelaxed) + doAssert expected == 2 + doAssert location.load == 3 + expected = 3 + doAssert location.compareExchangeWeak(expected, 4, moAcquire, moAcquire) + doAssert expected == 3 + doAssert location.load == 4 + expected = 4 + doAssert location.compareExchangeWeak(expected, 5, moRelease, moRelease) + doAssert expected == 4 + doAssert location.load == 5 + expected = 5 + doAssert location.compareExchangeWeak(expected, 6, moAcquireRelease, moAcquireRelease) + doAssert expected == 5 + doAssert location.load == 6 + + +block trivialCompareExchangeWeakSuccessFailureDoesNotExchange: + var location: Atomic[int] + var expected = 10 + location.store(1) + doAssert not location.compareExchangeWeak(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 3, moRelaxed, moRelaxed) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 4, moAcquire, moAcquire) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 5, moRelease, moRelease) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 6, moAcquireRelease, moAcquireRelease) + doAssert expected == 1 + doAssert location.load == 1 + + +# Atomic operations for non-trivial objects + + +block objectLoad: + var location: Atomic[Object] + location.store(Object(val: 1)) + doAssert location.load == Object(val: 1) + location.store(Object(val: 2)) + doAssert location.load(moRelaxed) == Object(val: 2) + location.store(Object(val: 3)) + doAssert location.load(moAcquire) == Object(val: 3) + + +block objectStore: + var location: Atomic[Object] + location.store(Object(val: 1)) + doAssert location.load == Object(val: 1) + location.store(Object(val: 2), moRelaxed) + doAssert location.load == Object(val: 2) + location.store(Object(val: 3), moRelease) + doAssert location.load == Object(val: 3) + + +block objectExchange: + var location: Atomic[Object] + location.store(Object(val: 1)) + doAssert location.exchange(Object(val: 2)) == Object(val: 1) + doAssert location.exchange(Object(val: 3), moRelaxed) == Object(val: 2) + doAssert location.exchange(Object(val: 4), moAcquire) == Object(val: 3) + doAssert location.exchange(Object(val: 5), moRelease) == Object(val: 4) + doAssert location.exchange(Object(val: 6), moAcquireRelease) == Object(val: 5) + doAssert location.load == Object(val: 6) + + +block objectCompareExchangeDoesExchange: + var location: Atomic[Object] + var expected = Object(val: 1) + location.store(Object(val: 1)) + doAssert location.compareExchange(expected, Object(val: 2)) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 2) + expected = Object(val: 2) + doAssert location.compareExchange(expected, Object(val: 3), moRelaxed) + doAssert expected == Object(val: 2) + doAssert location.load == Object(val: 3) + expected = Object(val: 3) + doAssert location.compareExchange(expected, Object(val: 4), moAcquire) + doAssert expected == Object(val: 3) + doAssert location.load == Object(val: 4) + expected = Object(val: 4) + doAssert location.compareExchange(expected, Object(val: 5), moRelease) + doAssert expected == Object(val: 4) + doAssert location.load == Object(val: 5) + expected = Object(val: 5) + doAssert location.compareExchange(expected, Object(val: 6), moAcquireRelease) + doAssert expected == Object(val: 5) + doAssert location.load == Object(val: 6) + + +block objectCompareExchangeDoesNotExchange: + var location: Atomic[Object] + var expected = Object(val: 10) + location.store(Object(val: 1)) + doAssert not location.compareExchange(expected, Object(val: 2)) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 3), moRelaxed) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 4), moAcquire) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 5), moRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 6), moAcquireRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + + +block objectCompareExchangeSuccessFailureDoesExchange: + var location: Atomic[Object] + var expected = Object(val: 1) + location.store(Object(val: 1)) + doAssert location.compareExchange(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 2) + expected = Object(val: 2) + doAssert location.compareExchange(expected, Object(val: 3), moRelaxed, moRelaxed) + doAssert expected == Object(val: 2) + doAssert location.load == Object(val: 3) + expected = Object(val: 3) + doAssert location.compareExchange(expected, Object(val: 4), moAcquire, moAcquire) + doAssert expected == Object(val: 3) + doAssert location.load == Object(val: 4) + expected = Object(val: 4) + doAssert location.compareExchange(expected, Object(val: 5), moRelease, moRelease) + doAssert expected == Object(val: 4) + doAssert location.load == Object(val: 5) + expected = Object(val: 5) + doAssert location.compareExchange(expected, Object(val: 6), moAcquireRelease, moAcquireRelease) + doAssert expected == Object(val: 5) + doAssert location.load == Object(val: 6) + + +block objectCompareExchangeSuccessFailureDoesNotExchange: + var location: Atomic[Object] + var expected = Object(val: 10) + location.store(Object(val: 1)) + doAssert not location.compareExchange(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 3), moRelaxed, moRelaxed) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 4), moAcquire, moAcquire) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 5), moRelease, moRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 6), moAcquireRelease, moAcquireRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + + +block objectCompareExchangeWeakDoesExchange: + var location: Atomic[Object] + var expected = Object(val: 1) + location.store(Object(val: 1)) + doAssert location.compareExchangeWeak(expected, Object(val: 2)) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 2) + expected = Object(val: 2) + doAssert location.compareExchangeWeak(expected, Object(val: 3), moRelaxed) + doAssert expected == Object(val: 2) + doAssert location.load == Object(val: 3) + expected = Object(val: 3) + doAssert location.compareExchangeWeak(expected, Object(val: 4), moAcquire) + doAssert expected == Object(val: 3) + doAssert location.load == Object(val: 4) + expected = Object(val: 4) + doAssert location.compareExchangeWeak(expected, Object(val: 5), moRelease) + doAssert expected == Object(val: 4) + doAssert location.load == Object(val: 5) + expected = Object(val: 5) + doAssert location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease) + doAssert expected == Object(val: 5) + doAssert location.load == Object(val: 6) + + +block objectCompareExchangeWeakDoesNotExchange: + var location: Atomic[Object] + var expected = Object(val: 10) + location.store(Object(val: 1)) + doAssert not location.compareExchangeWeak(expected, Object(val: 2)) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 3), moRelaxed) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 4), moAcquire) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 5), moRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + + +block objectCompareExchangeWeakSuccessFailureDoesExchange: + var location: Atomic[Object] + var expected = Object(val: 1) + location.store(Object(val: 1)) + doAssert location.compareExchangeWeak(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 2) + expected = Object(val: 2) + doAssert location.compareExchangeWeak(expected, Object(val: 3), moRelaxed, moRelaxed) + doAssert expected == Object(val: 2) + doAssert location.load == Object(val: 3) + expected = Object(val: 3) + doAssert location.compareExchangeWeak(expected, Object(val: 4), moAcquire, moAcquire) + doAssert expected == Object(val: 3) + doAssert location.load == Object(val: 4) + expected = Object(val: 4) + doAssert location.compareExchangeWeak(expected, Object(val: 5), moRelease, moRelease) + doAssert expected == Object(val: 4) + doAssert location.load == Object(val: 5) + expected = Object(val: 5) + doAssert location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease, moAcquireRelease) + doAssert expected == Object(val: 5) + doAssert location.load == Object(val: 6) + + +block objectCompareExchangeWeakSuccessFailureDoesNotExchange: + var location: Atomic[Object] + var expected = Object(val: 10) + location.store(Object(val: 1)) + doAssert not location.compareExchangeWeak(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 3), moRelaxed, moRelaxed) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 4), moAcquire, moAcquire) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 5), moRelease, moRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease, moAcquireRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + + +# Numerical operations + + +block fetchAdd: + var location: Atomic[int] + doAssert location.fetchAdd(1) == 0 + doAssert location.fetchAdd(1, moRelaxed) == 1 + doAssert location.fetchAdd(1, moRelease) == 2 + doAssert location.load == 3 + + +block fetchSub: + var location: Atomic[int] + doAssert location.fetchSub(1) == 0 + doAssert location.fetchSub(1, moRelaxed) == -1 + doAssert location.fetchSub(1, moRelease) == -2 + doAssert location.load == -3 + + +block fetchAnd: + var location: Atomic[int] + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchAnd(j) == i) + doAssert(location.load == i.bitand(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchAnd(j, moRelaxed) == i) + doAssert(location.load == i.bitand(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchAnd(j, moRelease) == i) + doAssert(location.load == i.bitand(j)) + + +block fetchOr: + var location: Atomic[int] + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchOr(j) == i) + doAssert(location.load == i.bitor(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchOr(j, moRelaxed) == i) + doAssert(location.load == i.bitor(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchOr(j, moRelease) == i) + doAssert(location.load == i.bitor(j)) + + +block fetchXor: + var location: Atomic[int] + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchXor(j) == i) + doAssert(location.load == i.bitxor(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchXor(j, moRelaxed) == i) + doAssert(location.load == i.bitxor(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchXor(j, moRelease) == i) + doAssert(location.load == i.bitxor(j)) + + +block atomicInc: + var location: Atomic[int] + location.atomicInc + doAssert location.load == 1 + location.atomicInc(1) + doAssert location.load == 2 + location += 1 + doAssert location.load == 3 + + +block atomicDec: + var location: Atomic[int] + location.atomicDec + doAssert location.load == -1 + location.atomicDec(1) + doAssert location.load == -2 + location -= 1 + doAssert location.load == -3 + + +# Flag operations + + +block testAndSet: + var location: AtomicFlag + doAssert not location.testAndSet + doAssert location.testAndSet + doAssert location.testAndSet + location.clear() + doAssert not location.testAndSet(moRelaxed) + doAssert location.testAndSet(moRelaxed) + doAssert location.testAndSet(moRelaxed) + location.clear() + doAssert not location.testAndSet(moRelease) + doAssert location.testAndSet(moRelease) + doAssert location.testAndSet(moRelease) + + +block clear: + var location: AtomicFlag + discard location.testAndSet + location.clear + doAssert not location.testAndSet + location.clear(moRelaxed) + doAssert not location.testAndSet + location.clear(moRelease) + doAssert not location.testAndSet + +block: # bug #18844 + when not defined(cpp): # cpp pending pr #18836 + type + Deprivation = object of RootObj + memes: Atomic[int] + Zoomer = object + dopamine: Deprivation + + block: + var x = Deprivation() + var y = Zoomer() + doAssert x.memes.load == 0 + doAssert y.dopamine.memes.load == 0 + + block: + var x: Deprivation + var y: Zoomer + doAssert x.memes.load == 0 + doAssert y.dopamine.memes.load == 0 diff --git a/tests/stdlib/concurrency/tatomics_size.nim b/tests/stdlib/concurrency/tatomics_size.nim new file mode 100644 index 000000000..f64adb308 --- /dev/null +++ b/tests/stdlib/concurrency/tatomics_size.nim @@ -0,0 +1,21 @@ +discard """ + # test C with -d:nimUseCppAtomics as well to check nothing breaks + matrix: "--mm:refc; --mm:orc; --mm:refc -d:nimUseCppAtomics; --mm:orc -d:nimUseCppAtomics" + targets: "c cpp" +""" +import std/atomics +import std/assertions + +block testSize: # issue 12726 + type + Node = ptr object + # works + next: Atomic[pointer] + f:AtomicFlag + MyChannel = object + # type not defined completely + back: Atomic[ptr int] + f: AtomicFlag + static: + doAssert sizeof(Node) == sizeof(pointer) + doAssert sizeof(MyChannel) == sizeof(pointer) * 2 diff --git a/tests/stdlib/config.nims b/tests/stdlib/config.nims new file mode 100644 index 000000000..dffae2812 --- /dev/null +++ b/tests/stdlib/config.nims @@ -0,0 +1,5 @@ +switch("styleCheck", "usages") +switch("styleCheck", "error") +switch("define", "nimPreviewSlimSystem") +switch("define", "nimPreviewCstringConversion") +switch("define", "nimPreviewProcConversion") diff --git a/tests/stdlib/mgenast.nim b/tests/stdlib/mgenast.nim new file mode 100644 index 000000000..b0904847e --- /dev/null +++ b/tests/stdlib/mgenast.nim @@ -0,0 +1,57 @@ +import std/genasts +import std/macros + +# Using a enum instead of, say, int, to make apparent potential bugs related to +# forgetting converting to NimNode via newLit, see bug #9607 + +type Foo* = enum kfoo0, kfoo1, kfoo2, kfoo3, kfoo4 + +proc myLocalPriv(): auto = kfoo1 +proc myLocalPriv2(): auto = kfoo1 +macro bindme2*(): untyped = + genAst: myLocalPriv() +macro bindme3*(): untyped = + ## myLocalPriv must be captured explicitly + genAstOpt({kDirtyTemplate}, myLocalPriv): myLocalPriv() + +macro bindme4*(): untyped = + ## calling this won't compile because `myLocalPriv` isn't captured + genAstOpt({kDirtyTemplate}): myLocalPriv() + +macro bindme5UseExpose*(): untyped = + genAst: myLocalPriv2() + +macro bindme5UseExposeFalse*(): untyped = + genAstOpt({kDirtyTemplate}): myLocalPriv2() + +# example from bug #7889 +from std/streams import newStringStream, readData, writeData + +macro bindme6UseExpose*(): untyped = + genAst: + var tst = "sometext" + var ss = newStringStream("anothertext") + when defined(gcArc) or defined(gcOrc): + prepareMutation(tst) + writeData(ss, tst[0].addr, 2) + discard readData(ss, tst[0].addr, 2) + +macro bindme6UseExposeFalse*(): untyped = + ## with `kDirtyTemplate`, requires passing all referenced symbols + ## which can be tedious + genAstOpt({kDirtyTemplate}, newStringStream, writeData, readData): + var tst = "sometext" + var ss = newStringStream("anothertext") + when defined(gcArc) or defined(gcOrc): + prepareMutation(tst) + writeData(ss, tst[0].addr, 2) + discard readData(ss, tst[0].addr, 2) + + +proc locafun1(): auto = "in locafun1" +proc locafun2(): auto = "in locafun2" +# locafun3 in caller scope only +macro mixinExample*(): untyped = + genAst: + mixin locafun1 + (locafun1(), locafun2(), locafun3()) diff --git a/tests/stdlib/mimportutils.nim b/tests/stdlib/mimportutils.nim new file mode 100644 index 000000000..678d9ec02 --- /dev/null +++ b/tests/stdlib/mimportutils.nim @@ -0,0 +1,38 @@ +type + A* = object + a0*: int + ha1: float + B = object + b0*: int + hb1: float + C* = ref object + c0: int + hc1: float + D* = ptr object + d0: int + hd1: float + PA* = ref A + PtA* = ptr A + E*[T] = object + he1: int + FSub[T1, T2] = object + h3: T1 + h4: T2 + F*[T1, T2] = ref FSub[T1, T2] + G*[T] = ref E[T] + H3*[T] = object + h5: T + H2*[T] = H3[T] + H1*[T] = ref H2[T] + H*[T] = H1[T] + + Pity[T] = object + a: T + PityRef*[T] = ref Pity[T] + Hope*[T] = ref object + a: T + +type BAalias* = typeof(B.default) + # typeof is not a transparent abstraction, creates a `tyAlias` + +proc initB*(): B = B() diff --git a/tests/stdlib/mintsets.nim b/tests/stdlib/mintsets.nim new file mode 100644 index 000000000..98786e9ba --- /dev/null +++ b/tests/stdlib/mintsets.nim @@ -0,0 +1,11 @@ +import std/intsets +import std/assertions + +proc test1*[]() = + let a = initIntSet() + doAssert len(a) == 0 + +proc test2*[]() = + var a = initIntSet() + var b = initIntSet() + a.incl b diff --git a/tests/stdlib/nre/captures.nim b/tests/stdlib/nre/captures.nim index bd5e83ecc..acc141baf 100644 --- a/tests/stdlib/nre/captures.nim +++ b/tests/stdlib/nre/captures.nim @@ -1,12 +1,12 @@ import unittest, optional_nonstrict include nre -suite "captures": - test "map capture names to numbers": +block: # captures + block: # map capture names to numbers check(getNameToNumberTable(re("(?<v1>1(?<v2>2(?<v3>3))(?'v4'4))()")) == { "v1" : 0, "v2" : 1, "v3" : 2, "v4" : 3 }.toTable()) - test "capture bounds are correct": + block: # capture bounds are correct let ex1 = re("([0-9])") check("1 23".find(ex1).matchBounds == 0 .. 0) check("1 23".find(ex1).captureBounds[0] == 0 .. 0) @@ -20,7 +20,7 @@ suite "captures": let ex3 = re("([0-9]+)") check("824".find(ex3).captureBounds[0] == 0 .. 2) - test "named captures": + block: # named captures let ex1 = "foobar".find(re("(?<foo>foo)(?<bar>bar)")) check(ex1.captures["foo"] == "foo") check(ex1.captures["bar"] == "bar") @@ -32,7 +32,7 @@ suite "captures": expect KeyError: discard ex2.captures["bar"] - test "named capture bounds": + block: # named capture bounds let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) check("foo" in ex1.captureBounds) check(ex1.captureBounds["foo"] == 0..2) @@ -40,12 +40,12 @@ suite "captures": expect KeyError: discard ex1.captures["bar"] - test "capture count": + block: # capture count let ex1 = re("(?<foo>foo)(?<bar>bar)?") check(ex1.captureCount == 2) check(ex1.captureNameId == {"foo" : 0, "bar" : 1}.toTable()) - test "named capture table": + block: # named capture table let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) check(ex1.captures.toTable == {"foo" : "foo"}.toTable()) check(ex1.captureBounds.toTable == {"foo" : 0..2}.toTable()) @@ -53,7 +53,7 @@ suite "captures": let ex2 = "foobar".find(re("(?<foo>foo)(?<bar>bar)?")) check(ex2.captures.toTable == {"foo" : "foo", "bar" : "bar"}.toTable()) - test "capture sequence": + block: # capture sequence let ex1 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) check(ex1.captures.toSeq == @[some("foo"), none(string)]) check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int])]) diff --git a/tests/stdlib/nre/escape.nim b/tests/stdlib/nre/escape.nim index db5e8a001..5e7dc0c0e 100644 --- a/tests/stdlib/nre/escape.nim +++ b/tests/stdlib/nre/escape.nim @@ -1,7 +1,7 @@ import nre, unittest -suite "escape strings": - test "escape strings": +block: # escape strings + block: # escape strings check("123".escapeRe() == "123") check("[]".escapeRe() == r"\[\]") check("()".escapeRe() == r"\(\)") diff --git a/tests/stdlib/nre/find.nim b/tests/stdlib/nre/find.nim index caa953ff4..7e7555d73 100644 --- a/tests/stdlib/nre/find.nim +++ b/tests/stdlib/nre/find.nim @@ -3,23 +3,23 @@ import nre except toSeq import optional_nonstrict import times, strutils -suite "find": - test "find text": +block: # find + block: # find text check("3213a".find(re"[a-z]").match == "a") check(toSeq(findIter("1 2 3 4 5 6 7 8 ", re" ")).map( proc (a: RegexMatch): string = a.match ) == @[" ", " ", " ", " ", " ", " ", " ", " "]) - test "find bounds": + block: # find bounds check(toSeq(findIter("1 2 3 4 5 ", re" ")).map( proc (a: RegexMatch): Slice[int] = a.matchBounds ) == @[1..1, 3..3, 5..5, 7..7, 9..9]) - test "overlapping find": + block: # overlapping find check("222".findAll(re"22") == @["22"]) check("2222".findAll(re"22") == @["22", "22"]) - test "len 0 find": + block: # len 0 find check("".findAll(re"\ ") == newSeq[string]()) check("".findAll(re"") == @[""]) check("abc".findAll(re"") == @["", "", "", ""]) @@ -27,7 +27,7 @@ suite "find": check("word\r\lword".findAll(re"(*ANYCRLF)(?m)$") == @["", ""]) check("слово слово".findAll(re"(*U)\b") == @["", "", "", ""]) - test "bail early": + block: # bail early ## we expect nothing to be found and we should be bailing out early which means that ## the timing difference between searching in small and large data should be well ## within a tolerance margin diff --git a/tests/stdlib/nre/init.nim b/tests/stdlib/nre/init.nim index 26e668104..f0c8e0a00 100644 --- a/tests/stdlib/nre/init.nim +++ b/tests/stdlib/nre/init.nim @@ -1,12 +1,12 @@ import unittest include nre -suite "Test NRE initialization": - test "correct initialization": +block: # Test NRE initialization + block: # correct initialization check(re("[0-9]+") != nil) check(re("(?i)[0-9]+") != nil) - test "options": + block: # options check(extractOptions("(*NEVER_UTF)") == ("", pcre.NEVER_UTF, true)) check(extractOptions("(*UTF8)(*ANCHORED)(*UCP)z") == @@ -19,14 +19,14 @@ suite "Test NRE initialization": check(extractOptions("(*LIMIT_MATCH=6)(*ANCHORED)z") == ("(*LIMIT_MATCH=6)z", pcre.ANCHORED, true)) - test "incorrect options": + block: # incorrect options for s in ["CR", "(CR", "(*CR", "(*abc)", "(*abc)CR", "(?i)", "(*LIMIT_MATCH=5", "(*NO_AUTO_POSSESS=5)"]: let ss = s & "(*NEVER_UTF)" check(extractOptions(ss) == (ss, 0, true)) - test "invalid regex": + block: # invalid regex expect(SyntaxError): discard re("[0-9") try: discard re("[0-9") diff --git a/tests/stdlib/nre/match.nim b/tests/stdlib/nre/match.nim index 06b69fd04..7e09a4b2f 100644 --- a/tests/stdlib/nre/match.nim +++ b/tests/stdlib/nre/match.nim @@ -1,12 +1,12 @@ include nre, unittest, optional_nonstrict -suite "match": - test "upper bound must be inclusive": +block: # match + block: # upper bound must be inclusive check("abc".match(re"abc", endpos = -1) == none(RegexMatch)) check("abc".match(re"abc", endpos = 1) == none(RegexMatch)) check("abc".match(re"abc", endpos = 2) != none(RegexMatch)) - test "match examples": + block: # match examples check("abc".match(re"(\w)").captures[0] == "a") check("abc".match(re"(?<letter>\w)").captures["letter"] == "a") check("abc".match(re"(\w)\w").captures[-1] == "ab") @@ -14,5 +14,5 @@ suite "match": check("abc".match(re"").captureBounds[-1] == 0 .. -1) check("abc".match(re"abc").captureBounds[-1] == 0 .. 2) - test "match test cases": + block: # match test cases check("123".match(re"").matchBounds == 0 .. -1) diff --git a/tests/stdlib/nre/misc.nim b/tests/stdlib/nre/misc.nim index f4a88b639..b7df08ee9 100644 --- a/tests/stdlib/nre/misc.nim +++ b/tests/stdlib/nre/misc.nim @@ -1,13 +1,13 @@ import unittest, nre, strutils, optional_nonstrict -suite "Misc tests": - test "unicode": +block: # Misc tests + block: # unicode check("".find(re"(*UTF8)").match == "") check("перевірка".replace(re"(*U)\w", "") == "") - test "empty or non-empty match": - check("abc".findall(re"|.").join(":") == ":a::b::c:") - check("abc".findall(re".|").join(":") == "a:b:c:") + block: # empty or non-empty match + check("abc".findAll(re"|.").join(":") == ":a::b::c:") + check("abc".findAll(re".|").join(":") == "a:b:c:") check("abc".replace(re"|.", "x") == "xxxxxxx") check("abc".replace(re".|", "x") == "xxxx") diff --git a/tests/stdlib/nre/replace.nim b/tests/stdlib/nre/replace.nim index 6f3436410..5cf659f21 100644 --- a/tests/stdlib/nre/replace.nim +++ b/tests/stdlib/nre/replace.nim @@ -1,13 +1,13 @@ include nre import unittest -suite "replace": - test "replace with 0-length strings": +block: # replace + block: # replace with 0-length strings check("".replace(re"1", proc (v: RegexMatch): string = "1") == "") check(" ".replace(re"", proc (v: RegexMatch): string = "1") == "1 1") check("".replace(re"", proc (v: RegexMatch): string = "1") == "1") - test "regular replace": + block: # regular replace check("123".replace(re"\d", "foo") == "foofoofoo") check("123".replace(re"(\d)", "$1$1") == "112233") check("123".replace(re"(\d)(\d)", "$1$2") == "123") @@ -15,7 +15,7 @@ suite "replace": check("123".replace(re"(?<foo>\d)(\d)", "$foo$#$#") == "1123") check("123".replace(re"(?<foo>\d)(\d)", "${foo}$#$#") == "1123") - test "replacing missing captures should throw instead of segfaulting": + block: # replacing missing captures should throw instead of segfaulting expect IndexDefect: discard "ab".replace(re"(a)|(b)", "$1$2") expect IndexDefect: discard "b".replace(re"(a)?(b)", "$1$2") expect KeyError: discard "b".replace(re"(a)?", "${foo}") diff --git a/tests/stdlib/nre/split.nim b/tests/stdlib/nre/split.nim index 9d57ea7d8..3cd57bb82 100644 --- a/tests/stdlib/nre/split.nim +++ b/tests/stdlib/nre/split.nim @@ -1,8 +1,8 @@ import unittest, strutils include nre -suite "string splitting": - test "splitting strings": +block: # string splitting + block: # splitting strings check("1 2 3 4 5 6 ".split(re" ") == @["1", "2", "3", "4", "5", "6", ""]) check("1 2 ".split(re(" ")) == @["1", "", "2", "", ""]) check("1 2".split(re(" ")) == @["1", "2"]) @@ -10,22 +10,22 @@ suite "string splitting": check("".split(re"foo") == @[""]) check("9".split(re"\son\s") == @["9"]) - test "captured patterns": + block: # captured patterns check("12".split(re"(\d)") == @["", "1", "", "2", ""]) - test "maxsplit": + block: # maxsplit check("123".split(re"", maxsplit = 2) == @["1", "23"]) check("123".split(re"", maxsplit = 1) == @["123"]) check("123".split(re"", maxsplit = -1) == @["1", "2", "3"]) - test "split with 0-length match": + block: # split with 0-length match check("12345".split(re("")) == @["1", "2", "3", "4", "5"]) check("".split(re"") == newSeq[string]()) check("word word".split(re"\b") == @["word", " ", "word"]) check("word\r\lword".split(re"(*ANYCRLF)(?m)$") == @["word", "\r\lword"]) check("слово слово".split(re"(*U)(\b)") == @["", "слово", "", " ", "", "слово", ""]) - test "perl split tests": + block: # perl split tests check("forty-two" .split(re"") .join(",") == "f,o,r,t,y,-,t,w,o") check("forty-two" .split(re"", 3) .join(",") == "f,o,rty-two") check("split this string" .split(re" ") .join(",") == "split,this,string") @@ -47,7 +47,7 @@ suite "string splitting": check("" .split(re"") .len == 0) check(":" .split(re"") .len == 1) - test "start position": + block: # start position check("abc".split(re"", start = 1) == @["b", "c"]) check("abc".split(re"", start = 2) == @["c"]) check("abc".split(re"", start = 3) == newSeq[string]()) diff --git a/tests/stdlib/t10231.nim b/tests/stdlib/t10231.nim index 2bb64b475..3d09721aa 100644 --- a/tests/stdlib/t10231.nim +++ b/tests/stdlib/t10231.nim @@ -1,10 +1,11 @@ discard """ - target: cpp + targets: "cpp" action: run exitcode: 0 """ import os +import std/assertions # consider moving this inside tosproc (taking care that it's for cpp mode) diff --git a/tests/stdlib/t14139.nim b/tests/stdlib/t14139.nim new file mode 100644 index 000000000..866bdb45f --- /dev/null +++ b/tests/stdlib/t14139.nim @@ -0,0 +1,10 @@ +import std/heapqueue +import std/assertions + +var test_queue : HeapQueue[int] + +test_queue.push(7) +test_queue.push(3) +test_queue.push(9) +let i = test_queue.pushpop(10) +doAssert i == 3 diff --git a/tests/stdlib/t15663.nim b/tests/stdlib/t15663.nim new file mode 100644 index 000000000..8e8bfd9a8 --- /dev/null +++ b/tests/stdlib/t15663.nim @@ -0,0 +1,9 @@ +discard """ + cmd: "nim c --gc:arc $file" + output: "Test" +""" + +import std/widestrs + +let ws = newWideCString("Test") +echo ws diff --git a/tests/stdlib/t19304.nim b/tests/stdlib/t19304.nim new file mode 100644 index 000000000..5e8795ac5 --- /dev/null +++ b/tests/stdlib/t19304.nim @@ -0,0 +1,7 @@ +import times + +type DjangoDateTime* = distinct DateTime + +# proc toTime*(x: DjangoDateTime): Time {.borrow.} # <-- works +proc format*(x: DjangoDateTime, f: TimeFormat, + loc: DateTimeLocale = DefaultLocale): string {.borrow.} diff --git a/tests/stdlib/t20023.nim b/tests/stdlib/t20023.nim new file mode 100644 index 000000000..8f12f8993 --- /dev/null +++ b/tests/stdlib/t20023.nim @@ -0,0 +1,10 @@ +import std/[tables, hashes, assertions] + + +let t = () +var a = toTable({t:t}) +del(a,t) +let b = default(typeof(a)) + +doAssert a==b , "tables are not equal" +doAssert hash(a) == hash(b), "table hashes are not equal" diff --git a/tests/stdlib/t21251.nim b/tests/stdlib/t21251.nim new file mode 100644 index 000000000..4402e9b7e --- /dev/null +++ b/tests/stdlib/t21251.nim @@ -0,0 +1,6 @@ +import std / [tables, sets, sharedtables] + +var shared: SharedTable[int, int] +shared.init + +shared[1] = 1 diff --git a/tests/stdlib/t21406.nim b/tests/stdlib/t21406.nim new file mode 100644 index 000000000..86bf7b0c7 --- /dev/null +++ b/tests/stdlib/t21406.nim @@ -0,0 +1,7 @@ +import std/[times, strformat] +import std/assertions + +let aTime = getTime() +doAssert fmt"{aTime}" == $aTime +let aNow = now() +doAssert fmt"{aNow}" == $aNow diff --git a/tests/stdlib/t21564.nim b/tests/stdlib/t21564.nim new file mode 100644 index 000000000..0a5777d12 --- /dev/null +++ b/tests/stdlib/t21564.nim @@ -0,0 +1,32 @@ +discard """ +targets: "c js" +""" + +import bitops +import std/assertions + +proc main() = + block: # bug #21564 + # tesk `bitops.bitsliced` patch + doAssert(0x17.bitsliced(4..7) == 0x01) + doAssert(0x17.bitsliced(0..3) == 0x07) + + block: + # test in-place `bitops.bitslice` + var t = 0x12F4 + t.bitslice(4..7) + + doAssert(t == 0xF) + + block: + # test `bitops.toMask` patch via bitops.masked + doAssert(0x12FFFF34.masked(8..23) == 0x00FFFF00) + + block: # bug #22687 + var a: uint8 = 0b1111_1111 + doAssert a.bitsliced(4..7).int == 15 + +main() + +static: + main() diff --git a/tests/stdlib/t7686.nim b/tests/stdlib/t7686.nim new file mode 100644 index 000000000..9902cfcb5 --- /dev/null +++ b/tests/stdlib/t7686.nim @@ -0,0 +1,10 @@ +import std/strutils +import std/assertions + +type + MyEnum = enum + A, + a + +doAssert parseEnum[MyEnum]("A") == A +doAssert parseEnum[MyEnum]("a") == a diff --git a/tests/stdlib/t8925.nim b/tests/stdlib/t8925.nim index dbf55fd88..c55e93e73 100644 --- a/tests/stdlib/t8925.nim +++ b/tests/stdlib/t8925.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "type mismatch between pattern '$i' (position: 1) and HourRange var 'hour'" + errormsg: "type mismatch between pattern '$$i' (position: 1) and HourRange var 'hour'" file: "strscans.nim" """ diff --git a/tests/stdlib/t9754.nim b/tests/stdlib/t9754.nim new file mode 100644 index 000000000..971b5a8fb --- /dev/null +++ b/tests/stdlib/t9754.nim @@ -0,0 +1,6 @@ +discard """ + joinable: false +""" + +import tmarshal +import tparsesql \ No newline at end of file diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim new file mode 100644 index 000000000..e2024df0c --- /dev/null +++ b/tests/stdlib/talgorithm.nim @@ -0,0 +1,275 @@ +discard """ + targets: "c js" + matrix: "--mm:refc; --mm:orc" + output:'''@["3", "2", "1"] +''' +""" +#12928,10456 + +import std/[sequtils, algorithm, json, sugar] +import std/assertions + +proc test() = + try: + let info = parseJson(""" + {"a": ["1", "2", "3"]} + """) + let prefixes = info["a"].getElems().mapIt(it.getStr()).sortedByIt(it).reversed() + echo prefixes + except: + discard + +test() + +block: + # Tests for lowerBound + var arr = @[1, 2, 3, 5, 6, 7, 8, 9] + doAssert arr.lowerBound(0) == 0 + doAssert arr.lowerBound(4) == 3 + doAssert arr.lowerBound(5) == 3 + doAssert arr.lowerBound(10) == 8 + arr = @[1, 5, 10] + doAssert arr.lowerBound(4) == 1 + doAssert arr.lowerBound(5) == 1 + doAssert arr.lowerBound(6) == 2 + # Tests for isSorted + var srt1 = [1, 2, 3, 4, 4, 4, 4, 5] + var srt2 = ["iello", "hello"] + var srt3 = [1.0, 1.0, 1.0] + var srt4: seq[int] + doAssert srt1.isSorted(cmp) == true + doAssert srt2.isSorted(cmp) == false + doAssert srt3.isSorted(cmp) == true + doAssert srt4.isSorted(cmp) == true + var srtseq = newSeq[int]() + doAssert srtseq.isSorted(cmp) == true + # Tests for reversed + var arr1 = @[0, 1, 2, 3, 4] + doAssert arr1.reversed() == @[4, 3, 2, 1, 0] + for i in 0 .. high(arr1): + doAssert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)] + doAssert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i] + +block: + var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + let list2 = list.rotatedLeft(1 ..< 9, 3) + let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10] + + doAssert list.rotateLeft(1 ..< 9, 3) == 6 + doAssert list == expected + doAssert list2 == @expected + + var s0, s1, s2, s3, s4, s5 = "xxxabcdefgxxx" + + doAssert s0.rotateLeft(3 ..< 10, 3) == 7 + doAssert s0 == "xxxdefgabcxxx" + doAssert s1.rotateLeft(3 ..< 10, 2) == 8 + doAssert s1 == "xxxcdefgabxxx" + doAssert s2.rotateLeft(3 ..< 10, 4) == 6 + doAssert s2 == "xxxefgabcdxxx" + doAssert s3.rotateLeft(3 ..< 10, -3) == 6 + doAssert s3 == "xxxefgabcdxxx" + doAssert s4.rotateLeft(3 ..< 10, -10) == 6 + doAssert s4 == "xxxefgabcdxxx" + doAssert s5.rotateLeft(3 ..< 10, 11) == 6 + doAssert s5 == "xxxefgabcdxxx" + + block product: + doAssert product(newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input" + doAssert product(@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input" + doAssert product(@[@[1, 2]]) == @[@[1, 2]], "a simple case of one element" + doAssert product(@[@[1, 2], @[3, 4]]) == @[@[2, 4], @[1, 4], @[2, 3], @[1, + 3]], "two elements" + doAssert product(@[@[1, 2], @[3, 4], @[5, 6]]) == @[@[2, 4, 6], @[1, 4, 6], + @[2, 3, 6], @[1, 3, 6], @[2, 4, 5], @[1, 4, 5], @[2, 3, 5], @[1, 3, 5]], "three elements" + doAssert product(@[@[1, 2], @[]]) == newSeq[seq[int]](), "two elements, but one empty" + + block lowerBound: + doAssert lowerBound([1, 2, 4], 3, system.cmp[int]) == 2 + doAssert lowerBound([1, 2, 2, 3], 4, system.cmp[int]) == 4 + doAssert lowerBound([1, 2, 3, 10], 11) == 4 + + block upperBound: + doAssert upperBound([1, 2, 4], 3, system.cmp[int]) == 2 + doAssert upperBound([1, 2, 2, 3], 3, system.cmp[int]) == 4 + doAssert upperBound([1, 2, 3, 5], 3) == 3 + + block fillEmptySeq: + var s = newSeq[int]() + s.fill(0) + + block testBinarySearch: + var noData: seq[int] + doAssert binarySearch(noData, 7) == -1 + let oneData = @[1] + doAssert binarySearch(oneData, 1) == 0 + doAssert binarySearch(oneData, 7) == -1 + let someData = @[1, 3, 4, 7] + doAssert binarySearch(someData, 1) == 0 + doAssert binarySearch(someData, 7) == 3 + doAssert binarySearch(someData, -1) == -1 + doAssert binarySearch(someData, 5) == -1 + doAssert binarySearch(someData, 13) == -1 + let moreData = @[1, 3, 5, 7, 4711] + doAssert binarySearch(moreData, -1) == -1 + doAssert binarySearch(moreData, 1) == 0 + doAssert binarySearch(moreData, 5) == 2 + doAssert binarySearch(moreData, 6) == -1 + doAssert binarySearch(moreData, 4711) == 4 + doAssert binarySearch(moreData, 4712) == -1 + +# merge +proc main() = + block: + var x = @[1, 7, 8, 11, 21, 33, 45, 99] + var y = @[6, 7, 9, 12, 57, 66] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged == sorted(x & y) + + block: + var x = @[111, 88, 76, 56, 45, 31, 22, 19, 11, 3] + var y = @[99, 85, 83, 82, 69, 64, 48, 42, 33, 31, 26, 13] + + var merged: seq[int] + merged.merge(x, y, (x, y) => -system.cmp(x, y)) + doAssert merged.isSorted((x, y) => -system.cmp(x, y)) + doAssert merged == sorted(x & y, SortOrder.Descending) + + block: + var x: seq[int] = @[] + var y = @[1] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged.isSorted(SortOrder.Descending) + doAssert merged == @[1] + + block: + var x = [1, 3, 5, 5, 7] + var y: seq[int] = @[] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged == @x + + block: + var x = [1, 3, 5, 5, 7] + var y: seq[int] = @[] + + var merged: seq[int] = @[1, 2, 3, 5, 6, 56, 99, 2, 34] + merged.merge(x, y) + doAssert merged == @[1, 2, 3, 5, 6, 56, 99, 2, 34, 1, 3, 5, 5, 7] + + + block: + var x: array[0, int] + var y = [1, 4, 6, 7, 9] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged == @y + + block: + var x: array[0, int] + var y: array[0, int] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged.len == 0 + + block: + var x: array[0, int] + var y: array[0, int] + + var merged: seq[int] = @[99, 99, 99] + merged.setLen(0) + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged.len == 0 + + block: + var x: seq[int] + var y: seq[int] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged.len == 0 + + block: + type + Record = object + id: int + + proc r(id: int): Record = + Record(id: id) + + proc cmp(x, y: Record): int = + if x.id == y.id: return 0 + if x.id < y.id: return -1 + result = 1 + + var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)] + var y = @[r(4), r(7), r(12), r(13), r(77), r(99)] + + var merged: seq[Record] = @[] + merged.merge(x, y, cmp) + doAssert merged.isSorted(cmp) + doAssert merged.len == 12 + + block: + type + Record = object + id: int + + proc r(id: int): Record = + Record(id: id) + + proc ascendingCmp(x, y: Record): int = + if x.id == y.id: return 0 + if x.id < y.id: return -1 + result = 1 + + proc descendingCmp(x, y: Record): int = + if x.id == y.id: return 0 + if x.id < y.id: return 1 + result = -1 + + var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)] + var y = @[r(4), r(7), r(12), r(13), r(77), r(99)] + + var merged: seq[Record] + merged.setLen(0) + merged.merge(x, y, ascendingCmp) + doAssert merged.isSorted(ascendingCmp) + doAssert merged == sorted(x & y, ascendingCmp) + + reverse(x) + reverse(y) + + merged.setLen(0) + merged.merge(x, y, descendingCmp) + doAssert merged.isSorted(descendingCmp) + doAssert merged == sorted(x & y, ascendingCmp, SortOrder.Descending) + + reverse(x) + reverse(y) + merged.setLen(0) + merged.merge(x, y, proc (x, y: Record): int = -descendingCmp(x, y)) + doAssert merged.isSorted(proc (x, y: Record): int = -descendingCmp(x, y)) + doAssert merged == sorted(x & y, ascendingCmp) + + + var x: seq[(int, int)] + x.merge([(1,1)], [(1,2)], (a,b) => a[0] - b[0]) + doAssert x == @[(1, 1), (1, 2)] + +static: main() +main() diff --git a/tests/stdlib/tarithmetics.nim b/tests/stdlib/tarithmetics.nim new file mode 100644 index 000000000..0a6dd1fcf --- /dev/null +++ b/tests/stdlib/tarithmetics.nim @@ -0,0 +1,50 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" +""" +import std/assertions +# TODO: in future work move existing arithmetic tests (tests/arithm/*) into this file +# FYI https://github.com/nim-lang/Nim/pull/17767 + +template main = + # put all arithmetic tests + + block tshr: + block: # Signed types + let + a1 = -3 + a2 = -2 + b1 = -4'i8 + b2 = 1'i8 + c1 = -5'i16 + c2 = 1'i16 + d1 = -7i32 + d2 = 1'i32 + e1 = -9'i64 + e2 = 1'i64 + doAssert a1 shr a2 == -1 + doAssert b1 shr b2 == -2 + doAssert c1 shr c2 == -3 + doAssert d1 shr d2 == -4 + doAssert e1 shr e2 == -5 + + block: # Unsigned types + let + a1 = 3'u + a2 = 2'u + b1 = 2'u8 + b2 = 1'u8 + c1 = 5'u16 + c2 = 1'u16 + d1 = 6'u32 + d2 = 1'u32 + e1 = 8'u64 + e2 = 1'u64 + doAssert a1 shr a2 == 0 + doAssert b1 shr b2 == 1 + doAssert c1 shr c2 == 2 + doAssert d1 shr d2 == 3 + doAssert e1 shr e2 == 4 + +static: main() +main() diff --git a/tests/stdlib/tasynchttpserver.nim b/tests/stdlib/tasynchttpserver.nim index 77ad7a071..5a7e2da40 100644 --- a/tests/stdlib/tasynchttpserver.nim +++ b/tests/stdlib/tasynchttpserver.nim @@ -7,6 +7,7 @@ discard """ import strutils from net import TimeoutError +import std/assertions import httpclient, asynchttpserver, asyncdispatch, asyncfutures @@ -39,7 +40,7 @@ proc test200() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == Http200) + doAssert(response.status == $Http200) doAssert(body == "Hello World, 200") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "16") @@ -60,7 +61,7 @@ proc test404() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == Http404) + doAssert(response.status == $Http404) doAssert(body == "Hello World, 404") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "16") @@ -81,7 +82,7 @@ proc testCustomEmptyHeaders() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == Http200) + doAssert(response.status == $Http200) doAssert(body == "Hello World, 200") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "16") @@ -104,16 +105,17 @@ proc testCustomContentLength() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == Http200) + doAssert(response.status == $Http200) doAssert(body == "") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "0") + doAssert contentLength(response) == 0 # bug #22778 runTest(handler, request, test) -waitfor(test200()) -waitfor(test404()) -waitfor(testCustomEmptyHeaders()) -waitfor(testCustomContentLength()) +waitFor(test200()) +waitFor(test404()) +waitFor(testCustomEmptyHeaders()) +waitFor(testCustomContentLength()) echo "OK" diff --git a/tests/stdlib/tasynchttpserver_transferencoding.nim b/tests/stdlib/tasynchttpserver_transferencoding.nim new file mode 100644 index 000000000..886ba0f33 --- /dev/null +++ b/tests/stdlib/tasynchttpserver_transferencoding.nim @@ -0,0 +1,91 @@ +discard """ + matrix: "--mm:arc; --mm:arc -d:danger; --mm:refc" + disabled: "freebsd" +""" + +import httpclient, asynchttpserver, asyncdispatch, asyncfutures +import net + +import std/asyncnet +import std/nativesockets +import std/assertions + +const postBegin = """ +POST / HTTP/1.1 +Transfer-Encoding:chunked + +""" + +template genTest(input, expected: string) = + proc handler(request: Request, future: Future[bool]) {.async, gcsafe.} = + doAssert(request.body == expected) + doAssert(request.headers.hasKey("Transfer-Encoding")) + doAssert(not request.headers.hasKey("Content-Length")) + future.complete(true) + await request.respond(Http200, "Good") + + proc sendData(data: string, port: Port) {.async.} = + var socket = newSocket() + defer: socket.close() + + socket.connect("127.0.0.1", port) + socket.send(data) + + proc runTest(): Future[bool] {.async.} = + var handlerFuture = newFuture[bool]("runTest") + let data = postBegin & input + let server = newAsyncHttpServer() + server.listen(Port(0)) + + proc wrapper(request: Request): Future[void] {.gcsafe, closure.} = + handler(request, handlerFuture) + + asyncCheck sendData(data, server.getPort) + asyncCheck server.acceptRequest(wrapper) + doAssert await handlerFuture + + server.close() + return true + + doAssert waitFor runTest() + +block: + const expected = "hello=world" + const input = ("b\r\n" & + "hello=world\r\n" & + "0\r\n" & + "\r\n") + genTest(input, expected) +block: + const expected = "hello encoding" + const input = ("e\r\n" & + "hello encoding\r\n" & + "0\r\n" & + "\r\n") + genTest(input, expected) +block: + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding + const expected = "MozillaDeveloperNetwork" + const input = ("7\r\n" & + "Mozilla\r\n" & + "9\r\n" & + "Developer\r\n" & + "7\r\n" & + "Network\r\n" & + "0\r\n" & + "\r\n") + genTest(input, expected) +block: + # https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example + const expected = "Wikipedia in \r\n\r\nchunks." + const input = ("4\r\n" & + "Wiki\r\n" & + "6\r\n" & + "pedia \r\n" & + "E\r\n" & + "in \r\n" & + "\r\n" & + "chunks.\r\n" & + "0\r\n" & + "\r\n") + genTest(input, expected) diff --git a/tests/stdlib/tbase64.nim b/tests/stdlib/tbase64.nim index 19b126437..c3bfb818e 100644 --- a/tests/stdlib/tbase64.nim +++ b/tests/stdlib/tbase64.nim @@ -1,14 +1,12 @@ discard """ - output: '''YQ==''' - nimout: '''YQ==''' + matrix: "--mm:refc; --mm:orc" + targets: "c js" """ -import base64 +import std/assertions +import std/base64 -import base64 -static: echo encode("a") -echo encode("a") - -proc main() = +template main() = + doAssert encode("a") == "YQ==" doAssert encode("Hello World") == "SGVsbG8gV29ybGQ=" doAssert encode("leasure.") == "bGVhc3VyZS4=" doAssert encode("easure.") == "ZWFzdXJlLg==" @@ -20,6 +18,8 @@ proc main() = doAssert encode("") == "" doAssert decode("") == "" + doAssert decode(" ") == "" + const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++" const testInputExpands = "++++++++++++++++++++++++++++++" const longText = """Man is distinguished, not only by his reason, but by this @@ -30,19 +30,15 @@ proc main() = const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.", "asure.", longText, testInputExpandsTo76, testInputExpands] - doAssert encodeMIME("foobarbaz", lineLen=4) == "Zm9v\r\nYmFy\r\nYmF6" + doAssert encodeMime("foobarbaz", lineLen=4) == "Zm9v\r\nYmFy\r\nYmF6" doAssert decode("Zm9v\r\nYmFy\r\nYmF6") == "foobarbaz" for t in items(tests): doAssert decode(encode(t)) == t - doAssert decode(encodeMIME(t, lineLen=40)) == t - doAssert decode(encodeMIME(t, lineLen=76)) == t + doAssert decode(encodeMime(t, lineLen=40)) == t + doAssert decode(encodeMime(t, lineLen=76)) == t - const invalid = "SGVsbG\x008gV29ybGQ=" - try: - doAssert decode(invalid) == "will throw error" - except ValueError: - discard + doAssertRaises(ValueError): discard decode("SGVsbG\x008gV29ybGQ=") block base64urlSafe: doAssert encode("c\xf7>", safe = true) == "Y_c-" @@ -59,4 +55,9 @@ proc main() = doAssert encode("", safe = true) == "" doAssert encode("the quick brown dog jumps over the lazy fox", safe = true) == "dGhlIHF1aWNrIGJyb3duIGRvZyBqdW1wcyBvdmVyIHRoZSBsYXp5IGZveA==" +func mainNoSideEffects() = main() + +static: main() main() +static: mainNoSideEffects() +mainNoSideEffects() diff --git a/tests/stdlib/tbitops.nim b/tests/stdlib/tbitops.nim index 2baa1c718..3ecab2c64 100644 --- a/tests/stdlib/tbitops.nim +++ b/tests/stdlib/tbitops.nim @@ -1,13 +1,14 @@ discard """ nimout: "OK" + matrix: "--mm:refc; --mm:orc" output: ''' OK -OK ''' """ import bitops +import std/assertions -proc main1() = +proc main() = const U8 = 0b0011_0010'u8 const I8 = 0b0011_0010'i8 const U16 = 0b00100111_00101000'u16 @@ -21,120 +22,120 @@ proc main1() = const U64C = 0b00101010_11110101_10001111_00101000_00000100_00000000_00000100_00000000'u64 const I64C = 0b00101010_11110101_10001111_00101000_00000100_00000000_00000100_00000000'i64 - doAssert( (U8 and U8) == bitand(U8,U8) ) - doAssert( (I8 and I8) == bitand(I8,I8) ) - doAssert( (U16 and U16) == bitand(U16,U16) ) - doAssert( (I16 and I16) == bitand(I16,I16) ) - doAssert( (U32 and U32) == bitand(U32,U32) ) - doAssert( (I32 and I32) == bitand(I32,I32) ) - doAssert( (U64A and U64B) == bitand(U64A,U64B) ) - doAssert( (I64A and I64B) == bitand(I64A,I64B) ) - doAssert( (U64A and U64B and U64C) == bitand(U64A,U64B,U64C) ) - doAssert( (I64A and I64B and I64C) == bitand(I64A,I64B,I64C) ) - - doAssert( (U8 or U8) == bitor(U8,U8) ) - doAssert( (I8 or I8) == bitor(I8,I8) ) - doAssert( (U16 or U16) == bitor(U16,U16) ) - doAssert( (I16 or I16) == bitor(I16,I16) ) - doAssert( (U32 or U32) == bitor(U32,U32) ) - doAssert( (I32 or I32) == bitor(I32,I32) ) - doAssert( (U64A or U64B) == bitor(U64A,U64B) ) - doAssert( (I64A or I64B) == bitor(I64A,I64B) ) - doAssert( (U64A or U64B or U64C) == bitor(U64A,U64B,U64C) ) - doAssert( (I64A or I64B or I64C) == bitor(I64A,I64B,I64C) ) - - doAssert( (U8 xor U8) == bitxor(U8,U8) ) - doAssert( (I8 xor I8) == bitxor(I8,I8) ) - doAssert( (U16 xor U16) == bitxor(U16,U16) ) - doAssert( (I16 xor I16) == bitxor(I16,I16) ) - doAssert( (U32 xor U32) == bitxor(U32,U32) ) - doAssert( (I32 xor I32) == bitxor(I32,I32) ) - doAssert( (U64A xor U64B) == bitxor(U64A,U64B) ) - doAssert( (I64A xor I64B) == bitxor(I64A,I64B) ) - doAssert( (U64A xor U64B xor U64C) == bitxor(U64A,U64B,U64C) ) - doAssert( (I64A xor I64B xor I64C) == bitxor(I64A,I64B,I64C) ) - - doAssert( not(U8) == bitnot(U8) ) - doAssert( not(I8) == bitnot(I8) ) - doAssert( not(U16) == bitnot(U16) ) - doAssert( not(I16) == bitnot(I16) ) - doAssert( not(U32) == bitnot(U32) ) - doAssert( not(I32) == bitnot(I32) ) - doAssert( not(U64A) == bitnot(U64A) ) - doAssert( not(I64A) == bitnot(I64A) ) - - doAssert( U64A.fastLog2 == 62) - doAssert( I64A.fastLog2 == 62) - doAssert( U64A.countLeadingZeroBits == 1) - doAssert( I64A.countLeadingZeroBits == 1) - doAssert( U64A.countTrailingZeroBits == 0) - doAssert( I64A.countTrailingZeroBits == 0) - doAssert( U64A.firstSetBit == 1) - doAssert( I64A.firstSetBit == 1) - doAssert( U64A.parityBits == 1) - doAssert( I64A.parityBits == 1) - doAssert( U64A.countSetBits == 29) - doAssert( I64A.countSetBits == 29) - doAssert( U64A.rotateLeftBits(37) == 0b00101001_00001111_01000010_00101000_10000111_11101111_10010001_01010011'u64) - doAssert( U64A.rotateRightBits(37) == 0b01010100_11001010_01000011_11010000_10001010_00100001_11111011_11100100'u64) - - doAssert( U64B.firstSetBit == 36) - doAssert( I64B.firstSetBit == 36) - - doAssert( U32.fastLog2 == 31) - doAssert( I32.fastLog2 == 31) - doAssert( U32.countLeadingZeroBits == 0) - doAssert( I32.countLeadingZeroBits == 0) - doAssert( U32.countTrailingZeroBits == 4) - doAssert( I32.countTrailingZeroBits == 4) - doAssert( U32.firstSetBit == 5) - doAssert( I32.firstSetBit == 5) - doAssert( U32.parityBits == 0) - doAssert( I32.parityBits == 0) - doAssert( U32.countSetBits == 16) - doAssert( I32.countSetBits == 16) - doAssert( U32.rotateLeftBits(21) == 0b01001010_00011010_10110011_10011011'u32) - doAssert( U32.rotateRightBits(21) == 0b11100110_11010010_10000110_10101100'u32) - - doAssert( U16.fastLog2 == 13) - doAssert( I16.fastLog2 == 13) - doAssert( U16.countLeadingZeroBits == 2) - doAssert( I16.countLeadingZeroBits == 2) - doAssert( U16.countTrailingZeroBits == 3) - doAssert( I16.countTrailingZeroBits == 3) - doAssert( U16.firstSetBit == 4) - doAssert( I16.firstSetBit == 4) - doAssert( U16.parityBits == 0) - doAssert( I16.parityBits == 0) - doAssert( U16.countSetBits == 6) - doAssert( I16.countSetBits == 6) - doAssert( U16.rotateLeftBits(12) == 0b10000010_01110010'u16) - doAssert( U16.rotateRightBits(12) == 0b01110010_10000010'u16) - - doAssert( U8.fastLog2 == 5) - doAssert( I8.fastLog2 == 5) - doAssert( U8.countLeadingZeroBits == 2) - doAssert( I8.countLeadingZeroBits == 2) - doAssert( U8.countTrailingZeroBits == 1) - doAssert( I8.countTrailingZeroBits == 1) - doAssert( U8.firstSetBit == 2) - doAssert( I8.firstSetBit == 2) - doAssert( U8.parityBits == 1) - doAssert( I8.parityBits == 1) - doAssert( U8.countSetBits == 3) - doAssert( I8.countSetBits == 3) - doAssert( U8.rotateLeftBits(3) == 0b10010001'u8) - doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8) + doAssert (U8 and U8) == bitand(U8,U8) + doAssert (I8 and I8) == bitand(I8,I8) + doAssert (U16 and U16) == bitand(U16,U16) + doAssert (I16 and I16) == bitand(I16,I16) + doAssert (U32 and U32) == bitand(U32,U32) + doAssert (I32 and I32) == bitand(I32,I32) + doAssert (U64A and U64B) == bitand(U64A,U64B) + doAssert (I64A and I64B) == bitand(I64A,I64B) + doAssert (U64A and U64B and U64C) == bitand(U64A,U64B,U64C) + doAssert (I64A and I64B and I64C) == bitand(I64A,I64B,I64C) + + doAssert (U8 or U8) == bitor(U8,U8) + doAssert (I8 or I8) == bitor(I8,I8) + doAssert (U16 or U16) == bitor(U16,U16) + doAssert (I16 or I16) == bitor(I16,I16) + doAssert (U32 or U32) == bitor(U32,U32) + doAssert (I32 or I32) == bitor(I32,I32) + doAssert (U64A or U64B) == bitor(U64A,U64B) + doAssert (I64A or I64B) == bitor(I64A,I64B) + doAssert (U64A or U64B or U64C) == bitor(U64A,U64B,U64C) + doAssert (I64A or I64B or I64C) == bitor(I64A,I64B,I64C) + + doAssert (U8 xor U8) == bitxor(U8,U8) + doAssert (I8 xor I8) == bitxor(I8,I8) + doAssert (U16 xor U16) == bitxor(U16,U16) + doAssert (I16 xor I16) == bitxor(I16,I16) + doAssert (U32 xor U32) == bitxor(U32,U32) + doAssert (I32 xor I32) == bitxor(I32,I32) + doAssert (U64A xor U64B) == bitxor(U64A,U64B) + doAssert (I64A xor I64B) == bitxor(I64A,I64B) + doAssert (U64A xor U64B xor U64C) == bitxor(U64A,U64B,U64C) + doAssert (I64A xor I64B xor I64C) == bitxor(I64A,I64B,I64C) + + doAssert not(U8) == bitnot(U8) + doAssert not(I8) == bitnot(I8) + doAssert not(U16) == bitnot(U16) + doAssert not(I16) == bitnot(I16) + doAssert not(U32) == bitnot(U32) + doAssert not(I32) == bitnot(I32) + doAssert not(U64A) == bitnot(U64A) + doAssert not(I64A) == bitnot(I64A) + + doAssert U64A.fastLog2 == 62 + doAssert I64A.fastLog2 == 62 + doAssert U64A.countLeadingZeroBits == 1 + doAssert I64A.countLeadingZeroBits == 1 + doAssert U64A.countTrailingZeroBits == 0 + doAssert I64A.countTrailingZeroBits == 0 + doAssert U64A.firstSetBit == 1 + doAssert I64A.firstSetBit == 1 + doAssert U64A.parityBits == 1 + doAssert I64A.parityBits == 1 + doAssert U64A.countSetBits == 29 + doAssert I64A.countSetBits == 29 + doAssert U64A.rotateLeftBits(37) == 0b00101001_00001111_01000010_00101000_10000111_11101111_10010001_01010011'u64 + doAssert U64A.rotateRightBits(37) == 0b01010100_11001010_01000011_11010000_10001010_00100001_11111011_11100100'u64 + + doAssert U64B.firstSetBit == 36 + doAssert I64B.firstSetBit == 36 + + doAssert U32.fastLog2 == 31 + doAssert I32.fastLog2 == 31 + doAssert U32.countLeadingZeroBits == 0 + doAssert I32.countLeadingZeroBits == 0 + doAssert U32.countTrailingZeroBits == 4 + doAssert I32.countTrailingZeroBits == 4 + doAssert U32.firstSetBit == 5 + doAssert I32.firstSetBit == 5 + doAssert U32.parityBits == 0 + doAssert I32.parityBits == 0 + doAssert U32.countSetBits == 16 + doAssert I32.countSetBits == 16 + doAssert U32.rotateLeftBits(21) == 0b01001010_00011010_10110011_10011011'u32 + doAssert U32.rotateRightBits(21) == 0b11100110_11010010_10000110_10101100'u32 + + doAssert U16.fastLog2 == 13 + doAssert I16.fastLog2 == 13 + doAssert U16.countLeadingZeroBits == 2 + doAssert I16.countLeadingZeroBits == 2 + doAssert U16.countTrailingZeroBits == 3 + doAssert I16.countTrailingZeroBits == 3 + doAssert U16.firstSetBit == 4 + doAssert I16.firstSetBit == 4 + doAssert U16.parityBits == 0 + doAssert I16.parityBits == 0 + doAssert U16.countSetBits == 6 + doAssert I16.countSetBits == 6 + doAssert U16.rotateLeftBits(12) == 0b10000010_01110010'u16 + doAssert U16.rotateRightBits(12) == 0b01110010_10000010'u16 + + doAssert U8.fastLog2 == 5 + doAssert I8.fastLog2 == 5 + doAssert U8.countLeadingZeroBits == 2 + doAssert I8.countLeadingZeroBits == 2 + doAssert U8.countTrailingZeroBits == 1 + doAssert I8.countTrailingZeroBits == 1 + doAssert U8.firstSetBit == 2 + doAssert I8.firstSetBit == 2 + doAssert U8.parityBits == 1 + doAssert I8.parityBits == 1 + doAssert U8.countSetBits == 3 + doAssert I8.countSetBits == 3 + doAssert U8.rotateLeftBits(3) == 0b10010001'u8 + doAssert U8.rotateRightBits(3) == 0b0100_0110'u8 template test_undefined_impl(ffunc: untyped; expected: int; is_static: bool) = - doAssert( ffunc(0'u8) == expected) - doAssert( ffunc(0'i8) == expected) - doAssert( ffunc(0'u16) == expected) - doAssert( ffunc(0'i16) == expected) - doAssert( ffunc(0'u32) == expected) - doAssert( ffunc(0'i32) == expected) - doAssert( ffunc(0'u64) == expected) - doAssert( ffunc(0'i64) == expected) + doAssert ffunc(0'u8) == expected + doAssert ffunc(0'i8) == expected + doAssert ffunc(0'u16) == expected + doAssert ffunc(0'i16) == expected + doAssert ffunc(0'u32) == expected + doAssert ffunc(0'i32) == expected + doAssert ffunc(0'u64) == expected + doAssert ffunc(0'i64) == expected template test_undefined(ffunc: untyped; expected: int) = test_undefined_impl(ffunc, expected, false) @@ -151,106 +152,106 @@ proc main1() = test_undefined(fastLog2, -1) # check for undefined behavior with rotate by zero. - doAssert( U8.rotateLeftBits(0) == U8) - doAssert( U8.rotateRightBits(0) == U8) - doAssert( U16.rotateLeftBits(0) == U16) - doAssert( U16.rotateRightBits(0) == U16) - doAssert( U32.rotateLeftBits(0) == U32) - doAssert( U32.rotateRightBits(0) == U32) - doAssert( U64A.rotateLeftBits(0) == U64A) - doAssert( U64A.rotateRightBits(0) == U64A) + doAssert U8.rotateLeftBits(0) == U8 + doAssert U8.rotateRightBits(0) == U8 + doAssert U16.rotateLeftBits(0) == U16 + doAssert U16.rotateRightBits(0) == U16 + doAssert U32.rotateLeftBits(0) == U32 + doAssert U32.rotateRightBits(0) == U32 + doAssert U64A.rotateLeftBits(0) == U64A + doAssert U64A.rotateRightBits(0) == U64A # check for undefined behavior with rotate by integer width. - doAssert( U8.rotateLeftBits(8) == U8) - doAssert( U8.rotateRightBits(8) == U8) - doAssert( U16.rotateLeftBits(16) == U16) - doAssert( U16.rotateRightBits(16) == U16) - doAssert( U32.rotateLeftBits(32) == U32) - doAssert( U32.rotateRightBits(32) == U32) - doAssert( U64A.rotateLeftBits(64) == U64A) - doAssert( U64A.rotateRightBits(64) == U64A) + doAssert U8.rotateLeftBits(8) == U8 + doAssert U8.rotateRightBits(8) == U8 + doAssert U16.rotateLeftBits(16) == U16 + doAssert U16.rotateRightBits(16) == U16 + doAssert U32.rotateLeftBits(32) == U32 + doAssert U32.rotateRightBits(32) == U32 + doAssert U64A.rotateLeftBits(64) == U64A + doAssert U64A.rotateRightBits(64) == U64A block: # basic mask operations (mutating) var v: uint8 v.setMask(0b1100_0000) v.setMask(0b0000_1100) - doAssert(v == 0b1100_1100) + doAssert v == 0b1100_1100 v.flipMask(0b0101_0101) - doAssert(v == 0b1001_1001) + doAssert v == 0b1001_1001 v.clearMask(0b1000_1000) - doAssert(v == 0b0001_0001) + doAssert v == 0b0001_0001 v.clearMask(0b0001_0001) - doAssert(v == 0b0000_0000) + doAssert v == 0b0000_0000 v.setMask(0b0001_1110) - doAssert(v == 0b0001_1110) + doAssert v == 0b0001_1110 v.mask(0b0101_0100) - doAssert(v == 0b0001_0100) + doAssert v == 0b0001_0100 block: # basic mask operations (non-mutating) let v = 0b1100_0000'u8 - doAssert(v.masked(0b0000_1100) == 0b0000_0000) - doAssert(v.masked(0b1000_1100) == 0b1000_0000) - doAssert(v.setMasked(0b0000_1100) == 0b1100_1100) - doAssert(v.setMasked(0b1000_1110) == 0b1100_1110) - doAssert(v.flipMasked(0b1100_1000) == 0b0000_1000) - doAssert(v.flipMasked(0b0000_1100) == 0b1100_1100) + doAssert v.masked(0b0000_1100) == 0b0000_0000 + doAssert v.masked(0b1000_1100) == 0b1000_0000 + doAssert v.setMasked(0b0000_1100) == 0b1100_1100 + doAssert v.setMasked(0b1000_1110) == 0b1100_1110 + doAssert v.flipMasked(0b1100_1000) == 0b0000_1000 + doAssert v.flipMasked(0b0000_1100) == 0b1100_1100 let t = 0b1100_0110'u8 - doAssert(t.clearMasked(0b0100_1100) == 0b1000_0010) - doAssert(t.clearMasked(0b1100_0000) == 0b0000_0110) + doAssert t.clearMasked(0b0100_1100) == 0b1000_0010 + doAssert t.clearMasked(0b1100_0000) == 0b0000_0110 block: # basic bitslice opeartions let a = 0b1111_1011'u8 - doAssert(a.bitsliced(0 .. 3) == 0b1011) - doAssert(a.bitsliced(2 .. 3) == 0b10) - doAssert(a.bitsliced(4 .. 7) == 0b1111) + doAssert a.bitsliced(0 .. 3) == 0b1011 + doAssert a.bitsliced(2 .. 3) == 0b10 + doAssert a.bitsliced(4 .. 7) == 0b1111 # same thing, but with exclusive ranges. - doAssert(a.bitsliced(0 ..< 4) == 0b1011) - doAssert(a.bitsliced(2 ..< 4) == 0b10) - doAssert(a.bitsliced(4 ..< 8) == 0b1111) + doAssert a.bitsliced(0 ..< 4) == 0b1011 + doAssert a.bitsliced(2 ..< 4) == 0b10 + doAssert a.bitsliced(4 ..< 8) == 0b1111 # mutating var b = 0b1111_1011'u8 b.bitslice(1 .. 3) - doAssert(b == 0b101) + doAssert b == 0b101 # loop test: let c = 0b1111_1111'u8 for i in 0 .. 7: - doAssert(c.bitsliced(i .. 7) == c shr i) + doAssert c.bitsliced(i .. 7) == c shr i block: # bitslice versions of mask operations (mutating) var a = 0b1100_1100'u8 let b = toMask[uint8](2 .. 3) a.mask(b) - doAssert(a == 0b0000_1100) + doAssert a == 0b0000_1100 a.setMask(4 .. 7) - doAssert(a == 0b1111_1100) + doAssert a == 0b1111_1100 a.flipMask(1 .. 3) - doAssert(a == 0b1111_0010) + doAssert a == 0b1111_0010 a.flipMask(2 .. 4) - doAssert(a == 0b1110_1110) + doAssert a == 0b1110_1110 a.clearMask(2 .. 4) - doAssert(a == 0b1110_0010) + doAssert a == 0b1110_0010 a.mask(0 .. 3) - doAssert(a == 0b0000_0010) + doAssert a == 0b0000_0010 # composition of mask from slices: let c = bitor(toMask[uint8](2 .. 3), toMask[uint8](5 .. 7)) - doAssert(c == 0b1110_1100'u8) + doAssert c == 0b1110_1100'u8 block: # bitslice versions of mask operations (non-mutating) let a = 0b1100_1100'u8 - doAssert(a.masked(toMask[uint8](2 .. 3)) == 0b0000_1100) - doAssert(a.masked(2 .. 3) == 0b0000_1100) - doAssert(a.setMasked(0 .. 3) == 0b1100_1111) - doAssert(a.setMasked(3 .. 4) == 0b1101_1100) - doAssert(a.flipMasked(0 .. 3) == 0b1100_0011) - doAssert(a.flipMasked(0 .. 7) == 0b0011_0011) - doAssert(a.flipMasked(2 .. 3) == 0b1100_0000) - doAssert(a.clearMasked(2 .. 3) == 0b1100_0000) - doAssert(a.clearMasked(3 .. 6) == 0b1000_0100) + doAssert a.masked(toMask[uint8](2 .. 3)) == 0b0000_1100 + doAssert a.masked(2 .. 3) == 0b0000_1100 + doAssert a.setMasked(0 .. 3) == 0b1100_1111 + doAssert a.setMasked(3 .. 4) == 0b1101_1100 + doAssert a.flipMasked(0 .. 3) == 0b1100_0011 + doAssert a.flipMasked(0 .. 7) == 0b0011_0011 + doAssert a.flipMasked(2 .. 3) == 0b1100_0000 + doAssert a.clearMasked(2 .. 3) == 0b1100_0000 + doAssert a.clearMasked(3 .. 6) == 0b1000_0100 block: # single bit operations var v: uint8 @@ -264,8 +265,8 @@ proc main1() = doAssert v == 0b1000_0010 v.flipBit(1) doAssert v == 0b1000_0000 - doAssert v.testbit(7) - doAssert not v.testbit(6) + doAssert v.testBit(7) + doAssert not v.testBit(6) block: # multi bit operations var v: uint8 @@ -287,7 +288,7 @@ proc main1() = block: proc testReverseBitsInvo(x: SomeUnsignedInt) = - doAssert(reverseBits(reverseBits(x)) == x) + doAssert reverseBits(reverseBits(x)) == x proc testReverseBitsPerType(x, reversed: uint64) = doAssert reverseBits(x) == reversed @@ -329,6 +330,9 @@ proc main1() = echo "OK" + # bug #7587 + doAssert popcount(0b11111111'i8) == 8 + block: # not ready for vm because exception is compile error try: var v: uint32 @@ -341,172 +345,7 @@ block: # not ready for vm because exception is compile error doAssert false -main1() +main() static: # test everything on vm as well - main1() - - - -proc main2() = - const U8 = 0b0011_0010'u8 - const I8 = 0b0011_0010'i8 - const U16 = 0b00100111_00101000'u16 - const I16 = 0b00100111_00101000'i16 - const U32 = 0b11010101_10011100_11011010_01010000'u32 - const I32 = 0b11010101_10011100_11011010_01010000'i32 - const U64A = 0b01000100_00111111_01111100_10001010_10011001_01001000_01111010_00010001'u64 - const I64A = 0b01000100_00111111_01111100_10001010_10011001_01001000_01111010_00010001'i64 - const U64B = 0b00110010_11011101_10001111_00101000_00000000_00000000_00000000_00000000'u64 - const I64B = 0b00110010_11011101_10001111_00101000_00000000_00000000_00000000_00000000'i64 - - doAssert( U64A.fastLog2 == 62) - doAssert( I64A.fastLog2 == 62) - doAssert( U64A.countLeadingZeroBits == 1) - doAssert( I64A.countLeadingZeroBits == 1) - doAssert( U64A.countTrailingZeroBits == 0) - doAssert( I64A.countTrailingZeroBits == 0) - doAssert( U64A.firstSetBit == 1) - doAssert( I64A.firstSetBit == 1) - doAssert( U64A.parityBits == 1) - doAssert( I64A.parityBits == 1) - doAssert( U64A.countSetBits == 29) - doAssert( I64A.countSetBits == 29) - doAssert( U64A.rotateLeftBits(37) == 0b00101001_00001111_01000010_00101000_10000111_11101111_10010001_01010011'u64) - doAssert( U64A.rotateRightBits(37) == 0b01010100_11001010_01000011_11010000_10001010_00100001_11111011_11100100'u64) - - doAssert( U64B.firstSetBit == 36) - doAssert( I64B.firstSetBit == 36) - - doAssert( U32.fastLog2 == 31) - doAssert( I32.fastLog2 == 31) - doAssert( U32.countLeadingZeroBits == 0) - doAssert( I32.countLeadingZeroBits == 0) - doAssert( U32.countTrailingZeroBits == 4) - doAssert( I32.countTrailingZeroBits == 4) - doAssert( U32.firstSetBit == 5) - doAssert( I32.firstSetBit == 5) - doAssert( U32.parityBits == 0) - doAssert( I32.parityBits == 0) - doAssert( U32.countSetBits == 16) - doAssert( I32.countSetBits == 16) - doAssert( U32.rotateLeftBits(21) == 0b01001010_00011010_10110011_10011011'u32) - doAssert( U32.rotateRightBits(21) == 0b11100110_11010010_10000110_10101100'u32) - - doAssert( U16.fastLog2 == 13) - doAssert( I16.fastLog2 == 13) - doAssert( U16.countLeadingZeroBits == 2) - doAssert( I16.countLeadingZeroBits == 2) - doAssert( U16.countTrailingZeroBits == 3) - doAssert( I16.countTrailingZeroBits == 3) - doAssert( U16.firstSetBit == 4) - doAssert( I16.firstSetBit == 4) - doAssert( U16.parityBits == 0) - doAssert( I16.parityBits == 0) - doAssert( U16.countSetBits == 6) - doAssert( I16.countSetBits == 6) - doAssert( U16.rotateLeftBits(12) == 0b10000010_01110010'u16) - doAssert( U16.rotateRightBits(12) == 0b01110010_10000010'u16) - - doAssert( U8.fastLog2 == 5) - doAssert( I8.fastLog2 == 5) - doAssert( U8.countLeadingZeroBits == 2) - doAssert( I8.countLeadingZeroBits == 2) - doAssert( U8.countTrailingZeroBits == 1) - doAssert( I8.countTrailingZeroBits == 1) - doAssert( U8.firstSetBit == 2) - doAssert( I8.firstSetBit == 2) - doAssert( U8.parityBits == 1) - doAssert( I8.parityBits == 1) - doAssert( U8.countSetBits == 3) - doAssert( I8.countSetBits == 3) - doAssert( U8.rotateLeftBits(3) == 0b10010001'u8) - doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8) - - static : - # test bitopts at compile time with vm - doAssert( U8.fastLog2 == 5) - doAssert( I8.fastLog2 == 5) - doAssert( U8.countLeadingZeroBits == 2) - doAssert( I8.countLeadingZeroBits == 2) - doAssert( U8.countTrailingZeroBits == 1) - doAssert( I8.countTrailingZeroBits == 1) - doAssert( U8.firstSetBit == 2) - doAssert( I8.firstSetBit == 2) - doAssert( U8.parityBits == 1) - doAssert( I8.parityBits == 1) - doAssert( U8.countSetBits == 3) - doAssert( I8.countSetBits == 3) - doAssert( U8.rotateLeftBits(3) == 0b10010001'u8) - doAssert( U8.rotateRightBits(3) == 0b0100_0110'u8) - - - - template test_undefined_impl(ffunc: untyped; expected: int; is_static: bool) = - doAssert( ffunc(0'u8) == expected) - doAssert( ffunc(0'i8) == expected) - doAssert( ffunc(0'u16) == expected) - doAssert( ffunc(0'i16) == expected) - doAssert( ffunc(0'u32) == expected) - doAssert( ffunc(0'i32) == expected) - doAssert( ffunc(0'u64) == expected) - doAssert( ffunc(0'i64) == expected) - - template test_undefined(ffunc: untyped; expected: int) = - test_undefined_impl(ffunc, expected, false) - static: - test_undefined_impl(ffunc, expected, true) - - when defined(noUndefinedBitOpts): - # check for undefined behavior with zero. - test_undefined(countSetBits, 0) - test_undefined(parityBits, 0) - test_undefined(firstSetBit, 0) - test_undefined(countLeadingZeroBits, 0) - test_undefined(countTrailingZeroBits, 0) - test_undefined(fastLog2, -1) - - # check for undefined behavior with rotate by zero. - doAssert( U8.rotateLeftBits(0) == U8) - doAssert( U8.rotateRightBits(0) == U8) - doAssert( U16.rotateLeftBits(0) == U16) - doAssert( U16.rotateRightBits(0) == U16) - doAssert( U32.rotateLeftBits(0) == U32) - doAssert( U32.rotateRightBits(0) == U32) - doAssert( U64A.rotateLeftBits(0) == U64A) - doAssert( U64A.rotateRightBits(0) == U64A) - - # check for undefined behavior with rotate by integer width. - doAssert( U8.rotateLeftBits(8) == U8) - doAssert( U8.rotateRightBits(8) == U8) - doAssert( U16.rotateLeftBits(16) == U16) - doAssert( U16.rotateRightBits(16) == U16) - doAssert( U32.rotateLeftBits(32) == U32) - doAssert( U32.rotateRightBits(32) == U32) - doAssert( U64A.rotateLeftBits(64) == U64A) - doAssert( U64A.rotateRightBits(64) == U64A) - - static: # check for undefined behavior with rotate by zero. - doAssert( U8.rotateLeftBits(0) == U8) - doAssert( U8.rotateRightBits(0) == U8) - doAssert( U16.rotateLeftBits(0) == U16) - doAssert( U16.rotateRightBits(0) == U16) - doAssert( U32.rotateLeftBits(0) == U32) - doAssert( U32.rotateRightBits(0) == U32) - doAssert( U64A.rotateLeftBits(0) == U64A) - doAssert( U64A.rotateRightBits(0) == U64A) - - # check for undefined behavior with rotate by integer width. - doAssert( U8.rotateLeftBits(8) == U8) - doAssert( U8.rotateRightBits(8) == U8) - doAssert( U16.rotateLeftBits(16) == U16) - doAssert( U16.rotateRightBits(16) == U16) - doAssert( U32.rotateLeftBits(32) == U32) - doAssert( U32.rotateRightBits(32) == U32) - doAssert( U64A.rotateLeftBits(64) == U64A) - doAssert( U64A.rotateRightBits(64) == U64A) - - echo "OK" - -main2() - + main() diff --git a/tests/stdlib/tbitops.nim.cfg b/tests/stdlib/tbitops.nim.cfg index f0d7668a7..013e8d38e 100644 --- a/tests/stdlib/tbitops.nim.cfg +++ b/tests/stdlib/tbitops.nim.cfg @@ -1 +1 @@ --d:noUndefinedBitOps +-d:noUndefinedBitOpts diff --git a/tests/stdlib/tbitops_utils.nim b/tests/stdlib/tbitops_utils.nim new file mode 100644 index 000000000..e3f96fecc --- /dev/null +++ b/tests/stdlib/tbitops_utils.nim @@ -0,0 +1,19 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/private/bitops_utils +import std/assertions + +template chk(a, b) = + let a2 = castToUnsigned(a) + doAssert a2 == b + doAssert type(a2) is type(b) + doAssert type(b) is type(a2) + +chk 1'i8, 1'u8 +chk -1'i8, 255'u8 +chk 1'u8, 1'u8 +chk 1'u, 1'u +chk -1, cast[uint](-1) +chk -1'i64, cast[uint64](-1) diff --git a/tests/stdlib/tcasts.nim b/tests/stdlib/tcasts.nim new file mode 100644 index 000000000..e01c7e940 --- /dev/null +++ b/tests/stdlib/tcasts.nim @@ -0,0 +1,26 @@ +import std/[strutils] +import std/[assertions, objectdollar] + +# bug #19101 +type + Small = object + a: int + + Big = object + a, b, c, d: int + +proc main = + var + n = 1'i8 + f = 2.0 + s = Small(a: 1) + b = Big(a: 12345, b: 23456, c: 34567, d: 45678) + + doAssert $cast[int](f).toBin(64) == "0100000000000000000000000000000000000000000000000000000000000000" + f = cast[float](n) + doAssert $cast[int](f).toBin(64) == "0000000000000000000000000000000000000000000000000000000000000001" + + doAssert $b == "(a: 12345, b: 23456, c: 34567, d: 45678)" + b = cast[Big](s) + doAssert $b == "(a: 1, b: 0, c: 0, d: 0)" +main() diff --git a/tests/stdlib/tcgi.nim b/tests/stdlib/tcgi.nim index 7df1b077d..ef39450da 100644 --- a/tests/stdlib/tcgi.nim +++ b/tests/stdlib/tcgi.nim @@ -1,16 +1,15 @@ discard """ - output: ''' -[Suite] Test cgi module -''' + matrix: "--mm:refc; --mm:orc" """ -import unittest -import cgi, strtabs +import std/unittest +import std/[cgi, strtabs, sugar] +import std/assertions -suite "Test cgi module": +block: # Test cgi module const queryString = "foo=bar&фу=бар&checked=✓&list=1,2,3&with_space=text%20with%20space" - test "test query parsing with readData": + block: # test query parsing with readData let parsedQuery = readData(queryString) check parsedQuery["foo"] == "bar" @@ -21,3 +20,12 @@ suite "Test cgi module": expect KeyError: discard parsedQuery["not_existing_key"] + +# bug #15369 +let queryString = "a=1&b=0&c=3&d&e&a=5&a=t%20e%20x%20t&e=http%3A%2F%2Fw3schools.com%2Fmy%20test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab" + +doAssert collect(for pair in decodeData(queryString): pair) == + @[("a", "1"), ("b", "0"), ("c", "3"), + ("d", ""),("e", ""), ("a", "5"), ("a", "t e x t"), + ("e", "http://w3schools.com/my test.asp?name=ståle&car=saab") +] diff --git a/tests/stdlib/tclosures.nim b/tests/stdlib/tclosures.nim new file mode 100644 index 000000000..84b033fa8 --- /dev/null +++ b/tests/stdlib/tclosures.nim @@ -0,0 +1,47 @@ +discard """ + targets: "c js" +""" + +import std/assertions + +block: # bug #4299 + proc scopeProc() = + proc normalProc() = + discard + + proc genericProc[T]() = + normalProc() + + genericProc[string]() + + scopeProc() + +block: # bug #12492 + proc foo() = + var i = 0 + proc bar() = + inc i + + bar() + doAssert i == 1 + + foo() + static: + foo() + +block: # bug #10849 + type + Generic[T] = ref object + getState: proc(): T + + proc newGeneric[T](): Generic[T] = + var state: T + + proc getState[T](): T = + state + + Generic[T](getState: getState) + + let g = newGeneric[int]() + let state = g.getState() + doAssert state == 0 diff --git a/tests/stdlib/tcmdline.nim b/tests/stdlib/tcmdline.nim new file mode 100644 index 000000000..8b428900b --- /dev/null +++ b/tests/stdlib/tcmdline.nim @@ -0,0 +1,13 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" + joinable: false +""" + +import std/os +import std/assertions + +var params = paramCount() +doAssert params == 0 +doAssert paramStr(0).len > 0 +doAssert commandLineParams().len == 0 diff --git a/tests/stdlib/tcomplex.nim b/tests/stdlib/tcomplex.nim new file mode 100644 index 000000000..ca83314b9 --- /dev/null +++ b/tests/stdlib/tcomplex.nim @@ -0,0 +1,115 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/[complex, math] +import std/assertions + +proc `=~`[T](x, y: Complex[T]): bool = + result = abs(x.re-y.re) < 1e-6 and abs(x.im-y.im) < 1e-6 + +proc `=~`[T](x: Complex[T]; y: T): bool = + result = abs(x.re-y) < 1e-6 and abs(x.im) < 1e-6 + +let + z: Complex64 = complex(0.0, 0.0) + oo: Complex64 = complex(1.0, 1.0) + a: Complex64 = complex(1.0, 2.0) + b: Complex64 = complex(-1.0, -2.0) + m1: Complex64 = complex(-1.0, 0.0) + i: Complex64 = complex(0.0, 1.0) + one: Complex64 = complex(1.0, 0.0) + tt: Complex64 = complex(10.0, 20.0) + ipi: Complex64 = complex(0.0, -PI) + +doAssert(a/2.0 =~ complex(0.5, 1.0)) +doAssert(a == a) +doAssert((a-a) == z) +doAssert((a+b) == z) +doAssert((a+b) =~ 0.0) +doAssert((a/b) == m1) +doAssert((1.0/a) =~ complex(0.2, -0.4)) +doAssert((a*b) == complex(3.0, -4.0)) +doAssert(10.0*a == tt) +doAssert(a*10.0 == tt) +doAssert(tt/10.0 == a) +doAssert(oo+(-1.0) == i) +doAssert( (-1.0)+oo == i) +doAssert(abs(oo) == sqrt(2.0)) +doAssert(conjugate(a) == complex(1.0, -2.0)) +doAssert(sqrt(m1) == i) +doAssert(exp(ipi) =~ m1) + +doAssert(pow(a, b) =~ complex(-3.72999124927876, -1.68815826725068)) +doAssert(pow(z, a) =~ complex(0.0, 0.0)) +doAssert(pow(z, z) =~ complex(1.0, 0.0)) +doAssert(pow(a, one) =~ a) +doAssert(pow(a, m1) =~ complex(0.2, -0.4)) +doAssert(pow(a, 2.0) =~ complex(-3.0, 4.0)) +doAssert(pow(a, 2) =~ complex(-3.0, 4.0)) +doAssert(not(pow(a, 2.0) =~ a)) + +doAssert(ln(a) =~ complex(0.804718956217050, 1.107148717794090)) +doAssert(log10(a) =~ complex(0.349485002168009, 0.480828578784234)) +doAssert(log2(a) =~ complex(1.16096404744368, 1.59727796468811)) + +doAssert(sin(a) =~ complex(3.16577851321617, 1.95960104142161)) +doAssert(cos(a) =~ complex(2.03272300701967, -3.05189779915180)) +doAssert(tan(a) =~ complex(0.0338128260798967, 1.0147936161466335)) +doAssert(cot(a) =~ 1.0 / tan(a)) +doAssert(sec(a) =~ 1.0 / cos(a)) +doAssert(csc(a) =~ 1.0 / sin(a)) +doAssert(arcsin(a) =~ complex(0.427078586392476, 1.528570919480998)) +doAssert(arccos(a) =~ complex(1.14371774040242, -1.52857091948100)) +doAssert(arctan(a) =~ complex(1.338972522294494, 0.402359478108525)) +doAssert(arccot(a) =~ complex(0.2318238045004031, -0.402359478108525)) +doAssert(arcsec(a) =~ complex(1.384478272687081, 0.3965682301123288)) +doAssert(arccsc(a) =~ complex(0.1863180541078155, -0.3965682301123291)) + +doAssert(cosh(a) =~ complex(-0.642148124715520, 1.068607421382778)) +doAssert(sinh(a) =~ complex(-0.489056259041294, 1.403119250622040)) +doAssert(tanh(a) =~ complex(1.1667362572409199, -0.243458201185725)) +doAssert(sech(a) =~ 1.0 / cosh(a)) +doAssert(csch(a) =~ 1.0 / sinh(a)) +doAssert(coth(a) =~ 1.0 / tanh(a)) +doAssert(arccosh(a) =~ complex(1.528570919480998, 1.14371774040242)) +doAssert(arcsinh(a) =~ complex(1.469351744368185, 1.06344002357775)) +doAssert(arctanh(a) =~ complex(0.173286795139986, 1.17809724509617)) +doAssert(arcsech(a) =~ arccosh(1.0/a)) +doAssert(arccsch(a) =~ arcsinh(1.0/a)) +doAssert(arccoth(a) =~ arctanh(1.0/a)) + +doAssert(phase(a) == 1.1071487177940904) +let t = polar(a) +doAssert(rect(t.r, t.phi) =~ a) +doAssert(rect(1.0, 2.0) =~ complex(-0.4161468365471424, 0.9092974268256817)) + +doAssert(almostEqual(a, a + complex(1e-16, 1e-16))) +doAssert(almostEqual(a, a + complex(2e-15, 2e-15), unitsInLastPlace = 5)) + + +let + i64: Complex32 = complex(0.0f, 1.0f) + a64: Complex32 = 2.0f*i64 + 1.0.float32 + b64: Complex32 = complex(-1.0'f32, -2.0'f32) + +doAssert(a64 == a64) +doAssert(a64 == -b64) +doAssert(a64 + b64 =~ 0.0'f32) +doAssert(not(pow(a64, b64) =~ a64)) +doAssert(pow(a64, 0.5f) =~ sqrt(a64)) +doAssert(pow(a64, 2) =~ complex(-3.0'f32, 4.0'f32)) +doAssert(sin(arcsin(b64)) =~ b64) +doAssert(cosh(arccosh(a64)) =~ a64) + +doAssert(phase(a64) - 1.107149f < 1e-6) +let t64 = polar(a64) +doAssert(rect(t64.r, t64.phi) =~ a64) +doAssert(rect(1.0f, 2.0f) =~ complex(-0.4161468f, 0.90929742f)) +doAssert(sizeof(a64) == 8) +doAssert(sizeof(a) == 16) + +doAssert 123.0.im + 456.0 == complex64(456, 123) + +let localA = complex(0.1'f32) +doAssert localA.im is float32 diff --git a/tests/stdlib/tcookies.nim b/tests/stdlib/tcookies.nim new file mode 100644 index 000000000..3ff0f3bae --- /dev/null +++ b/tests/stdlib/tcookies.nim @@ -0,0 +1,22 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + + +import std/[cookies, times, strtabs] +import std/assertions + +let expire = fromUnix(0) + 1.seconds + +let theCookies = [ + setCookie("test", "value", expire), + setCookie("test", "value", expire.local), + setCookie("test", "value", expire.utc) +] +let expected = "Set-Cookie: test=value; Expires=Thu, 01 Jan 1970 00:00:01 GMT" +doAssert theCookies == [expected, expected, expected] + +let table = parseCookies("uid=1; kp=2") +doAssert table["uid"] == "1" +doAssert table["kp"] == "2" diff --git a/tests/stdlib/tcritbits.nim b/tests/stdlib/tcritbits.nim new file mode 100644 index 000000000..e6282f045 --- /dev/null +++ b/tests/stdlib/tcritbits.nim @@ -0,0 +1,89 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + +import std/[sequtils,critbits] +import std/assertions + +template main = + var r: CritBitTree[void] + r.incl "abc" + r.incl "xyz" + r.incl "def" + r.incl "definition" + r.incl "prefix" + r.incl "foo" + + doAssert r.contains"def" + + r.excl "def" + doAssert r.missingOrExcl("foo") == false + doAssert "foo" notin toSeq(r.items) + + doAssert r.missingOrExcl("foo") == true + + doAssert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"] + + doAssert toSeq(r.itemsWithPrefix("de")) == @["definition"] + var c = CritBitTree[int]() + + c.inc("a") + doAssert c["a"] == 1 + + c.inc("a", 4) + doAssert c["a"] == 5 + + c.inc("a", -5) + doAssert c["a"] == 0 + + c.inc("b", 2) + doAssert c["b"] == 2 + + c.inc("c", 3) + doAssert c["c"] == 3 + + c.inc("a", 1) + doAssert c["a"] == 1 + + var cf = CritBitTree[float]() + + cf.incl("a", 1.0) + doAssert cf["a"] == 1.0 + + cf.incl("b", 2.0) + doAssert cf["b"] == 2.0 + + cf.incl("c", 3.0) + doAssert cf["c"] == 3.0 + + doAssert cf.len == 3 + cf.excl("c") + doAssert cf.len == 2 + + var cb: CritBitTree[string] + cb.incl("help", "help") + for k in cb.keysWithPrefix("helpp"): + doAssert false, "there is no prefix helpp" + + block: # bug #14339 + var strings: CritBitTree[int] + discard strings.containsOrIncl("foo", 3) + doAssert strings["foo"] == 3 + + block tcritbitsToString: + block: + var t: CritBitTree[int] + t["a"] = 1 + doAssert $t == """{"a": 1}""" + block: + var t: CritBitTree[string] + t["a"] = "1" + doAssert $t == """{"a": "1"}""" + block: + var t: CritBitTree[char] + t["a"] = '1' + doAssert $t == """{"a": '1'}""" + +main() +static: main() diff --git a/tests/stdlib/tcstring.nim b/tests/stdlib/tcstring.nim new file mode 100644 index 000000000..d7fdd7738 --- /dev/null +++ b/tests/stdlib/tcstring.nim @@ -0,0 +1,93 @@ +discard """ + targets: "c cpp js" + matrix: "--gc:refc; --gc:arc" +""" + +from std/sugar import collect +from stdtest/testutils import whenRuntimeJs, whenVMorJs +import std/assertions + +template testMitems() = + block: + var a = "abc" + var b = a.cstring + let s = collect: + for bi in mitems(b): + if bi == 'b': bi = 'B' + bi + whenRuntimeJs: + discard # xxx mitems should give CT error instead of @['\x00', '\x00', '\x00'] + do: + doAssert s == @['a', 'B', 'c'] + + block: + var a = "abc\0def" + var b = a.cstring + let s = collect: + for bi in mitems(b): + if bi == 'b': bi = 'B' + bi + whenRuntimeJs: + discard # ditto + do: + doAssert s == @['a', 'B', 'c'] + +proc mainProc() = + testMitems() + +template main() = + block: # bug #13859 + let str = "abc".cstring + doAssert len(str).int8 == 3 + doAssert len(str).int16 == 3 + doAssert len(str).int32 == 3 + var str2 = "cde".cstring + doAssert len(str2).int8 == 3 + doAssert len(str2).int16 == 3 + doAssert len(str2).int32 == 3 + + const str3 = "abc".cstring + doAssert len(str3).int32 == 3 + doAssert len("abc".cstring).int16 == 3 + doAssert len("abc".cstring).float32 == 3.0 + + block: # bug #17159 + block: + var a = "abc" + var b = a.cstring + doAssert $(b, ) == """("abc",)""" + let s = collect: + for bi in b: bi + doAssert s == @['a', 'b', 'c'] + + block: + var a = "abc\0def" + var b = a.cstring + let s = collect: + for bi in b: bi + whenRuntimeJs: + doAssert $(b, ) == """("abc\x00def",)""" + doAssert s == @['a', 'b', 'c', '\x00', 'd', 'e', 'f'] + do: + doAssert $(b, ) == """("abc",)""" + doAssert s == @['a', 'b', 'c'] + + block: + when defined(gcArc): # xxx SIGBUS + discard + else: + mainProc() + when false: # xxx bug vm: Error: unhandled exception: 'node' is not accessible using discriminant 'kind' of type 'TFullReg' [FieldDefect] + testMitems() + + block: # bug #13321: [codegen] --gc:arc does not properly emit cstring, results in SIGSEGV + let a = "hello".cstring + doAssert $a == "hello" + doAssert $a[0] == "h" + doAssert $a[4] == "o" + whenVMorJs: discard # xxx this should work in vm, refs https://github.com/timotheecour/Nim/issues/619 + do: + doAssert a[a.len] == '\0' + +static: main() +main() diff --git a/tests/stdlib/tcstrutils.nim b/tests/stdlib/tcstrutils.nim new file mode 100644 index 000000000..e73b2b681 --- /dev/null +++ b/tests/stdlib/tcstrutils.nim @@ -0,0 +1,39 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" +""" + +import std/cstrutils +import std/assertions + +proc main() = + let s = cstring "abcdef" + doAssert s.startsWith("a") + doAssert not s.startsWith("b") + doAssert s.endsWith("f") + doAssert not s.endsWith("a") + doAssert s.startsWith("") + doAssert s.endsWith("") + + let a = cstring "abracadabra" + doAssert a.startsWith("abra") + doAssert not a.startsWith("bra") + doAssert a.endsWith("abra") + doAssert not a.endsWith("dab") + doAssert a.startsWith("") + doAssert a.endsWith("") + + doAssert cmpIgnoreCase(cstring "FooBar", "foobar") == 0 + doAssert cmpIgnoreCase(cstring "bar", "Foo") < 0 + doAssert cmpIgnoreCase(cstring "Foo5", "foo4") > 0 + + doAssert cmpIgnoreStyle(cstring "foo_bar", "FooBar") == 0 + doAssert cmpIgnoreStyle(cstring "foo_bar_5", "FooBar4") > 0 + + doAssert cmpIgnoreCase(cstring "", cstring "") == 0 + doAssert cmpIgnoreCase(cstring "", cstring "Hello") < 0 + doAssert cmpIgnoreCase(cstring "wind", cstring "") > 0 + + +static: main() +main() diff --git a/tests/stdlib/tdb.nim b/tests/stdlib/tdb.nim new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/stdlib/tdb.nim diff --git a/tests/stdlib/tdb.nims b/tests/stdlib/tdb.nims new file mode 100644 index 000000000..d31d0b26f --- /dev/null +++ b/tests/stdlib/tdb.nims @@ -0,0 +1 @@ +--styleCheck:off \ No newline at end of file diff --git a/tests/stdlib/tdb_mysql.nim b/tests/stdlib/tdb_mysql.nim new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/stdlib/tdb_mysql.nim diff --git a/tests/stdlib/tdecls.nim b/tests/stdlib/tdecls.nim index 3567639e0..42dc646f2 100644 --- a/tests/stdlib/tdecls.nim +++ b/tests/stdlib/tdecls.nim @@ -1,6 +1,11 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" +""" +import std/assertions import std/decls -block: +template fun() = var s = @[10,11,12] var a {.byaddr.} = s[0] a+=100 @@ -9,6 +14,8 @@ block: var b {.byaddr.}: int = s[0] doAssert a.addr == b.addr + {.push warningAsError[ImplicitTemplateRedefinition]: on.} + # in the future ImplicitTemplateRedefinition will be an error anyway doAssert not compiles(block: # redeclaration not allowed var foo = 0 @@ -18,6 +25,7 @@ block: # ditto var foo {.byaddr.} = s[0] var foo {.byaddr.} = s[0]) + {.pop.} block: var b {.byaddr.} = s[1] # redeclaration ok in sub scope @@ -34,37 +42,9 @@ block: doAssert compiles(block: var b2 {.byaddr.}: int = s[2]) -## We can define custom pragmas in user code -template byUnsafeAddr(lhs, typ, expr) = - when typ is type(nil): - let tmp = unsafeAddr(expr) - else: - let tmp: ptr typ = unsafeAddr(expr) - template lhs: untyped = tmp[] - -block: - let s = @["foo", "bar"] - let a {.byUnsafeAddr.} = s[0] - doAssert a == "foo" - doAssert a[0].unsafeAddr == s[0][0].unsafeAddr - -block: # nkAccQuoted - # shows using a keyword, which requires nkAccQuoted - template `cast`(lhs, typ, expr) = - when typ is type(nil): - let tmp = unsafeAddr(expr) - else: - let tmp: ptr typ = unsafeAddr(expr) - template lhs: untyped = tmp[] - - block: - let s = @["foo", "bar"] - let a {.`byUnsafeAddr`.} = s[0] - doAssert a == "foo" - doAssert a[0].unsafeAddr == s[0][0].unsafeAddr - - block: - let s = @["foo", "bar"] - let a {.`cast`.} = s[0] - doAssert a == "foo" - doAssert a[0].unsafeAddr == s[0][0].unsafeAddr +proc fun2() = fun() +fun() +fun2() +static: fun2() +when false: # pending bug #13887 + static: fun() diff --git a/tests/stdlib/tdecode_helpers.nim b/tests/stdlib/tdecode_helpers.nim new file mode 100644 index 000000000..1c0735e05 --- /dev/null +++ b/tests/stdlib/tdecode_helpers.nim @@ -0,0 +1,27 @@ +import std/private/decode_helpers +import std/assertions + +block: + var i = 0 + let c = decodePercent("%t9", i) + doAssert (i, c) == (0, '%') + +block: + var i = 0 + let c = decodePercent("19", i) + doAssert (i, c) == (0, '%') + +block: + var i = 0 + let c = decodePercent("%19", i) + doAssert (i, c) == (2, '\x19') + +block: + var i = 0 + let c = decodePercent("%A9", i) + doAssert (i, c) == (2, '\xA9') + +block: + var i = 0 + let c = decodePercent("%Aa", i) + doAssert (i, c) == (2, '\xAA') diff --git a/tests/stdlib/tdeques.nim b/tests/stdlib/tdeques.nim new file mode 100644 index 000000000..39ff996d1 --- /dev/null +++ b/tests/stdlib/tdeques.nim @@ -0,0 +1,243 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" +""" + +import std/deques +from std/sequtils import toSeq +import std/assertions + +block: + proc index(self: Deque[int], idx: Natural): int = + self[idx] + + proc main = + var testDeque = initDeque[int]() + testDeque.addFirst(1) + doAssert testDeque.index(0) == 1 + + main() + +block: + var d = initDeque[int]() + d.addLast(1) + doAssert $d == "[1]" +block: + var d = initDeque[string]() + d.addLast("1") + doAssert $d == """["1"]""" +block: + var d = initDeque[char]() + d.addLast('1') + doAssert $d == "['1']" + +block: + var deq = initDeque[int](1) + deq.addLast(4) + deq.addFirst(9) + deq.addFirst(123) + var first = deq.popFirst() + deq.addLast(56) + doAssert(deq.peekLast() == 56) + deq.addLast(6) + doAssert(deq.peekLast() == 6) + var second = deq.popFirst() + deq.addLast(789) + doAssert(deq.peekLast() == 789) + + doAssert first == 123 + doAssert second == 9 + doAssert($deq == "[4, 56, 6, 789]") + doAssert deq == [4, 56, 6, 789].toDeque + + doAssert deq[0] == deq.peekFirst and deq.peekFirst == 4 + #doAssert deq[^1] == deq.peekLast and deq.peekLast == 789 + deq[0] = 42 + deq[deq.len - 1] = 7 + + doAssert 6 in deq and 789 notin deq + doAssert deq.find(6) >= 0 + doAssert deq.find(789) < 0 + + block: + var d = initDeque[int](1) + d.addLast 7 + d.addLast 8 + d.addLast 10 + d.addFirst 5 + d.addFirst 2 + d.addFirst 1 + d.addLast 20 + d.shrink(fromLast = 2) + doAssert($d == "[1, 2, 5, 7, 8]") + d.shrink(2, 1) + doAssert($d == "[5, 7]") + d.shrink(2, 2) + doAssert d.len == 0 + + for i in -2 .. 10: + if i in deq: + doAssert deq.contains(i) and deq.find(i) >= 0 + else: + doAssert(not deq.contains(i) and deq.find(i) < 0) + + when compileOption("boundChecks"): + try: + echo deq[99] + doAssert false + except IndexDefect: + discard + + try: + doAssert deq.len == 4 + for i in 0 ..< 5: deq.popFirst() + doAssert false + except IndexDefect: + discard + + # grabs some types of resize error. + deq = initDeque[int]() + for i in 1 .. 4: deq.addLast i + deq.popFirst() + deq.popLast() + for i in 5 .. 8: deq.addFirst i + doAssert $deq == "[8, 7, 6, 5, 2, 3]" + + # Similar to proc from the documentation example + proc foo(a, b: Positive) = # assume random positive values for `a` and `b`. + var deq = initDeque[int]() + doAssert deq.len == 0 + for i in 1 .. a: deq.addLast i + + if b < deq.len: # checking before indexed access. + doAssert deq[b] == b + 1 + + # The following two lines don't need any checking on access due to the logic + # of the program, but that would not be the case if `a` could be 0. + doAssert deq.peekFirst == 1 + doAssert deq.peekLast == a + + while deq.len > 0: # checking if the deque is empty + doAssert deq.popFirst() > 0 + + #foo(0,0) + foo(8, 5) + foo(10, 9) + foo(1, 1) + foo(2, 1) + foo(1, 5) + foo(3, 2) + +import std/sets + +block t13310: + proc main() = + var q = initDeque[HashSet[int16]](2) + q.addFirst([1'i16].toHashSet) + q.addFirst([2'i16].toHashSet) + q.addFirst([3'i16].toHashSet) + doAssert $q == "[{3}, {2}, {1}]" + + static: + main() + + +proc main() = + block: + let a = [10, 20, 30].toDeque + doAssert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)] + + block: + let q = [7, 9].toDeque + doAssert 7 in q + doAssert q.contains(7) + doAssert 8 notin q + + block: + let a = [10, 20, 30, 40, 50].toDeque + doAssert $a == "[10, 20, 30, 40, 50]" + doAssert a.peekFirst == 10 + doAssert len(a) == 5 + + block: + let a = [10, 20, 30, 40, 50].toDeque + doAssert $a == "[10, 20, 30, 40, 50]" + doAssert a.peekLast == 50 + doAssert len(a) == 5 + + block: + var a = [10, 20, 30, 40, 50].toDeque + doAssert $a == "[10, 20, 30, 40, 50]" + doAssert a.popFirst == 10 + doAssert $a == "[20, 30, 40, 50]" + + block: + var a = [10, 20, 30, 40, 50].toDeque + doAssert $a == "[10, 20, 30, 40, 50]" + doAssert a.popLast == 50 + doAssert $a == "[10, 20, 30, 40]" + + block: + var a = [10, 20, 30, 40, 50].toDeque + doAssert $a == "[10, 20, 30, 40, 50]" + clear(a) + doAssert len(a) == 0 + + block: # bug #21278 + var a = [10, 20, 30, 40].toDeque + + a.shrink(fromFirst = 0, fromLast = 1) + doAssert $a == "[10, 20, 30]" + + block: + var a, b: Deque[int] + for i in 1 .. 256: + a.addLast(i) + for i in 1 .. 255: + a.popLast + b.addLast(1) + doAssert a == b + + block: + # Issue 23275 + # Test `==`. + block: + var a, b = initDeque[int]() + doAssert a == b + doAssert a.hash == b.hash + a.addFirst(1) + doAssert a != b + doAssert a.hash != b.hash + b.addLast(1) + doAssert a == b + doAssert a.hash == b.hash + a.popFirst + b.popLast + doAssert a == b + doAssert a.hash == b.hash + a.addLast 2 + doAssert a != b + doAssert a.hash != b.hash + b.addFirst 2 + doAssert a == b + doAssert a.hash == b.hash + + block: + var a, b = initDeque[int]() + for i in countDown(100, 1): + a.addFirst(i) + for i in 1..100: + b.addLast(i) + doAssert a == b + for i in 1..99: + a.popLast + let a1 = [1].toDeque + doAssert a == a1 + doAssert a.hash == a1.hash + var c = initDeque[int]() + c.addLast(1) + doAssert a == c + doAssert a.hash == c.hash + +static: main() +main() diff --git a/tests/stdlib/tdiff.nim b/tests/stdlib/tdiff.nim new file mode 100644 index 000000000..132f7120b --- /dev/null +++ b/tests/stdlib/tdiff.nim @@ -0,0 +1,75 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + +import experimental/diff +import std/strutils +import std/assertions + +proc testHelper(f: seq[Item]): string = + for it in f: + result.add( + $it.deletedA & "." & $it.insertedB & "." & $it.startA & "." & $it.startB & "*" + ) + +proc main() = + var a, b: string + + # Diff Self Test + # test all changes + a = "a,b,c,d,e,f,g,h,i,j,k,l".replace(',', '\n') + b = "0,1,2,3,4,5,6,7,8,9".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "12.10.0.0*", + "all-changes test failed.") + # test all same + a = "a,b,c,d,e,f,g,h,i,j,k,l".replace(',', '\n') + b = a + doAssert(testHelper(diffText(a, b)) == + "", + "all-same test failed.") + + # test snake + a = "a,b,c,d,e,f".replace(',', '\n') + b = "b,c,d,e,f,x".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "1.0.0.0*0.1.6.5*", + "snake test failed.") + + # 2002.09.20 - repro + a = "c1,a,c2,b,c,d,e,g,h,i,j,c3,k,l".replace(',', '\n') + b = "C1,a,C2,b,c,d,e,I1,e,g,h,i,j,C3,k,I2,l".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "1.1.0.0*1.1.2.2*0.2.7.7*1.1.11.13*0.1.13.15*", + "repro20020920 test failed.") + + # 2003.02.07 - repro + a = "F".replace(',', '\n') + b = "0,F,1,2,3,4,5,6,7".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "0.1.0.0*0.7.1.2*", + "repro20030207 test failed.") + + # Muegel - repro + a = "HELLO\nWORLD" + b = "\n\nhello\n\n\n\nworld\n" + doAssert(testHelper(diffText(a, b)) == + "2.8.0.0*", + "repro20030409 test failed.") + + # test some differences + a = "a,b,-,c,d,e,f,f".replace(',', '\n') + b = "a,b,x,c,e,f".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "1.1.2.2*1.0.4.4*1.0.7.6*", + "some-changes test failed.") + + # test one change within long chain of repeats + a = "a,a,a,a,a,a,a,a,a,a".replace(',', '\n') + b = "a,a,a,a,-,a,a,a,a,a".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "0.1.4.4*1.0.9.10*", + "long chain of repeats test failed.") +main() +static: main() diff --git a/tests/stdlib/tdistros_detect.nim b/tests/stdlib/tdistros_detect.nim new file mode 100644 index 000000000..1176c8993 --- /dev/null +++ b/tests/stdlib/tdistros_detect.nim @@ -0,0 +1,16 @@ +import std/[assertions, distros] + +when defined(windows): + doAssert detectOs(Windows) == true + doAssert detectOs(Linux) == false + doAssert detectOs(MacOSX) == false + +when defined(linux): + doAssert detectOs(Linux) == true + doAssert detectOs(Windows) == false + doAssert detectOs(MacOSX) == false + +when defined(macosx): + doAssert detectOs(MacOSX) == true + doAssert detectOs(Windows) == false + doAssert detectOs(Linux) == false diff --git a/tests/stdlib/tdochelpers.nim b/tests/stdlib/tdochelpers.nim new file mode 100644 index 000000000..4d532b5d0 --- /dev/null +++ b/tests/stdlib/tdochelpers.nim @@ -0,0 +1,221 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + output: ''' + +[Suite] Integration with Nim +''' +""" + +# tests for dochelpers.nim module + +import ../../lib/packages/docutils/[rstast, rst, dochelpers] +import unittest +import std/assertions + +proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind, + arg: string) = + doAssert msgkind == mwBrokenLink + +proc fromRst(text: string): LangSymbol = + let r = rstParse(text, "-input-", LineRstInit, ColRstInit, + {roNimFile}, + msgHandler=testMsgHandler) + assert r.node.kind == rnRstRef + result = toLangSymbol(r.node) + +proc fromMd(text: string): LangSymbol = + let r = rstParse(text, "-input-", LineRstInit, ColRstInit, + {roPreferMarkdown, roSupportMarkdown, roNimFile}, + msgHandler=testMsgHandler) + assert r.node.kind == rnPandocRef + assert r.node.len == 2 + # this son is the target: + assert r.node.sons[1].kind == rnInner + result = toLangSymbol(r.node.sons[1]) + +suite "Integration with Nim": + test "simple symbol parsing (shortest form)": + let expected = LangSymbol(symKind: "", name: "g") + check "g_".fromRst == expected + check "[g]".fromMd == expected + # test also alternative syntax variants of Pandoc Markdown: + check "[g][]".fromMd == expected + check "[this symbol][g]".fromMd == expected + + test "simple symbol parsing (group of words)": + #let input1 = "`Y`_".rstParseTest + let expected1 = LangSymbol(symKind: "", name: "Y") + check "`Y`_".fromRst == expected1 + check "[Y]".fromMd == expected1 + + # this means not a statement 'type', it's a backticked identifier `type`: + let expected2 = LangSymbol(symKind: "", name: "type") + check "`type`_".fromRst == expected2 + check "[type]".fromMd == expected2 + + let expected3 = LangSymbol(symKind: "", name: "[]") + check "`[]`_".fromRst == expected3 + # Markdown syntax for this case is NOT [[]] + check "[`[]`]".fromMd == expected3 + + let expected4 = LangSymbol(symKind: "", name: "Xyz") + check "`X Y Z`_".fromRst == expected4 + check "[X Y Z]".fromMd == expected4 + + test "simple proc parsing": + let expected = LangSymbol(symKind: "proc", name: "f") + check "`proc f`_".fromRst == expected + check "[proc f]".fromMd == expected + + test "another backticked name": + let expected = LangSymbol(symKind: "template", name: "type") + check """`template \`type\``_""".fromRst == expected + # no backslash in Markdown: + check """[template `type`]""".fromMd == expected + + test "simple proc parsing with parameters": + let expected = LangSymbol(symKind: "proc", name: "f", + parametersProvided: true) + check "`proc f*()`_".fromRst == expected + check "`proc f()`_".fromRst == expected + check "[proc f*()]".fromMd == expected + check "[proc f()]".fromMd == expected + + test "symbol parsing with 1 parameter": + let expected = LangSymbol(symKind: "", name: "f", + parameters: @[("G[int]", "")], + parametersProvided: true) + check "`f(G[int])`_".fromRst == expected + check "[f(G[int])]".fromMd == expected + + test "more proc parsing": + let input1 = "`proc f[T](x:G[T]):M[T]`_".fromRst + let input2 = "`proc f[ T ] ( x: G [T] ): M[T]`_".fromRst + let input3 = "`proc f*[T](x: G[T]): M[T]`_".fromRst + let expected = LangSymbol(symKind: "proc", + name: "f", + generics: "[T]", + parameters: @[("x", "G[T]")], + parametersProvided: true, + outType: "M[T]") + check(input1 == expected) + check(input2 == expected) + check(input3 == expected) + + test "advanced proc parsing with Nim identifier normalization": + let inputRst = """`proc binarySearch*[T, K](a: openarray[T]; key: K; + cmp: proc (x: T; y: K): int)`_""" + let inputMd = """[proc binarySearch*[T, K](a: openarray[T]; key: K; + cmp: proc (x: T; y: K): int)]""" + let expected = LangSymbol(symKind: "proc", + name: "binarysearch", + generics: "[T,K]", + parameters: @[ + ("a", "openarray[T]"), + ("key", "K"), + ("cmp", "proc(x:T;y:K):int")], + parametersProvided: true, + outType: "") + check(inputRst.fromRst == expected) + check(inputMd.fromMd == expected) + + test "the same without proc": + let input = """`binarySearch*[T, K](a: openarray[T]; key: K; + cmp: proc (x: T; y: K): int {.closure.})`_""" + let expected = LangSymbol(symKind: "", + name: "binarysearch", + generics: "[T,K]", + parameters: @[ + ("a", "openarray[T]"), + ("key", "K"), + ("cmp", "proc(x:T;y:K):int")], + parametersProvided: true, + outType: "") + check(input.fromRst == expected) + let inputMd = """[binarySearch*[T, K](a: openarray[T]; key: K; + cmp: proc (x: T; y: K): int {.closure.})]""" + check(inputMd.fromMd == expected) + + test "operator $ with and without backticks": + let input1 = """`func \`$\`*[T](a: \`open Array\`[T]): string`_""" + let input1md = "[func `$`*[T](a: `open Array`[T]): string]" + let input2 = """`func $*[T](a: \`open Array\`[T]): string`_""" + let input2md = "[func $*[T](a: `open Array`[T]): string]" + let expected = LangSymbol(symKind: "func", + name: "$", + generics: "[T]", + parameters: @[("a", "openarray[T]")], + parametersProvided: true, + outType: "string") + check input1.fromRst == expected + check input2.fromRst == expected + check input1md.fromMd == expected + check input2md.fromMd == expected + + test "operator [] with and without backticks": + let input1 = """`func \`[]\`[T](a: \`open Array\`[T], idx: int): T`_""" + let input1md = "[func `[]`[T](a: `open Array`[T], idx: int): T]" + let input2 = """`func [][T](a: \`open Array\`[T], idx: int): T`_""" + let input2md = "[func [][T](a: `open Array`[T], idx: int): T]" + let expected = LangSymbol(symKind: "func", + name: "[]", + generics: "[T]", + parameters: @[("a", "openarray[T]"), + ("idx", "int")], + parametersProvided: true, + outType: "T") + check input1.fromRst == expected + check input2.fromRst == expected + check input1md.fromMd == expected + check input2md.fromMd == expected + + test "postfix symbol specifier #1": + let input = "`walkDir iterator`_" + let inputMd = "[walkDir iterator]" + let expected = LangSymbol(symKind: "iterator", + name: "walkdir") + check input.fromRst == expected + check inputMd.fromMd == expected + + test "postfix symbol specifier #2": + let input1 = """`\`[]\`[T](a: \`open Array\`[T], idx: int): T func`_""" + let input1md = "[`[]`[T](a: `open Array`[T], idx: int): T func]" + let input2 = """`[][T](a: \`open Array\`[T], idx: int): T func`_""" + # note again that ` is needed between 1st and second [ + let input2md = "[`[]`[T](a: `open Array`[T], idx: int): T func]" + let expected = LangSymbol(symKind: "func", + name: "[]", + generics: "[T]", + parameters: @[("a", "openarray[T]"), + ("idx", "int")], + parametersProvided: true, + outType: "T") + check input1.fromRst == expected + check input2.fromRst == expected + check input1md.fromMd == expected + check input2md.fromMd == expected + + test "type of type": + let inputRst = "`CopyFlag enum`_" + let inputMd = "[CopyFlag enum]" + let expected = LangSymbol(symKind: "type", + symTypeKind: "enum", + name: "Copyflag") + check inputRst.fromRst == expected + check inputMd.fromMd == expected + + test "prefixed module": + let inputRst = "`module std / paths`_" + let inputMd = "[module std / paths]" + let expected = LangSymbol(symKind: "module", + name: "std/paths") + check inputRst.fromRst == expected + check inputMd.fromMd == expected + + test "postfixed module": + let inputRst = "`std / paths module`_" + let inputMd = "[std / paths module]" + let expected = LangSymbol(symKind: "module", + name: "std/paths") + check inputRst.fromRst == expected + check inputMd.fromMd == expected diff --git a/tests/stdlib/teditdistance.nim b/tests/stdlib/teditdistance.nim new file mode 100644 index 000000000..14ba6df97 --- /dev/null +++ b/tests/stdlib/teditdistance.nim @@ -0,0 +1,45 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/editdistance +import std/assertions + +doAssert editDistance("", "") == 0 +doAssert editDistance("kitten", "sitting") == 3 # from Wikipedia +doAssert editDistance("flaw", "lawn") == 2 # from Wikipedia + +doAssert editDistance("привет", "превет") == 1 +doAssert editDistance("Åge", "Age") == 1 +# editDistance, one string is longer in bytes, but shorter in rune length +# first string: 4 bytes, second: 6 bytes, but only 3 runes +doAssert editDistance("aaaa", "×××") == 4 + +block veryLongStringEditDistanceTest: + const cap = 256 + var + s1 = newStringOfCap(cap) + s2 = newStringOfCap(cap) + while len(s1) < cap: + s1.add 'a' + while len(s2) < cap: + s2.add 'b' + doAssert editDistance(s1, s2) == cap + +block combiningCodePointsEditDistanceTest: + const s = "A\xCC\x8Age" + doAssert editDistance(s, "Age") == 1 + +doAssert editDistanceAscii("", "") == 0 +doAssert editDistanceAscii("kitten", "sitting") == 3 # from Wikipedia +doAssert editDistanceAscii("flaw", "lawn") == 2 # from Wikipedia + + +doAssert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffix") == 0) +doAssert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffi1") == 1) +doAssert(editDistance("prefix__hallo_suffix", "prefix__HALLO_suffix") == 5) +doAssert(editDistance("prefix__hallo_suffix", "prefix__ha_suffix") == 3) +doAssert(editDistance("prefix__hallo_suffix", "prefix") == 14) +doAssert(editDistance("prefix__hallo_suffix", "suffix") == 14) +doAssert(editDistance("prefix__hallo_suffix", "prefix__hao_suffix") == 2) +doAssert(editDistance("main", "malign") == 2) \ No newline at end of file diff --git a/tests/stdlib/tencodings.nim b/tests/stdlib/tencodings.nim new file mode 100644 index 000000000..2f4daaba3 --- /dev/null +++ b/tests/stdlib/tencodings.nim @@ -0,0 +1,107 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/encodings +import std/assertions + +var fromGBK = open("utf-8", "gbk") +var toGBK = open("gbk", "utf-8") + +var fromGB2312 = open("utf-8", "gb2312") +var toGB2312 = open("gb2312", "utf-8") + + +block: + let data = "\215\237\186\243\178\187\214\170\204\236\212\218\203\174\163\172\194\250\180\178\208\199\195\206\209\185\208\199\186\211" + doAssert fromGBK.convert(data) == "醉后不知天在水,满床星梦压星河" + +block: + let data = "万两黄金容易得,知心一个也难求" + doAssert toGBK.convert(data) == "\205\242\193\189\187\198\189\240\200\221\210\215\181\195\163\172\214\170\208\196\210\187\184\246\210\178\196\209\199\243" + + +block: + let data = "\215\212\208\197\200\203\201\250\182\254\176\217\196\234\163\172\187\225\181\177\203\174\187\247\200\253\199\167\192\239" + doAssert fromGB2312.convert(data) == "自信人生二百年,会当水击三千里" + +block: + let data = "谁怕?一蓑烟雨任平生" + doAssert toGB2312.convert(data) == "\203\173\197\194\163\191\210\187\203\242\209\204\211\234\200\206\198\189\201\250" + + +when defined(windows): + block should_throw_on_unsupported_conversions: + let original = "some string" + + doAssertRaises(EncodingError): + discard convert(original, "utf-8", "utf-32") + + doAssertRaises(EncodingError): + discard convert(original, "utf-8", "unicodeFFFE") + + doAssertRaises(EncodingError): + discard convert(original, "utf-8", "utf-32BE") + + doAssertRaises(EncodingError): + discard convert(original, "unicodeFFFE", "utf-8") + + doAssertRaises(EncodingError): + discard convert(original, "utf-32", "utf-8") + + doAssertRaises(EncodingError): + discard convert(original, "utf-32BE", "utf-8") + + block should_convert_from_utf16_to_utf8: + let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест" + let result = convert(original, "utf-8", "utf-16") + doAssert(result == "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82") + + block should_convert_from_utf16_to_win1251: + let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест" + let result = convert(original, "windows-1251", "utf-16") + doAssert(result == "\xf2\xe5\xf1\xf2") + + block should_convert_from_win1251_to_koi8r: + let original = "\xf2\xe5\xf1\xf2" # win1251 test string "тест" + let result = convert(original, "koi8-r", "windows-1251") + doAssert(result == "\xd4\xc5\xd3\xd4") + + block should_convert_from_koi8r_to_win1251: + let original = "\xd4\xc5\xd3\xd4" # koi8r test string "тест" + let result = convert(original, "windows-1251", "koi8-r") + doAssert(result == "\xf2\xe5\xf1\xf2") + + block should_convert_from_utf8_to_win1251: + let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест" + let result = convert(original, "windows-1251", "utf-8") + doAssert(result == "\xf2\xe5\xf1\xf2") + + block should_convert_from_utf8_to_utf16: + let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест" + let result = convert(original, "utf-16", "utf-8") + doAssert(result == "\x42\x04\x35\x04\x41\x04\x42\x04") + + block should_handle_empty_string_for_any_conversion: + let original = "" + var result = convert(original, "utf-16", "utf-8") + doAssert(result == "") + result = convert(original, "utf-8", "utf-16") + doAssert(result == "") + result = convert(original, "windows-1251", "koi8-r") + doAssert(result == "") + + +block: + let + orig = "öäüß" + cp1252 = convert(orig, "CP1252", "UTF-8") + ibm850 = convert(cp1252, "ibm850", "CP1252") + current = getCurrentEncoding() + doAssert orig == "\195\182\195\164\195\188\195\159" + doAssert ibm850 == "\148\132\129\225" + doAssert convert(ibm850, current, "ibm850") == orig + +block: # fixes about #23481 + doAssertRaises EncodingError: + discard open(destEncoding="this is a invalid enc") diff --git a/tests/stdlib/tenumerate.nim b/tests/stdlib/tenumerate.nim new file mode 100644 index 000000000..2789ebe3a --- /dev/null +++ b/tests/stdlib/tenumerate.nim @@ -0,0 +1,24 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/enumerate +import std/assertions + +let a = @[1, 3, 5, 7] + +block: + var res: seq[(int, int)] + for i, x in enumerate(a): + res.add (i, x) + doAssert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] +block: + var res: seq[(int, int)] + for (i, x) in enumerate(a.items): + res.add (i, x) + doAssert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] +block: + var res: seq[(int, int)] + for i, x in enumerate(3, a): + res.add (i, x) + doAssert res == @[(3, 1), (4, 3), (5, 5), (6, 7)] diff --git a/tests/stdlib/tenumutils.nim b/tests/stdlib/tenumutils.nim new file mode 100644 index 000000000..2662a660d --- /dev/null +++ b/tests/stdlib/tenumutils.nim @@ -0,0 +1,49 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + +import std/enumutils +from std/sequtils import toSeq +import std/assertions + +template main = + block: # items + type A = enum a0 = 2, a1 = 4, a2 + type B[T] = enum b0 = 2, b1 = 4 + doAssert A.toSeq == [a0, a1, a2] + doAssert B[float].toSeq == [B[float].b0, B[float].b1] + + block: # symbolName + block: + type A2 = enum a20, a21, a22 + doAssert $a21 == "a21" + doAssert a21.symbolName == "a21" + proc `$`(a: A2): string = "foo" + doAssert $a21 == "foo" + doAssert a21.symbolName == "a21" + var a = a22 + doAssert $a == "foo" + doAssert a.symbolName == "a22" + + type B = enum + b0 = (10, "kb0") + b1 = "kb1" + b2 + let b = B.low + doAssert b.symbolName == "b0" + doAssert $b == "kb0" + static: doAssert B.high.symbolName == "b2" + + block: + type + Color = enum + Red = "red", Yellow = "yellow", Blue = "blue" + + var s = Red + doAssert symbolName(s) == "Red" + var x: range[Red..Blue] = Yellow + doAssert symbolName(x) == "Yellow" + +static: main() +main() diff --git a/tests/stdlib/tenvvars.nim b/tests/stdlib/tenvvars.nim new file mode 100644 index 000000000..1a07f02b8 --- /dev/null +++ b/tests/stdlib/tenvvars.nim @@ -0,0 +1,162 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + joinable: false + targets: "c js cpp" +""" + +import std/envvars +from std/sequtils import toSeq +import stdtest/testutils +import std/[assertions] + +when not defined(js): + import std/typedthreads + +# "LATIN CAPITAL LETTER AE" in UTF-8 (0xc386) +const unicodeUtf8 = "\xc3\x86" + +template main = + block: # delEnv, existsEnv, getEnv, envPairs + for val in ["val", "", unicodeUtf8]: # ensures empty val works too + const key = "NIM_TESTS_TOSENV_KEY" + doAssert not existsEnv(key) + + putEnv(key, "tempval") + doAssert existsEnv(key) + doAssert getEnv(key) == "tempval" + + putEnv(key, val) # change a key that already exists + doAssert existsEnv(key) + doAssert getEnv(key) == val + + doAssert (key, val) in toSeq(envPairs()) + delEnv(key) + doAssert (key, val) notin toSeq(envPairs()) + doAssert not existsEnv(key) + delEnv(key) # deleting an already deleted env var + doAssert not existsEnv(key) + + block: + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "") == "" + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", " ") == " " + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "defval") == "defval" + + whenVMorJs: discard # xxx improve + do: + doAssertRaises(OSError, putEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE", "NEW_DUMMY_VALUE")) + doAssertRaises(OSError, putEnv("", "NEW_DUMMY_VALUE")) + doAssert not existsEnv("") + doAssert not existsEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE") + doAssert not existsEnv("NIM_TESTS_TOSENV_PUT") + +static: main() +main() + +when defined(windows): + import std/widestrs + proc c_wgetenv(env: WideCString): WideCString {.importc: "_wgetenv", header: "<stdlib.h>".} +proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "<stdlib.h>".} + +when not defined(js) and not defined(nimscript): + block: # bug #18533 + var thr: Thread[void] + proc threadFunc {.thread.} = putEnv("foo", "fooVal2") + + putEnv("foo", "fooVal1") + doAssert getEnv("foo") == "fooVal1" + createThread(thr, threadFunc) + joinThreads(thr) + when defined(windows): + doAssert getEnv("foo") == $c_wgetenv("foo".newWideCString) + else: + doAssert getEnv("foo") == $c_getenv("foo".cstring) + + doAssertRaises(OSError): delEnv("foo=bar") + +when defined(windows) and not defined(nimscript): + import std/encodings + + proc c_putenv(env: cstring): int32 {.importc: "putenv", header: "<stdlib.h>".} + proc c_wputenv(env: WideCString): int32 {.importc: "_wputenv", header: "<stdlib.h>".} + + block: # Bug #20083 + # These test that `getEnv`, `putEnv` and `existsEnv` handle Unicode + # characters correctly. This means that module X in the process calling the + # CRT environment variable API will get the correct string. Raw CRT API + # calls below represent module X. + + # Getting an env. var. with unicode characters returns the correct UTF-8 + # encoded string. + block: + const envName = "twin_envvars1" + doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0 + doAssert existsEnv(envName) + doAssert getEnv(envName) == unicodeUtf8 + + # Putting an env. var. with unicode characters gives the correct UTF-16 + # encoded string from low-level routine. + block: + const envName = "twin_envvars2" + putEnv(envName, unicodeUtf8) + doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8 + + # Env. name containing Unicode characters is retrieved correctly + block: + const envName = unicodeUtf8 & "1" + doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0 + doAssert existsEnv(envName) + doAssert getEnv(envName) == unicodeUtf8 + + # Env. name containing Unicode characters is set correctly + block: + const envName = unicodeUtf8 & "2" + putEnv(envName, unicodeUtf8) + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8 + + # Env. name containing Unicode characters and empty value is set correctly + block: + const envName = unicodeUtf8 & "3" + putEnv(envName, "") + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == "" + + # It's hard to test on Windows code pages, because there is no "change + # a process' locale" API. + if getCurrentEncoding(true) == "windows-1252": + const + unicodeAnsi = "\xc6" # `unicodeUtf8` in `windows-1252` encoding + + # Test that env. var. ANSI API has correct encoding + block: + const + envName = unicodeUtf8 & "4" + envNameAnsi = unicodeAnsi & "4" + putEnv(envName, unicodeUtf8) + doAssert $c_getenv(envNameAnsi.cstring) == unicodeAnsi + + block: + const + envName = unicodeUtf8 & "5" + envNameAnsi = unicodeAnsi & "5" + doAssert c_putenv((envNameAnsi & "=" & unicodeAnsi).cstring) == 0 + doAssert getEnv(envName) == unicodeUtf8 + + # Env. name containing Unicode characters and empty value is set correctly; + # and, if env. name. characters cannot be represented in codepage, don't + # raise an error. + # + # `win_setenv.nim` converts UTF-16 to ANSI when setting empty env. var. The + # windows-1250 locale has no representation of `abreveUtf8` below, so the + # conversion will fail, but this must not be fatal. It is expected that the + # routine ignores updating MBCS environment (`environ` global) and carries + # on. + block: + const + # "LATIN SMALL LETTER A WITH BREVE" in UTF-8 + abreveUtf8 = "\xc4\x83" + envName = abreveUtf8 & "6" + putEnv(envName, "") + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == "" + doAssert getEnv(envName) == "" diff --git a/tests/stdlib/texitprocs.nim b/tests/stdlib/texitprocs.nim index 9d5378fe8..ea29d8f58 100644 --- a/tests/stdlib/texitprocs.nim +++ b/tests/stdlib/texitprocs.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc; --mm:orc" targets: "c cpp js" output: ''' ok4 diff --git a/tests/stdlib/tfdleak.nim b/tests/stdlib/tfdleak.nim index 79d7ee0d0..272a7507c 100644 --- a/tests/stdlib/tfdleak.nim +++ b/tests/stdlib/tfdleak.nim @@ -1,12 +1,15 @@ discard """ exitcode: 0 output: "" - matrix: "; -d:nimInheritHandles" + matrix: "; -d:nimInheritHandles; --mm:refc" joinable: false """ import os, osproc, strutils, nativesockets, net, selectors, memfiles, asyncdispatch, asyncnet + +import std/[assertions, syncio] + when defined(windows): import winlean @@ -56,7 +59,7 @@ proc isValidHandle(f: int): bool = proc main() = if paramCount() == 0: # Parent process - let f = system.open("__test_fdleak", fmReadWrite) + let f = syncio.open("__test_fdleak", fmReadWrite) defer: close f leakCheck(f.getOsFileHandle, "system.open()") diff --git a/tests/stdlib/tfdleak_multiple.nim b/tests/stdlib/tfdleak_multiple.nim index 22387607f..c26681217 100644 --- a/tests/stdlib/tfdleak_multiple.nim +++ b/tests/stdlib/tfdleak_multiple.nim @@ -3,6 +3,7 @@ joinable: false """ import os, osproc, strutils +import std/assertions const Iterations = 200 diff --git a/tests/stdlib/tfenv.nim b/tests/stdlib/tfenv.nim new file mode 100644 index 000000000..a486b8a9d --- /dev/null +++ b/tests/stdlib/tfenv.nim @@ -0,0 +1,8 @@ +import std/fenv +import std/assertions + + +func is_significant(x: float): bool = + x > minimumPositiveValue(float) and x < maximumPositiveValue(float) + +doAssert is_significant(10.0) diff --git a/tests/stdlib/tfilesanddirs.nim b/tests/stdlib/tfilesanddirs.nim new file mode 100644 index 000000000..a1920d4f2 --- /dev/null +++ b/tests/stdlib/tfilesanddirs.nim @@ -0,0 +1,36 @@ +import std/[paths, files, dirs, appdirs] + +from stdtest/specialpaths import buildDir +import std/[syncio, assertions] + +block fileOperations: + let files = @[Path"these.txt", Path"are.x", Path"testing.r", Path"files.q"] + let dirs = @[Path"some", Path"created", Path"test", Path"dirs"] + + let dname = Path"__really_obscure_dir_name" + + createDir(dname.Path) + doAssert dirExists(Path(dname)) + + # Test creating files and dirs + for dir in dirs: + createDir(Path(dname/dir)) + doAssert dirExists(Path(dname/dir)) + + for file in files: + let fh = open(string(dname/file), fmReadWrite) # createFile + fh.close() + doAssert fileExists(Path(dname/file)) + +block: # getCacheDir + doAssert getCacheDir().dirExists + +block: # moveFile + let tempDir = getTempDir() / Path("D20221022T151608") + createDir(tempDir) + defer: removeDir(tempDir) + +block: # moveDir + let tempDir = getTempDir() / Path("D20220609T161443") + createDir(tempDir) + defer: removeDir(tempDir) diff --git a/tests/stdlib/tfrexp1.nim b/tests/stdlib/tfrexp1.nim index 6da185420..aa734ddac 100644 --- a/tests/stdlib/tfrexp1.nim +++ b/tests/stdlib/tfrexp1.nim @@ -1,9 +1,10 @@ discard """ - targets: "js c c++" - output: '''ok''' + matrix: "--mm:refc; --mm:orc" + targets: "js c cpp" """ -import math +import std/math +import std/assertions const manualTest = false @@ -43,4 +44,12 @@ when manualTest: else: frexp_test(-200000.0, 200000.0, 0.125) -echo "ok" + +doAssert frexp(8.0) == (0.5, 4) +doAssert frexp(-8.0) == (-0.5, 4) +doAssert frexp(0.0) == (0.0, 0) + +block: + var x: int + doAssert frexp(5.0, x) == 0.625 + doAssert x == 3 diff --git a/tests/stdlib/tgenast.nim b/tests/stdlib/tgenast.nim new file mode 100644 index 000000000..d99c9312e --- /dev/null +++ b/tests/stdlib/tgenast.nim @@ -0,0 +1,274 @@ +discard """ + matrix: "--mm:orc; --mm:refc" +""" + +# xxx also test on js + +import std/genasts +import std/macros +from std/strformat import `&` +import std/assertions +import ./mgenast + +proc main = + block: + macro bar(x0: static Foo, x1: Foo, x2: Foo, xignored: Foo): untyped = + let s0 = "not captured!" + let s1 = "not captured!" + let xignoredLocal = kfoo4 + + # newLit optional: + let x3 = newLit kfoo4 + let x3b = kfoo4 + + result = genAstOpt({kDirtyTemplate}, s1=true, s2="asdf", x0, x1=x1, x2, x3, x3b): + doAssert not declared(xignored) + doAssert not declared(xignoredLocal) + (s1, s2, s0, x0, x1, x2, x3, x3b) + + let s0 = "caller scope!" + + doAssert bar(kfoo1, kfoo2, kfoo3, kfoo4) == + (true, "asdf", "caller scope!", kfoo1, kfoo2, kfoo3, kfoo4, kfoo4) + + block: + # doesn't have limitation mentioned in https://github.com/nim-lang/RFCs/issues/122#issue-401636535 + macro abc(name: untyped): untyped = + result = genAst(name): + type name = object + + abc(Bar) + doAssert Bar.default == Bar() + + block: + # backticks parser limitations / ambiguities not are an issue with `genAst`: + # (#10326 #9745 are fixed but `quote do` still has underlying ambiguity issue + # with backticks) + type Foo = object + a: int + + macro m1(): untyped = + # result = quote do: # Error: undeclared identifier: 'a1' + result = genAst: + template `a1=`(x: var Foo, val: int) = + x.a = val + + m1() + var x0: Foo + x0.a1 = 10 + doAssert x0 == Foo(a: 10) + + block: + # avoids bug #7375 + macro fun(b: static[bool], b2: bool): untyped = + result = newStmtList() + macro foo(c: bool): untyped = + var b = false + result = genAst(b, c): + fun(b, c) + + foo(true) + + block: + # avoids bug #7589 + # since `==` works with genAst, the problem goes away + macro foo2(): untyped = + # result = quote do: # Error: '==' cannot be passed to a procvar + result = genAst: + `==`(3,4) + doAssert not foo2() + + block: + # avoids bug #7726 + # expressions such as `a.len` are just passed as arguments to `genAst`, and + # caller scope is not polluted with definitions such as `let b = newLit a.len` + macro foo(): untyped = + let a = @[1, 2, 3, 4, 5] + result = genAst(a, b = a.len): # shows 2 ways to get a.len + (a.len, b) + doAssert foo() == (5, 5) + + block: + # avoids bug #9607 + proc fun1(info:LineInfo): string = "bar1" + proc fun2(info:int): string = "bar2" + + macro bar2(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + let fun1 = bindSym"fun1" # optional; we can remove this and also the + # capture of fun1, as show in next example + result = genAst(info, fun1): + (fun1(info), fun2(info.line)) + doAssert bar2() == ("bar1", "bar2") + + macro bar3(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + result = genAst(info): + (fun1(info), fun2(info.line)) + doAssert bar3() == ("bar1", "bar2") + + macro bar(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + let fun1 = bindSym"fun1" + let fun2 = bindSym"fun2" + result = genAstOpt({kDirtyTemplate}, info): + (fun1(info), fun2(info.line)) + doAssert bar() == ("bar1", "bar2") + + block: + # example from bug #7889 works + # after changing method call syntax to regular call syntax; this is a + # limitation described in bug #7085 + # note that `quote do` would also work after that change in this example. + doAssert bindme2() == kfoo1 + doAssert bindme3() == kfoo1 + doAssert not compiles(bindme4()) # correctly gives Error: undeclared identifier: 'myLocalPriv' + proc myLocalPriv2(): auto = kfoo2 + doAssert bindme5UseExpose() == kfoo1 + + # example showing hijacking behavior when using `kDirtyTemplate` + doAssert bindme5UseExposeFalse() == kfoo2 + # local `myLocalPriv2` hijacks symbol `mgenast.myLocalPriv2`. In most + # use cases this is probably not what macro writer intends as it's + # surprising; hence `kDirtyTemplate` is not the default. + + when nimvm: # disabled because `newStringStream` is used + discard + else: + bindme6UseExpose() + bindme6UseExposeFalse() + + block: + macro mbar(x3: Foo, x3b: static Foo): untyped = + var x1=kfoo3 + var x2=newLit kfoo3 + var x4=kfoo3 + var xLocal=kfoo3 + + proc funLocal(): auto = kfoo4 + + result = genAst(x1, x2, x3, x4): + # local x1 overrides remote x1 + when false: + # one advantage of using `kDirtyTemplate` is that these would hold: + doAssert not declared xLocal + doAssert not compiles(echo xLocal) + # however, even without it, we at least correctly generate CT error + # if trying to use un-captured symbol; this correctly gives: + # Error: internal error: environment misses: xLocal + echo xLocal + + proc foo1(): auto = + # note that `funLocal` is captured implicitly, according to hygienic + # template rules; with `kDirtyTemplate` it would not unless + # captured in `genAst` capture list explicitly + (a0: xRemote, a1: x1, a2: x2, a3: x3, a4: x4, a5: funLocal()) + + return result + + proc main()= + var xRemote=kfoo1 + var x1=kfoo2 + mbar(kfoo4, kfoo4) + doAssert foo1() == (a0: kfoo1, a1: kfoo3, a2: kfoo3, a3: kfoo4, a4: kfoo3, a5: kfoo4) + + main() + + block: + # With `kDirtyTemplate`, the example from #8220 works. + # See https://nim-lang.github.io/Nim/strformat.html#limitations for + # an explanation of why {.dirty.} is needed. + macro foo(): untyped = + result = genAstOpt({kDirtyTemplate}): + let bar = "Hello, World" + &"Let's interpolate {bar} in the string" + doAssert foo() == "Let's interpolate Hello, World in the string" + + + block: # nested application of genAst + macro createMacro(name, obj, field: untyped): untyped = + result = genAst(obj = newDotExpr(obj, field), lit = 10, name, field): + # can't reuse `result` here, would clash + macro name(arg: untyped): untyped = + genAst(arg2=arg): # somehow `arg2` rename is needed + (obj, astToStr(field), lit, arg2) + + var x = @[1, 2, 3] + createMacro foo, x, len + doAssert (foo 20) == (3, "len", 10, 20) + + block: # test with kNoNewLit + macro bar(): untyped = + let s1 = true + template boo(x): untyped = + fun(x) + result = genAstOpt({kNoNewLit}, s1=newLit(s1), s1b=s1): (s1, s1b) + doAssert bar() == (true, 1) + + block: # sanity check: check passing `{}` also works + macro bar(): untyped = + result = genAstOpt({}, s1=true): s1 + doAssert bar() == true + + block: # test passing function and type symbols + proc z1(): auto = 41 + type Z4 = type(1'i8) + macro bar(Z1: typedesc): untyped = + proc z2(): auto = 42 + proc z3[T](a: T): auto = 43 + let Z2 = genAst(): + type(true) + let z4 = genAst(): + proc myfun(): auto = 44 + myfun + type Z3 = type(1'u8) + result = genAst(z4, Z1, Z2): + # z1, z2, z3, Z3, Z4 are captured automatically + # z1, z2, z3 can optionally be specified in capture list + (z1(), z2(), z3('a'), z4(), $Z1, $Z2, $Z3, $Z4) + type Z1 = type('c') + doAssert bar(Z1) == (41, 42, 43, 44, "char", "bool", "uint8", "int8") + + block: # fix bug #11986 + proc foo(): auto = + var s = { 'a', 'b' } + # var n = quote do: `s` # would print {97, 98} + var n = genAst(s): s + n.repr + static: doAssert foo() == "{'a', 'b'}" + + block: # also from #11986 + macro foo(): untyped = + var s = { 'a', 'b' } + # quote do: + # let t = `s` + # $typeof(t) # set[range 0..65535(int)] + genAst(s): + let t = s + $typeof(t) + doAssert foo() == "set[char]" + + block: + macro foo(): untyped = + type Foo = object + template baz2(a: int): untyped = a*10 + macro baz3(a: int): untyped = newLit 13 + result = newStmtList() + + result.add genAst(Foo, baz2, baz3) do: # shows you can pass types, templates etc + var x: Foo + $($typeof(x), baz2(3), baz3(4)) + + let ret = genAst() do: # shows you don't have to, since they're inject'd + var x: Foo + $($typeof(x), baz2(3), baz3(4)) + doAssert foo() == """("Foo", 30, 13)""" + + block: # illustrates how symbol visiblity can be controlled precisely using `mixin` + proc locafun1(): auto = "in locafun1 (caller scope)" # this will be used because of `mixin locafun1` => explicit hijacking is ok + proc locafun2(): auto = "in locafun2 (caller scope)" # this won't be used => no hijacking + proc locafun3(): auto = "in locafun3 (caller scope)" + doAssert mixinExample() == ("in locafun1 (caller scope)", "in locafun2", "in locafun3 (caller scope)") + +static: main() +main() diff --git a/tests/stdlib/tgetaddrinfo.nim b/tests/stdlib/tgetaddrinfo.nim index ed8ec8b68..3a90034c8 100644 --- a/tests/stdlib/tgetaddrinfo.nim +++ b/tests/stdlib/tgetaddrinfo.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc" exitcode: 0 output: "" """ @@ -6,6 +7,7 @@ discard """ # bug: https://github.com/nim-lang/Nim/issues/10198 import nativesockets +import std/assertions block DGRAM_UDP: let aiList = getAddrInfo("127.0.0.1", 999.Port, AF_INET, SOCK_DGRAM, IPPROTO_UDP) diff --git a/tests/stdlib/tgetfileinfo.nim b/tests/stdlib/tgetfileinfo.nim index 19de193e4..ae1480a4c 100644 --- a/tests/stdlib/tgetfileinfo.nim +++ b/tests/stdlib/tgetfileinfo.nim @@ -1,8 +1,11 @@ discard """ + matrix: "--mm:refc; --mm:orc" output: "pcDir\npcFile\npcLinkToDir\npcLinkToFile\n" + joinable: false """ import os, strutils +import std/[syncio, assertions] # Cases # 1 - String : Existing File : Symlink true # 2 - String : Existing File : Symlink false @@ -125,10 +128,37 @@ proc testGetFileInfo = echo pcLinkToDir echo pcLinkToFile + doAssert dirInfo.isSpecial == false + doAssert fileInfo.isSpecial == false + when defined(posix): + doAssert linkDirInfo.isSpecial == false + doAssert linkFileInfo.isSpecial == false + removeDir(dirPath) removeFile(filePath) when defined(posix): removeFile(linkDirPath) removeFile(linkFilePath) + # Test that `isSpecial` is set correctly + block: + when defined(posix): + let + tmp = getTempDir() + fifoPath = tmp / "test-fifo" + linkFifoPath = tmp / "test-link-fifo" + + doAssert execShellCmd("mkfifo " & fifoPath) == 0 + createSymlink(fifoPath, linkFifoPath) + + let + fifoInfo = getFileInfo(fifoPath) + linkFifoInfo = getFileInfo(linkFifoPath) + + doAssert fifoInfo.isSpecial == true + doAssert linkFifoInfo.isSpecial == true + + removeFile(fifoPath) + removeFile(linkFifoPath) + testGetFileInfo() diff --git a/tests/stdlib/tgetprotobyname.nim b/tests/stdlib/tgetprotobyname.nim new file mode 100644 index 000000000..1fc060ffe --- /dev/null +++ b/tests/stdlib/tgetprotobyname.nim @@ -0,0 +1,19 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import nativesockets +import std/assertions + +doAssert getProtoByName("ipv6") == 41 +doAssert getProtoByName("tcp") == 6 +doAssert getProtoByName("udp") == 17 +doAssert getProtoByName("icmp") == 1 +doAssert getProtoByName("ipv6-icmp") == 58 + +when defined(windows): + doAssertRaises(OSError): + discard getProtoByName("raw") + +doAssertRaises(OSError): + discard getProtoByName("Error") diff --git a/tests/stdlib/tglobs.nim b/tests/stdlib/tglobs.nim new file mode 100644 index 000000000..4aa21992c --- /dev/null +++ b/tests/stdlib/tglobs.nim @@ -0,0 +1,25 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/private/globs +import std/assertions + +template main = + when defined(windows): + doAssert nativeToUnixPath("C:") == "/C" + doAssert nativeToUnixPath(r"D:\") == "/D/" + doAssert nativeToUnixPath(r"E:\a") == "/E/a" + doAssert nativeToUnixPath(r"E:\a1\") == "/E/a1/" + doAssert nativeToUnixPath(r"E:\a1\bc") == "/E/a1/bc" + doAssert nativeToUnixPath(r"\a1\bc") == "/a1/bc" + doAssert nativeToUnixPath(r"a1\bc") == "a1/bc" + doAssert nativeToUnixPath("a1") == "a1" + doAssert nativeToUnixPath("") == "" + doAssert nativeToUnixPath(".") == "." + doAssert nativeToUnixPath("..") == ".." + doAssert nativeToUnixPath(r"..\") == "../" + doAssert nativeToUnixPath(r"..\..\.\") == "../.././" + +static: main() +main() diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 259ced2aa..4555fbcb3 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,34 +1,242 @@ - discard """ -output: ''' -[Suite] hashes + matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:on; --backend:c -d:nimStringHash2; --backend:cpp -d:nimStringHash2; --backend:js -d:nimStringHash2" +""" -[Suite] hashing +import std/hashes +from stdtest/testutils import disableVm, whenVMorJs +import std/assertions -''' -""" +when not defined(js) and not defined(cpp): + block: + var x = 12 + iterator hello(): int {.closure.} = + yield x + + discard hash(hello) + +block hashes: + block hashing: + var dummy = 0.0 + doAssert hash(dummy) == hash(-dummy) + + # "VM and runtime should make the same hash value (hashIdentity)" + block: + const hi123 = hashIdentity(123) + doAssert hashIdentity(123) == hi123 + + # "VM and runtime should make the same hash value (hashWangYi1)" + block: + const wy123 = hashWangYi1(123) + doAssert wy123 != 0 + doAssert hashWangYi1(123) == wy123 + const wyNeg123 = hashWangYi1(-123) + doAssert wyNeg123 != 0 + when not defined(js): # TODO: fixme it doesn't work for JS + doAssert hashWangYi1(-123) == wyNeg123 + + + # "hashIdentity value incorrect at 456" + block: + doAssert hashIdentity(456) == 456 + + # "hashWangYi1 value incorrect at 456" + block: + when Hash.sizeof < 8: + doAssert hashWangYi1(456) == 1293320666 + else: + doAssert hashWangYi1(456) == -6421749900419628582 + +template jsNoInt64: untyped = + when defined js: + when compiles(compileOption("jsbigint64")): + when not compileOption("jsbigint64"): true + else: false + else: false + else: false +const sHash2 = (when defined(nimStringHash2) or jsNoInt64(): true else: false) + +block empty: + const emptyStrHash = # Hash=int=4B on js even w/--jsbigint64:on => cast[Hash] + when sHash2: 0 else: cast[Hash](-7286425919675154353i64) + var + a = "" + b = newSeq[char]() + c = newSeq[int]() + d = cstring"" + e = "abcd" + doAssert hash(a) == emptyStrHash + doAssert hash(b) == emptyStrHash + doAssert hash(c) == 0 + doAssert hash(d) == emptyStrHash + doAssert hashIgnoreCase(a) == 0 + doAssert hashIgnoreStyle(a) == 0 + doAssert hash(e, 3, 2) == emptyStrHash + +block sameButDifferent: + doAssert hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) + doAssert hash("aa bb aaaa1234") == hash(cstring"aa bb aaaa1234") + doAssert hashIgnoreCase("aA bb aAAa1234") == hashIgnoreCase("aa bb aaaa1234") + doAssert hashIgnoreStyle("aa_bb_AAaa1234") == hashIgnoreCase("aaBBAAAa1234") + +block smallSize: # no multibyte hashing + let + xx = @['H', 'i'] + ii = @[72'u8, 105] + ss = "Hi" + doAssert hash(xx) == hash(ii) + doAssert hash(xx) == hash(ss) + doAssert hash(xx) == hash(xx, 0, xx.high) + doAssert hash(ss) == hash(ss, 0, ss.high) + +block largeSize: # longer than 4 characters + let + xx = @['H', 'e', 'l', 'l', 'o'] + xxl = @['H', 'e', 'l', 'l', 'o', 'w', 'e', 'e', 'n', 's'] + ssl = "Helloweens" + doAssert hash(xxl) == hash(ssl) + doAssert hash(xxl) == hash(xxl, 0, xxl.high) + doAssert hash(ssl) == hash(ssl, 0, ssl.high) + doAssert hash(xx) == hash(xxl, 0, 4) + doAssert hash(xx) == hash(ssl, 0, 4) + doAssert hash(xx, 0, 3) == hash(xxl, 0, 3) + doAssert hash(xx, 0, 3) == hash(ssl, 0, 3) + +proc main() = + doAssert hash(0.0) == hash(0) + # bug #16061 + when not sHash2: # Hash=int=4B on js even w/--jsbigint64:on => cast[Hash] + doAssert hash(cstring"abracadabra") == cast[Hash](-1119910118870047694i64) + else: + doAssert hash(cstring"abracadabra") == 97309975 + doAssert hash(cstring"abracadabra") == hash("abracadabra") + + when sizeof(int) == 8 or defined(js): + block: + var s: seq[Hash] + for a in [0.0, 1.0, -1.0, 1000.0, -1000.0]: + let b = hash(a) + doAssert b notin s + s.add b + when defined(js): + doAssert hash(0.345602) == 2035867618 + doAssert hash(234567.45) == -20468103 + doAssert hash(-9999.283456) == -43247422 + doAssert hash(84375674.0) == 707542256 + else: + doAssert hash(0.345602) == 387936373221941218 + doAssert hash(234567.45) == -8179139172229468551 + doAssert hash(-9999.283456) == 5876943921626224834 + doAssert hash(84375674.0) == 1964453089107524848 + else: + doAssert hash(0.345602) != 0 + doAssert hash(234567.45) != 0 + doAssert hash(-9999.283456) != 0 + doAssert hash(84375674.0) != 0 + + block: # bug #16555 + proc fn(): auto = + # avoids hardcoding values + var a = "abc\0def" + var b = a.cstring + result = (hash(a), hash(b)) + doAssert result[0] != result[1] + when not defined(js): + doAssert fn() == static(fn()) + else: + # xxx this is a tricky case; consistency of hashes for cstring's containing + # '\0\' matters for c backend but less for js backend since such strings + # are much less common in js backend; we make vm for js backend consistent + # with c backend instead of js backend because FFI code (or other) could + # run at CT, expecting c semantics. + discard + + block: # hash(object) + type + Obj = object + x: int + y: string + Obj2[T] = object + x: int + y: string + Obj3 = object + x: int + y: string + Obj4 = object + case t: bool + of false: + x: int + of true: + y: int + z: int + Obj5 = object + case t: bool + of false: + x: int + of true: + y: int + z: int + + proc hash(a: Obj2): Hash = hash(a.x) + proc hash(a: Obj3): Hash = hash((a.x,)) + proc hash(a: Obj5): Hash = + case a.t + of false: hash(a.x) + of true: hash(a.y) + + doAssert hash(Obj(x: 520, y: "Nim")) != hash(Obj(x: 520, y: "Nim2")) + doAssert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2")) + doAssert hash(Obj2[float](x: 520, y: "Nim")) != hash(Obj2[float](x: 521, y: "Nim2")) + doAssert hash(Obj3(x: 520, y: "Nim")) == hash(Obj3(x: 520, y: "Nim2")) + + doAssert hash(Obj4(t: false, x: 1)) == hash(Obj4(t: false, x: 1)) + doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: false, x: 2)) + doAssert hash(Obj4(t: false, x: 1)) != hash(Obj4(t: true, y: 1)) -import unittest, hashes + doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: false, x: 2)) + doAssert hash(Obj5(t: false, x: 1)) == hash(Obj5(t: true, y: 1)) + doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: true, y: 2)) -suite "hashes": - suite "hashing": - test "0.0 and -0.0 should have the same hash value": - var dummy = 0.0 - check hash(dummy) == hash(-dummy) + block: # hash(ref|ptr|pointer) + var a: array[10, uint8] + # disableVm: + whenVMorJs: + # pending fix proposed in https://github.com/nim-lang/Nim/issues/15952#issuecomment-786312417 + discard + do: + doAssert a[0].addr.hash != a[1].addr.hash + doAssert cast[pointer](a[0].addr).hash == a[0].addr.hash - test "VM and runtime should make the same hash value (hashIdentity)": - const hi123 = hashIdentity(123) - check hashIdentity(123) == hi123 + block: # hash(ref) + type A = ref object + x: int + let a = A(x: 3) + disableVm: # xxx Error: VM does not support 'cast' from tyRef to tyPointer + let ha = a.hash + doAssert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`. + a.x = 4 + doAssert ha == a.hash # the hash only depends on the address - test "VM and runtime should make the same hash value (hashWangYi1)": - const wy123 = hashWangYi1(123) - check hashWangYi1(123) == wy123 + block: # hash(proc) + proc fn(a: int): auto = a*2 + doAssert fn isnot "closure" + doAssert fn is (proc) + const fn2 = fn + let fn3 = fn + whenVMorJs: discard + do: + doAssert hash(fn2) == hash(fn) + doAssert hash(fn3) == hash(fn) - test "hashIdentity value incorrect at 456": - check hashIdentity(456) == 456 + block: # hash(closure) + proc outer() = + var a = 0 + proc inner() = a.inc + doAssert inner is "closure" + let inner2 = inner + whenVMorJs: discard + do: + doAssert hash(inner2) == hash(inner) + outer() - test "hashWangYi1 value incorrect at 456": - when Hash.sizeof < 8: - check hashWangYi1(456) == 1293320666 - else: - check hashWangYi1(456) == -6421749900419628582 +static: main() +main() diff --git a/tests/stdlib/theapqueue.nim b/tests/stdlib/theapqueue.nim new file mode 100644 index 000000000..afb09c7e3 --- /dev/null +++ b/tests/stdlib/theapqueue.nim @@ -0,0 +1,106 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/heapqueue +import std/assertions + +proc toSortedSeq[T](h: HeapQueue[T]): seq[T] = + var tmp = h + result = @[] + while tmp.len > 0: + result.add(pop(tmp)) + +proc heapProperty[T](h: HeapQueue[T]): bool = + for k in 0 .. h.len - 2: # the last element is always a leaf + let left = 2 * k + 1 + if left < h.len and h[left] < h[k]: + return false + let right = left + 1 + if right < h.len and h[right] < h[k]: + return false + true + +template main() = + block: # simple sanity test + var heap = initHeapQueue[int]() + let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] + for item in data: + push(heap, item) + doAssert(heap == data.toHeapQueue) + doAssert(heap[0] == 0) + doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + + block: # test del + var heap = initHeapQueue[int]() + let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] + for item in data: push(heap, item) + + heap.del(0) + doAssert(heap[0] == 1) + + heap.del(heap.find(7)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9]) + + heap.del(heap.find(5)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9]) + + heap.del(heap.find(6)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9]) + + heap.del(heap.find(2)) + doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9]) + + doAssert(heap.find(2) == -1) + + block: # test del last + var heap = initHeapQueue[int]() + let data = [1, 2, 3] + for item in data: push(heap, item) + + heap.del(2) + doAssert(heap.toSortedSeq == @[1, 2]) + + heap.del(1) + doAssert(heap.toSortedSeq == @[1]) + + heap.del(0) + doAssert(heap.toSortedSeq == @[]) + + block: # testing the heap proeprty + var heap = [1, 4, 2, 5].toHeapQueue + doAssert heapProperty(heap) + + heap.push(42) + doAssert heapProperty(heap) + heap.push(0) + doAssert heapProperty(heap) + heap.push(3) + doAssert heapProperty(heap) + heap.push(3) + doAssert heapProperty(heap) + + # [0, 3, 1, 4, 42, 2, 3, 5] + + discard heap.pop() + doAssert heapProperty(heap) + discard heap.pop() + doAssert heapProperty(heap) + + heap.del(2) + doAssert heapProperty(heap) + + # [2, 3, 5, 4, 42] + + discard heap.replace(12) + doAssert heapProperty(heap) + discard heap.replace(1) + doAssert heapProperty(heap) + + discard heap.pushpop(2) + doAssert heapProperty(heap) + discard heap.pushpop(0) + doAssert heapProperty(heap) + +static: main() +main() diff --git a/tests/stdlib/thighlite.nim b/tests/stdlib/thighlite.nim new file mode 100644 index 000000000..0cd334254 --- /dev/null +++ b/tests/stdlib/thighlite.nim @@ -0,0 +1,45 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import unittest, strutils +import ../../lib/packages/docutils/highlite +import std/objectdollar + +block: # Nim tokenizing + test "string literals and escape seq": + check("\"ok1\\nok2\\nok3\"".tokenize(langNim) == + @[("\"ok1", gtStringLit), ("\\n", gtEscapeSequence), ("ok2", gtStringLit), + ("\\n", gtEscapeSequence), ("ok3\"", gtStringLit) + ]) + check("\"\"\"ok1\\nok2\\nok3\"\"\"".tokenize(langNim) == + @[("\"\"\"ok1\\nok2\\nok3\"\"\"", gtLongStringLit) + ]) + + test "whitespace at beginning of line is preserved": + check(" discard 1".tokenize(langNim) == + @[(" ", gtWhitespace), ("discard", gtKeyword), (" ", gtWhitespace), + ("1", gtDecNumber) + ]) + +block: # Cmd (shell) tokenizing + test "cmd with dollar and output": + check( + dedent""" + $ nim c file.nim + out: file [SuccessX]""" + .tokenize(langConsole) == + @[("$ ", gtPrompt), ("nim", gtProgram), + (" ", gtWhitespace), ("c", gtOption), (" ", gtWhitespace), + ("file.nim", gtIdentifier), ("\n", gtWhitespace), + ("out: file [SuccessX]", gtProgramOutput) + ]) + +block: # bug #21232 + let code = "/" + var toknizr: GeneralTokenizer + + initGeneralTokenizer(toknizr, code) + + getNextToken(toknizr, langC) + check $toknizr == """(kind: gtOperator, start: 0, length: 1, buf: "/", pos: 1, state: gtEof, lang: langC)""" diff --git a/tests/stdlib/thtmlparser.nim b/tests/stdlib/thtmlparser.nim index 2ac7b4004..853a1c0cc 100644 --- a/tests/stdlib/thtmlparser.nim +++ b/tests/stdlib/thtmlparser.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc" targets: "c js" output: ''' true @@ -11,7 +12,7 @@ import htmlparser import xmltree import strutils from streams import newStringStream - +import std/assertions block t2813: const @@ -47,7 +48,7 @@ block t2813: for n in tree.findAll("table"): n.findAll("tr", rows) # len = 2 break - assert tree.findAll("tr").len == rows.len + doAssert tree.findAll("tr").len == rows.len block t2814: diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index 6a634d90f..0bd479670 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -1,15 +1,22 @@ discard """ cmd: "nim c --threads:on -d:ssl $file" - exitcode: 0 - output: "OK" - disabled: true + disabled: "openbsd" + disabled: "freebsd" + disabled: "windows" """ +#[ +disabled: see https://github.com/timotheecour/Nim/issues/528 +]# + import strutils from net import TimeoutError import nativesockets, os, httpclient, asyncdispatch +import std/[assertions, syncio] +from stdtest/testutils import enableRemoteNetworking + const manualTests = false proc makeIPv6HttpServer(hostname: string, port: Port, @@ -17,7 +24,7 @@ proc makeIPv6HttpServer(hostname: string, port: Port, let fd = createNativeSocket(AF_INET6) setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1) var aiList = getAddrInfo(hostname, port, AF_INET6) - if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: + if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: freeAddrInfo(aiList) raiseOSError(osLastError()) freeAddrInfo(aiList) @@ -39,19 +46,20 @@ proc makeIPv6HttpServer(hostname: string, port: Port, proc asyncTest() {.async.} = var client = newAsyncHttpClient() - var resp = await client.request("http://example.com/") + var resp = await client.request("http://example.com/", HttpGet) doAssert(resp.code.is2xx) var body = await resp.body body = await resp.body # Test caching doAssert("<title>Example Domain</title>" in body) resp = await client.request("http://example.com/404") - doAssert(resp.code.is4xx) - doAssert(resp.code == Http404) - doAssert(resp.status == Http404) + doAssert(resp.code.is4xx or resp.code.is5xx) + doAssert(resp.code == Http404 or resp.code == Http500) + doAssert(resp.status == $Http404 or resp.status == $Http500) - resp = await client.request("https://google.com/") - doAssert(resp.code.is2xx or resp.code.is3xx) + when false: # occasionally does not give success code + resp = await client.request("https://google.com/") + doAssert(resp.code.is2xx or resp.code.is3xx) # getContent try: @@ -102,17 +110,18 @@ proc asyncTest() {.async.} = proc syncTest() = var client = newHttpClient() - var resp = client.request("http://example.com/") + var resp = client.request("http://example.com/", HttpGet) doAssert(resp.code.is2xx) doAssert("<title>Example Domain</title>" in resp.body) resp = client.request("http://example.com/404") - doAssert(resp.code.is4xx) - doAssert(resp.code == Http404) - doAssert(resp.status == Http404) + doAssert(resp.code.is4xx or resp.code.is5xx) + doAssert(resp.code == Http404 or resp.code == Http500) + doAssert(resp.status == $Http404 or resp.status == $Http500) - resp = client.request("https://google.com/") - doAssert(resp.code.is2xx or resp.code.is3xx) + when false: # occasionally does not give success code + resp = client.request("https://google.com/") + doAssert(resp.code.is2xx or resp.code.is3xx) # getContent try: @@ -144,6 +153,12 @@ proc syncTest() = client.close() + # SIGSEGV on HEAD body read: issue #16743 + block: + let client = newHttpClient() + let resp = client.head("http://httpbin.org/head") + doAssert(resp.body == "") + when false: # Disabled for now because it causes troubles with AppVeyor # Timeout test. @@ -166,7 +181,7 @@ proc ipv6Test() = client.close() ipv6Test() -syncTest() -waitFor(asyncTest()) -echo "OK" +when enableRemoteNetworking: + syncTest() + waitFor(asyncTest()) diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim index 71745c9df..6b963f029 100644 --- a/tests/stdlib/thttpclient_ssl.nim +++ b/tests/stdlib/thttpclient_ssl.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim $target --threads:on -d:ssl $options $file" + cmd: "nim $target --mm:refc -d:ssl $options $file" disabled: "openbsd" """ @@ -13,9 +13,12 @@ discard """ ## Test with: ## ./bin/nim c -d:ssl -p:. --threads:on -r tests/stdlib/thttpclient_ssl.nim -when not defined(windows): - # Disabled on Windows due to old OpenSSL version +from stdtest/testutils import disableSSLTesting + +when not defined(windows) and not disableSSLTesting(): + # Disabled on Windows due to old OpenSSL version + import std/[formatfloat, syncio] import httpclient, net, @@ -101,7 +104,7 @@ when not defined(windows): let t = spawn runServer(port) sleep(100) - var client = newHttpClient() + var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyNone)) try: log "client: connect" discard client.getContent("https://127.0.0.1:12345") @@ -129,3 +132,19 @@ when not defined(windows): msg.contains("certificate verify failed")): echo "CVerifyPeer exception: " & msg check(false) + + test "HttpClient with CVerifyPeerUseEnvVars": + const port = 12346.Port + let t = spawn runServer(port) + sleep(100) + + putEnv("SSL_CERT_FILE", getCurrentDir() / certFile) + var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeerUseEnvVars)) + try: + log "client: connect" + discard client.getContent("https://127.0.0.1:12346") + except: + let msg = getCurrentExceptionMsg() + log "client: exception: " & msg + log "getContent should not have raised an exception" + fail() diff --git a/tests/stdlib/thttpclient_standalone.nim b/tests/stdlib/thttpclient_standalone.nim new file mode 100644 index 000000000..2f432eede --- /dev/null +++ b/tests/stdlib/thttpclient_standalone.nim @@ -0,0 +1,59 @@ +discard """ + cmd: "nim c --threads:on $file" +""" + +import asynchttpserver, httpclient, asyncdispatch, strutils, net + +import std/assertions + +block: # bug #16436 + proc startServer(): AsyncHttpServer = + result = newAsyncHttpServer() + result.listen(Port(0)) + + proc processRequest(server: AsyncHttpServer) {.async.} = + proc cb(req: Request) {.async.} = + let headers = { "Content-length": "15"} # Provide invalid content-length + await req.respond(Http200, "Hello World", headers.newHttpHeaders()) + + await server.acceptRequest(cb) + + proc runClient(port: Port) {.async.} = + let c = newAsyncHttpClient(headers = {"Connection": "close"}.newHttpHeaders) + discard await c.getContent("http://127.0.0.1:" & $port) + doAssert false, "should fail earlier" + + let server = startServer() + asyncCheck processRequest(server) + let port = server.getPort() + doAssertRaises(ProtocolError): + waitFor runClient(port) + +block: # bug #14794 (And test for presence of content-length header when using postContent) + proc startServer(): AsyncHttpServer = + result = newAsyncHttpServer() + result.listen(Port(0)) + + proc runServer(server: AsyncHttpServer) {.async.} = + proc cb(req: Request) {.async.} = + doAssert(req.body.endsWith(httpNewLine), "Multipart body does not end with a newline.") + # this next line is probably not required because asynchttpserver does not call + # the callback when there is no content-length header. It instead errors with + # Error: unhandled exception: 411 Length Required + # Added for good measure in case the server becomes more permissive. + doAssert(req.headers.hasKey("content-length"), "Content-Length header is not present.") + asyncCheck req.respond(Http200, "OK") + + await server.acceptRequest(cb) + + proc runClient(port: Port) {.async.} = + let c = newAsyncHttpClient() + let data = newMultipartData() + data.add("file.txt", "This is intended to be an example text file.\r\nThis would be the second line.\r\n") + discard await c.postContent("http://127.0.0.1:" & $port, multipart = data) + c.close() + + let server = startServer() + let port = server.getPort() + asyncCheck runServer(server) + waitFor runClient(port) diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim index f96fcc3b8..93e7d85c6 100644 --- a/tests/stdlib/thttpcore.nim +++ b/tests/stdlib/thttpcore.nim @@ -1,36 +1,103 @@ discard """ -output: "[Suite] httpcore" + matrix: "--mm:refc; --mm:orc" """ -import unittest - import httpcore, strutils +import std/assertions -suite "httpcore": - - test "HttpCode": - assert $Http418 == "418 I'm a teapot" - assert Http418.is4xx() == true - assert Http418.is2xx() == false +block: + block HttpCode: + doAssert $Http418 == "418 I'm a teapot" + doAssert Http418.is4xx() == true + doAssert Http418.is2xx() == false - test "headers": + block headers: var h = newHttpHeaders() - assert h.len == 0 + doAssert h.len == 0 h.add("Cookie", "foo") - assert h.len == 1 - assert h.hasKey("cooKIE") - assert h["Cookie"] == "foo" - assert h["cookie"] == "foo" + doAssert h.len == 1 + doAssert h.hasKey("cooKIE") + doAssert h["Cookie"] == "foo" + doAssert h["cookie"] == "foo" h["cookie"] = @["bar", "x"] - assert h["Cookie"] == "bar" - assert h["Cookie", 1] == "x" - assert h["Cookie"].contains("BaR") == true - assert h["Cookie"].contains("X") == true - assert "baR" in h["cookiE"] + doAssert h["Cookie"] == "bar" + doAssert h["Cookie", 1] == "x" + doAssert h["Cookie"].contains("BaR") == true + doAssert h["Cookie"].contains("X") == true + doAssert "baR" in h["cookiE"] h.del("coOKie") - assert h.len == 0 + doAssert h.len == 0 # Test that header constructor works with repeated values let h1 = newHttpHeaders({"a": "1", "a": "2", "A": "3"}) - assert seq[string](h1["a"]).join(",") == "1,2,3" + doAssert seq[string](h1["a"]).join(",") == "1,2,3" + + block test_cookies_with_comma: + doAssert parseHeader("cookie: foo, bar") == ("cookie", @["foo, bar"]) + doAssert parseHeader("cookie: foo, bar, prologue") == ("cookie", @["foo, bar, prologue"]) + doAssert parseHeader("cookie: foo, bar, prologue, starlight") == ("cookie", @["foo, bar, prologue, starlight"]) + + doAssert parseHeader("cookie: foo, bar") == ("cookie", @["foo, bar"]) + doAssert parseHeader("cookie: foo, bar, prologue") == ("cookie", @["foo, bar, prologue"]) + doAssert parseHeader("cookie: foo, bar, prologue, starlight") == ("cookie", @["foo, bar, prologue, starlight"]) + + doAssert parseHeader("Cookie: foo, bar") == (key: "Cookie", value: @["foo, bar"]) + doAssert parseHeader("Cookie: foo, bar, prologue") == (key: "Cookie", value: @["foo, bar, prologue"]) + doAssert parseHeader("Cookie: foo, bar, prologue, starlight") == (key: "Cookie", value: @["foo, bar, prologue, starlight"]) + + doAssert parseHeader("Accept: foo, bar") == (key: "Accept", value: @["foo", "bar"]) + doAssert parseHeader("Accept: foo, bar, prologue") == (key: "Accept", value: @["foo", "bar", "prologue"]) + doAssert parseHeader("Accept: foo, bar, prologue, starlight") == (key: "Accept", value: @["foo", "bar", "prologue", "starlight"]) + + block add_empty_sequence_to_HTTP_headers: + block: + var headers = newHttpHeaders() + headers["empty"] = @[] + + doAssert not headers.hasKey("empty") + + block: + var headers = newHttpHeaders() + headers["existing"] = "true" + headers["existing"] = @[] + + doAssert not headers.hasKey("existing") + + block: + var headers = newHttpHeaders() + headers["existing"] = @["true"] + headers["existing"] = @[] + + doAssert not headers.hasKey("existing") + + block: + var headers = newHttpHeaders() + headers["existing"] = @[] + headers["existing"] = @["true"] + doAssert headers.hasKey("existing") + +block: + var test = newHttpHeaders() + test["Connection"] = @["Upgrade", "Close"] + doAssert test["Connection", 0] == "Upgrade" + doAssert test["Connection", 1] == "Close" + test.add("Connection", "Test") + doAssert test["Connection", 2] == "Test" + doAssert "upgrade" in test["Connection"] + + # Bug #5344. + doAssert parseHeader("foobar: ") == ("foobar", @[""]) + let (key, value) = parseHeader("foobar: ") + test = newHttpHeaders() + test[key] = value + doAssert test["foobar"] == "" + + doAssert parseHeader("foobar:") == ("foobar", @[""]) + + block: # test title case + var testTitleCase = newHttpHeaders(titleCase=true) + testTitleCase.add("content-length", "1") + doAssert testTitleCase.hasKey("Content-Length") + for key, val in testTitleCase: + doAssert key == "Content-Length" diff --git a/tests/stdlib/timportutils.nim b/tests/stdlib/timportutils.nim new file mode 100644 index 000000000..672092282 --- /dev/null +++ b/tests/stdlib/timportutils.nim @@ -0,0 +1,151 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/[importutils, assertions] +import stdtest/testutils +import mimportutils + +template main = + block: # privateAccess + assertAll: + var a: A + var b = initB() # B is private + compiles(a.a0) + compiles(b.b0) + not compiles(a.ha1) + not compiles(b.hb1) + + block: + assertAll: + privateAccess A + compiles(a.ha1) + a.ha1 == 0.0 + not compiles(a.hb1) + privateAccess b.typeof + b.hb1 = 3 + type B2 = b.typeof + let b2 = B2(b0: 4, hb1: 5) + b.hb1 == 3 + b2 == B2(b0: 4, hb1: 5) + + assertAll: + not compiles(a.ha1) + not compiles(b.hb1) + + block: + assertAll: + not compiles(C(c0: 1, hc1: 2)) + privateAccess C + let c = C(c0: 1, hc1: 2) + c.hc1 == 2 + + block: + assertAll: + not compiles(E[int](he1: 1)) + privateAccess E[int] + var e = E[int](he1: 1) + e.he1 == 1 + e.he1 = 2 + e.he1 == 2 + e.he1 += 3 + e.he1 == 5 + # xxx caveat: this currently compiles but in future, we may want + # to make `privateAccess E[int]` only affect a specific instantiation; + # note that `privateAccess E` does work to cover all instantiations. + var e2 = E[float](he1: 1) + + block: + assertAll: + not compiles(E[int](he1: 1)) + privateAccess E + var e = E[int](he1: 1) + e.he1 == 1 + + block: + assertAll: + not compiles(F[int, int](h3: 1)) + privateAccess F[int, int] + var e = F[int, int](h3: 1) + e.h3 == 1 + + block: + assertAll: + not compiles(F[int, int](h3: 1)) + privateAccess F[int, int].default[].typeof + var e = F[int, int](h3: 1) + e.h3 == 1 + + block: + assertAll: + var a = G[int]() + var b = a.addr + privateAccess b.type + discard b.he1 + discard b[][].he1 + + block: + assertAll: + privateAccess H[int] + var a = H[int](h5: 2) + + block: + assertAll: + privateAccess PA + var pa = PA(a0: 1, ha1: 2) + pa.ha1 == 2 + pa.ha1 = 3 + pa.ha1 == 3 + + block: + assertAll: + var b = BAalias() + not compiles(b.hb1) + privateAccess BAalias + discard b.hb1 + + block: + assertAll: + var a = A(a0: 1) + var a2 = a.addr + not compiles(a2.ha1) + privateAccess PtA + a2.type is PtA + a2.ha1 = 2 + a2.ha1 == 2 + a.ha1 = 3 + a2.ha1 == 3 + + block: + disableVm: + assertAll: + var a = A.create() + defer: dealloc(a) + a is PtA + a.typeof is PtA + not compiles(a.ha1) + privateAccess a.typeof + a.ha1 = 2 + a.ha1 == 2 + a[].ha1 = 3 + a.ha1 == 3 + + block: + disableVm: + assertAll: + var a = A.create() + defer: dealloc(a) + privateAccess PtA + a.ha1 == 0 + + block: + privateAccess PityRef + let x = PityRef[int](a: 1) # works + doAssert x.a == 1 + + privateAccess Hope + let y = Hope[int](a: 1) + doAssert y.a == 1 + +static: main() +main() diff --git a/tests/stdlib/tintsets.nim b/tests/stdlib/tintsets.nim index f859b87ae..191ef117e 100644 --- a/tests/stdlib/tintsets.nim +++ b/tests/stdlib/tintsets.nim @@ -1,65 +1,6 @@ -import intsets -import std/sets +import ./mintsets -from sequtils import toSeq -from algorithm import sorted - -proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted -template sortedItems(t: untyped): untyped = sorted(toSeq(t)) - -block: # we use HashSet as groundtruth, it's well tested elsewhere - template testDel(t, t0) = - - template checkEquals() = - doAssert t.len == t0.len - for k in t0: - doAssert k in t - for k in t: - doAssert k in t0 - - doAssert sortedItems(t) == sortedItems(t0) - - template incl2(i) = - t.incl i - t0.incl i - - template excl2(i) = - t.excl i - t0.excl i - - block: - var expected: seq[int] - let n = 100 - let n2 = n*2 - for i in 0..<n: - incl2(i) - checkEquals() - for i in 0..<n: - if i mod 3 == 0: - if i < n div 2: - excl2(i) - else: - t0.excl i - doAssert i in t - doAssert not t.missingOrExcl(i) - - checkEquals() - for i in n..<n2: - incl2(i) - checkEquals() - for i in 0..<n2: - if i mod 7 == 0: - excl2(i) - checkEquals() - - # notin check - for i in 0..<t.len: - if i mod 7 == 0: - doAssert i notin t0 - doAssert i notin t - # issue #13505 - doAssert t.missingOrExcl(i) - - var t: IntSet - var t0: HashSet[int] - testDel(t, t0) +block: # bug https://github.com/nim-lang/Nim/pull/15564#issuecomment-729878104 + # related to bug #11167 + test1() + test2() diff --git a/tests/stdlib/tio.nim b/tests/stdlib/tio.nim new file mode 100644 index 000000000..80a119763 --- /dev/null +++ b/tests/stdlib/tio.nim @@ -0,0 +1,60 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +# xxx move to here other tests that belong here; io is a proper module + +import std/os +from stdtest/specialpaths import buildDir +import std/[assertions, syncio] + +block: # readChars + let file = buildDir / "D20201118T205105.txt" + let s = "he\0l\0lo" + writeFile(file, s) + defer: removeFile(file) + let f = open(file) + defer: close(f) + let n = f.getFileInfo.blockSize + var buf = newString(n) + template fn = + let n2 = f.readChars(buf) + doAssert n2 == s.len + doAssert buf[0..<n2] == s + fn() + setFilePos(f, 0) + fn() + + block: + setFilePos(f, 0) + var s2: string + let nSmall = 2 + for ai in buf.mitems: ai = '\0' + var n2s: seq[int] + while true: + let n2 = f.readChars(toOpenArray(buf, 0, nSmall-1)) + # xxx: maybe we could support: toOpenArray(buf, 0..nSmall) + n2s.add n2 + s2.add buf[0..<n2] + if n2 == 0: + break + doAssert n2s == @[2,2,2,1,0] + doAssert s2 == s + + +import std/strutils + +block: # bug #21273 + let FILE = buildDir / "D20220119T134305.txt" + + let hex = "313632313920313632343720313632353920313632363020313632393020323035363520323037323120323131353020323239393820323331303520323332313020323332343820323332363820" + + + writeFile FILE, parseHexStr(hex) + + doAssert readFile(FILE).toHex == hex + + let f = open(FILE) + var s = newString(80) + while f.readLine(s): + doAssert s.toHex == hex diff --git a/tests/stdlib/tisolation.nim b/tests/stdlib/tisolation.nim new file mode 100644 index 000000000..18b83ea2e --- /dev/null +++ b/tests/stdlib/tisolation.nim @@ -0,0 +1,135 @@ +discard """ + targets: "c cpp" + matrix: "--gc:refc; --gc:orc" +""" + +import std/[isolation, json] +import std/[assertions, objectdollar] + + +proc main(moveZeroesOut: static bool) = + block: + type + Empty = ref object + + + var x = isolate(Empty()) + discard extract(x) + + block: # string literals + var data = isolate("string") + doAssert data.extract == "string" + if moveZeroesOut: + doAssert data.extract == "" + + block: # string literals + var data = isolate("") + doAssert data.extract == "" + if moveZeroesOut: + doAssert data.extract == "" + + block: + var src = "string" + var data = isolate(move src) + doAssert data.extract == "string" + if moveZeroesOut: + doAssert src.len == 0 + + block: # int literals + var data = isolate(1) + doAssert data.extract == 1 + if moveZeroesOut: + doAssert data.extract == 0 + + block: # float literals + var data = isolate(1.6) + doAssert data.extract == 1.6 + if moveZeroesOut: + doAssert data.extract == 0.0 + + block: + var data = isolate(@["1", "2"]) + doAssert data.extract == @["1", "2"] + if moveZeroesOut: + doAssert data.extract == @[] + + block: + var data = isolate(@["1", "2", "3", "4", "5"]) + doAssert data.extract == @["1", "2", "3", "4", "5"] + if moveZeroesOut: + doAssert data.extract == @[] + + block: + var data = isolate(@["", ""]) + doAssert data.extract == @["", ""] + if moveZeroesOut: + doAssert data.extract == @[] + + block: + var src = @["1", "2"] + var data = isolate(move src) + doAssert data.extract == @["1", "2"] + if moveZeroesOut: + doAssert src.len == 0 + + block: + var data = isolate(@[1, 2]) + doAssert data.extract == @[1, 2] + if moveZeroesOut: + doAssert data.extract == @[] + + block: + var data = isolate(["1", "2"]) + doAssert data.extract == ["1", "2"] + if moveZeroesOut: + doAssert data.extract == ["", ""] + + block: + var data = isolate([1, 2]) + doAssert data.extract == [1, 2] + if moveZeroesOut: + doAssert data.extract == [0, 0] + + block: + type + Test = object + id: int + + var data = isolate(Test(id: 12)) + doAssert data.extract.id == 12 + + block: + type + Test = object + id: int + + var src = Test(id: 12) + var data = isolate(src) + doAssert data.extract.id == 12 + + block: + type + Test = object + id: int + + var src = Test(id: 12) + var data = isolate(move src) + doAssert data.extract.id == 12 + + block: + type + Test = ref object + id: int + + var data = isolate(Test(id: 12)) + doAssert data.extract.id == 12 + + block: + var x: seq[Isolated[JsonNode]] + x.add isolate(newJString("1234")) + + doAssert $x == """@[(value: "1234")]""" + + +static: main(moveZeroesOut = false) +main(moveZeroesOut = true) diff --git a/tests/stdlib/tjsbigints.nim b/tests/stdlib/tjsbigints.nim new file mode 100644 index 000000000..29b0ac3e7 --- /dev/null +++ b/tests/stdlib/tjsbigints.nim @@ -0,0 +1,46 @@ +discard """ + targets: "js" +""" + +import std/[jsbigints, assertions] + + +let big1: JsBigInt = big"2147483647" +let big2: JsBigInt = big"666" +var big3: JsBigInt = big"2" + +doAssert big3 == big"2" +doAssert (big3 xor big2) == big"664" +doAssert (big"555" and big"2") == big"2" +doAssert (big"555" or big"2") == big"555" +doAssert (big1 mod big2) == big"613" +doAssert -big1 == big"-2147483647" +doAssert big1 div big2 == big"3224449" +doAssert big1 + big2 == big"2147484313" +doAssert big1 - big2 == big"2147482981" +doAssert big1 shl big3 == big"8589934588" +doAssert big1 shr big3 == big"536870911" +doAssert big1 * big2 == big"1430224108902" +doAssert $big1 == "2147483647n" +doAssert big1.toCstring(10) == "2147483647".cstring +doAssert big2 ** big3 == big(443556) +var huge = big"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" +huge.inc +huge = huge + -999999999999999999999999999999999999999999999999999999999999999999999999999999999999999'big +doAssert huge == big"1" +var list: seq[JsBigInt] +for i in big"0" .. big"5": + doAssert i is JsBigInt + list.add i +doAssert list == @[big"0", big"1", big"2", big"3", big"4", big"5"] +list = @[] +for i in big"0" ..< big"5": + doAssert i is JsBigInt + list.add i +doAssert list == @[big"0", big"1", big"2", big"3", big"4"] + +block: + let b = 2'big + doAssert -b ** 3'big == -8'big + doAssert -b ** big"2" == big"4" # not -4 because of precedence + doAssert -big"3" == big"-3" diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index bc7ff02b2..e425501f6 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -1,8 +1,37 @@ +discard """ + matrix: "; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on" +""" + + #[ Note: Macro tests are in tests/stdlib/tjsonmacro.nim ]# -import std/[json,parsejson,strutils,streams] +import std/[json,parsejson,strutils] +import std/private/jsutils +from std/math import isNaN +when not defined(js): + import std/streams +import stdtest/testutils +from std/fenv import epsilon +import std/[assertions, objectdollar] + +proc testRoundtrip[T](t: T, expected: string) = + # checks that `T => json => T2 => json2` is such that json2 = json + let j = %t + doAssert $j == expected, $j + doAssert %(j.to(T)) == j + +proc testRoundtripVal[T](t: T, expected: string) = + # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T` + # note that this isn't always possible, e.g. for pointer-like types or nans + let j = %t + doAssert $j == expected, $j + let j2 = ($j).parseJson + doAssert $j2 == expected, $(j2, t) + let t2 = j2.to(T) + doAssert t2 == t + doAssert $(%* t2) == expected # sanity check, because -0.0 = 0.0 but their json representation differs let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}""" # nil passthrough @@ -232,3 +261,122 @@ doAssert isRefSkipDistinct(MyRef) doAssert not isRefSkipDistinct(MyObject) doAssert isRefSkipDistinct(MyDistinct) doAssert isRefSkipDistinct(MyOtherDistinct) + +let x = parseJson("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999") + +doAssert x.kind == JString + +block: # bug #15835 + type + Foo = object + ii*: int + data*: JsonNode + + block: + const jt = """{"ii": 123, "data": ["some", "data"]}""" + let js = parseJson(jt) + discard js.to(Foo) + + block: + const jt = """{"ii": 123}""" + let js = parseJson(jt) + doAssertRaises(KeyError): + echo js.to(Foo) + +type + ContentNodeKind* = enum + P, + Br, + Text, + ContentNode* = object + case kind*: ContentNodeKind + of P: pChildren*: seq[ContentNode] + of Br: nil + of Text: textStr*: string + +let mynode = ContentNode(kind: P, pChildren: @[ + ContentNode(kind: Text, textStr: "mychild"), + ContentNode(kind: Br) +]) + +doAssert $mynode == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])""" + +let jsonNode = %*mynode +doAssert $jsonNode == """{"kind":"P","pChildren":[{"kind":"Text","textStr":"mychild"},{"kind":"Br"}]}""" +doAssert $jsonNode.to(ContentNode) == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])""" + +block: # bug #17383 + testRoundtrip(int32.high): "2147483647" + testRoundtrip(uint32.high): "4294967295" + when int.sizeof == 4: + testRoundtrip(int.high): "2147483647" + testRoundtrip(uint.high): "4294967295" + else: + testRoundtrip(int.high): "9223372036854775807" + testRoundtrip(uint.high): "18446744073709551615" + whenJsNoBigInt64: discard + do: + testRoundtrip(int64.high): "9223372036854775807" + testRoundtrip(uint64.high): "18446744073709551615" + +block: # bug #18007 + testRoundtrip([NaN, Inf, -Inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]""" + # pending https://github.com/nim-lang/Nim/issues/18025 use: + # testRoundtrip([float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0]) + let inf = float32(Inf) + testRoundtrip([float32(NaN), inf, -inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]""" + when not defined(js): # because of Infinity vs inf + testRoundtripVal([inf, -inf, 0.0, -0.0, 1.0]): """["inf","-inf",0.0,-0.0,1.0]""" + let a = parseJson($(%NaN)).to(float) + doAssert a.isNaN + + whenRuntimeJs: discard # refs bug #18009 + do: + testRoundtripVal(0.0): "0.0" + testRoundtripVal(-0.0): "-0.0" + +block: # bug #15397, bug #13196 + testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" + testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" + +block: + let a = "18446744073709551615" + let b = a.parseJson + doAssert b.kind == JString + let c = $b + when defined(js): + doAssert c == "18446744073709552000" + else: + doAssert c == "18446744073709551615" + +block: + let a = """ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ +""" + + when not defined(js): + try: + discard parseJson(a) + except JsonParsingError: + doAssert getCurrentExceptionMsg().contains("] expected") diff --git a/tests/stdlib/tjson_unmarshall.nim b/tests/stdlib/tjson_unmarshall.nim deleted file mode 100644 index 69bed3ac9..000000000 --- a/tests/stdlib/tjson_unmarshall.nim +++ /dev/null @@ -1,31 +0,0 @@ -discard """ - output: ''' -Original: (kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)]) -jsonNode: {"kind":"P","pChildren":[{"kind":"Text","textStr":"mychild"},{"kind":"Br"}]} -Reversed: (kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)]) -''' -""" - -import json - -type - ContentNodeKind* = enum - P, - Br, - Text, - ContentNode* = object - case kind*: ContentNodeKind - of P: pChildren*: seq[ContentNode] - of Br: nil - of Text: textStr*: string - -let mynode = ContentNode(kind: P, pChildren: @[ - ContentNode(kind: Text, textStr: "mychild"), - ContentNode(kind: Br) -]) - -echo "Original: " & $mynode - -let jsonNode = %*mynode -echo "jsonNode: " & $jsonNode -echo "Reversed: " & $jsonNode.to(ContentNode) diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index 938030d8e..5a1b4b294 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -1,9 +1,11 @@ discard """ output: "" + matrix: "--mm:refc; --mm:orc" targets: "c js" """ import json, strutils, options, tables +import std/assertions # The definition of the `%` proc needs to be here, since the `% c` calls below # can only find our custom `%` proc for `Pix` if defined in global scope. @@ -259,7 +261,7 @@ proc testJson() = colors: array[2, BirdColor] var red = BirdColor(name: "red", rgb: [1.0, 0.0, 0.0]) - var blue = Birdcolor(name: "blue", rgb: [0.0, 0.0, 1.0]) + var blue = BirdColor(name: "blue", rgb: [0.0, 0.0, 1.0]) var b = Bird(age: 3, height: 1.734, name: "bardo", colors: [red, blue]) let jnode = %b let data = jnode.to(Bird) @@ -435,11 +437,7 @@ proc testJson() = block: let s = """{"a": 1, "b": 2}""" let t = parseJson(s).to(Table[string, int]) - when not defined(js): - # For some reason on the JS backend `{"b": 2, "a": 0}` is - # sometimes the value of `t`. This needs investigation. I can't - # reproduce it right now in an isolated test. - doAssert t["a"] == 1 + doAssert t["a"] == 1 doAssert t["b"] == 2 block: @@ -497,7 +495,7 @@ proc testJson() = doAssert array[3, float](t.arr) == [1.0,2.0,7.0] doAssert MyRef(t.person).name == "boney" - doAssert MyObj(t.distFruit).color == 11 + doAssert MyObj(t.distfruit).color == 11 doAssert t.dog.name == "honey" doAssert t.fruit.color == 10 doAssert seq[string](t.emails) == @["abc", "123"] @@ -633,6 +631,18 @@ proc testJson() = except KeyError: doAssert getCurrentExceptionMsg().contains ".member.list[2].value" + block: + # Enum indexed array test + type Test = enum + one, two, three, four, five + let a = [ + one: 300, + two: 20, + three: 10, + four: 0, + five: -10 + ] + doAssert (%* a).to(a.typeof) == a testJson() diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 0b2ec7179..9acf4c9e5 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -1,20 +1,37 @@ discard """ + matrix: "--mm:refc; --mm:orc" targets: "c cpp js" """ import std/jsonutils import std/json +from std/math import isNaN, signbit +from std/fenv import epsilon +from stdtest/testutils import whenRuntimeJs +import std/[assertions, objectdollar] proc testRoundtrip[T](t: T, expected: string) = + # checks that `T => json => T2 => json2` is such that json2 = json let j = t.toJson - doAssert $j == expected, $j + doAssert $j == expected, "\n" & $j & "\n" & expected doAssert j.jsonTo(T).toJson == j var t2: T t2.fromJson(j) doAssert t2.toJson == j -import tables -import strtabs +proc testRoundtripVal[T](t: T, expected: string) = + # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T` + # note that this isn't always possible, e.g. for pointer-like types. + let j = t.toJson + let j2 = $j + doAssert j2 == expected, j2 + let j3 = j2.parseJson + let t2 = j3.jsonTo(T) + doAssert t2 == t + doAssert $t2.toJson == j2 # still needed, because -0.0 = 0.0 but their json representation differs + +import tables, sets, algorithm, sequtils, options, strtabs +from strutils import contains type Foo = ref object id: int @@ -22,7 +39,14 @@ type Foo = ref object proc `==`(a, b: Foo): bool = a.id == b.id -template fn() = +type MyEnum = enum me0, me1 = "me1Alt", me2, me3, me4 + +proc `$`(a: MyEnum): string = + # putting this here pending https://github.com/nim-lang/Nim/issues/13747 + if a == me2: "me2Modif" + else: system.`$`(a) + +template fn() = block: # toJson, jsonTo type Foo = distinct float testRoundtrip('x', """120""") @@ -37,38 +61,117 @@ template fn() = testRoundtrip(pointer(nil)): """0""" testRoundtrip(cast[pointer](nil)): """0""" - # causes workaround in `fromJson` potentially related to - # https://github.com/nim-lang/Nim/issues/12282 + # refs bug #9423 testRoundtrip(Foo(1.5)): """1.5""" - block: + block: # OrderedTable testRoundtrip({"z": "Z", "y": "Y"}.toOrderedTable): """{"z":"Z","y":"Y"}""" - when not defined(js): # pending https://github.com/nim-lang/Nim/issues/14574 - testRoundtrip({"z": (f1: 'f'), }.toTable): """{"z":{"f1":102}}""" + doAssert toJson({"z": 10, "": 11}.newTable).`$`.contains """"":11""" # allows hash to change + testRoundtrip({"z".cstring: 1, "".cstring: 2}.toOrderedTable): """{"z":1,"":2}""" + testRoundtrip({"z": (f1: 'f'), }.toTable): """{"z":{"f1":102}}""" - block: + block: # StringTable testRoundtrip({"name": "John", "city": "Monaco"}.newStringTable): """{"mode":"modeCaseSensitive","table":{"city":"Monaco","name":"John"}}""" block: # complex example let t = {"z": "Z", "y": "Y"}.newStringTable type A = ref object a1: string - let a = (1.1, "fo", 'x', @[10,11], [true, false], [t,newStringTable()], [0'i8,3'i8], -4'i16, (foo: 0.5'f32, bar: A(a1: "abc"), bar2: A.default)) + let a = (1.1, "fo", 'x', @[10,11], [true, false], [t,newStringTable()], [0'i8,3'i8], -4'i16, (foo: 0.5'f32, bar: A(a1: "abc"), bar2: A.default, cstring1: "foo", cstring2: "", cstring3: cstring(nil))) testRoundtrip(a): - """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],[0,3],-4,{"foo":0.5,"bar":{"a1":"abc"},"bar2":null}]""" + """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],[0,3],-4,{"foo":0.5,"bar":{"a1":"abc"},"bar2":null,"cstring1":"foo","cstring2":"","cstring3":null}]""" block: - # edge case when user defined `==` doesn't handle `nil` well, eg: + # edge case when user defined `==` doesn't handle `nil` well, e.g.: # https://github.com/nim-lang/nimble/blob/63695f490728e3935692c29f3d71944d83bb1e83/src/nimblepkg/version.nim#L105 testRoundtrip(@[Foo(id: 10), nil]): """[{"id":10},null]""" - block: + block: # enum type Foo = enum f1, f2, f3, f4, f5 type Bar = enum b1, b2, b3, b4 let a = [f2: b2, f3: b3, f4: b4] doAssert b2.ord == 1 # explains the `1` testRoundtrip(a): """[1,2,3]""" + block: # JsonNode + let a = ((1, 2.5, "abc").toJson, (3, 4.5, "foo")) + testRoundtripVal(a): """[[1,2.5,"abc"],[3,4.5,"foo"]]""" + + block: + template toInt(a): untyped = cast[int](a) + + let a = 3.toJson + let b = (a, a) + + let c1 = b.toJson + doAssert c1[0].toInt == a.toInt + doAssert c1[1].toInt == a.toInt + + let c2 = b.toJson(ToJsonOptions(jsonNodeMode: joptJsonNodeAsCopy)) + doAssert c2[0].toInt != a.toInt + doAssert c2[1].toInt != c2[0].toInt + doAssert c2[1] == c2[0] + + let c3 = b.toJson(ToJsonOptions(jsonNodeMode: joptJsonNodeAsObject)) + doAssert $c3 == """[{"isUnquoted":false,"kind":2,"num":3},{"isUnquoted":false,"kind":2,"num":3}]""" + + block: # ToJsonOptions + let a = (me1, me2) + doAssert $a.toJson() == "[1,2]" + doAssert $a.toJson(ToJsonOptions(enumMode: joptEnumSymbol)) == """["me1","me2"]""" + doAssert $a.toJson(ToJsonOptions(enumMode: joptEnumString)) == """["me1Alt","me2Modif"]""" + + block: # set + type Foo = enum f1, f2, f3, f4, f5 + type Goo = enum g1 = 10, g2 = 15, g3 = 17, g4 + let a = ({f1, f3}, {1'u8, 7'u8}, {'0'..'9'}, {123'u16, 456, 789, 1121, 1122, 1542}, {g2, g3}) + testRoundtrip(a): """[[0,2],[1,7],[48,49,50,51,52,53,54,55,56,57],[123,456,789,1121,1122,1542],[15,17]]""" + + block: # bug #17383 + block: + let a = (int32.high, uint32.high) + testRoundtrip(a): "[2147483647,4294967295]" + when int.sizeof > 4: + block: + let a = (int64.high, uint64.high) + testRoundtrip(a): "[9223372036854775807,18446744073709551615]" + block: + let a = (int.high, uint.high) + when int.sizeof == 4: + testRoundtrip(a): "[2147483647,4294967295]" + else: + testRoundtrip(a): "[9223372036854775807,18446744073709551615]" + + block: # bug #18007 + testRoundtrip((NaN, Inf, -Inf, 0.0, -0.0, 1.0)): """["nan","inf","-inf",0.0,-0.0,1.0]""" + testRoundtrip((float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0)): """["nan","inf","-inf",0.0,-0.0,1.0]""" + testRoundtripVal((Inf, -Inf, 0.0, -0.0, 1.0)): """["inf","-inf",0.0,-0.0,1.0]""" + doAssert ($NaN.toJson).parseJson.jsonTo(float).isNaN + + block: # bug #18009; unfixable unless we change parseJson (which would have overhead), + # but at least we can guarantee that the distinction between 0.0 and -0.0 is preserved. + let a = (0, 0.0, -0.0, 0.5, 1, 1.0) + testRoundtripVal(a): "[0,0.0,-0.0,0.5,1,1.0]" + let a2 = $($a.toJson).parseJson + whenRuntimeJs: + doAssert a2 == "[0,0,-0.0,0.5,1,1]" + do: + doAssert a2 == "[0,0.0,-0.0,0.5,1,1.0]" + let b = a2.parseJson.jsonTo(type(a)) + doAssert not b[1].signbit + doAssert b[2].signbit + doAssert not b[3].signbit + + block: # bug #15397, bug #13196 + let a = 0.1 + let x = 0.12345678901234567890123456789 + let b = (a + 0.2, 0.3, x) + testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]" + + testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" + testRoundtripVal(epsilon(float64)): "2.220446049250313e-16" + testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" + block: # case object type Foo = object x0: float @@ -119,5 +222,255 @@ template fn() = testRoundtrip(Foo[int](t1: false, z2: 7)): """{"t1":false,"z2":7}""" # pending https://github.com/nim-lang/Nim/issues/14698, test with `type Foo[T] = ref object` + block: # bug: pass opt params in fromJson + type Foo = object + a: int + b: string + c: float + type Bar = object + foo: Foo + boo: string + var f: seq[Foo] + try: + fromJson(f, parseJson """[{"b": "bbb"}]""") + doAssert false + except ValueError: + doAssert true + fromJson(f, parseJson """[{"b": "bbb"}]""", Joptions(allowExtraKeys: true, allowMissingKeys: true)) + doAssert f == @[Foo(a: 0, b: "bbb", c: 0.0)] + var b: Bar + fromJson(b, parseJson """{"foo": {"b": "bbb"}}""", Joptions(allowExtraKeys: true, allowMissingKeys: true)) + doAssert b == Bar(foo: Foo(a: 0, b: "bbb", c: 0.0)) + block: # jsonTo with `opt` + let b2 = """{"foo": {"b": "bbb"}}""".parseJson.jsonTo(Bar, Joptions(allowExtraKeys: true, allowMissingKeys: true)) + doAssert b2 == Bar(foo: Foo(a: 0, b: "bbb", c: 0.0)) + + block testHashSet: + testRoundtrip(HashSet[string]()): "[]" + testRoundtrip([""].toHashSet): """[""]""" + testRoundtrip(["one"].toHashSet): """["one"]""" + + var s: HashSet[string] + fromJson(s, parseJson("""["one","two"]""")) + doAssert s == ["one", "two"].toHashSet + + let jsonNode = toJson(s) + doAssert jsonNode.elems.mapIt(it.str).sorted == @["one", "two"] + + block testOrderedSet: + testRoundtrip(["one", "two", "three"].toOrderedSet): + """["one","two","three"]""" + + block testOption: + testRoundtrip(some("test")): "\"test\"" + testRoundtrip(none[string]()): "null" + testRoundtrip(some(42)): "42" + testRoundtrip(none[int]()): "null" + + block testStrtabs: + testRoundtrip(newStringTable(modeStyleInsensitive)): + """{"mode":"modeStyleInsensitive","table":{}}""" + + testRoundtrip( + newStringTable("name", "John", "surname", "Doe", modeCaseSensitive)): + """{"mode":"modeCaseSensitive","table":{"name":"John","surname":"Doe"}}""" + + block testJoptions: + type + AboutLifeUniverseAndEverythingElse = object + question: string + answer: int + + block testExceptionOnExtraKeys: + var guide: AboutLifeUniverseAndEverythingElse + let json = parseJson( + """{"question":"6*9=?","answer":42,"author":"Douglas Adams"}""") + doAssertRaises ValueError, fromJson(guide, json) + doAssertRaises ValueError, + fromJson(guide, json, Joptions(allowMissingKeys: true)) + + type + A = object + a1,a2,a3: int + var a: A + let j = parseJson("""{"a3": 1, "a4": 2}""") + doAssertRaises ValueError, + fromJson(a, j, Joptions(allowMissingKeys: true)) + + block testExceptionOnMissingKeys: + var guide: AboutLifeUniverseAndEverythingElse + let json = parseJson("""{"answer":42}""") + doAssertRaises ValueError, fromJson(guide, json) + doAssertRaises ValueError, + fromJson(guide, json, Joptions(allowExtraKeys: true)) + + block testAllowExtraKeys: + var guide: AboutLifeUniverseAndEverythingElse + let json = parseJson( + """{"question":"6*9=?","answer":42,"author":"Douglas Adams"}""") + fromJson(guide, json, Joptions(allowExtraKeys: true)) + doAssert guide == AboutLifeUniverseAndEverythingElse( + question: "6*9=?", answer: 42) + + block refObject: #bug 17986 + type A = ref object + case is_a: bool + of true: + a: int + else: + b: int + + var a = A() + fromJson(a, """{"is_a": true, "a":1, "extra_key": 1}""".parseJson, Joptions(allowExtraKeys: true)) + doAssert $a[] == "(is_a: true, a: 1)" + + block testAllowMissingKeys: + var guide = AboutLifeUniverseAndEverythingElse( + question: "6*9=?", answer: 54) + let json = parseJson("""{"answer":42}""") + fromJson(guide, json, Joptions(allowMissingKeys: true)) + doAssert guide == AboutLifeUniverseAndEverythingElse( + question: "6*9=?", answer: 42) + + block testAllowExtraAndMissingKeys: + var guide = AboutLifeUniverseAndEverythingElse( + question: "6*9=?", answer: 54) + let json = parseJson( + """{"answer":42,"author":"Douglas Adams"}""") + fromJson(guide, json, Joptions( + allowExtraKeys: true, allowMissingKeys: true)) + doAssert guide == AboutLifeUniverseAndEverythingElse( + question: "6*9=?", answer: 42) + + type + Foo = object + a: array[2, string] + case b: bool + of false: f: float + of true: t: tuple[i: int, s: string] + case c: range[0 .. 2] + of 0: c0: int + of 1: c1: float + of 2: c2: string + + block testExceptionOnMissingDiscriminantKey: + var foo: Foo + let json = parseJson("""{"a":["one","two"]}""") + doAssertRaises ValueError, fromJson(foo, json) + + block testDoNotResetMissingFieldsWhenHaveDiscriminantKey: + var foo = Foo(a: ["one", "two"], b: true, t: (i: 42, s: "s"), + c: 0, c0: 1) + let json = parseJson("""{"b":true,"c":2}""") + fromJson(foo, json, Joptions(allowMissingKeys: true)) + doAssert foo.a == ["one", "two"] + doAssert foo.b + doAssert foo.t == (i: 42, s: "s") + doAssert foo.c == 2 + doAssert foo.c2 == "" + + block testAllowMissingDiscriminantKeys: + var foo: Foo + let json = parseJson("""{"a":["one","two"],"c":1,"c1":3.14159}""") + fromJson(foo, json, Joptions(allowMissingKeys: true)) + doAssert foo.a == ["one", "two"] + doAssert not foo.b + doAssert foo.f == 0.0 + doAssert foo.c == 1 + doAssert foo.c1 == 3.14159 + + block testExceptionOnWrongDiscirminatBranchInJson: + var foo = Foo(b: false, f: 3.14159, c: 0, c0: 42) + let json = parseJson("""{"c2": "hello"}""") + doAssertRaises ValueError, + fromJson(foo, json, Joptions(allowMissingKeys: true)) + # Test that the original fields are not reset. + doAssert not foo.b + doAssert foo.f == 3.14159 + doAssert foo.c == 0 + doAssert foo.c0 == 42 + + block testNoExceptionOnRightDiscriminantBranchInJson: + var foo = Foo(b: false, f: 0, c:1, c1: 0) + let json = parseJson("""{"f":2.71828,"c1": 3.14159}""") + fromJson(foo, json, Joptions(allowMissingKeys: true)) + doAssert not foo.b + doAssert foo.f == 2.71828 + doAssert foo.c == 1 + doAssert foo.c1 == 3.14159 + + block testAllowExtraKeysInJsonOnWrongDisciriminatBranch: + var foo = Foo(b: false, f: 3.14159, c: 0, c0: 42) + let json = parseJson("""{"c2": "hello"}""") + fromJson(foo, json, Joptions(allowMissingKeys: true, + allowExtraKeys: true)) + # Test that the original fields are not reset. + doAssert not foo.b + doAssert foo.f == 3.14159 + doAssert foo.c == 0 + doAssert foo.c0 == 42 + + + block testInvalidTupleLength: + let json = parseJson("[0]") + # Should raise ValueError instead of index error + doAssertRaises(ValueError): + discard json.jsonTo((int, int)) + + type + InnerEnum = enum + A + B + C + InnerObject = object + x: string + y: InnerEnum + + block testOptionsArePassedWhenDeserialising: + let json = parseJson("""{"x": "hello"}""") + let inner = json.jsonTo(Option[InnerObject], Joptions(allowMissingKeys: true)) + doAssert inner.isSome() + doAssert inner.get().x == "hello" + doAssert inner.get().y == A + + block testOptionsArePassedWhenSerialising: + let inner = some InnerObject(x: "hello", y: A) + let json = inner.toJson(ToJsonOptions(enumMode: joptEnumSymbol)) + doAssert $json == """{"x":"hello","y":"A"}""" + + block: # bug #21638 + type Something = object + + doAssert "{}".parseJson.jsonTo(Something) == Something() + + when false: + ## TODO: Implement support for nested variant objects allowing the tests + ## bellow to pass. + block testNestedVariantObjects: + type + Variant = object + case b: bool + of false: + case bf: bool + of false: bff: int + of true: bft: float + of true: + case bt: bool + of false: btf: string + of true: btt: char + + testRoundtrip(Variant(b: false, bf: false, bff: 42)): + """{"b": false, "bf": false, "bff": 42}""" + testRoundtrip(Variant(b: false, bf: true, bft: 3.14159)): + """{"b": false, "bf": true, "bft": 3.14159}""" + testRoundtrip(Variant(b: true, bt: false, btf: "test")): + """{"b": true, "bt": false, "btf": "test"}""" + testRoundtrip(Variant(b: true, bt: true, btt: 'c')): + """{"b": true, "bt": true, "btt": "c"}""" + + # TODO: Add additional tests with missing and extra JSON keys, both when + # allowed and forbidden analogous to the tests for the not nested + # variant objects. + static: fn() fn() diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index a288af781..5993278c7 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -1,67 +1,277 @@ discard """ - output: '''true''' + matrix: "--mm:refc; --mm:orc" + targets: "c js" """ -import lists +import std/[lists, sequtils] +import std/assertions const data = [1, 2, 3, 4, 5, 6] -block SinglyLinkedListTest1: - var L: SinglyLinkedList[int] - for d in items(data): L.prepend(d) - for d in items(data): L.append(d) - assert($L == "[6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6]") - assert(4 in L) +template main = + block SinglyLinkedListTest1: + var L: SinglyLinkedList[int] + for d in items(data): L.prepend(d) + for d in items(data): L.add(d) + doAssert($L == "[6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6]") -block SinglyLinkedListTest2: - var L: SinglyLinkedList[string] - for d in items(data): L.prepend($d) - assert($L == """["6", "5", "4", "3", "2", "1"]""") + doAssert(4 in L) - assert("4" in L) + block SinglyLinkedListTest2: + var L: SinglyLinkedList[string] + for d in items(data): L.prepend($d) + doAssert($L == """["6", "5", "4", "3", "2", "1"]""") + doAssert("4" in L) -block DoublyLinkedListTest1: - var L: DoublyLinkedList[int] - for d in items(data): L.prepend(d) - for d in items(data): L.append(d) - L.remove(L.find(1)) - assert($L == "[6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6]") - assert(4 in L) + block DoublyLinkedListTest1: + var L: DoublyLinkedList[int] + for d in items(data): L.prepend(d) + for d in items(data): L.add(d) + L.remove(L.find(1)) + doAssert($L == "[6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6]") -block SinglyLinkedRingTest1: - var L: SinglyLinkedRing[int] - L.prepend(4) - assert($L == "[4]") - L.prepend(4) + doAssert(4 in L) - assert($L == "[4, 4]") - assert(4 in L) + block SinglyLinkedRingTest1: + var L: SinglyLinkedRing[int] + L.prepend(4) + doAssert($L == "[4]") + L.prepend(4) + doAssert($L == "[4, 4]") + doAssert(4 in L) -block DoublyLinkedRingTest1: - var L: DoublyLinkedRing[int] - L.prepend(4) - assert($L == "[4]") - L.prepend(4) - assert($L == "[4, 4]") - assert(4 in L) + block DoublyLinkedRingTest1: + var L: DoublyLinkedRing[int] + L.prepend(4) + doAssert($L == "[4]") + L.prepend(4) - L.append(3) - L.append(5) - assert($L == "[4, 4, 3, 5]") + doAssert($L == "[4, 4]") + doAssert(4 in L) - L.remove(L.find(3)) - L.remove(L.find(5)) - L.remove(L.find(4)) - L.remove(L.find(4)) - assert($L == "[]") - assert(4 notin L) + L.add(3) + L.add(5) + doAssert($L == "[4, 4, 3, 5]") + L.remove(L.find(3)) + L.remove(L.find(5)) + L.remove(L.find(4)) + L.remove(L.find(4)) + doAssert($L == "[]") + doAssert(4 notin L) -echo "true" + block tlistsToString: + block: + var l = initDoublyLinkedList[int]() + l.add(1) + l.add(2) + l.add(3) + doAssert $l == "[1, 2, 3]" + block: + var l = initDoublyLinkedList[string]() + l.add("1") + l.add("2") + l.add("3") + doAssert $l == """["1", "2", "3"]""" + block: + var l = initDoublyLinkedList[char]() + l.add('1') + l.add('2') + l.add('3') + doAssert $l == """['1', '2', '3']""" + # Copied here until it is merged into sequtils + template take(a: untyped, max: int): untyped = + type T = typeof(block: (for ai in a: ai)) + var ret: seq[T] + var i = 0 + if max > 0: + for ai in a: + ret.add ai + i.inc + if i >= max: break + ret + + template testCommon(initList, toList) = + + block: # toSinglyLinkedList, toDoublyLinkedList + let l = seq[int].default + doAssert l.toList.toSeq == [] + doAssert [1].toList.toSeq == [1] + doAssert [1, 2, 3].toList.toSeq == [1, 2, 3] + + block copy: + doAssert array[0, int].default.toList.copy.toSeq == [] + doAssert [1].toList.copy.toSeq == [1] + doAssert [1, 2].toList.copy.toSeq == [1, 2] + doAssert [1, 2, 3].toList.copy.toSeq == [1, 2, 3] + type Foo = ref object + x: int + var f0 = Foo(x: 0) + let f1 = Foo(x: 1) + var a = [f0].toList + var b = a.copy + b.add f1 + doAssert a.toSeq == [f0] + doAssert b.toSeq == [f0, f1] + f0.x = 42 + doAssert a.head.value.x == 42 + doAssert b.head.value.x == 42 + + block: # add, addMoved + block: + var + l0 = initList[int]() + l1 = [1].toList + l2 = [2, 3].toList + l3 = [4, 5, 6].toList + l0.add l3 + l1.add l3 + l2.addMoved l3 + doAssert l0.toSeq == [4, 5, 6] + doAssert l1.toSeq == [1, 4, 5, 6] + doAssert l2.toSeq == [2, 3, 4, 5, 6] + doAssert l3.toSeq == [] + l2.add l3 # re-adding l3 that was destroyed is now a no-op + doAssert l2.toSeq == [2, 3, 4, 5, 6] + doAssert l3.toSeq == [] + block: + var + l0 = initList[int]() + l1 = [1].toList + l2 = [2, 3].toList + l3 = [4, 5, 6].toList + l3.addMoved l0 + l2.addMoved l1 + doAssert l3.toSeq == [4, 5, 6] + doAssert l2.toSeq == [2, 3, 1] + l3.add l0 + doAssert l3.toSeq == [4, 5, 6] + block: + var c = [0, 1].toList + c.addMoved c + doAssert c.take(6) == [0, 1, 0, 1, 0, 1] + + block: # prepend, prependMoved + block: + var + l0 = initList[int]() + l1 = [1].toList + l2 = [2, 3].toList + l3 = [4, 5, 6].toList + l0.prepend l3 + l1.prepend l3 + doAssert l3.toSeq == [4, 5, 6] + l2.prependMoved l3 + doAssert l0.toSeq == [4, 5, 6] + doAssert l1.toSeq == [4, 5, 6, 1] + doAssert l2.toSeq == [4, 5, 6, 2, 3] + doAssert l3.toSeq == [] + l2.prepend l3 # re-prepending l3 that was destroyed is now a no-op + doAssert l2.toSeq == [4, 5, 6, 2, 3] + doAssert l3.toSeq == [] + block: + var + l0 = initList[int]() + l1 = [1].toList + l2 = [2, 3].toList + l3 = [4, 5, 6].toList + l3.prependMoved l0 + l2.prependMoved l1 + doAssert l3.toSeq == [4, 5, 6] + doAssert l2.toSeq == [1, 2, 3] + l3.prepend l0 + doAssert l3.toSeq == [4, 5, 6] + block: + var c = [0, 1].toList + c.prependMoved c + doAssert c.take(6) == [0, 1, 0, 1, 0, 1] + + block remove: + var l = [0, 1, 2, 3].toList + let + l0 = l.head + l1 = l0.next + l2 = l1.next + l3 = l2.next + l.remove l0 + doAssert l.toSeq == [1, 2, 3] + l.remove l2 + doAssert l.toSeq == [1, 3] + l.remove l2 + doAssert l.toSeq == [1, 3] + l.remove l3 + doAssert l.toSeq == [1] + l.remove l1 + doAssert l.toSeq == [] + # Cycle preservation + var a = [10, 11, 12].toList + a.addMoved a + doAssert a.take(6) == @[10, 11, 12, 10, 11, 12] + a.remove a.head.next + doAssert a.take(6) == @[10, 12, 10, 12, 10, 12] + a.remove a.head + doAssert a.take(6) == @[12, 12, 12, 12, 12, 12] + + testCommon initSinglyLinkedList, toSinglyLinkedList + testCommon initDoublyLinkedList, toDoublyLinkedList + + block remove: # return value check + var l = [0, 1, 2, 3].toSinglyLinkedList + let n = l.head.next.next + doAssert l.remove(n) == true + doAssert l.toSeq == [0, 1, 3] + doAssert l.remove(n) == false + doAssert l.toSeq == [0, 1, 3] + doAssert l.remove(l.head) == true + doAssert l.toSeq == [1, 3] + doAssert l.remove(l.head.next) == true + doAssert l.toSeq == [1] + doAssert l.remove(l.head) == true + doAssert l.toSeq == [] + + block issue19297: # add (appends a shallow copy) + var a: SinglyLinkedList[int] + var b: SinglyLinkedList[int] + + doAssert a.toSeq == @[] + a.add(1) + doAssert a.toSeq == @[1] + a.add(b) + doAssert a.toSeq == @[1] + a.add(2) + doAssert a.toSeq == @[1, 2] + + block issue19314: # add (appends a shallow copy) + var a: DoublyLinkedList[int] + var b: DoublyLinkedList[int] + + doAssert a.toSeq == @[] + a.add(1) + doAssert a.toSeq == @[1] + a.add(b) + doAssert a.toSeq == @[1] + a.add(2) + doAssert a.toSeq == @[1, 2] + + block RemoveLastNodeFromSinglyLinkedList: + var list = initSinglyLinkedList[string]() + let n1 = newSinglyLinkedNode("sonic") + let n2 = newSinglyLinkedNode("the") + let n3 = newSinglyLinkedNode("tiger") + let n4 = newSinglyLinkedNode("hedgehog") + list.add(n1) + list.add(n2) + list.add(n3) + list.remove(n3) + list.add(n4) + doAssert list.toSeq == @["sonic", "the", "hedgehog"] + +static: main() +main() diff --git a/tests/stdlib/tlocks.nim b/tests/stdlib/tlocks.nim index e567cfde8..1c5f67119 100644 --- a/tests/stdlib/tlocks.nim +++ b/tests/stdlib/tlocks.nim @@ -1,10 +1,11 @@ discard """ - output: '''3''' - cmd: "nim $target --threads:on $options $file" + targets: "c cpp js" + matrix: "--mm:refc; --mm:orc" """ #bug #6049 import uselocks +import std/assertions var m = createMyType[int]() -echo $m.use() +doAssert m.use() == 3 diff --git a/tests/stdlib/tlwip.nim b/tests/stdlib/tlwip.nim new file mode 100644 index 000000000..fc53be592 --- /dev/null +++ b/tests/stdlib/tlwip.nim @@ -0,0 +1,30 @@ +discard """ + targets: "c" + cmd: "nim $target --compileOnly --os:freertos --gc:arc $options $file" + disabled: "bsd" + disabled: "windows" + action: compile +""" + +# Note: +# This file tests FreeRTOS/LwIP cross-compilation on UNIX platforms +# Windows should run when compiled with esp-idf, however I'm not +# sure how to test for only compilation on Windows without running +# a test exe +# +# Note: +# disabling *BSDs since they're not playing well with `gcc` + +import net +import asynchttpserver, asyncdispatch + +proc cb*(req: Request) {.async.} = + await req.respond(Http200, "Hello World") + +proc run_http_server*() {.exportc.} = + echo "starting http server" + var server = newAsyncHttpServer() + + waitFor server.serve(Port(8181), cb) + +echo("ok") diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim new file mode 100644 index 000000000..06a9a9c27 --- /dev/null +++ b/tests/stdlib/tmacros.nim @@ -0,0 +1,349 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +#[ +xxx macros tests need to be reorganized to makes sure each API is tested once +See also: + tests/macros/tdumpast.nim for treeRepr + friends +]# + +import std/macros +import std/assertions + +block: # hasArgOfName + macro m(u: untyped): untyped = + for name in ["s","i","j","k","b","xs","ys"]: + doAssert hasArgOfName(params u,name) + doAssert not hasArgOfName(params u,"nonexistent") + + proc p(s: string; i,j,k: int; b: bool; xs,ys: seq[int] = @[]) {.m.} = discard + +block: # bug #17454 + proc f(v: NimNode): string {.raises: [].} = $v + +block: # unpackVarargs + block: + proc bar1(a: varargs[int]): string = + for ai in a: result.add " " & $ai + proc bar2(a: varargs[int]) = + let s1 = bar1(a) + let s2 = unpackVarargs(bar1, a) # `unpackVarargs` makes no difference here + doAssert s1 == s2 + bar2(1, 2, 3) + bar2(1) + bar2() + + block: + template call1(fun: typed; args: varargs[untyped]): untyped = + unpackVarargs(fun, args) + template call2(fun: typed; args: varargs[untyped]): untyped = + # fun(args) # works except for last case with empty `args`, pending bug #9996 + when varargsLen(args) > 0: fun(args) + else: fun() + + proc fn1(a = 0, b = 1) = discard (a, b) + + call1(fn1) + call1(fn1, 10) + call1(fn1, 10, 11) + + call2(fn1) + call2(fn1, 10) + call2(fn1, 10, 11) + + block: + template call1(fun: typed; args: varargs[typed]): untyped = + unpackVarargs(fun, args) + template call2(fun: typed; args: varargs[typed]): untyped = + # xxx this would give a confusing error message: + # required type for a: varargs[typed] [varargs] but expression '[10]' is of type: varargs[typed] [varargs] + when varargsLen(args) > 0: fun(args) + else: fun() + macro toString(a: varargs[typed, `$`]): string = + var msg = genSym(nskVar, "msg") + result = newStmtList() + result.add quote do: + var `msg` = "" + for ai in a: + result.add quote do: `msg`.add $`ai` + result.add quote do: `msg` + doAssert call1(toString) == "" + doAssert call1(toString, 10) == "10" + doAssert call1(toString, 10, 11) == "1011" + +block: # SameType + type + A = int + B = distinct int + C = object + Generic[T, Y] = object + macro isSameType(a, b: typed): untyped = + newLit(sameType(a, b)) + + static: + assert Generic[int, int].isSameType(Generic[int, int]) + assert Generic[A, string].isSameType(Generic[int, string]) + assert not Generic[A, string].isSameType(Generic[B, string]) + assert not Generic[int, string].isSameType(Generic[int, int]) + assert isSameType(int, A) + assert isSameType(10, 20) + assert isSameType("Hello", "world") + assert not isSameType("Hello", cstring"world") + assert not isSameType(int, B) + assert not isSameType(int, Generic[int, int]) + assert not isSameType(C, string) + assert not isSameType(C, int) + + + #[ + # compiler sameType fails for the following, read more in `types.nim`'s `sameTypeAux`. + type + D[T] = C + G[T] = T + static: + assert isSameType(D[int], C) + assert isSameType(D[int], D[float]) + assert isSameType(G[float](1.0), float(1.0)) + assert isSameType(float(1.0), G[float](1.0)) + ]# + + type Tensor[T] = object + data: T + + macro testTensorInt(x: typed): untyped = + let + tensorIntType = getTypeInst(Tensor[int])[1] + xTyp = x.getTypeInst + + newLit(xTyp.sameType(tensorIntType)) + + var + x: Tensor[int] + x1 = Tensor[float]() + x2 = Tensor[A]() + x3 = Tensor[B]() + + static: + assert testTensorInt(x) + assert not testTensorInt(x1) + assert testTensorInt(x2) + assert not testTensorInt(x3) + +block: # extractDocCommentsAndRunnables + macro checkRunnables(prc: untyped) = + let runnables = prc.body.extractDocCommentsAndRunnables() + doAssert runnables[0][0].eqIdent("runnableExamples") + + macro checkComments(comment: static[string], prc: untyped) = + let comments = prc.body.extractDocCommentsAndRunnables() + doAssert comments[0].strVal == comment + + proc a() {.checkRunnables.} = + runnableExamples: discard + discard + + proc b() {.checkRunnables.} = + runnableExamples "-d:ssl": discard + discard + + proc c() {.checkComments("Hello world").} = + ## Hello world + +block: # bug #19020 + type + foo = object + + template typ(T:typedesc) {.pragma.} + + proc bar() {.typ: foo.} = discard + + static: + doAssert $bar.getCustomPragmaVal(typ) == "foo" + doAssert $bar.getCustomPragmaVal(typ) == "foo" + +block hasCustomPragmaGeneric: + template examplePragma() {.pragma.} + type + Foo[T] {.examplePragma.} = object + x {.examplePragma.}: T + var f: Foo[string] + doAssert f.hasCustomPragma(examplePragma) + doAssert f.x.hasCustomPragma(examplePragma) + +block getCustomPragmaValGeneric: + template examplePragma(x: int) {.pragma.} + type + Foo[T] {.examplePragma(42).} = object + x {.examplePragma(25).}: T + var f: Foo[string] + doAssert f.getCustomPragmaVal(examplePragma) == 42 + doAssert f.x.getCustomPragmaVal(examplePragma) == 25 + +block: # bug #21326 + macro foo(body: untyped): untyped = + let a = body.lineInfoObj() + let aLit = a.newLit + result = quote do: + doAssert $`a` == $`aLit` + + foo: + let c = 1 + + template name(a: LineInfo): untyped = + discard a # `aLit` works though + + macro foo3(body: untyped): untyped = + let a = body.lineInfoObj() + # let ax = newLit(a) + result = getAst(name(a)) + + foo3: + let c = 1 + +block: # bug #7375 + macro fails(b: static[bool]): untyped = + doAssert b == false + result = newStmtList() + + macro foo(): untyped = + + var b = false + + ## Fails + result = quote do: + fails(`b`) + + foo() + + macro someMacro(): untyped = + template tmpl(boolean: bool) = + when boolean: + discard "it's true!" + else: + doAssert false + result = getAst(tmpl(true)) + + someMacro() + +block: + macro foo(): untyped = + result = quote do: `littleEndian` + + doAssert littleEndian == foo() + +block: + macro eqSym(x, y: untyped): untyped = + let eq = $x == $y # Unfortunately eqIdent compares to string. + result = quote do: `eq` + + var r, a, b: int + + template fma(result: var int, a, b: int, op: untyped) = + # fused multiple-add + when eqSym(op, `+=`): + discard "+=" + else: + discard "+" + + fma(r, a, b, `+=`) + +block: + template test(boolArg: bool) = + static: + doAssert typeof(boolArg) is bool + let x: bool = boolArg # compile error here, because boolArg became an int + + macro testWrapped1(boolArg: bool): untyped = + # forwarding boolArg directly works + result = getAst(test(boolArg)) + + macro testWrapped2(boolArg: bool): untyped = + # forwarding boolArg via a local variable also works + let b = boolArg + result = getAst(test(b)) + + macro testWrapped3(boolArg: bool): untyped = + # but using a literal `true` as a local variable will be converted to int + let b = true + result = getAst(test(b)) + + test(true) # ok + testWrapped1(true) # ok + testWrapped2(true) # ok + testWrapped3(true) + +block: + macro foo(): untyped = + var s = { 'a', 'b' } + quote do: + let t = `s` + doAssert $typeof(t) == "set[char]" + + foo() + +block: # bug #9607 + proc fun1(info:LineInfo): string = "bar" + proc fun2(info:int): string = "bar" + + macro echoL(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + let fun1 = bindSym"fun1" + let fun2 = bindSym"fun2" + + # this would work instead + # result = newCall(bindSym"fun2", info.line.newLit) + + result = quote do: + + # BUG1: ???(0, 0) Error: internal error: genLiteral: ty is nil + `fun1`(`info`) + + macro echoM(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + let fun1 = bindSym"fun1" + let fun2 = bindSym"fun2" + + # this would work instead + # result = newCall(bindSym"fun2", info.line.newLit) + + result = quote do: + + # BUG1: ???(0, 0) Error: internal error: genLiteral: ty is nil + `fun2`(`info`.line) + + + doAssert echoL() == "bar" + doAssert echoM() == "bar" + +block: + macro hello[T](x: T): untyped = + result = quote do: + let m: `T` = `x` + discard m + + hello(12) + +block: + proc hello(x: int, y: typedesc) = + discard + + macro main = + let x = 12 + result = quote do: + `hello`(12, type(x)) + + main() + +block: # bug #22947 + macro bar[N: static int](a: var array[N, int]) = + result = quote do: + for i in 0 ..< `N`: + `a`[i] = i + + func foo[N: static int](a: var array[N, int]) = + bar(a) + + + var a: array[4, int] + foo(a) diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index 118d0ae02..32991ccc9 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -1,28 +1,24 @@ discard """ - output: '''{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"} -true -true -alpha 100 -omega 200 -''' -joinable: false + matrix: "--mm:orc; --mm:refc" """ -#[ -joinable: false pending https://github.com/nim-lang/Nim/issues/9754 -]# +import std/marshal +import std/[assertions, objectdollar, streams] -import marshal +# TODO: add static tests -template testit(x) = discard $$to[type(x)]($$x) +proc testit[T](x: T): string = $$to[T]($$x) -var x: array[0..4, array[0..4, string]] = [ - ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], - ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], - ["test", "1", "2", "3", "4"]] -testit(x) -var test2: tuple[name: string, s: int] = ("tuple test", 56) -testit(test2) +template check1 = + let test1: array[0..1, array[0..4, string]] = [ + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]] + doAssert testit(test1) == + """[["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]]""" + let test2: tuple[name: string, s: int] = ("tuple test", 56) + doAssert testit(test2) == """{"Field0": "tuple test", "Field1": 56}""" + +static: check1() +check1() type TE = enum @@ -53,73 +49,185 @@ proc buildList(): PNode = result.prev.next = result result.prev.prev = result.next -var test3: TestObj -test3.test = 42 -test3.test2 = blah -testit(test3) +let test3 = TestObj(test: 42, test2: blah) +doAssert testit(test3) == + """{"test": 42, "asd": 0, "test2": "blah", "help": ""}""" var test4: ref tuple[a, b: string] new(test4) test4.a = "ref string test: A" test4.b = "ref string test: B" -testit(test4) +discard testit(test4) # serialization uses the pointer address, which is not consistent -var test5 = @[(0,1),(2,3),(4,5)] -testit(test5) +let test5 = @[(0,1),(2,3),(4,5)] +doAssert testit(test5) == + """[{"Field0": 0, "Field1": 1}, {"Field0": 2, "Field1": 3}, {"Field0": 4, "Field1": 5}]""" -var test7 = buildList() -testit(test7) +let test6: set[char] = {'A'..'Z', '_'} +doAssert testit(test6) == + """[65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 95]""" -var test6: set[char] = {'A'..'Z', '_'} -testit(test6) +let test7 = buildList() +discard testit(test7) # serialization uses the pointer address, which is not consistent # bug #1352 +block: + type + Entity = object of RootObj + name: string + + Person = object of Entity + age: int + bio: string + blob: string + + let instance1 = Person(name: "Cletus", age: 12, + bio: "Я Cletus", + blob: "ABC\x80") + doAssert $$instance1 == """{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"}""" + doAssert to[Person]($$instance1).bio == instance1.bio + doAssert to[Person]($$instance1).blob == instance1.blob + +# bug #5757 +block: + type + Something = object + x: string + y: int + + let data1 = """{"x": "alpha", "y": 100}""" + let data2 = """{"x": "omega", "y": 200}""" + + var r = to[Something](data1) + doAssert $r.x & " " & $r.y == "alpha 100" + r = to[Something](data2) + doAssert $r.x & " " & $r.y == "omega 200" + +block: + type + Foo = object + a1: string + a2: string + a3: seq[string] + a4: seq[int] + a5: seq[int] + a6: seq[int] + var foo = Foo(a2: "", a4: @[], a6: @[1]) + foo.a6.setLen 0 + doAssert $$foo == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}""" + doAssert testit(foo) == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}""" + +import std/[options, json] + +# bug #15934 +block: + let + a1 = some(newJNull()) + a2 = none(JsonNode) + doAssert $($$a1).to[:Option[JsonNode]] == "some(null)" + doAssert $($$a2).to[:Option[JsonNode]] == "none(JsonNode)" + doAssert ($$a1).to[:Option[JsonNode]] == some(newJNull()) + doAssert ($$a2).to[:Option[JsonNode]] == none(JsonNode) + +# bug #15620 +block: + let str = """{"numeric": null}""" + + type + LegacyEntry = object + numeric: string + + let test = to[LegacyEntry](str) + doAssert $test == """(numeric: "")""" + +block: + let str = """{"numeric": null}""" + + type + LegacyEntry = object + numeric: seq[int] + + var test = to[LegacyEntry](str) + doAssert $test == """(numeric: @[])""" + +# bug #16022 +block: + let p: proc (): string = proc (): string = "hello world" + let poc = to[typeof(p)]($$p) + doAssert poc() == "hello world" + +block: + type + A {.inheritable.} = object + B = object of A + f: int + + let a: ref A = new(B) + doAssert $$a[] == "{}" # not "{f: 0}" + +# bug #16496 +block: + type + A = ref object + data: seq[int] + + B = ref object + x: A + let o = A(data: @[1, 2, 3, 4]) + let s1 = @[B(x: o), B(x: o)] + let m = $$ s1 + let s2 = to[seq[B]](m) + doAssert s2[0].x.data == s2[1].x.data + doAssert s1[0].x.data == s2[1].x.data + + +block: + type + Obj = ref object + i: int + b: bool + + let + strm = newStringStream() + + var + o = Obj(i: 1, b: false) + t1 = @[o, o] + t2: seq[Obj] + + doAssert t1[0] == t1[1] + + strm.store(t1) + strm.setPosition(0) + strm.load(t2) + strm.close() + + doAssert t2[0] == t2[1] + + +template checkMarshal(data: typed) = + let orig = data + let m = $$orig + + let old = to[typeof(orig)](m) + doAssert data == old + +template main() = + type + Book = object + page: int + name: string + + let book = Book(page: 12, name: "persona") + + checkMarshal(486) + checkMarshal(3.14) + checkMarshal("azure sky") + checkMarshal(book) + checkMarshal([1, 2, 3]) + checkMarshal(@[1.5, 2.7, 3.9, 4.2]) + checkMarshal(@["dream", "is", "possible"]) -type - Entity = object of RootObj - name: string - - Person = object of Entity - age: int - bio: string - blob: string - -var instance1 = Person(name: "Cletus", age: 12, - bio: "Я Cletus", - blob: "ABC\x80") -echo($$instance1) -echo(to[Person]($$instance1).bio == instance1.bio) -echo(to[Person]($$instance1).blob == instance1.blob) - -# bug 5757 - -type - Something = object - x: string - y: int - -var data1 = """{"x": "alpha", "y": 100}""" -var data2 = """{"x": "omega", "y": 200}""" - -var r = to[Something](data1) - -echo r.x, " ", r.y - -r = to[Something](data2) - -echo r.x, " ", r.y - - -type - Foo = object - a1: string - a2: string - a3: seq[string] - a4: seq[int] - a5: seq[int] - a6: seq[int] -var foo = Foo(a2: "", a4: @[], a6: @[1]) -foo.a6.setLen 0 -doAssert $$foo == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}""" -testit(foo) +static: main() +main() diff --git a/tests/stdlib/tmarshalsegfault.nim b/tests/stdlib/tmarshalsegfault.nim new file mode 100644 index 000000000..71f2766c8 --- /dev/null +++ b/tests/stdlib/tmarshalsegfault.nim @@ -0,0 +1,54 @@ +# issue #12405 + +import std/[marshal, streams, times, tables, os, assertions] + +type AiredEpisodeState * = ref object + airedAt * : DateTime + tvShowId * : string + seasonNumber * : int + number * : int + title * : string + +type ShowsWatchlistState * = ref object + aired * : seq[AiredEpisodeState] + +type UiState * = ref object + shows: ShowsWatchlistState + +# Helpers to marshal and unmarshal +proc load * ( state : var UiState, file : string ) = + var strm = newFileStream( file, fmRead ) + + strm.load( state ) + + strm.close() + +proc store * ( state : UiState, file : string ) = + var strm = newFileStream( file, fmWrite ) + + strm.store( state ) + + strm.close() + +# 1. We fill the state initially +var state : UiState = UiState( shows: ShowsWatchlistState( aired: @[] ) ) + +# VERY IMPORTANT: For some reason, small numbers (like 2 or 3) don't trigger the bug. Anything above 7 or 8 on my machine triggers though +for i in 0..30: + var episode = AiredEpisodeState( airedAt: now(), tvShowId: "1", seasonNumber: 1, number: 1, title: "string" ) + + state.shows.aired.add( episode ) + +# 2. Store it in a file with the marshal module, and then load it back up +store( state, "tmarshalsegfault_data" ) +load( state, "tmarshalsegfault_data" ) +removeFile("tmarshalsegfault_data") + +# 3. VERY IMPORTANT: Without this line, for some reason, everything works fine +state.shows.aired[ 0 ] = AiredEpisodeState( airedAt: now(), tvShowId: "1", seasonNumber: 1, number: 1, title: "string" ) + +# 4. And formatting the airedAt date will now trigger the exception +for ep in state.shows.aired: + let x = $ep.seasonNumber & "x" & $ep.number & " (" & $ep.airedAt & ")" + let y = $ep.seasonNumber & "x" & $ep.number & " (" & $ep.airedAt & ")" + doAssert x == y diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 0de09a858..22e5f7d88 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -1,147 +1,473 @@ discard """ - action: run - output: '''[Suite] random int + targets: "c cpp js" + matrix:"; -d:danger; --mm:refc" +""" -[Suite] random float +# xxx: there should be a test with `-d:nimTmathCase2 -d:danger --passc:-ffast-math`, +# but it requires disabling certain lines with `when not defined(nimTmathCase2)` -[Suite] cumsum +import std/math +import std/assertions -[Suite] random sample -[Suite] ^ +# Function for approximate comparison of floats +proc `==~`(x, y: float): bool = abs(x - y) < 1e-9 -''' -""" -import math, random, os -import unittest -import sets, tables - -suite "random int": - test "there might be some randomness": - var set = initHashSet[int](128) - - for i in 1..1000: - incl(set, rand(high(int))) - check len(set) == 1000 - test "single number bounds work": - - var rand: int - for i in 1..1000: - rand = rand(1000) - check rand < 1000 - check rand > -1 - test "slice bounds work": - - var rand: int - for i in 1..1000: - rand = rand(100..1000) - check rand < 1000 - check rand >= 100 - test " again gives new numbers": - - var rand1 = rand(1000000) - os.sleep(200) - - var rand2 = rand(1000000) - check rand1 != rand2 - - -suite "random float": - test "there might be some randomness": - var set = initHashSet[float](128) - - for i in 1..100: - incl(set, rand(1.0)) - check len(set) == 100 - test "single number bounds work": - - var rand: float - for i in 1..1000: - rand = rand(1000.0) - check rand < 1000.0 - check rand > -1.0 - test "slice bounds work": - - var rand: float - for i in 1..1000: - rand = rand(100.0..1000.0) - check rand < 1000.0 - check rand >= 100.0 - test " again gives new numbers": - - var rand1:float = rand(1000000.0) - os.sleep(200) - - var rand2:float = rand(1000000.0) - check rand1 != rand2 - -suite "cumsum": - test "cumsum int seq return": - let counts = [ 1, 2, 3, 4 ] - check counts.cumsummed == [ 1, 3, 6, 10 ] - - test "cumsum float seq return": - let counts = [ 1.0, 2.0, 3.0, 4.0 ] - check counts.cumsummed == [ 1.0, 3.0, 6.0, 10.0 ] - - test "cumsum int in-place": - var counts = [ 1, 2, 3, 4 ] - counts.cumsum - check counts == [ 1, 3, 6, 10 ] - - test "cumsum float in-place": - var counts = [ 1.0, 2.0, 3.0, 4.0 ] - counts.cumsum - check counts == [ 1.0, 3.0, 6.0, 10.0 ] - -suite "random sample": - test "non-uniform array sample unnormalized int CDF": - let values = [ 10, 20, 30, 40, 50 ] # values - let counts = [ 4, 3, 2, 1, 0 ] # weights aka unnormalized probabilities - var histo = initCountTable[int]() - let cdf = counts.cumsummed # unnormalized CDF - for i in 0 ..< 5000: - histo.inc(sample(values, cdf)) - check histo.len == 4 # number of non-zero in `counts` - # Any one bin is a binomial random var for n samples, each with prob p of - # adding a count to k; E[k]=p*n, Var k=p*(1-p)*n, approximately Normal for - # big n. So, P(abs(k - p*n)/sqrt(p*(1-p)*n))>3.0) =~ 0.0027, while - # P(wholeTestFails) =~ 1 - P(binPasses)^4 =~ 1 - (1-0.0027)^4 =~ 0.01. - for i, c in counts: - if c == 0: - check values[i] notin histo - continue - let p = float(c) / float(cdf[^1]) - let n = 5000.0 - let expected = p * n - let stdDev = sqrt(n * p * (1.0 - p)) - check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev - - test "non-uniform array sample normalized float CDF": - let values = [ 10, 20, 30, 40, 50 ] # values - let counts = [ 0.4, 0.3, 0.2, 0.1, 0 ] # probabilities - var histo = initCountTable[int]() - let cdf = counts.cumsummed # normalized CDF - for i in 0 ..< 5000: - histo.inc(sample(values, cdf)) - check histo.len == 4 # number of non-zero in ``counts`` - for i, c in counts: - if c == 0: - check values[i] notin histo - continue - let p = float(c) / float(cdf[^1]) - let n = 5000.0 - let expected = p * n - let stdDev = sqrt(n * p * (1.0 - p)) - # NOTE: like unnormalized int CDF test, P(wholeTestFails) =~ 0.01. - check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev - -suite "^": - test "compiles for valid types": - check: compiles(5 ^ 2) - check: compiles(5.5 ^ 2) - check: compiles(5.5 ^ 2.int8) - check: compiles(5.5 ^ 2.uint) - check: compiles(5.5 ^ 2.uint8) - check: not compiles(5.5 ^ 2.2) +template main() = + block: + when not defined(js): + # check for no side effect annotation + proc mySqrt(num: float): float {.noSideEffect.} = + # xxx unused + sqrt(num) + + # check gamma function + doAssert gamma(5.0) == 24.0 # 4! + doAssert almostEqual(gamma(0.5), sqrt(PI)) + doAssert almostEqual(gamma(-0.5), -2 * sqrt(PI)) + doAssert lgamma(1.0) == 0.0 # ln(1.0) == 0.0 + doAssert almostEqual(lgamma(0.5), 0.5 * ln(PI)) + doAssert erf(6.0) > erf(5.0) + doAssert erfc(6.0) < erfc(5.0) + + block: # sgn() tests + doAssert sgn(1'i8) == 1 + doAssert sgn(1'i16) == 1 + doAssert sgn(1'i32) == 1 + doAssert sgn(1'i64) == 1 + doAssert sgn(1'u8) == 1 + doAssert sgn(1'u16) == 1 + doAssert sgn(1'u32) == 1 + doAssert sgn(1'u64) == 1 + doAssert sgn(-12342.8844'f32) == -1 + doAssert sgn(123.9834'f64) == 1 + doAssert sgn(0'i32) == 0 + doAssert sgn(0'f32) == 0 + doAssert sgn(-0.0'f64) == 0 + doAssert sgn(NegInf) == -1 + doAssert sgn(Inf) == 1 + doAssert sgn(NaN) == 0 + + block: # fac() tests + when nimvm: discard + else: + try: + discard fac(-1) + except AssertionDefect: + discard + + doAssert fac(0) == 1 + doAssert fac(1) == 1 + doAssert fac(2) == 2 + doAssert fac(3) == 6 + doAssert fac(4) == 24 + doAssert fac(5) == 120 + + block: # floorMod/floorDiv + doAssert floorDiv(8, 3) == 2 + doAssert floorMod(8, 3) == 2 + + doAssert floorDiv(8, -3) == -3 + doAssert floorMod(8, -3) == -1 + + doAssert floorDiv(-8, 3) == -3 + doAssert floorMod(-8, 3) == 1 + + doAssert floorDiv(-8, -3) == 2 + doAssert floorMod(-8, -3) == -2 + + doAssert floorMod(8.0, -3.0) == -1.0 + doAssert floorMod(-8.5, 3.0) == 0.5 + + block: # euclDiv/euclMod + doAssert euclDiv(8, 3) == 2 + doAssert euclMod(8, 3) == 2 + + doAssert euclDiv(8, -3) == -2 + doAssert euclMod(8, -3) == 2 + + doAssert euclDiv(-8, 3) == -3 + doAssert euclMod(-8, 3) == 1 + + doAssert euclDiv(-8, -3) == 3 + doAssert euclMod(-8, -3) == 1 + + doAssert euclMod(8.0, -3.0) == 2.0 + doAssert euclMod(-8.5, 3.0) == 0.5 + + doAssert euclDiv(9, 3) == 3 + doAssert euclMod(9, 3) == 0 + + doAssert euclDiv(9, -3) == -3 + doAssert euclMod(9, -3) == 0 + + doAssert euclDiv(-9, 3) == -3 + doAssert euclMod(-9, 3) == 0 + + doAssert euclDiv(-9, -3) == 3 + doAssert euclMod(-9, -3) == 0 + + block: # ceilDiv + doAssert ceilDiv(8, 3) == 3 + doAssert ceilDiv(8, 4) == 2 + doAssert ceilDiv(8, 5) == 2 + doAssert ceilDiv(11, 3) == 4 + doAssert ceilDiv(12, 3) == 4 + doAssert ceilDiv(13, 3) == 5 + doAssert ceilDiv(41, 7) == 6 + doAssert ceilDiv(0, 1) == 0 + doAssert ceilDiv(1, 1) == 1 + doAssert ceilDiv(1, 2) == 1 + doAssert ceilDiv(2, 1) == 2 + doAssert ceilDiv(2, 2) == 1 + doAssert ceilDiv(0, high(int)) == 0 + doAssert ceilDiv(1, high(int)) == 1 + doAssert ceilDiv(0, high(int) - 1) == 0 + doAssert ceilDiv(1, high(int) - 1) == 1 + doAssert ceilDiv(high(int) div 2, high(int) div 2 + 1) == 1 + doAssert ceilDiv(high(int) div 2, high(int) div 2 + 2) == 1 + doAssert ceilDiv(high(int) div 2 + 1, high(int) div 2) == 2 + doAssert ceilDiv(high(int) div 2 + 2, high(int) div 2) == 2 + doAssert ceilDiv(high(int) div 2 + 1, high(int) div 2 + 1) == 1 + doAssert ceilDiv(high(int), 1) == high(int) + doAssert ceilDiv(high(int) - 1, 1) == high(int) - 1 + doAssert ceilDiv(high(int) - 1, 2) == high(int) div 2 + doAssert ceilDiv(high(int) - 1, high(int)) == 1 + doAssert ceilDiv(high(int) - 1, high(int) - 1) == 1 + doAssert ceilDiv(high(int) - 1, high(int) - 2) == 2 + doAssert ceilDiv(high(int), high(int)) == 1 + doAssert ceilDiv(high(int), high(int) - 1) == 2 + doAssert ceilDiv(255'u8, 1'u8) == 255'u8 + doAssert ceilDiv(254'u8, 2'u8) == 127'u8 + when not defined(danger): + doAssertRaises(AssertionDefect): discard ceilDiv(41, 0) + doAssertRaises(AssertionDefect): discard ceilDiv(41, -1) + doAssertRaises(AssertionDefect): discard ceilDiv(-1, 1) + doAssertRaises(AssertionDefect): discard ceilDiv(-1, -1) + doAssertRaises(AssertionDefect): discard ceilDiv(254'u8, 3'u8) + doAssertRaises(AssertionDefect): discard ceilDiv(255'u8, 2'u8) + + block: # splitDecimal() tests + doAssert splitDecimal(54.674).intpart == 54.0 + doAssert splitDecimal(54.674).floatpart ==~ 0.674 + doAssert splitDecimal(-693.4356).intpart == -693.0 + doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356 + doAssert splitDecimal(0.0).intpart == 0.0 + doAssert splitDecimal(0.0).floatpart == 0.0 + + block: # trunc tests for vcc + doAssert trunc(-1.1) == -1 + doAssert trunc(1.1) == 1 + doAssert trunc(-0.1) == -0 + doAssert trunc(0.1) == 0 + + # special case + doAssert classify(trunc(1e1000000)) == fcInf + doAssert classify(trunc(-1e1000000)) == fcNegInf + when not defined(nimTmathCase2): + doAssert classify(trunc(0.0/0.0)) == fcNan + doAssert classify(trunc(0.0)) == fcZero + + # trick the compiler to produce signed zero + let + f_neg_one = -1.0 + f_zero = 0.0 + f_nan = f_zero / f_zero + + doAssert classify(trunc(f_neg_one*f_zero)) == fcNegZero + + doAssert trunc(-1.1'f32) == -1 + doAssert trunc(1.1'f32) == 1 + doAssert trunc(-0.1'f32) == -0 + doAssert trunc(0.1'f32) == 0 + doAssert classify(trunc(1e1000000'f32)) == fcInf + doAssert classify(trunc(-1e1000000'f32)) == fcNegInf + when not defined(nimTmathCase2): + doAssert classify(trunc(f_nan.float32)) == fcNan + doAssert classify(trunc(0.0'f32)) == fcZero + + block: # divmod + doAssert divmod(int.high, 1) == (int.high, 0) + doAssert divmod(-1073741823, 17) == (-63161283, -12) + doAssert divmod(int32.high, 1.int32) == (int32.high, 0.int32) + doAssert divmod(1073741823.int32, 5.int32) == (214748364.int32, 3.int32) + doAssert divmod(4611686018427387903.int64, 5.int64) == (922337203685477580.int64, 3.int64) + when not defined(js) and (not compileOption("panics")) and compileOption("overflowChecks"): + when nimvm: + discard # cannot catch OverflowDefect here + else: + doAssertRaises(OverflowDefect, (discard divmod(cint.low, -1.cint))) + doAssertRaises(OverflowDefect, (discard divmod(clong.low, -1.clong))) + doAssertRaises(OverflowDefect, (discard divmod(clonglong.low, -1.clonglong))) + doAssertRaises(DivByZeroDefect, (discard divmod(1, 0))) + + block: # log + doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0) + doAssert log2(8.0'f64) == 3.0'f64 + doAssert log2(4.0'f64) == 2.0'f64 + doAssert log2(2.0'f64) == 1.0'f64 + doAssert log2(1.0'f64) == 0.0'f64 + doAssert classify(log2(0.0'f64)) == fcNegInf + + doAssert log2(8.0'f32) == 3.0'f32 + doAssert log2(4.0'f32) == 2.0'f32 + doAssert log2(2.0'f32) == 1.0'f32 + doAssert log2(1.0'f32) == 0.0'f32 + doAssert classify(log2(0.0'f32)) == fcNegInf + + block: # cumsum + block: # cumsum int seq return + let counts = [1, 2, 3, 4] + doAssert counts.cumsummed == @[1, 3, 6, 10] + let empty: seq[int] = @[] + doAssert empty.cumsummed == @[] + + block: # cumsum float seq return + let counts = [1.0, 2.0, 3.0, 4.0] + doAssert counts.cumsummed == @[1.0, 3.0, 6.0, 10.0] + let empty: seq[float] = @[] + doAssert empty.cumsummed == @[] + + block: # cumsum int in-place + var counts = [1, 2, 3, 4] + counts.cumsum + doAssert counts == [1, 3, 6, 10] + var empty: seq[int] = @[] + empty.cumsum + doAssert empty == @[] + + block: # cumsum float in-place + var counts = [1.0, 2.0, 3.0, 4.0] + counts.cumsum + doAssert counts == [1.0, 3.0, 6.0, 10.0] + var empty: seq[float] = @[] + empty.cumsum + doAssert empty == @[] + + block: # ^ compiles for valid types + doAssert: compiles(5 ^ 2) + doAssert: compiles(5.5 ^ 2) + doAssert: compiles(5.5 ^ 2.int8) + doAssert: compiles(5.5 ^ 2.uint) + doAssert: compiles(5.5 ^ 2.uint8) + doAssert: not compiles(5.5 ^ 2.2) + + block: # isNaN + doAssert NaN.isNaN + doAssert not Inf.isNaN + doAssert isNaN(Inf - Inf) + doAssert not isNaN(0.0) + doAssert not isNaN(3.1415926) + doAssert not isNaN(0'f32) + + block: # signbit + doAssert not signbit(0.0) + doAssert signbit(-0.0) + doAssert signbit(-0.1) + doAssert not signbit(0.1) + + doAssert not signbit(Inf) + doAssert signbit(-Inf) + doAssert not signbit(NaN) + + let x1 = NaN + let x2 = -NaN + let x3 = -x1 + + doAssert isNaN(x1) + doAssert isNaN(x2) + doAssert isNaN(x3) + doAssert not signbit(x1) + doAssert signbit(x2) + doAssert signbit(x3) + + block: # copySign + doAssert copySign(10.0, 1.0) == 10.0 + doAssert copySign(10.0, -1.0) == -10.0 + doAssert copySign(-10.0, -1.0) == -10.0 + doAssert copySign(-10.0, 1.0) == 10.0 + doAssert copySign(float(10), -1.0) == -10.0 + + doAssert copySign(10.0'f64, 1.0) == 10.0 + doAssert copySign(10.0'f64, -1.0) == -10.0 + doAssert copySign(-10.0'f64, -1.0) == -10.0 + doAssert copySign(-10.0'f64, 1.0) == 10.0 + doAssert copySign(10'f64, -1.0) == -10.0 + + doAssert copySign(10.0'f32, 1.0) == 10.0 + doAssert copySign(10.0'f32, -1.0) == -10.0 + doAssert copySign(-10.0'f32, -1.0) == -10.0 + doAssert copySign(-10.0'f32, 1.0) == 10.0 + doAssert copySign(10'f32, -1.0) == -10.0 + + doAssert copySign(Inf, -1.0) == -Inf + doAssert copySign(-Inf, 1.0) == Inf + doAssert copySign(Inf, 1.0) == Inf + doAssert copySign(-Inf, -1.0) == -Inf + doAssert copySign(Inf, 0.0) == Inf + doAssert copySign(Inf, -0.0) == -Inf + doAssert copySign(-Inf, 0.0) == Inf + doAssert copySign(-Inf, -0.0) == -Inf + doAssert copySign(1.0, -0.0) == -1.0 + doAssert copySign(0.0, -0.0) == -0.0 + doAssert copySign(-1.0, 0.0) == 1.0 + doAssert copySign(10.0, 0.0) == 10.0 + doAssert copySign(-1.0, NaN) == 1.0 + doAssert copySign(10.0, NaN) == 10.0 + + doAssert copySign(NaN, NaN).isNaN + doAssert copySign(-NaN, NaN).isNaN + doAssert copySign(NaN, -NaN).isNaN + doAssert copySign(-NaN, -NaN).isNaN + doAssert copySign(NaN, 0.0).isNaN + doAssert copySign(NaN, -0.0).isNaN + doAssert copySign(-NaN, 0.0).isNaN + doAssert copySign(-NaN, -0.0).isNaN + + doAssert copySign(-1.0, NaN) == 1.0 + doAssert copySign(-1.0, -NaN) == -1.0 + doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0 + + block: # almostEqual + doAssert almostEqual(3.141592653589793, 3.1415926535897936) + doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32) + doAssert almostEqual(Inf, Inf) + doAssert almostEqual(-Inf, -Inf) + doAssert not almostEqual(Inf, -Inf) + doAssert not almostEqual(-Inf, Inf) + doAssert not almostEqual(Inf, NaN) + doAssert not almostEqual(NaN, NaN) + + block: # round + block: # Round to 0 decimal places + doAssert round(54.652) == 55.0 + doAssert round(54.352) == 54.0 + doAssert round(-54.652) == -55.0 + doAssert round(-54.352) == -54.0 + doAssert round(0.0) == 0.0 + doAssert 1 / round(0.0) == Inf + doAssert 1 / round(-0.0) == -Inf + doAssert round(Inf) == Inf + doAssert round(-Inf) == -Inf + doAssert round(NaN).isNaN + doAssert round(-NaN).isNaN + doAssert round(-0.5) == -1.0 + doAssert round(0.5) == 1.0 + doAssert round(-1.5) == -2.0 + doAssert round(1.5) == 2.0 + doAssert round(-2.5) == -3.0 + doAssert round(2.5) == 3.0 + doAssert round(2.5'f32) == 3.0'f32 + doAssert round(2.5'f64) == 3.0'f64 + + block: # func round*[T: float32|float64](x: T, places: int): T + doAssert round(54.345, 0) == 54.0 + template fn(x) = + doAssert round(x, 2).almostEqual 54.35 + doAssert round(x, 2).almostEqual 54.35 + doAssert round(x, -1).almostEqual 50.0 + doAssert round(x, -2).almostEqual 100.0 + doAssert round(x, -3).almostEqual 0.0 + fn(54.346) + fn(54.346'f32) + + block: # abs + doAssert 1.0 / abs(-0.0) == Inf + doAssert 1.0 / abs(0.0) == Inf + doAssert -1.0 / abs(-0.0) == -Inf + doAssert -1.0 / abs(0.0) == -Inf + doAssert abs(0.0) == 0.0 + doAssert abs(0.0'f32) == 0.0'f32 + + doAssert abs(Inf) == Inf + doAssert abs(-Inf) == Inf + doAssert abs(NaN).isNaN + doAssert abs(-NaN).isNaN + + block: # classify + doAssert classify(0.3) == fcNormal + doAssert classify(-0.3) == fcNormal + doAssert classify(5.0e-324) == fcSubnormal + doAssert classify(-5.0e-324) == fcSubnormal + doAssert classify(0.0) == fcZero + doAssert classify(-0.0) == fcNegZero + doAssert classify(NaN) == fcNan + doAssert classify(0.3 / 0.0) == fcInf + doAssert classify(Inf) == fcInf + doAssert classify(-0.3 / 0.0) == fcNegInf + doAssert classify(-Inf) == fcNegInf + + block: # sum + let empty: seq[int] = @[] + doAssert sum(empty) == 0 + doAssert sum([1, 2, 3, 4]) == 10 + doAssert sum([-4, 3, 5]) == 4 + + block: # prod + let empty: seq[int] = @[] + doAssert prod(empty) == 1 + doAssert prod([1, 2, 3, 4]) == 24 + doAssert prod([-4, 3, 5]) == -60 + doAssert almostEqual(prod([1.5, 3.4]), 5.1) + let x: seq[float] = @[] + doAssert prod(x) == 1.0 + + block: # clamp range + doAssert clamp(10, 1..5) == 5 + doAssert clamp(3, 1..5) == 3 + doAssert clamp(5, 1..5) == 5 + doAssert clamp(42.0, 1.0 .. 3.1415926535) == 3.1415926535 + doAssert clamp(NaN, 1.0 .. 2.0).isNaN + doAssert clamp(-Inf, -Inf .. -1.0) == -Inf + type A = enum a0, a1, a2, a3, a4, a5 + doAssert a1.clamp(a2..a4) == a2 + doAssert clamp((3, 0), (1, 0) .. (2, 9)) == (2, 9) + + block: # edge cases + doAssert sqrt(-4.0).isNaN + + doAssert ln(0.0) == -Inf + doAssert ln(-0.0) == -Inf + doAssert ln(-12.0).isNaN + + doAssert log10(0.0) == -Inf + doAssert log10(-0.0) == -Inf + doAssert log10(-12.0).isNaN + + doAssert log2(0.0) == -Inf + doAssert log2(-0.0) == -Inf + doAssert log2(-12.0).isNaN + + when nimvm: discard + else: + doAssert frexp(0.0) == (0.0, 0) + doAssert frexp(-0.0) == (-0.0, 0) + doAssert classify(frexp(-0.0)[0]) == fcNegZero + + when not defined(js): + doAssert gamma(0.0) == Inf + doAssert gamma(-0.0) == -Inf + doAssert gamma(-1.0).isNaN + + doAssert lgamma(0.0) == Inf + doAssert lgamma(-0.0) == Inf + doAssert lgamma(-1.0) == Inf + +static: main() +main() + +when not defined(js) and not defined(danger): + block: # bug #21792 + block: + type Digit = 0..9 + var x = [Digit 4, 7] + + doAssertRaises(RangeDefect): + discard sum(x) + + block: + var x = [int8 124, 127] + + doAssertRaises(OverflowDefect): + discard sum(x) diff --git a/tests/stdlib/tmemfiles1.nim b/tests/stdlib/tmemfiles1.nim index 21a65369f..33657256c 100644 --- a/tests/stdlib/tmemfiles1.nim +++ b/tests/stdlib/tmemfiles1.nim @@ -1,4 +1,6 @@ import memfiles, os +import std/syncio + var mm: MemFile fn = "test.mmap" diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim index 1b249898e..c79f85ebf 100644 --- a/tests/stdlib/tmemfiles2.nim +++ b/tests/stdlib/tmemfiles2.nim @@ -4,6 +4,9 @@ discard """ Half read size: 10 Data: Hello''' """ import memfiles, os +import std/syncio + + const fn = "test.mmap" var @@ -12,8 +15,9 @@ var if fileExists(fn): removeFile(fn) -# Create a new file, data all zeros -mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20) +# Create a new file, data all zeros, starting at size 10 +mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 10, allowRemap=true) +mm.resize 20 # resize up to 20 mm.close() # read, change diff --git a/tests/stdlib/tmemlinesBuf.nim b/tests/stdlib/tmemlinesBuf.nim index 97ad751ee..7bd89d4f2 100644 --- a/tests/stdlib/tmemlinesBuf.nim +++ b/tests/stdlib/tmemlinesBuf.nim @@ -1,15 +1,9 @@ -discard """ -output: "15" -disabled: "appveyor" -""" - -import memfiles +import std/[memfiles, assertions] var inp = memfiles.open("tests/stdlib/tmemlinesBuf.nim") -var buffer: TaintedString = "" +var buffer: string = "" var lineCount = 0 for line in lines(inp, buffer): lineCount += 1 close(inp) - -echo lineCount +doAssert lineCount == 9, $lineCount # this file's number of lines diff --git a/tests/stdlib/tmemmapstreams.nim b/tests/stdlib/tmemmapstreams.nim index dd011d777..9cfae62c7 100644 --- a/tests/stdlib/tmemmapstreams.nim +++ b/tests/stdlib/tmemmapstreams.nim @@ -12,6 +12,8 @@ Readed line: Hello! Position after reading line: 7''' """ import os, streams, memfiles +import std/syncio + const fn = "test.mmapstream" var diff --git a/tests/stdlib/tmersenne.nim b/tests/stdlib/tmersenne.nim new file mode 100644 index 000000000..64450a045 --- /dev/null +++ b/tests/stdlib/tmersenne.nim @@ -0,0 +1,13 @@ +import std/mersenne +import std/assertions + +template main() = + var mt = newMersenneTwister(2525) + + doAssert mt.getNum == 407788156'u32 + doAssert mt.getNum == 1071751096'u32 + doAssert mt.getNum == 3805347140'u32 + + +static: main() +main() diff --git a/tests/stdlib/tmget.nim b/tests/stdlib/tmget.nim index 52e61fd24..f41963f02 100644 --- a/tests/stdlib/tmget.nim +++ b/tests/stdlib/tmget.nim @@ -1,16 +1,21 @@ discard """ + matrix: "--mm:refc; --mm:orc" output: '''Can't access 6 10 11 +2 Can't access 6 10 11 +2 Can't access 6 10 11 +2 Can't access 6 10 11 +2 0 10 11 @@ -40,6 +45,9 @@ block: x[5] += 1 var c = x[5] echo c + x.mgetOrPut(7).inc + x.mgetOrPut(7).inc + echo x[7] block: var x = newTable[int, int]() @@ -52,6 +60,9 @@ block: x[5] += 1 var c = x[5] echo c + x.mgetOrPut(7).inc + x.mgetOrPut(7).inc + echo x[7] block: var x = initOrderedTable[int, int]() @@ -64,6 +75,9 @@ block: x[5] += 1 var c = x[5] echo c + x.mgetOrPut(7).inc + x.mgetOrPut(7).inc + echo x[7] block: var x = newOrderedTable[int, int]() @@ -76,6 +90,9 @@ block: x[5] += 1 var c = x[5] echo c + x.mgetOrPut(7).inc + x.mgetOrPut(7).inc + echo x[7] block: var x = initCountTable[int]() diff --git a/tests/stdlib/tmimetypes.nim b/tests/stdlib/tmimetypes.nim new file mode 100644 index 000000000..fd66ebd97 --- /dev/null +++ b/tests/stdlib/tmimetypes.nim @@ -0,0 +1,28 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + +import std/mimetypes +import std/assertions + + +template main() = + var m = newMimetypes() + doAssert m.getMimetype("mp4") == "video/mp4" + doAssert m.getExt("application/json") == "json" + doAssert m.getMimetype("json") == "application/json" + m.register("foo", "baa") + doAssert m.getMimetype("foo") == "baa" + doAssert m.getMimetype("txt") == "text/plain" + doAssert m.getExt("text/plain") == "txt" + # see also `runnableExamples`. + # xxx we should have a way to avoid duplicating code between runnableExamples and tests + + doAssert m.getMimetype("nim") == "text/nim" + doAssert m.getMimetype("nimble") == "text/nimble" + doAssert m.getMimetype("nimf") == "text/nim" + doAssert m.getMimetype("nims") == "text/nim" + +static: main() +main() diff --git a/tests/stdlib/tmisc_issues.nim b/tests/stdlib/tmisc_issues.nim new file mode 100644 index 000000000..86dcf4162 --- /dev/null +++ b/tests/stdlib/tmisc_issues.nim @@ -0,0 +1,39 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" +""" + +import std/assertions + +# bug #20227 +type + Data = object + id: int + + Test = distinct Data + + Object = object + data: Test + + +var x: Object = Object(data: Test(Data(id: 12))) +doAssert Data(x.data).id == 12 + +block: # bug #16771 + type A = object + n: int + + proc foo(a, b: var A) = + swap a, b + + var a, b: A + a.n = 42 + b.n = 1 + doAssert a.n == 42 + doAssert b.n == 1 + a.swap b + doAssert a.n == 1 + doAssert b.n == 42 + a.foo b + doAssert a.n == 42 + doAssert b.n == 1 diff --git a/tests/stdlib/tmitems.nim b/tests/stdlib/tmitems.nim index 17265e1f7..cc515a175 100644 --- a/tests/stdlib/tmitems.nim +++ b/tests/stdlib/tmitems.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc" output: '''@[11, 12, 13] @[11, 12, 13] @[1, 3, 5] @@ -16,7 +17,18 @@ fpqeew <Students> <Student Name="Aprilfoo" /> <Student Name="bar" /> -</Students>''' +</Students> +<chapter> + <title>This is a Docbook title</title> + <para> + This is a Docbook paragraph containing <emphasis>emphasized</emphasis>, + <literal>literal</literal> and <replaceable>replaceable</replaceable> + text. Sometimes scrunched together like this: + <literal>literal</literal><replaceable>replaceable</replaceable> + and sometimes not: + <literal>literal</literal> <replaceable>replaceable</replaceable> + </para> +</chapter>''' """ block: @@ -51,6 +63,7 @@ block: block: var x = "foobar" + prepareMutation(x) var y = cast[cstring](addr x[0]) for c in y.mitems: inc c @@ -64,6 +77,7 @@ block: block: var x = "foobar" + prepareMutation(x) var y = cast[cstring](addr x[0]) for i, c in y.mpairs: inc c, i @@ -123,7 +137,7 @@ block: x.num += 10 echo j -import xmltree, xmlparser, streams, strtabs +import xmltree, xmlparser, parsexml, streams, strtabs block: var d = parseXml(newStringStream """<Students> @@ -134,3 +148,17 @@ block: x = <>Student(Name=x.attrs["Name"] & "foo") d[1].attrs["Name"] = "bar" echo d + +block: + var d = parseXml(newStringStream """<chapter> + <title>This is a Docbook title</title> + <para> + This is a Docbook paragraph containing <emphasis>emphasized</emphasis>, + <literal>literal</literal> and <replaceable>replaceable</replaceable> + text. Sometimes scrunched together like this: + <literal>literal</literal><replaceable>replaceable</replaceable> + and sometimes not: + <literal>literal</literal> <replaceable>replaceable</replaceable> + </para> +</chapter>""",{reportComments, reportWhitespace}) + echo d diff --git a/tests/stdlib/tmonotimes.nim b/tests/stdlib/tmonotimes.nim new file mode 100644 index 000000000..1366dbfe9 --- /dev/null +++ b/tests/stdlib/tmonotimes.nim @@ -0,0 +1,22 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + +import std/[monotimes, times] +import std/assertions + +let d = initDuration(nanoseconds = 10) +let t1 = getMonoTime() +let t2 = t1 + d + +doAssert t2 - t1 == d +doAssert t1 == t1 +doAssert t1 != t2 +doAssert t2 - d == t1 +doAssert t1 < t2 +doAssert t1 <= t2 +doAssert t1 <= t1 +doAssert not(t2 < t1) +doAssert t1 < high(MonoTime) +doAssert low(MonoTime) < t1 diff --git a/tests/stdlib/tnativesockets.nim b/tests/stdlib/tnativesockets.nim new file mode 100644 index 000000000..8242beb83 --- /dev/null +++ b/tests/stdlib/tnativesockets.nim @@ -0,0 +1,30 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/nativesockets +import stdtest/testutils +import std/assertions + +block: + let hostname = getHostname() + doAssert hostname.len > 0 + +when defined(windows): + assertAll: + toInt(IPPROTO_IP) == 0 + toInt(IPPROTO_ICMP) == 1 + toInt(IPPROTO_TCP) == 6 + toInt(IPPROTO_UDP) == 17 + toInt(IPPROTO_IPV6) == 41 + toInt(IPPROTO_ICMPV6) == 58 + toInt(IPPROTO_RAW) == 20 + + # no changes to enum value + ord(IPPROTO_TCP) == 6 + ord(IPPROTO_UDP) == 17 + ord(IPPROTO_IP) == 18 + ord(IPPROTO_IPV6) == 19 + ord(IPPROTO_RAW) == 20 + ord(IPPROTO_ICMP) == 21 + ord(IPPROTO_ICMPV6) == 22 diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim index 2dd22796c..27a6ac49c 100644 --- a/tests/stdlib/tnet.nim +++ b/tests/stdlib/tnet.nim @@ -1,55 +1,92 @@ discard """ +matrix: "--mm:refc; --mm:orc" outputsub: "" """ import net, nativesockets import unittest +import std/assertions -suite "isIpAddress tests": - test "127.0.0.1 is valid": +block: # isIpAddress tests + block: # 127.0.0.1 is valid check isIpAddress("127.0.0.1") == true - test "ipv6 localhost is valid": + block: # ipv6 localhost is valid check isIpAddress("::1") == true - test "fqdn is not an ip address": + block: # fqdn is not an ip address check isIpAddress("example.com") == false - test "random string is not an ipaddress": + block: # random string is not an ipaddress check isIpAddress("foo bar") == false - test "5127.0.0.1 is invalid": + block: # 5127.0.0.1 is invalid check isIpAddress("5127.0.0.1") == false - test "ipv6 is valid": + block: # ipv6 is valid check isIpAddress("2001:cdba:0000:0000:0000:0000:3257:9652") == true - test "invalid ipv6": + block: # invalid ipv6 check isIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652") == false -suite "parseIpAddress tests": - test "127.0.0.1 is valid": +block: # parseIpAddress tests + block: # 127.0.0.1 is valid discard parseIpAddress("127.0.0.1") - test "ipv6 localhost is valid": + block: # ipv6 localhost is valid discard parseIpAddress("::1") - test "fqdn is not an ip address": + block: # fqdn is not an ip address expect(ValueError): discard parseIpAddress("example.com") - test "random string is not an ipaddress": + block: # random string is not an ipaddress expect(ValueError): discard parseIpAddress("foo bar") - test "ipv6 is valid": + block: # ipv6 is valid discard parseIpAddress("2001:cdba:0000:0000:0000:0000:3257:9652") - test "invalid ipv6": + block: # invalid ipv6 expect(ValueError): discard parseIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652") + block: # ipv4-compatible ipv6 address (embedded ipv4 address) + check parseIpAddress("::ffff:10.0.0.23") == parseIpAddress("::ffff:0a00:0017") + + block: # octal number in ipv4 address + expect(ValueError): + discard parseIpAddress("010.8.8.8") + expect(ValueError): + discard parseIpAddress("8.010.8.8") + + block: # hexadecimal number in ipv4 address + expect(ValueError): + discard parseIpAddress("0xc0.168.0.1") + expect(ValueError): + discard parseIpAddress("192.0xa8.0.1") + + block: # less than 4 numbers in ipv4 address + expect(ValueError): + discard parseIpAddress("127.0.1") + + block: # octal number in embedded ipv4 address + expect(ValueError): + discard parseIpAddress("::ffff:010.8.8.8") + expect(ValueError): + discard parseIpAddress("::ffff:8.010.8.8") + + block: # hexadecimal number in embedded ipv4 address + expect(ValueError): + discard parseIpAddress("::ffff:0xc0.168.0.1") + expect(ValueError): + discard parseIpAddress("::ffff:192.0xa8.0.1") + + block: # less than 4 numbers in embedded ipv4 address + expect(ValueError): + discard parseIpAddress("::ffff:127.0.1") + block: # "IpAddress/Sockaddr conversion" proc test(ipaddrstr: string) = var ipaddr_1 = parseIpAddress(ipaddrstr) @@ -58,7 +95,7 @@ block: # "IpAddress/Sockaddr conversion" doAssert($ipaddrstr == $ipaddr_1) var sockaddr: Sockaddr_storage - var socklen: Socklen + var socklen: SockLen var ipaddr_2: IpAddress var port_2: Port diff --git a/tests/stdlib/tnet_ll.nim b/tests/stdlib/tnet_ll.nim index 2ac272fd1..199946482 100644 --- a/tests/stdlib/tnet_ll.nim +++ b/tests/stdlib/tnet_ll.nim @@ -1,42 +1,52 @@ -discard """ - action: run - output: ''' -[Suite] inet_ntop tests -''' -""" - -when defined(windows): - import winlean -elif defined(posix): - import posix -else: - {.error: "Unsupported OS".} - -import unittest, strutils - -suite "inet_ntop tests": - - setup: - when defined(windows): - var wsa: WSAData - discard wsaStartup(0x101'i16, wsa.addr) - - test "IP V4": - var ip4 = 0x10111213 - var buff: array[0..255, char] - let r = inet_ntop(AF_INET, ip4.addr, buff[0].addr, buff.sizeof.int32) - let res = if r == nil: "" else: $r - check: res == "19.18.17.16" - - - test "IP V6": - when defined(windows): - let ipv6Support = (getVersion() and 0xff) > 0x5 - else: - let ipv6Support = true - - var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001] - var buff: array[0..255, char] - let r = inet_ntop(AF_INET6, ip6[0].addr, buff[0].addr, buff.sizeof.int32) - let res = if r == nil: "" else: $r - check: not ipv6Support or res == "10:110:20:120:30:130:40:140" +discard """ + action: run + matrix: "--mm:refc; --mm:orc" + output: ''' + +[Suite] inet_ntop tests +''' +""" + +when defined(windows): + import winlean +elif defined(posix): + import posix +else: + {.error: "Unsupported OS".} + +import unittest, strutils + +suite "inet_ntop tests": + + setup: + when defined(windows): + var wsa: WSAData + discard wsaStartup(0x101'i16, wsa.addr) + + test "IP V4": + # regular + var ip4 = InAddr() + ip4.s_addr = 0x10111213'u32 + check: ip4.s_addr == 0x10111213'u32 + + var buff: array[0..255, char] + let r = inet_ntop(AF_INET, cast[pointer](ip4.s_addr.addr), cast[cstring](buff[0].addr), buff.len.int32) + let res = if r == nil: "" else: $r + check: res == "19.18.17.16" + + test "IP V6": + when defined(windows): + let ipv6Support = (getVersion() and 0xff) > 0x5 + else: + let ipv6Support = true + + var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001] + var buff: array[0..255, char] + let r = inet_ntop(AF_INET6, cast[pointer](ip6[0].addr), cast[cstring](buff[0].addr), buff.len.int32) + let res = if r == nil: "" else: $r + check: not ipv6Support or res == "10:110:20:120:30:130:40:140" + + test "InAddr": + # issue 19244 + var ip4 = InAddr(s_addr: 0x10111213'u32) + check: ip4.s_addr == 0x10111213'u32 diff --git a/tests/stdlib/tnetbind.nim b/tests/stdlib/tnetbind.nim index 734b6c5e7..84f9ac464 100644 --- a/tests/stdlib/tnetbind.nim +++ b/tests/stdlib/tnetbind.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc; --mm:orc" joinable: false """ diff --git a/tests/stdlib/tnetconnect.nim b/tests/stdlib/tnetconnect.nim new file mode 100644 index 000000000..ae654aed9 --- /dev/null +++ b/tests/stdlib/tnetconnect.nim @@ -0,0 +1,30 @@ +discard """ + disabled: "i386" + matrix: "-d:ssl" +""" + +import std/net +from std/strutils import `%` +from stdtest/testutils import enableRemoteNetworking + +# bug #15215 +proc test() = + let ctx = newContext() + + proc fn(url: string) = + let socket = newSocket() + defer: close(socket) + connect(socket, url, Port(443), 5000) # typically 20 could be enough + send(socket, "GET / HTTP/1.0\nHost: $#\nConnection: close\n\n" % [url]) + wrapSocket(ctx, socket) + + # trying 2 sites makes it more resilent: refs #17458 this could give: + # * Call to 'connect' timed out. [TimeoutError] + # * No route to host [OSError] + try: + fn("www.nim-lang.org") + except TimeoutError, OSError: + fn("www.google.com") + +when enableRemoteNetworking: + test() diff --git a/tests/stdlib/tnetdial.nim b/tests/stdlib/tnetdial.nim index f940bd630..a1e147ad5 100644 --- a/tests/stdlib/tnetdial.nim +++ b/tests/stdlib/tnetdial.nim @@ -2,10 +2,10 @@ discard """ cmd: "nim c --threads:on $file" exitcode: 0 output: "OK" - disabled: "travis" """ import os, net, nativesockets, asyncdispatch +import std/[assertions, typedthreads] ## Test for net.dial @@ -15,7 +15,7 @@ proc initIPv6Server(hostname: string, port: Port): AsyncFD = let fd = createNativeSocket(AF_INET6) setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1) var aiList = getAddrInfo(hostname, port, AF_INET6) - if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: + if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: freeAddrInfo(aiList) raiseOSError(osLastError()) freeAddrInfo(aiList) diff --git a/tests/stdlib/tnre.nim b/tests/stdlib/tnre.nim index 0929956cb..3b40e9e83 100644 --- a/tests/stdlib/tnre.nim +++ b/tests/stdlib/tnre.nim @@ -1,24 +1,8 @@ discard """ +matrix: "--mm:refc; --mm:orc" # Since the tests for nre are all bundled together we treat failure in one test as an nre failure # When running 'testament/tester' a failed check() in the test suite will cause the exit # codes to differ and be reported as a failure - - output: - '''[Suite] Test NRE initialization - -[Suite] captures - -[Suite] find - -[Suite] string splitting - -[Suite] match - -[Suite] replace - -[Suite] escape strings - -[Suite] Misc tests''' """ import nre diff --git a/tests/stdlib/tntpath.nim b/tests/stdlib/tntpath.nim new file mode 100644 index 000000000..8efdd6bd0 --- /dev/null +++ b/tests/stdlib/tntpath.nim @@ -0,0 +1,50 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/private/ntpath +import std/assertions + +block: # From Python's `Lib/test/test_ntpath.py` + doAssert splitDrive(r"c:\foo\bar") == (r"c:", r"\foo\bar") + doAssert splitDrive(r"c:/foo/bar") == (r"c:", r"/foo/bar") + doAssert splitDrive(r"\\conky\mountpoint\foo\bar") == (r"\\conky\mountpoint", r"\foo\bar") + doAssert splitDrive(r"//conky/mountpoint/foo/bar") == (r"//conky/mountpoint", r"/foo/bar") + doAssert splitDrive(r"\\\conky\mountpoint\foo\bar") == (r"", r"\\\conky\mountpoint\foo\bar") + doAssert splitDrive(r"///conky/mountpoint/foo/bar") == (r"", r"///conky/mountpoint/foo/bar") + doAssert splitDrive(r"\\conky\\mountpoint\foo\bar") == (r"", r"\\conky\\mountpoint\foo\bar") + doAssert splitDrive(r"//conky//mountpoint/foo/bar") == (r"", r"//conky//mountpoint/foo/bar") + # Issue #19911: UNC part containing U+0130 + doAssert splitDrive(r"//conky/MOUNTPOİNT/foo/bar") == (r"//conky/MOUNTPOİNT", r"/foo/bar") + # gh-81790: support device namespace, including UNC drives. + doAssert splitDrive(r"//?/c:") == (r"//?/c:", r"") + doAssert splitDrive(r"//?/c:/") == (r"//?/c:", r"/") + doAssert splitDrive(r"//?/c:/dir") == (r"//?/c:", r"/dir") + doAssert splitDrive(r"//?/UNC") == (r"", r"//?/UNC") + doAssert splitDrive(r"//?/UNC/") == (r"", r"//?/UNC/") + doAssert splitDrive(r"//?/UNC/server/") == (r"//?/UNC/server/", r"") + doAssert splitDrive(r"//?/UNC/server/share") == (r"//?/UNC/server/share", r"") + doAssert splitDrive(r"//?/UNC/server/share/dir") == (r"//?/UNC/server/share", r"/dir") + doAssert splitDrive(r"//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam") == (r"//?/VOLUME{00000000-0000-0000-0000-000000000000}", r"/spam") + doAssert splitDrive(r"//?/BootPartition/") == (r"//?/BootPartition", r"/") + + doAssert splitDrive(r"\\?\c:") == (r"\\?\c:", r"") + doAssert splitDrive(r"\\?\c:\") == (r"\\?\c:", r"\") + doAssert splitDrive(r"\\?\c:\dir") == (r"\\?\c:", r"\dir") + doAssert splitDrive(r"\\?\UNC") == (r"", r"\\?\UNC") + doAssert splitDrive(r"\\?\UNC\") == (r"", r"\\?\UNC\") + doAssert splitDrive(r"\\?\UNC\server\") == (r"\\?\UNC\server\", r"") + doAssert splitDrive(r"\\?\UNC\server\share") == (r"\\?\UNC\server\share", r"") + doAssert splitDrive(r"\\?\UNC\server\share\dir") == (r"\\?\UNC\server\share", r"\dir") + doAssert splitDrive(r"\\?\VOLUME{00000000-0000-0000-0000-000000000000}\spam") == (r"\\?\VOLUME{00000000-0000-0000-0000-000000000000}", r"\spam") + doAssert splitDrive(r"\\?\BootPartition\") == (r"\\?\BootPartition", r"\") + +block: + doAssert splitDrive(r"C:") == (r"C:", r"") + doAssert splitDrive(r"C:\") == (r"C:", r"\") + doAssert splitDrive(r"non/absolute/path") == (r"", r"non/absolute/path") + + # Special for `\`-rooted paths on Windows. I don't know if this is correct, + # rbut `\` is not recognized as a drive, in contrast to `C:` or `\?\c:`. + # This behavior is the same for Python's `splitdrive` function. + doAssert splitDrive(r"\\") == (r"", r"\\") diff --git a/tests/stdlib/tobjectdollar.nim b/tests/stdlib/tobjectdollar.nim new file mode 100644 index 000000000..cf78fa255 --- /dev/null +++ b/tests/stdlib/tobjectdollar.nim @@ -0,0 +1,14 @@ +discard """ + matrix: "-d:nimPreviewSlimSystem" +""" + +import std/assertions + +type Foo = object + a, b: int + +let x = Foo(a: 23, b: 45) +doAssert not compiles($x) +import std/objectdollar +doAssert compiles($x) +doAssert $x == "(a: 23, b: 45)" diff --git a/tests/stdlib/toids.nim b/tests/stdlib/toids.nim new file mode 100644 index 000000000..dd5b84c51 --- /dev/null +++ b/tests/stdlib/toids.nim @@ -0,0 +1,15 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/oids +import std/assertions + +block: # genOid + let x = genOid() + doAssert ($x).len == 24 + +block: + let x = genOid() + let y = parseOid(cstring($x)) + doAssert x == y diff --git a/tests/stdlib/topenssl.nim b/tests/stdlib/topenssl.nim new file mode 100644 index 000000000..af259627f --- /dev/null +++ b/tests/stdlib/topenssl.nim @@ -0,0 +1,46 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/wordwrap +import openssl +import std/assertions + +const PubKey = r"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAknKWvrdnncCIzBnIGrZ5qtZrPH+Yo3t7ag9WZIu6Gmc/JgIDDaZhJeyGW0YSnifeAEhooWvM4jDWhTEARzktalSHqYtmwI/1Oxwp6NTYH8akMe2LCpZ5pX9FVA6m9o2tkbdXatbDKRqeD4UA8Ow7Iyrdo6eb1SU8vk+26i+uXHTtsb25p8uf2ppOJrJCy+1vr8Gsnuwny1UdoYZTxMsxRFPf+UX/LrSXMHVq/oPVa3SJ4VHMpYrG/httAugVP6K58xiZ93jst63/dd0JL85mWJu1uS3uz92aL5O97xzth3wR4BbdmDUlN4LuTIwi6DtEcC7gUOTnOzH4zgp2b5RyHwIDAQAB" +const PrivateKey = r"MIIEpAIBAAKCAQEAknKWvrdnncCIzBnIGrZ5qtZrPH+Yo3t7ag9WZIu6Gmc/JgIDDaZhJeyGW0YSnifeAEhooWvM4jDWhTEARzktalSHqYtmwI/1Oxwp6NTYH8akMe2LCpZ5pX9FVA6m9o2tkbdXatbDKRqeD4UA8Ow7Iyrdo6eb1SU8vk+26i+uXHTtsb25p8uf2ppOJrJCy+1vr8Gsnuwny1UdoYZTxMsxRFPf+UX/LrSXMHVq/oPVa3SJ4VHMpYrG/httAugVP6K58xiZ93jst63/dd0JL85mWJu1uS3uz92aL5O97xzth3wR4BbdmDUlN4LuTIwi6DtEcC7gUOTnOzH4zgp2b5RyHwIDAQABAoIBACSOxmLFlfAjaALLTNCeTLEA5bQshgYJhT1sprxixQpiS7lJN0npBsdYzBFs5KjmetzHNpdVOcgdOO/204L0Gwo4H8WLLxNS3HztAulEeM813zc3fUYfWi6eHshk//j8VR/TDNd21TElm99z7FA4KGsXAE0iQhxrN0aqz5aWYIhjprtHA5KxXIiESnTkof5Cud8oXEnPiwPGNhq93QeQzh7xQIKSaDKBcdAa6edTFhzc4RLUQRfrik/GqJzouEDQ9v6H/uiOLTB3FxxwErQIf6dvSVhD9gs1nSLQfyj3S2Hxe9S2zglTl07EsawTQUxtVQkdZUOok67c7CPBxecZ2wECgYEA2c31gr/UJwczT+P/AE52GkHHETXMxqE3Hnh9n4CitfAFSD5X0VwZvGjZIlln2WjisTd92Ymf65eDylX2kCm93nzZ2GfXgS4zl4oY1N87+VeNQlx9f2+6GU7Hs0HFdfu8bGd+0sOuWA1PFqQCobxCACMPTkuzsG9M7knUTN59HS8CgYEArCEoP4ReYoOFveXUE0AteTPb4hryvR9VDEolP+LMoiPe8AzBMeB5fP493TPdjtnWmrPCXNLc7UAFSj2CZsRhau4PuiqnNrsb5iz/7iXVl3E8wZvS4w7WYpO4m33L0cijA6MdcdqilQu4Z5tw4nG45lAW9UYyOc9D4hJTzgtGHhECgYA6QyDoj931brSoK0ocT+DB11Sj4utbOuberMaV8zgTSRhwodSl+WgdAUMMMDRacPcrBrgQiAMSZ15msqYZHEFhEa7Id8arFKvSXquTzf9iDKyJ0unzO/ThLjS3W+GxVNyrdufzA0tQ3IaKfOcDUrOpC7fdbtyrVqqSl4dF5MI9GwKBgQCl3OF6qyOEDDZgsUk1L59h7k3QR6VmBf4e9IeGUxZamvQlHjU/yY1nm1mjgGnbUB/SPKtqZKoMV6eBTVoNiuhQcItpGda9D3mnx+7p3T0/TBd+fJeuwcplfPDjrEktogcq5w/leQc3Ve7gr1EMcwb3r28f8/9L42QHQR/OKODs8QKBgQCFAvxDRPyYg7V/AgD9rt1KzXi4+b3Pls5NXZa2g/w+hmdhHUNxV5IGmHlqFnptGyshgYgQGxMMkW0iJ1j8nLamFnkbFQOp5/UKbdPLRKiB86oPpxsqYtPXucDUqEfcMsp57mD1CpGVODbspogFpSUvQpMECkhvI0XLMbolMdo53g==" + +proc rsaPublicEncrypt(fr: string): string = + let mKey = "-----BEGIN PUBLIC KEY-----\n" & PubKey.wrapWords(64) & "\n-----END PUBLIC KEY-----" + let bio = bioNew(bioSMem()) + doAssert BIO_write(bio, mKey.cstring, mKey.len.cint) >= 0 + let rsa = PEM_read_bio_RSA_PUBKEY(bio, nil, nil, nil) + doAssert rsa != nil + doAssert BIO_free(bio) >= 0 + result = newString(RSA_size(rsa)) + let frdata = cast[ptr uint8](fr.cstring) + var todata = cast[ptr uint8](result.cstring) + doAssert RSA_public_encrypt(fr.len.cint, frdata, todata, rsa, RSA_PKCS1_PADDING) != -1 + RSA_free(rsa) + +proc rasPrivateDecrypt(fr: string): string = + let mKey = "-----BEGIN RSA PRIVATE KEY-----\n" & PrivateKey.wrapWords(64) & "\n-----END RSA PRIVATE KEY-----" + let bio = bioNew(bioSMem()) + doAssert BIO_write(bio, mKey.cstring, mKey.len.cint) >= 0 + let rsa = PEM_read_bio_RSAPrivateKey(bio, nil, nil, nil) + doAssert rsa != nil + doAssert BIO_free(bio) >= 0 + let rsaLen = RSA_size(rsa) + result = newString(rsaLen) + let frdata = cast[ptr uint8](fr.cstring) + var todata = cast[ptr uint8](result.cstring) + let lenOrig = RSA_private_decrypt(rsaLen, frdata, todata, rsa, RSA_PKCS1_PADDING) + doAssert lenOrig >= 0 and lenOrig < result.len + doAssert result[lenOrig] == '\0' + result.setLen lenOrig + RSA_free(rsa) + +let res = "TEST" +let miwen = rsaPublicEncrypt(res) +let mingwen = rasPrivateDecrypt(miwen) +doAssert mingwen == res + diff --git a/tests/stdlib/toptions.nim b/tests/stdlib/toptions.nim new file mode 100644 index 000000000..63a10e746 --- /dev/null +++ b/tests/stdlib/toptions.nim @@ -0,0 +1,207 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + +import std/[json, options] + +import std/assertions +import std/objectdollar + + +# RefPerson is used to test that overloaded `==` operator is not called by +# options. It is defined here in the global scope, because otherwise the test +# will not even consider the `==` operator. Different bug? +type RefPerson = ref object + name: string + +proc `==`(a, b: RefPerson): bool = + assert(not a.isNil and not b.isNil) + a.name == b.name + + +template disableJsVm(body) = + # something doesn't work in JS VM + when defined(js): + when nimvm: discard + else: body + else: + body + +proc main() = + type + Foo = ref object + test: string + Test = object + foo: Option[Foo] + + let js = """{"foo": {"test": "123"}}""" + let parsed = parseJson(js) + let a = parsed.to(Test) + doAssert $(%*a) == """{"foo":{"test":"123"}}""" + + block options: + # work around a bug in unittest + let intNone = none(int) + let stringNone = none(string) + + block example: + proc find(haystack: string, needle: char): Option[int] = + for i, c in haystack: + if c == needle: + return some i + + doAssert("abc".find('c').get() == 2) + + let result = "team".find('i') + + doAssert result == intNone + doAssert result.isNone + + block some: + doAssert some(6).get() == 6 + doAssert some("a").unsafeGet() == "a" + doAssert some(6).isSome + doAssert some("a").isSome + + block none: + doAssertRaises UnpackDefect: + discard none(int).get() + doAssert(none(int).isNone) + doAssert(not none(string).isSome) + + block equality: + doAssert some("a") == some("a") + doAssert some(7) != some(6) + doAssert some("a") != stringNone + doAssert intNone == intNone + + when compiles(some("a") == some(5)): + doAssert false + when compiles(none(string) == none(int)): + doAssert false + + block get_with_a_default_value: + doAssert(some("Correct").get("Wrong") == "Correct") + doAssert(stringNone.get("Correct") == "Correct") + + block stringify: + doAssert($(some("Correct")) == "some(\"Correct\")") + doAssert($(stringNone) == "none(string)") + + disableJsVm: + block map_with_a_void_result: + var procRan = 0 + # TODO closure anonymous functions doesn't work in VM with JS + # Error: cannot evaluate at compile time: procRan + some(123).map(proc (v: int) = procRan = v) + doAssert procRan == 123 + intNone.map(proc (v: int) = doAssert false) + + block map: + doAssert(some(123).map(proc (v: int): int = v * 2) == some(246)) + doAssert(intNone.map(proc (v: int): int = v * 2).isNone) + + block filter: + doAssert(some(123).filter(proc (v: int): bool = v == 123) == some(123)) + doAssert(some(456).filter(proc (v: int): bool = v == 123).isNone) + doAssert(intNone.filter(proc (v: int): bool = doAssert false).isNone) + + block flatMap: + proc addOneIfNotZero(v: int): Option[int] = + if v != 0: + result = some(v + 1) + else: + result = none(int) + + doAssert(some(1).flatMap(addOneIfNotZero) == some(2)) + doAssert(some(0).flatMap(addOneIfNotZero) == none(int)) + doAssert(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3)) + + proc maybeToString(v: int): Option[string] = + if v != 0: + result = some($v) + else: + result = none(string) + + doAssert(some(1).flatMap(maybeToString) == some("1")) + + proc maybeExclaim(v: string): Option[string] = + if v != "": + result = some v & "!" + else: + result = none(string) + + doAssert(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!")) + doAssert(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string)) + + block SomePointer: + var intref: ref int + doAssert(option(intref).isNone) + intref.new + doAssert(option(intref).isSome) + + let tmp = option(intref) + doAssert(sizeof(tmp) == sizeof(ptr int)) + + var prc = proc (x: int): int = x + 1 + doAssert(option(prc).isSome) + prc = nil + doAssert(option(prc).isNone) + + block: + doAssert(none[int]().isNone) + doAssert(none(int) == none[int]()) + + # "$ on typed with .name" + block: + type Named = object + name: string + + let nobody = none(Named) + doAssert($nobody == "none(Named)") + + # "$ on type with name()" + block: + type Person = object + myname: string + + let noperson = none(Person) + doAssert($noperson == "none(Person)") + + # "Ref type with overloaded `==`" + block: + let p = some(RefPerson.new()) + doAssert p.isSome + + block: # test cstring + block: + let x = some("".cstring) + doAssert x.isSome + doAssert x.get == "" + + block: + let x = some("12345".cstring) + doAssert x.isSome + doAssert x.get == "12345" + + block: + let x = "12345".cstring + let y = some(x) + doAssert y.isSome + doAssert y.get == "12345" + + block: + let x = none(cstring) + doAssert x.isNone + doAssert $x == "none(cstring)" + +static: main() +main() + +when not defined(js): + block: # bug #22932 + var it = iterator: int {.closure.} = discard + doAssert it.option.isSome # Passes. + it = nil + doAssert it.option.isNone # Passes. diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index 814ff103d..611659fdb 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -22,10 +22,13 @@ __really_obscure_dir_name/test Raises Raises ''' + matrix: "--mm:refc; --mm:orc" + joinable: false """ # test os path creation, iteration, and deletion -import os, strutils, pathnorm +from stdtest/specialpaths import buildDir +import std/[syncio, assertions, osproc, os, strutils, pathnorm] block fileOperations: let files = @["these.txt", "are.x", "testing.r", "files.q"] @@ -36,6 +39,30 @@ block fileOperations: createDir(dname) doAssert dirExists(dname) + block: # copyFile, copyFileToDir + doAssertRaises(OSError): copyFile(dname/"nonexistent.txt", dname/"nonexistent.txt") + let fname = "D20201009T112235" + let fname2 = "D20201009T112235.2" + let str = "foo1\0foo2\nfoo3\0" + let file = dname/fname + let file2 = dname/fname2 + writeFile(file, str) + doAssert readFile(file) == str + let sub = "sub" + doAssertRaises(OSError): copyFile(file, dname/sub/fname2) + doAssertRaises(OSError): copyFileToDir(file, dname/sub) + doAssertRaises(ValueError): copyFileToDir(file, "") + copyFile(file, file2) + doAssert fileExists(file2) + doAssert readFile(file2) == str + createDir(dname/sub) + copyFileToDir(file, dname/sub) + doAssert fileExists(dname/sub/fname) + removeDir(dname/sub) + doAssert not dirExists(dname/sub) + removeFile(file) + removeFile(file2) + # Test creating files and dirs for dir in dirs: createDir(dname/dir) @@ -130,6 +157,172 @@ block fileOperations: doAssert fileExists("../dest/a/file.txt") removeDir("../dest") + # createDir should not fail if `dir` is empty + createDir("") + + + when defined(linux): # bug #24174 + createDir("a/b") + open("a/file.txt", fmWrite).close + + if not fileExists("a/fifoFile"): + doAssert execCmd("mkfifo -m 600 a/fifoFile") == 0 + + copyDir("a/", "../dest/a/", skipSpecial = true) + copyDirWithPermissions("a/", "../dest2/a/", skipSpecial = true) + removeDir("a") + + # Symlink handling in `copyFile`, `copyFileWithPermissions`, `copyFileToDir`, + # `copyDir`, `copyDirWithPermissions`, `moveFile`, and `moveDir`. + block: + const symlinksAreHandled = not defined(windows) + const dname = buildDir/"D20210116T140629" + const subDir = dname/"sub" + const subDir2 = dname/"sub2" + const brokenSymlinkName = "D20210101T191320_BROKEN_SYMLINK" + const brokenSymlink = dname/brokenSymlinkName + const brokenSymlinkSrc = "D20210101T191320_nonexistent" + const brokenSymlinkCopy = brokenSymlink & "_COPY" + const brokenSymlinkInSubDir = subDir/brokenSymlinkName + const brokenSymlinkInSubDir2 = subDir2/brokenSymlinkName + + createDir(subDir) + createSymlink(brokenSymlinkSrc, brokenSymlink) + + # Test copyFile + when symlinksAreHandled: + doAssertRaises(OSError): + copyFile(brokenSymlink, brokenSymlinkCopy) + doAssertRaises(OSError): + copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkFollow}) + copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkIgnore}) + doAssert not fileExists(brokenSymlinkCopy) + copyFile(brokenSymlink, brokenSymlinkCopy, {cfSymlinkAsIs}) + when symlinksAreHandled: + doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc + removeFile(brokenSymlinkCopy) + else: + doAssert not fileExists(brokenSymlinkCopy) + doAssertRaises(AssertionDefect): + copyFile(brokenSymlink, brokenSymlinkCopy, + {cfSymlinkAsIs, cfSymlinkFollow}) + + # Test copyFileWithPermissions + when symlinksAreHandled: + doAssertRaises(OSError): + copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy) + doAssertRaises(OSError): + copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy, + options = {cfSymlinkFollow}) + copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy, + options = {cfSymlinkIgnore}) + doAssert not fileExists(brokenSymlinkCopy) + copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy, + options = {cfSymlinkAsIs}) + when symlinksAreHandled: + doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc + removeFile(brokenSymlinkCopy) + else: + doAssert not fileExists(brokenSymlinkCopy) + doAssertRaises(AssertionDefect): + copyFileWithPermissions(brokenSymlink, brokenSymlinkCopy, + options = {cfSymlinkAsIs, cfSymlinkFollow}) + + # Test copyFileToDir + when symlinksAreHandled: + doAssertRaises(OSError): + copyFileToDir(brokenSymlink, subDir) + doAssertRaises(OSError): + copyFileToDir(brokenSymlink, subDir, {cfSymlinkFollow}) + copyFileToDir(brokenSymlink, subDir, {cfSymlinkIgnore}) + doAssert not fileExists(brokenSymlinkInSubDir) + copyFileToDir(brokenSymlink, subDir, {cfSymlinkAsIs}) + when symlinksAreHandled: + doAssert expandSymlink(brokenSymlinkInSubDir) == brokenSymlinkSrc + removeFile(brokenSymlinkInSubDir) + else: + doAssert not fileExists(brokenSymlinkInSubDir) + + createSymlink(brokenSymlinkSrc, brokenSymlinkInSubDir) + + # Test copyDir + copyDir(subDir, subDir2) + when symlinksAreHandled: + doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc + else: + doAssert not fileExists(brokenSymlinkInSubDir2) + removeDir(subDir2) + + # Test copyDirWithPermissions + copyDirWithPermissions(subDir, subDir2) + when symlinksAreHandled: + doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc + else: + doAssert not fileExists(brokenSymlinkInSubDir2) + removeDir(subDir2) + + # Test moveFile + moveFile(brokenSymlink, brokenSymlinkCopy) + when not defined(windows): + doAssert expandSymlink(brokenSymlinkCopy) == brokenSymlinkSrc + else: + doAssert symlinkExists(brokenSymlinkCopy) + removeFile(brokenSymlinkCopy) + + # Test moveDir + moveDir(subDir, subDir2) + when not defined(windows): + doAssert expandSymlink(brokenSymlinkInSubDir2) == brokenSymlinkSrc + else: + doAssert symlinkExists(brokenSymlinkInSubDir2) + + removeDir(dname) + +block: # moveFile + let tempDir = getTempDir() / "D20210609T151608" + createDir(tempDir) + defer: removeDir(tempDir) + + writeFile(tempDir / "a.txt", "") + moveFile(tempDir / "a.txt", tempDir / "b.txt") + doAssert not fileExists(tempDir / "a.txt") + doAssert fileExists(tempDir / "b.txt") + removeFile(tempDir / "b.txt") + + createDir(tempDir / "moveFile_test") + writeFile(tempDir / "moveFile_test/a.txt", "") + moveFile(tempDir / "moveFile_test/a.txt", tempDir / "moveFile_test/b.txt") + doAssert not fileExists(tempDir / "moveFile_test/a.txt") + doAssert fileExists(tempDir / "moveFile_test/b.txt") + removeDir(tempDir / "moveFile_test") + + createDir(tempDir / "moveFile_test") + writeFile(tempDir / "a.txt", "") + moveFile(tempDir / "a.txt", tempDir / "moveFile_test/b.txt") + doAssert not fileExists(tempDir / "a.txt") + doAssert fileExists(tempDir / "moveFile_test/b.txt") + removeDir(tempDir / "moveFile_test") + +block: # moveDir + let tempDir = getTempDir() / "D20210609T161443" + createDir(tempDir) + defer: removeDir(tempDir) + + createDir(tempDir / "moveDir_test") + moveDir(tempDir / "moveDir_test/", tempDir / "moveDir_test_dest") + doAssert not dirExists(tempDir / "moveDir_test") + doAssert dirExists(tempDir / "moveDir_test_dest") + removeDir(tempDir / "moveDir_test_dest") + + createDir(tempDir / "moveDir_test") + writeFile(tempDir / "moveDir_test/a.txt", "") + moveDir(tempDir / "moveDir_test", tempDir / "moveDir_test_dest") + doAssert not dirExists(tempDir / "moveDir_test") + doAssert not fileExists(tempDir / "moveDir_test/a.txt") + doAssert dirExists(tempDir / "moveDir_test_dest") + doAssert fileExists(tempDir / "moveDir_test_dest/a.txt") + removeDir(tempDir / "moveDir_test_dest") + import times block modificationTime: # Test get/set modification times @@ -154,7 +347,7 @@ block walkDirRec: doAssert p.startsWith("walkdir_test") var s: seq[string] - for p in walkDirRec("walkdir_test", {pcFile}, {pcDir}, relative=true): + for p in walkDirRec("walkdir_test", {pcFile}, {pcDir}, relative = true): s.add(p) doAssert s.len == 2 @@ -163,11 +356,13 @@ block walkDirRec: removeDir("walkdir_test") +import std/sequtils + block: # walkDir doAssertRaises(OSError): - for a in walkDir("nonexistant", checkDir = true): discard + for a in walkDir("nonexistent", checkDir = true): discard doAssertRaises(OSError): - for p in walkDirRec("nonexistant", checkDir = true): discard + for p in walkDirRec("nonexistent", checkDir = true): discard when not defined(windows): block walkDirRelative: @@ -177,6 +372,21 @@ block: # walkDir doAssert k == pcLinkToDir removeDir("walkdir_test") + when defined(posix): + block walkDirSpecial: + createDir("walkdir_test") + doAssert execShellCmd("mkfifo walkdir_test/fifo") == 0 + createSymlink("fifo", "walkdir_test/fifo_link") + let withSpecialFiles = toSeq(walkDir("walkdir_test", relative = true)) + doAssert (withSpecialFiles.len == 2 and + (pcFile, "fifo") in withSpecialFiles and + (pcLinkToFile, "fifo_link") in withSpecialFiles) + # now Unix special files are excluded from walkdir output: + let skipSpecialFiles = toSeq(walkDir("walkdir_test", relative = true, + skipSpecial = true)) + doAssert skipSpecialFiles.len == 0 + removeDir("walkdir_test") + block normalizedPath: doAssert normalizedPath("") == "" block relative: @@ -222,7 +432,7 @@ block absolutePath: doAssertRaises(ValueError): discard absolutePath("a", "b") doAssert absolutePath("a") == getCurrentDir() / "a" doAssert absolutePath("a", "/b") == "/b" / "a" - when defined(Posix): + when defined(posix): doAssert absolutePath("a", "/b/") == "/b" / "a" doAssert absolutePath("a", "/b/c") == "/b/c" / "a" doAssert absolutePath("/a", "b/") == "/a" @@ -331,7 +541,11 @@ block ospaths: doAssert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim" doAssert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim" - doAssert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim" + + # `//` is a UNC path, `/` is the current working directory's drive, so can't + # run this test on Windows. + when not doslikeFileSystem: + doAssert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim" doAssert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim" doAssert relativePath("", "/users/moo", '/') == "" doAssert relativePath("foo", "", '/') == "foo" @@ -376,20 +590,20 @@ block ospaths: doAssert joinPath("/", "") == unixToNativePath"/" doAssert joinPath("/" / "") == unixToNativePath"/" # weird test case... doAssert joinPath("/", "/a/b/c") == unixToNativePath"/a/b/c" - doAssert joinPath("foo/","") == unixToNativePath"foo/" - doAssert joinPath("foo/","abc") == unixToNativePath"foo/abc" - doAssert joinPath("foo//./","abc/.//") == unixToNativePath"foo/abc/" - doAssert joinPath("foo","abc") == unixToNativePath"foo/abc" - doAssert joinPath("","abc") == unixToNativePath"abc" + doAssert joinPath("foo/", "") == unixToNativePath"foo/" + doAssert joinPath("foo/", "abc") == unixToNativePath"foo/abc" + doAssert joinPath("foo//./", "abc/.//") == unixToNativePath"foo/abc/" + doAssert joinPath("foo", "abc") == unixToNativePath"foo/abc" + doAssert joinPath("", "abc") == unixToNativePath"abc" - doAssert joinPath("gook/.","abc") == unixToNativePath"gook/abc" + doAssert joinPath("zook/.", "abc") == unixToNativePath"zook/abc" - # controversial: inconsistent with `joinPath("gook/.","abc")` + # controversial: inconsistent with `joinPath("zook/.","abc")` # on linux, `./foo` and `foo` are treated a bit differently for executables # but not `./foo/bar` and `foo/bar` doAssert joinPath(".", "/lib") == unixToNativePath"./lib" - doAssert joinPath(".","abc") == unixToNativePath"./abc" - + doAssert joinPath(".", "abc") == unixToNativePath"./abc" + # cases related to issue #13455 doAssert joinPath("foo", "", "") == "foo" doAssert joinPath("foo", "") == "foo" @@ -425,16 +639,8 @@ block getTempDir: else: doAssert getTempDir() == "/tmp/" -block osenv: - block delEnv: - const dummyEnvVar = "DUMMY_ENV_VAR" # This env var wouldn't be likely to exist to begin with - doAssert existsEnv(dummyEnvVar) == false - putEnv(dummyEnvVar, "1") - doAssert existsEnv(dummyEnvVar) == true - delEnv(dummyEnvVar) - doAssert existsEnv(dummyEnvVar) == false - delEnv(dummyEnvVar) # deleting an already deleted env var - doAssert existsEnv(dummyEnvVar) == false +block: # getCacheDir + doAssert getCacheDir().dirExists block isRelativeTo: doAssert isRelativeTo("/foo", "/") @@ -449,19 +655,30 @@ block isRelativeTo: doAssert not isRelativeTo("/foo2", "/foo") block: # quoteShellWindows - assert quoteShellWindows("aaa") == "aaa" - assert quoteShellWindows("aaa\"") == "aaa\\\"" - assert quoteShellWindows("") == "\"\"" + doAssert quoteShellWindows("aaa") == "aaa" + doAssert quoteShellWindows("aaa\"") == "aaa\\\"" + doAssert quoteShellWindows("") == "\"\"" -block: # quoteShellWindows - assert quoteShellPosix("aaa") == "aaa" - assert quoteShellPosix("aaa a") == "'aaa a'" - assert quoteShellPosix("") == "''" - assert quoteShellPosix("a'a") == "'a'\"'\"'a'" +block: # quoteShellCommand + when defined(windows): + doAssert quoteShellCommand(["a b c", "d", "e"]) == """"a b c" d e""" + doAssert quoteShellCommand(["""ab"c""", r"\", "d"]) == """ab\"c \ d""" + doAssert quoteShellCommand(["""ab"c""", """ \""", "d"]) == """ab\"c " \\" d""" + doAssert quoteShellCommand(["""a\\\b""", """de fg""", "h"]) == """a\\\b "de fg" h""" + doAssert quoteShellCommand(["""a\"b""", "c", "d"]) == """a\\\"b c d""" + doAssert quoteShellCommand(["""a\\b c""", "d", "e"]) == """"a\\b c" d e""" + doAssert quoteShellCommand(["""a\\b\ c""", "d", "e"]) == """"a\\b\ c" d e""" + doAssert quoteShellCommand(["ab", ""]) == """ab """"" + +block: # quoteShellPosix + doAssert quoteShellPosix("aaa") == "aaa" + doAssert quoteShellPosix("aaa a") == "'aaa a'" + doAssert quoteShellPosix("") == "''" + doAssert quoteShellPosix("a'a") == "'a'\"'\"'a'" block: # quoteShell when defined(posix): - assert quoteShell("") == "''" + doAssert quoteShell("") == "''" block: # normalizePathEnd # handle edge cases correctly: shouldn't affect whether path is @@ -475,7 +692,7 @@ block: # normalizePathEnd doAssert "//".normalizePathEnd(false) == "/" doAssert "foo.bar//".normalizePathEnd == "foo.bar" doAssert "bar//".normalizePathEnd(trailingSep = true) == "bar/" - when defined(Windows): + when defined(windows): doAssert r"C:\foo\\".normalizePathEnd == r"C:\foo" doAssert r"C:\foo".normalizePathEnd(trailingSep = true) == r"C:\foo\" # this one is controversial: we could argue for returning `D:\` instead, @@ -484,7 +701,121 @@ block: # normalizePathEnd doAssert r"E:/".normalizePathEnd(trailingSep = true) == r"E:\" doAssert "/".normalizePathEnd == r"\" -block: # isValidFilename + +import sugar + +block: # normalizeExe + doAssert "".dup(normalizeExe) == "" + when defined(posix): + doAssert "foo".dup(normalizeExe) == "./foo" + doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar" + when defined(windows): + doAssert "foo".dup(normalizeExe) == "foo" + +block: # isAdmin + let isAzure = existsEnv("TF_BUILD") # xxx factor with testament.specs.isAzure + # In Azure on Windows tests run as an admin user + if isAzure and defined(windows): doAssert isAdmin() + # In Azure on POSIX tests run as a normal user + if isAzure and defined(posix): doAssert not isAdmin() + + +import sugar + +block: # normalizeExe + doAssert "".dup(normalizeExe) == "" + when defined(posix): + doAssert "foo".dup(normalizeExe) == "./foo" + doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar" + when defined(windows): + doAssert "foo".dup(normalizeExe) == "foo" + +block: # isAdmin + let isAzure = existsEnv("TF_BUILD") # xxx factor with testament.specs.isAzure + # In Azure on Windows tests run as an admin user + if isAzure and defined(windows): doAssert isAdmin() + # In Azure on POSIX tests run as a normal user + if isAzure and defined(posix): doAssert not isAdmin() + +when doslikeFileSystem: + import std/private/ntpath + + block: # Bug #19103 UNC paths + + # Easiest way of generating a valid, readable and writable UNC path + let tempDir = r"\\?\" & getTempDir() + doAssert dirExists tempDir + createDir tempDir / "test" + removeDir tempDir / "test" + createDir tempDir / "recursive" / "test" + removeDir tempDir / "recursive" / "test" + + let tempDir2 = getTempDir() + let (drive, pathNoDrive) = splitDrive(tempDir2) + setCurrentDir drive + doAssert cmpIgnoreCase(getCurrentDir().splitDrive.drive, drive) == 0 + + # Test `\Users` path syntax on Windows by stripping away drive. `\` + # resolves to the drive in current working directory. This drive will be + # the same as `tempDir2` because of the `setCurrentDir` above. + doAssert pathNoDrive[0] == '\\' + createDir pathNoDrive / "test" + doAssert dirExists pathNoDrive / "test" + removeDir pathNoDrive / "test" + + doAssert splitPath("//?/c:") == ("//?/c:", "") + + doAssert relativePath("//?/c:///Users//me", "//?/c:", '/') == "Users/me" + + doAssert parentDir(r"\\?\c:") == r"" + doAssert parentDir(r"//?/c:/Users") == r"\\?\c:" + doAssert parentDir(r"\\localhost\c$") == r"" + doAssert parentDir(r"\Users") == r"\" + + doAssert tailDir("//?/c:") == "" + doAssert tailDir("//?/c:/Users") == "Users" + doAssert tailDir(r"\\localhost\c$\Windows\System32") == r"Windows\System32" + + doAssert isRootDir("//?/c:") + doAssert isRootDir("//?/UNC/localhost/c$") + doAssert not isRootDir(r"\\?\c:\Users") + + doAssert parentDirs(r"C:\Users", fromRoot = true).toSeq == @[r"C:\", r"C:\Users"] + doAssert parentDirs(r"C:\Users", fromRoot = false).toSeq == @[r"C:\Users", r"C:"] + doAssert parentDirs(r"\\?\c:\Users", fromRoot = true).toSeq == + @[r"\\?\c:\", r"\\?\c:\Users"] + doAssert parentDirs(r"\\?\c:\Users", fromRoot = false).toSeq == + @[r"\\?\c:\Users", r"\\?\c:"] + doAssert parentDirs(r"//localhost/c$/Users", fromRoot = true).toSeq == + @[r"//localhost/c$/", r"//localhost/c$/Users"] + doAssert parentDirs(r"//?/UNC/localhost/c$/Users", fromRoot = false).toSeq == + @[r"//?/UNC/localhost/c$/Users", r"\\?\UNC\localhost\c$"] + doAssert parentDirs(r"\Users", fromRoot = true).toSeq == @[r"\", r"\Users"] + doAssert parentDirs(r"\Users", fromRoot = false).toSeq == @[r"\Users", r"\"] + + doAssert r"//?/c:" /../ "d/e" == r"\\?\c:\d\e" + doAssert r"//?/c:/Users" /../ "d/e" == r"\\?\c:\d\e" + doAssert r"\\localhost\c$" /../ "d/e" == r"\\localhost\c$\d\e" + + doAssert splitFile("//?/c:") == ("//?/c:", "", "") + doAssert splitFile("//?/c:/Users") == ("//?/c:", "Users", "") + doAssert splitFile(r"\\localhost\c$\test.txt") == (r"\\localhost\c$", "test", ".txt") + +else: + block: # parentDirs + doAssert parentDirs("/home", fromRoot=true).toSeq == @["/", "/home"] + doAssert parentDirs("/home", fromRoot=false).toSeq == @["/home", "/"] + doAssert parentDirs("home", fromRoot=true).toSeq == @["home"] + doAssert parentDirs("home", fromRoot=false).toSeq == @["home"] + + doAssert parentDirs("/home/user", fromRoot=true).toSeq == @["/", "/home/", "/home/user"] + doAssert parentDirs("/home/user", fromRoot=false).toSeq == @["/home/user", "/home", "/"] + doAssert parentDirs("home/user", fromRoot=true).toSeq == @["home/", "home/user"] + doAssert parentDirs("home/user", fromRoot=false).toSeq == @["home/user", "home"] + + +# https://github.com/nim-lang/Nim/pull/19643#issuecomment-1235102314 +block: # isValidFilename # Negative Tests. doAssert not isValidFilename("abcd", maxLen = 2) doAssert not isValidFilename("0123456789", maxLen = 8) @@ -511,12 +842,34 @@ block: # isValidFilename doAssert isValidFilename("nim.nim") doAssert isValidFilename("foo.log") -import sugar +block: # searchExtPos + doAssert "foo.nim".searchExtPos == 3 + doAssert "/foo.nim".searchExtPos == 4 + doAssert "".searchExtPos == -1 + doAssert "/".searchExtPos == -1 + doAssert "a.b/foo".searchExtPos == -1 + doAssert ".".searchExtPos == -1 + doAssert "foo.".searchExtPos == 3 + doAssert "foo..".searchExtPos == 4 + doAssert "..".searchExtPos == -1 + doAssert "...".searchExtPos == -1 + doAssert "./".searchExtPos == -1 + doAssert "../".searchExtPos == -1 + doAssert "/.".searchExtPos == -1 + doAssert "/..".searchExtPos == -1 + doAssert ".b".searchExtPos == -1 + doAssert "..b".searchExtPos == -1 + doAssert "/.b".searchExtPos == -1 + doAssert "a/.b".searchExtPos == -1 + doAssert ".a.b".searchExtPos == 2 + doAssert "a/.b.c".searchExtPos == 4 + doAssert "a/..b".searchExtPos == -1 + doAssert "a/b..c".searchExtPos == 4 -block: # normalizeExe - doAssert "".dup(normalizeExe) == "" - when defined(posix): - doAssert "foo".dup(normalizeExe) == "./foo" - doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar" - when defined(windows): - doAssert "foo".dup(normalizeExe) == "foo" + when doslikeFileSystem: + doAssert "c:a.b".searchExtPos == 3 + doAssert "c:.a".searchExtPos == -1 + doAssert r"c:\.a".searchExtPos == -1 + doAssert "c:..a".searchExtPos == -1 + doAssert r"c:\..a".searchExtPos == -1 + doAssert "c:.a.b".searchExtPos == 4 diff --git a/tests/stdlib/tos_unc.nim b/tests/stdlib/tos_unc.nim index e55de11ce..194deeb42 100644 --- a/tests/stdlib/tos_unc.nim +++ b/tests/stdlib/tos_unc.nim @@ -1,9 +1,11 @@ discard """ + matrix: "--mm:refc; --mm:orc" disabled: "posix" """ # bug 10952, UNC paths import os +import std/assertions doAssert r"\\hostname\foo\bar" / "baz" == r"\\hostname\foo\bar\baz" doAssert r"\\?\C:\foo" / "bar" == r"\\?\C:\foo\bar" diff --git a/tests/stdlib/tosenv.nim b/tests/stdlib/tosenv.nim new file mode 100644 index 000000000..17e397987 --- /dev/null +++ b/tests/stdlib/tosenv.nim @@ -0,0 +1,163 @@ +discard """ + matrix: "--mm:refc; --mm:arc" + joinable: false + targets: "c js cpp" +""" + +import std/os +from std/sequtils import toSeq +import stdtest/testutils + +when defined(nimPreviewSlimSystem): + import std/[assertions] + +# "LATIN CAPITAL LETTER AE" in UTF-8 (0xc386) +const unicodeUtf8 = "\xc3\x86" + +template main = + block: # delEnv, existsEnv, getEnv, envPairs + for val in ["val", "", unicodeUtf8]: # ensures empty val works too + const key = "NIM_TESTS_TOSENV_KEY" + doAssert not existsEnv(key) + + putEnv(key, "tempval") + doAssert existsEnv(key) + doAssert getEnv(key) == "tempval" + + putEnv(key, val) # change a key that already exists + doAssert existsEnv(key) + doAssert getEnv(key) == val + + doAssert (key, val) in toSeq(envPairs()) + delEnv(key) + doAssert (key, val) notin toSeq(envPairs()) + doAssert not existsEnv(key) + delEnv(key) # deleting an already deleted env var + doAssert not existsEnv(key) + + block: + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "") == "" + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", " ") == " " + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "defval") == "defval" + + whenVMorJs: discard # xxx improve + do: + doAssertRaises(OSError, putEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE", "NEW_DUMMY_VALUE")) + doAssertRaises(OSError, putEnv("", "NEW_DUMMY_VALUE")) + doAssert not existsEnv("") + doAssert not existsEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE") + doAssert not existsEnv("NIM_TESTS_TOSENV_PUT") + +static: main() +main() + +when defined(windows): + import std/widestrs + proc c_wgetenv(env: WideCString): WideCString {.importc: "_wgetenv", header: "<stdlib.h>".} +proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "<stdlib.h>".} + +when not defined(js) and not defined(nimscript): + when defined(nimPreviewSlimSystem): + import std/typedthreads + block: # bug #18533 + var thr: Thread[void] + proc threadFunc {.thread.} = putEnv("foo", "fooVal2") + + putEnv("foo", "fooVal1") + doAssert getEnv("foo") == "fooVal1" + createThread(thr, threadFunc) + joinThreads(thr) + when defined(windows): + doAssert getEnv("foo") == $c_wgetenv("foo".newWideCString) + else: + doAssert getEnv("foo") == $c_getenv("foo".cstring) + + doAssertRaises(OSError): delEnv("foo=bar") + +when defined(windows) and not defined(nimscript): + import std/encodings + + proc c_putenv(env: cstring): int32 {.importc: "putenv", header: "<stdlib.h>".} + proc c_wputenv(env: WideCString): int32 {.importc: "_wputenv", header: "<stdlib.h>".} + + block: # Bug #20083 + # These test that `getEnv`, `putEnv` and `existsEnv` handle Unicode + # characters correctly. This means that module X in the process calling the + # CRT environment variable API will get the correct string. Raw CRT API + # calls below represent module X. + + # Getting an env. var. with unicode characters returns the correct UTF-8 + # encoded string. + block: + const envName = "twin_envvars1" + doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0 + doAssert existsEnv(envName) + doAssert getEnv(envName) == unicodeUtf8 + + # Putting an env. var. with unicode characters gives the correct UTF-16 + # encoded string from low-level routine. + block: + const envName = "twin_envvars2" + putEnv(envName, unicodeUtf8) + doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8 + + # Env. name containing Unicode characters is retrieved correctly + block: + const envName = unicodeUtf8 & "1" + doAssert c_wputenv(newWideCString(envName & "=" & unicodeUtf8)) == 0 + doAssert existsEnv(envName) + doAssert getEnv(envName) == unicodeUtf8 + + # Env. name containing Unicode characters is set correctly + block: + const envName = unicodeUtf8 & "2" + putEnv(envName, unicodeUtf8) + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == unicodeUtf8 + + # Env. name containing Unicode characters and empty value is set correctly + block: + const envName = unicodeUtf8 & "3" + putEnv(envName, "") + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == "" + + # It's hard to test on Windows code pages, because there is no "change + # a process' locale" API. + if getCurrentEncoding(true) == "windows-1252": + const + unicodeAnsi = "\xc6" # `unicodeUtf8` in `windows-1252` encoding + + # Test that env. var. ANSI API has correct encoding + block: + const + envName = unicodeUtf8 & "4" + envNameAnsi = unicodeAnsi & "4" + putEnv(envName, unicodeUtf8) + doAssert $c_getenv(envNameAnsi.cstring) == unicodeAnsi + + block: + const + envName = unicodeUtf8 & "5" + envNameAnsi = unicodeAnsi & "5" + doAssert c_putenv((envNameAnsi & "=" & unicodeAnsi).cstring) == 0 + doAssert getEnv(envName) == unicodeUtf8 + + # Env. name containing Unicode characters and empty value is set correctly; + # and, if env. name. characters cannot be represented in codepage, don't + # raise an error. + # + # `win_setenv.nim` converts UTF-16 to ANSI when setting empty env. var. The + # windows-1250 locale has no representation of `abreveUtf8` below, so the + # conversion will fail, but this must not be fatal. It is expected that the + # routine ignores updating MBCS environment (`environ` global) and carries + # on. + block: + const + # "LATIN SMALL LETTER A WITH BREVE" in UTF-8 + abreveUtf8 = "\xc4\x83" + envName = abreveUtf8 & "6" + putEnv(envName, "") + doAssert existsEnv(envName) + doAssert $c_wgetenv(envName.newWideCString) == "" + doAssert getEnv(envName) == "" diff --git a/tests/stdlib/toserrors.nim b/tests/stdlib/toserrors.nim new file mode 100644 index 000000000..e907dfe63 --- /dev/null +++ b/tests/stdlib/toserrors.nim @@ -0,0 +1,9 @@ +discard """ + action: compile +""" + +import std/oserrors + +let x1 = osLastError() +raiseOSError(x1) +echo osErrorMsg(x1) diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index 363bd5d74..da4f6252d 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc; --mm:orc" joinable: false """ @@ -9,10 +10,11 @@ because it'd need cleanup up stdout see also: tests/osproc/*.nim; consider merging those into a single test here (easier to factor and test more things as a single self contained test) ]# +import std/[assertions, syncio] when defined(case_testfile): # compiled test file for child process from posix import exitnow - proc c_exit2(code: c_int): void {.importc: "_exit", header: "<unistd.h>".} + proc c_exit2(code: cint): void {.importc: "_exit", header: "<unistd.h>".} import os var a = 0 proc fun(b = 0) = @@ -29,6 +31,12 @@ when defined(case_testfile): # compiled test file for child process case arg of "exit_0": if true: quit(0) + of "exit_1": + if true: quit(1) + of "exit_2": + if true: quit(2) + of "exit_42": + if true: quit(42) of "exitnow_139": if true: exitnow(139) of "c_exit2_139": @@ -86,9 +94,7 @@ else: # main driver const sourcePath = currentSourcePath() let dir = getCurrentDir() / "tests" / "osproc" - template deferScoped(cleanup, body) = - # pending https://github.com/nim-lang/RFCs/issues/236#issuecomment-646855314 - # xxx move to std/sugar or (preferably) some low level module + template deferring(cleanup, body) = try: body finally: cleanup @@ -113,7 +119,19 @@ else: # main driver runTest("exit_0", 0) runTest("exitnow_139", 139) runTest("c_exit2_139", 139) - runTest("quit_139", 139) + when defined(posix): + runTest("quit_139", 127) # The quit value gets saturated to 127 + else: + runTest("quit_139", 139) + + block execCmdTest: + let output = compileNimProg("-d:release -d:case_testfile", "D20220705T221100") + doAssert execCmd(output & " exit_0") == 0 + doAssert execCmd(output & " exit_1") == 1 + doAssert execCmd(output & " exit_2") == 2 + doAssert execCmd(output & " exit_42") == 42 + + import std/streams block execProcessTest: let dir = sourcePath.parentDir @@ -132,30 +150,80 @@ else: # main driver doAssert outStr2 == absolutePath(testDir) & "\nx yz\n" removeDir(testDir) + + # test for PipeOutStream + var + p = startProcess(exePath, args = ["abcdefghi", "foo", "bar", "0123456"]) + outStrm = p.peekableOutputStream + + var tmp: string + doAssert outStrm.readLine(tmp) + doAssert outStrm.readChar == 'a' + doAssert outStrm.peekChar == 'b' + doAssert outStrm.readChar == 'b' + doAssert outStrm.readChar == 'c' + doAssert outStrm.peekChar == 'd' + doAssert outStrm.peekChar == 'd' + doAssert outStrm.readChar == 'd' + doAssert outStrm.readStr(2) == "ef" + doAssert outStrm.peekStr(2) == "gh" + doAssert outStrm.peekStr(2) == "gh" + doAssert outStrm.readStr(1) == "g" + doAssert outStrm.readStr(3) == "hi\n" + + doAssert outStrm.readLine == "foo" + doAssert outStrm.readChar == 'b' + doAssert outStrm.peekChar == 'a' + doAssert outStrm.readLine == "ar" + + tmp.setLen(4) + tmp[0] = 'n' + doAssert outStrm.readDataStr(tmp, 1..3) == 3 + doAssert tmp == "n012" + doAssert outStrm.peekStr(3) == "345" + doAssert outStrm.readDataStr(tmp, 1..2) == 2 + doAssert tmp == "n342" + doAssert outStrm.peekStr(2) == "56" + doAssert outStrm.readDataStr(tmp, 0..3) == 3 + doAssert tmp == "56\n2" + p.close + + p = startProcess(exePath, args = ["123"]) + outStrm = p.peekableOutputStream + let c = outStrm.peekChar + doAssert outStrm.readLine(tmp) + doAssert tmp[0] == c + tmp.setLen(7) + doAssert outStrm.peekData(addr tmp[0], 7) == 4 + doAssert tmp[0..3] == "123\n" + doAssert outStrm.peekData(addr tmp[0], 7) == 4 + doAssert tmp[0..3] == "123\n" + doAssert outStrm.readData(addr tmp[0], 7) == 4 + doAssert tmp[0..3] == "123\n" + p.close + try: removeFile(exePath) except OSError: discard - import std/streams - block: # test for startProcess (more tests needed) # bugfix: windows stdin.close was a noop and led to blocking reads proc startProcessTest(command: string, options: set[ProcessOption] = { poStdErrToStdOut, poUsePath}, input = ""): tuple[ - output: TaintedString, + output: string, exitCode: int] {.tags: [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = var p = startProcess(command, options = options + {poEvalCommand}) var outp = outputStream(p) if input.len > 0: inputStream(p).write(input) close inputStream(p) - result = (TaintedString"", -1) - var line = newStringOfCap(120).TaintedString + result = ("", -1) + var line = newStringOfCap(120) while true: if outp.readLine(line): - result[0].string.add(line.string) - result[0].string.add("\n") + result[0].add(line) + result[0].add("\n") else: result[1] = peekExitCode(p) if result[1] != -1: break @@ -171,7 +239,7 @@ else: # main driver p.inputStream.flush() var line = "" var s: seq[string] - while p.outputStream.readLine(line.TaintedString): + while p.outputStream.readLine(line): s.add line doAssert s == @["10"] @@ -180,18 +248,18 @@ else: # main driver var x = newStringOfCap(120) block: # startProcess stdout poStdErrToStdOut (replaces old test `tstdout` + `ta_out`) var p = startProcess(output, dir, options={poStdErrToStdOut}) - deferScoped: p.close() + deferring: p.close() do: var sout: seq[string] - while p.outputStream.readLine(x.TaintedString): sout.add x + while p.outputStream.readLine(x): sout.add x doAssert sout == @["start ta_out", "to stdout", "to stdout", "to stderr", "to stderr", "to stdout", "to stdout", "end ta_out"] block: # startProcess stderr (replaces old test `tstderr` + `ta_out`) var p = startProcess(output, dir, options={}) - deferScoped: p.close() + deferring: p.close() do: var serr, sout: seq[string] - while p.errorStream.readLine(x.TaintedString): serr.add x - while p.outputStream.readLine(x.TaintedString): sout.add x + while p.errorStream.readLine(x): serr.add x + while p.outputStream.readLine(x): sout.add x doAssert serr == @["to stderr", "to stderr"] doAssert sout == @["start ta_out", "to stdout", "to stdout", "to stdout", "to stdout", "end ta_out"] @@ -219,10 +287,30 @@ else: # main driver var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4") stripLineEnd(result[0]) doAssert result == ("12", 0) - doAssert execCmdEx("ls --nonexistant").exitCode != 0 + when not defined(windows): + doAssert execCmdEx("ls --nonexistent").exitCode != 0 when false: # bug: on windows, this raises; on posix, passes - doAssert execCmdEx("nonexistant").exitCode != 0 + doAssert execCmdEx("nonexistent").exitCode != 0 when defined(posix): doAssert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0) doAssert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0) + + block: # bug #17749 + let output = compileNimProg("-d:case_testfile4", "D20210417T011153") + var p = startProcess(output, dir) + let inp = p.inputStream + var count = 0 + when defined(windows): + # xxx we should make osproc.hsWriteData raise IOError on windows, consistent + # with posix; we could also (in addition) make IOError a subclass of OSError. + type SIGPIPEError = OSError + else: + type SIGPIPEError = IOError + doAssertRaises(SIGPIPEError): + for i in 0..<100000: + count.inc + inp.writeLine "ok" # was giving SIGPIPE and crashing + doAssert count >= 100 + doAssert waitForExit(p) == QuitFailure + close(p) # xxx isn't that missing in other places? diff --git a/tests/stdlib/tosprocterminate.nim b/tests/stdlib/tosprocterminate.nim index c02e0a8da..93b0317f7 100644 --- a/tests/stdlib/tosprocterminate.nim +++ b/tests/stdlib/tosprocterminate.nim @@ -1,12 +1,13 @@ discard """ cmd: "nim $target $options -r $file" targets: "c cpp" - matrix: "--threads:on; " + matrix: "--mm:refc; --mm:orc" """ import os, osproc, times, std / monotimes +import std/assertions -when defined(Windows): +when defined(windows): const ProgramWhichDoesNotEnd = "notepad" elif defined(openbsd): const ProgramWhichDoesNotEnd = "/bin/cat" diff --git a/tests/stdlib/tpackedsets.nim b/tests/stdlib/tpackedsets.nim new file mode 100644 index 000000000..f519c08a7 --- /dev/null +++ b/tests/stdlib/tpackedsets.nim @@ -0,0 +1,265 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/packedsets +import std/sets + +import sequtils +import algorithm + +import std/assertions + +block basicIntSetTests: + var y = initPackedSet[int]() + y.incl(1) + y.incl(2) + y.incl(7) + y.incl(1056) + + y.incl(1044) + y.excl(1044) + + doAssert y == [1, 2, 7, 1056].toPackedSet + doAssert toSeq(y.items) == [1, 2, 7, 1056] + + doAssert y.containsOrIncl(888) == false + doAssert 888 in y + doAssert y.containsOrIncl(888) == true + + doAssert y.missingOrExcl(888) == false + doAssert 888 notin y + doAssert y.missingOrExcl(888) == true + +proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted +template sortedItems(t: untyped): untyped = sorted(toSeq(t)) + +type Id = distinct int +proc `$`(x: Id): string {.borrow.} +proc cmp(a: Id, b: Id): int {.borrow.} +proc `==`(a: Id, b: Id): bool {.borrow.} +proc `<`(a: Id, b: Id): bool {.borrow.} + +block genericTests: + # we use HashSet as groundtruth, it's well tested elsewhere + template testDel(A: typedesc, t: typed, t0: typed) = + + block: + template checkEquals() = + doAssert t.len == t0.len + for k in t0: + doAssert k in t + for k in t: + doAssert k in t0 + + doAssert sortedItems(t) == sortedItems(t0) + + template incl2(i) = + t.incl i + t0.incl i + + template excl2(i) = + t.excl i + t0.excl i + + var expected: seq[A] + let n = 100 + let n2 = n*2 + for i in 0..<n: + incl2(A(i)) + checkEquals() + for i in 0..<n: + if i mod 3 == 0: + if i < n div 2: + excl2(A(i)) + else: + t0.excl A(i) + doAssert A(i) in t + doAssert not t.missingOrExcl A(i) + + checkEquals() + for i in n..<n2: + incl2(A(i)) + checkEquals() + for i in 0..<n2: + if i mod 7 == 0: + excl2(A(i)) + checkEquals() + + # notin check + for i in 0..<t.len: + if i mod 7 == 0: + doAssert A(i) notin t0 + doAssert A(i) notin t + # issue #13505 + doAssert t.missingOrExcl(A(i)) + + var t: PackedSet[int] + var t0: HashSet[int] + testDel(int, t, t0) + + var distT: PackedSet[Id] + var distT0: HashSet[Id] + testDel(Id, distT, distT0) + + doAssert union(distT, initPackedSet[Id]()) == distT + + var charT: PackedSet[char] + var charT0: HashSet[char] + testDel(char, charT, charT0) + + +block typeSafetyTest: + # mixing sets of different types shouldn't compile + doAssert not compiles( union(initPackedSet[Id](), initPackedSet[int]()) ) + doAssert compiles( union(initPackedSet[Id](), initPackedSet[Id]())) + + var ids: PackedSet[Id] + doAssert not compiles( ids.incl(3) ) + doAssert compiles( ids.incl(Id(3)) ) + + type NonOrdinal = string + doAssert not compiles( initPackedSet[NonOrdinal]() ) + +type EnumABCD = enum A, B, C, D + +block enumTest: + var letterSet = initPackedSet[EnumABCD]() + + for x in [A, C]: + letterSet.incl(x) + + doAssert A in letterSet + doAssert B notin letterSet + doAssert C in letterSet + doAssert D notin letterSet + +type Foo = distinct int16 +proc `$`(a: Foo): string {.borrow.} # `echo a` below won't work without `$` defined, as expected + +block printTest: + var a = initPackedSet[EnumABCD]() + a.incl A + a.incl C + doAssert $a == "{A, C}" + +import intsets + +block legacyMainModuleTests: + template genericTests(A: typedesc[Ordinal], x: typed) = + block: + proc typSeq(s: seq[int]): seq[A] = s.map(proc (i: int): A = A(i)) + x.incl(A(1)) + x.incl(A(2)) + x.incl(A(7)) + x.incl(A(1056)) + + x.incl(A(1044)) + x.excl(A(1044)) + + doAssert x == typSeq(@[1, 2, 7, 1056]).toPackedSet + + doAssert x.containsOrIncl(A(888)) == false + doAssert A(888) in x + doAssert x.containsOrIncl(A(888)) == true + + doAssert x.missingOrExcl(A(888)) == false + doAssert A(888) notin x + doAssert x.missingOrExcl(A(888)) == true + + var xs = toSeq(items(x)) + xs.sort(cmp[A]) + doAssert xs == typSeq(@[1, 2, 7, 1056]) + + var y: PackedSet[A] + assign(y, x) + var ys = toSeq(items(y)) + ys.sort(cmp[A]) + doAssert ys == typSeq(@[1, 2, 7, 1056]) + + doAssert x == y + + var z: PackedSet[A] + for i in 0..1000: + incl z, A(i) + doAssert z.len() == i+1 + for i in 0..1000: + doAssert z.contains(A(i)) + + var w = initPackedSet[A]() + w.incl(A(1)) + w.incl(A(4)) + w.incl(A(50)) + w.incl(A(1001)) + w.incl(A(1056)) + + var xuw = x.union(w) + var xuws = toSeq(items(xuw)) + xuws.sort(cmp) + doAssert xuws == typSeq(@[1, 2, 4, 7, 50, 1001, 1056]) + + var xiw = x.intersection(w) + var xiws = toSeq(items(xiw)) + xiws.sort(cmp) + doAssert xiws == @[A(1), A(1056)] + + var xdw = x.difference(w) + var xdws = toSeq(items(xdw)) + xdws.sort(cmp[A]) + doAssert xdws == @[A(2), A(7)] + + var xsw = x.symmetricDifference(w) + var xsws = toSeq(items(xsw)) + xsws.sort(cmp[A]) + doAssert xsws == typSeq(@[2, 4, 7, 50, 1001]) + + x.incl(w) + xs = toSeq(items(x)) + xs.sort(cmp[A]) + doAssert xs == typSeq(@[1, 2, 4, 7, 50, 1001, 1056]) + + doAssert w <= x + + doAssert w < x + + doAssert(not disjoint(w, x)) + + var u = initPackedSet[A]() + u.incl(A(3)) + u.incl(A(5)) + u.incl(A(500)) + doAssert disjoint(u, x) + + var v = initPackedSet[A]() + v.incl(A(2)) + v.incl(A(50)) + + x.excl(v) + xs = toSeq(items(x)) + xs.sort(cmp[A]) + doAssert xs == typSeq(@[1, 4, 7, 1001, 1056]) + + proc bug12366 = + var + x = initPackedSet[A]() + y = initPackedSet[A]() + n = 3584 + + for i in 0..n: + x.incl(A(i)) + y.incl(A(i)) + + let z = symmetricDifference(x, y) + doAssert z.len == 0 + doAssert $z == "{}" + + bug12366() + + var legacyInit = initIntSet() + genericTests(int, legacyInit) + + var intGenericInit = initPackedSet[int]() + genericTests(int, intGenericInit) + + var intDistinct = initPackedSet[Id]() + genericTests(Id, intDistinct) diff --git a/tests/stdlib/tparscfg.nim b/tests/stdlib/tparscfg.nim deleted file mode 100644 index ddb9b02b7..000000000 --- a/tests/stdlib/tparscfg.nim +++ /dev/null @@ -1,56 +0,0 @@ -discard """ - targets: "c js" -""" -import parsecfg, streams - -## Creating a configuration file. -var dict1=newConfig() -dict1.setSectionKey("","charset","utf-8") -dict1.setSectionKey("Package","name","hello") -dict1.setSectionKey("Package","--threads","on") -dict1.setSectionKey("Author","name","lihf8515") -dict1.setSectionKey("Author","qq","10214028") -dict1.setSectionKey("Author","email","lihaifeng@wxm.com") -var ss = newStringStream() -dict1.writeConfig(ss) - -## Reading a configuration file. -var dict2 = loadConfig(newStringStream(ss.data)) -var charset = dict2.getSectionValue("","charset") -var threads = dict2.getSectionValue("Package","--threads") -var pname = dict2.getSectionValue("Package","name") -var name = dict2.getSectionValue("Author","name") -var qq = dict2.getSectionValue("Author","qq") -var email = dict2.getSectionValue("Author","email") -doAssert charset == "utf-8" -doAssert threads == "on" -doAssert pname == "hello" -doAssert name == "lihf8515" -doAssert qq == "10214028" -doAssert email == "lihaifeng@wxm.com" - -## Modifying a configuration file. -var dict3 = loadConfig(newStringStream(ss.data)) -dict3.setSectionKey("Author","name","lhf") -doAssert $dict3 == """charset=utf-8 -[Package] -name=hello ---threads:on -[Author] -name=lhf -qq=10214028 -email="lihaifeng@wxm.com" -""" - -## Deleting a section key in a configuration file. -var dict4 = loadConfig(newStringStream(ss.data)) -dict4.delSectionKey("Author","email") -doAssert $dict4 == """charset=utf-8 -[Package] -name=hello ---threads:on -[Author] -name=lihf8515 -qq=10214028 -""" - diff --git a/tests/stdlib/tparsecfg.nim b/tests/stdlib/tparsecfg.nim index 1c214e5d2..2600d6f66 100644 --- a/tests/stdlib/tparsecfg.nim +++ b/tests/stdlib/tparsecfg.nim @@ -1,23 +1,132 @@ discard """ - output: '''OK''' + matrix: "--mm:refc; --mm:orc" + targets: "c js" """ -#bug #6046 -import parsecfg - -var config = newConfig() -config.setSectionKey("foo","bar","-1") -config.setSectionKey("foo","foo","abc") -config.writeConfig("test.ini") - -# test.ini now contains -# [foo] -# bar=-1 -# foo=abc - -var config2 = loadConfig("test.ini") -let bar = config2.getSectionValue("foo","bar") -let foo = config2.getSectionValue("foo","foo") -assert(bar == "-1") -assert(foo == "abc") -echo "OK" +import parsecfg, streams, sequtils +import std/assertions + +when not defined(js): + from stdtest/specialpaths import buildDir + import os + # bug #6046 + block: + var config = newConfig() + config.setSectionKey("foo", "bar", "-1") + config.setSectionKey("foo", "foo", "abc") + + const file = buildDir / "tparsecfg.ini" + config.writeConfig(file) + + # file now contains + # [foo] + # bar=-1 + # foo=abc + + var config2 = loadConfig(file) + let bar = config2.getSectionValue("foo", "bar") + let foo = config2.getSectionValue("foo", "foo") + doAssert(bar == "-1") + doAssert(foo == "abc") + +## Creating a configuration file. +var dict1 = newConfig() +dict1.setSectionKey("", "charset", "utf-8") +dict1.setSectionKey("Package", "name", "hello") +dict1.setSectionKey("Package", "--threads", "on") +dict1.setSectionKey("Author", "name", "lihf8515") +dict1.setSectionKey("Author", "qq", "10214028") +dict1.setSectionKey("Author", "email", "lihaifeng@wxm.com") +var ss = newStringStream() +dict1.writeConfig(ss) + +## Reading a configuration file. +let dict2 = loadConfig(newStringStream(ss.data)) +doAssert dict2.getSectionValue("", "charset") == "utf-8" +doAssert dict2.getSectionValue("Package", "--threads") == "on" +doAssert dict2.getSectionValue("Package", "name") == "hello" +doAssert dict2.getSectionValue("Author", "name") == "lihf8515" +doAssert dict2.getSectionValue("Author", "qq") == "10214028" +doAssert dict2.getSectionValue("Author", "email") == "lihaifeng@wxm.com" +doAssert toSeq(dict2.sections) == @["", "Package", "Author"] + +## Modifying a configuration file. +var dict3 = loadConfig(newStringStream(ss.data)) +dict3.setSectionKey("Author", "name", "lhf") +doAssert $dict3 == """charset=utf-8 +[Package] +name=hello +--threads:on +[Author] +name=lhf +qq=10214028 +email="lihaifeng@wxm.com" +""" + +## Deleting a section key in a configuration file. +var dict4 = loadConfig(newStringStream(ss.data)) +dict4.delSectionKey("Author", "email") +doAssert $dict4 == """charset=utf-8 +[Package] +name=hello +--threads:on +[Author] +name=lihf8515 +qq=10214028 +""" + +block: + var dict = loadConfig(newStringStream("""[Simple Values] + key=value + spaces in keys=allowed + spaces in values=allowed as well + spaces around the delimiter = obviously + you can also use : to delimit keys from values + [All Values Are Strings] + values like this: 19990429 + or this: 3.14159265359 + are they treated as numbers : no + integers floats and booleans are held as: strings + can use the API to get converted values directly: true + [No Values] + key_without_value + # empty string value is not allowed = + [ Seletion A ] + space around section name will be ignored + [You can use comments] + # like this + ; or this + # By default only in an empty line. + # Inline comments can be harmful because they prevent users + # from using the delimiting characters as parts of values. + # That being said, this can be customized. + [Sections Can Be Indented] + can_values_be_as_well = True + does_that_mean_anything_special = False + purpose = formatting for readability + # Did I mention we can indent comments, too? + """) + ) + + let section1 = "Simple Values" + doAssert dict.getSectionValue(section1, "key") == "value" + doAssert dict.getSectionValue(section1, "spaces in keys") == "allowed" + doAssert dict.getSectionValue(section1, "spaces in values") == "allowed as well" + doAssert dict.getSectionValue(section1, "spaces around the delimiter") == "obviously" + doAssert dict.getSectionValue(section1, "you can also use") == "to delimit keys from values" + + let section2 = "All Values Are Strings" + doAssert dict.getSectionValue(section2, "values like this") == "19990429" + doAssert dict.getSectionValue(section2, "or this") == "3.14159265359" + doAssert dict.getSectionValue(section2, "are they treated as numbers") == "no" + doAssert dict.getSectionValue(section2, "integers floats and booleans are held as") == "strings" + doAssert dict.getSectionValue(section2, "can use the API to get converted values directly") == "true" + + let section3 = "Seletion A" + doAssert dict.getSectionValue(section3, + "space around section name will be ignored", "not an empty value") == "" + + let section4 = "Sections Can Be Indented" + doAssert dict.getSectionValue(section4, "can_values_be_as_well") == "True" + doAssert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False" + doAssert dict.getSectionValue(section4, "purpose") == "formatting for readability" diff --git a/tests/stdlib/tparsecsv.nim b/tests/stdlib/tparsecsv.nim new file mode 100644 index 000000000..5a1e41bce --- /dev/null +++ b/tests/stdlib/tparsecsv.nim @@ -0,0 +1,36 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +include parsecsv +import strutils, os +import std/assertions + +block: # Tests for reading the header row + let content = "\nOne,Two,Three,Four\n1,2,3,4\n10,20,30,40,\n100,200,300,400\n" + writeFile("temp.csv", content) + + var p: CsvParser + p.open("temp.csv") + p.readHeaderRow() + while p.readRow(): + let zeros = repeat('0', p.currRow-2) + doAssert p.rowEntry("One") == "1" & zeros + doAssert p.rowEntry("Two") == "2" & zeros + doAssert p.rowEntry("Three") == "3" & zeros + doAssert p.rowEntry("Four") == "4" & zeros + p.close() + + when not defined(testing): + var parser: CsvParser + parser.open("temp.csv") + parser.readHeaderRow() + while parser.readRow(): + echo "new row: " + for col in items(parser.headers): + echo "##", col, ":", parser.rowEntry(col), "##" + parser.close() + removeFile("temp.csv") + + # Tidy up + removeFile("temp.csv") diff --git a/tests/stdlib/tparseipv6.nim b/tests/stdlib/tparseipv6.nim index 3e1c23e58..31ec4ecfb 100644 --- a/tests/stdlib/tparseipv6.nim +++ b/tests/stdlib/tparseipv6.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc" output: "all ok" """ @@ -8,23 +9,23 @@ const positives = [ "::f:8:a8f:218.17.235.229", "::b:228.19.241.2", - "::8:c:a:f:8.35.8.096", - "::3:e:a:bc:04.19.2.9", + "::8:c:a:f:8.35.8.96", + "::3:e:a:bc:4.19.2.9", "::2:212.242.248.19", "::df:a5f:3.250.208.9", "::8:c:5:e63:250.208.249.0", "::b:f:181.12.9.98", - "::a:f8:77.08.243.232", - "::a:b:85:e4d9:252.9.229.056", + "::a:f8:77.8.243.232", + "::a:b:85:e4d9:252.9.229.56", "941:c:8a:c:e::917", "e8:7a:e:ad:88a:8:203.235.225.46", - "139c:9e::f8:254.08.21.249", + "139c:9e::f8:254.8.21.249", "b38:f0:e::f9:89.6.12.18", "ef::8", "5::ab", "a::8:255.247.96.253", "b:c0::c:254.248.95.254", - "::8c:2:99.251.024.3", + "::8c:2:99.251.24.3", "98::c:247.240.249.57", "9::9", "628::f1ed:f", @@ -106,68 +107,68 @@ const "::315", "::a:a", "::aed3:a", - "f0eb:0:e8:b:c:a:254.098.233.17", + "f0eb:0:e8:b:c:a:254.98.233.17", "bfa:7fc:c66d:15:e9a:ded:254.119.9.9", - "d:ffa8:9:a:879:3:202.39.08.245", + "d:ffa8:9:a:879:3:202.39.8.245", "8e:2:8:fa8a:f1d1:1aa8:252.254.245.81", - "5:d4:a:e9:8:8:06.38.98.253", - "9c5:4:a5c:f:a6:8c9d:05.250.8.2", + "5:d4:a:e9:8:8:6.38.98.253", + "9c5:4:a5c:f:a6:8c9d:5.250.8.2", "d19a:2:f808:be:f:c:98.86.197.249", - "8:26ac:8:8:cb:f:242.00.254.85", + "8:26ac:8:8:cb:f:242.0.254.85", "38:e:1:0b88:f:0:8.89.248.92", - "e7:ff96:a:f:f:b:253.91.052.195", + "e7:ff96:a:f:f:b:253.91.52.195", "d:8:2:5:894:5:254.0.240.199", "2:98:9:8aa:9c8f:fa:252.98.248.17", - "e9:d4f:890:ccbe:5:8:088.200.228.216", + "e9:d4f:890:ccbe:5:8:88.200.228.216", "3:3:9:5:6a:df5:255.251.8.12", - "0280:3:8:8:4:9:255.000.251.249", + "0280:3:8:8:4:9:255.0.251.249", "8:af7:db:aa:0:9:238.248.250.255", - "ff:ee:9a:9252:a:289:059.083.18.255", + "ff:ee:9a:9252:a:289:59.83.18.255", "9f6:5:fc9:b:a89:a:142.1.250.254", "e:981a:da:bf94:9:f8:254.242.18.95", "3c:1:4:f2:89:f:8.91.255.14", - "e::9a2:c:9.050.80.8", + "e::9a2:c:9.50.80.8", "9::4a:07:fb:211.241.254.228", "9be::2:e:215.189.48.188", - "f::f:d:069.148.99.168", + "f::f:d:69.148.99.168", "f::a:97.18.240.47", "c::a98e:1:251.253.252.254", "668::82:214.87.208.9", "9c0::cf0:ecb:253.208.238.255", - "a::0:f1:210.240.238.049", - "8::a:1:251.238.34.09", + "a::0:f1:210.240.238.49", + "8::a:1:251.238.34.9", "81:dfe::b8:8.255.249.248", - "d3::7:b:9:83.189.08.244", - "8::9:8:8:00.7.11.252", + "d3::7:b:9:83.189.8.244", + "8::9:8:8:0.7.11.252", "2:8::c:a8:250.221.9.249", "2::f:99.8.249.247", "c:22f5::5:2c:243.15.79.89", "e:8e::da:251.243.255.2", "f15f:9::a:255.70.247.218", - "f:b::9f38:31.220.94.022", - "9::9a48:03.98.249.119", + "f:b::9f38:31.220.94.22", + "9::9a48:3.98.249.119", "d:d:9b87::2d:a:249.253.38.8", "d86d:99b::a9b:5:242.236.8.244", "eb:3::f:9cf:1.253.1.228", "b::ba2:255.247.114.64", - "2f:ec:bcb::9:219.254.250.094", + "2f:ec:bcb::9:219.254.250.94", "da8a:f6::a:e0:19.251.241.251", - "5e:c1::a:021.250.8.254", + "5e:c1::a:21.250.8.254", "c:9::8c9b:248.219.212.252", "2:a::8d4a:216.255.198.223", - "1f::66:255.30.08.150", + "1f::66:255.30.8.150", "bc2b:8f::2ff9:6.245.99.230", "a:8::a8:9.251.246.255", - "f:7:7::98:06.14.1.208", + "f:7:7::98:6.14.1.208", "e:2::9:218.249.255.254", "79:f::6:250.255.98.246", - "47:9:fb9f::9:038.136.17.251", + "47:9:fb9f::9:38.136.17.251", "ed::a:247.9.23.239", "6f::f1:88.254.119.9", "a::d:218.199.236.0", - "fc88::9:203.196.04.95", - "::8.048.255.85", - "::253.07.255.36", + "fc88::9:203.196.4.95", + "::8.48.255.85", + "::253.7.255.36", "9:d::253.7.178.229", "::250.84.158.253", "::8.55.204.248", @@ -175,39 +176,47 @@ const "df9:88ca::248.255.108.17", "8e9b::250.206.0.82", "::209.8.254.209", - "::247.088.8.8", + "::247.88.8.8", "::cb:f:ba41:250.208.19.249", "::fe:0e8:243.240.229.5", "::c:223.251.5.226", - "::8:08.03.8.250", + "::8:8.3.8.250", "::f:8.88.11.255", - "::fda:48:aa:05.189.07.2", - "::8:c3f:f:240.06.212.255", + "::fda:48:aa:5.189.7.2", + "::8:c3f:f:240.6.212.255", "::f:0aa:244.123.99.16", - "::c9b5:c:034.8.090.196", + "::c9b5:c:34.8.90.196", "::98:c9:254.14.241.81" ] negatives = ["foo.bar", "::::::::::::", "yet another failure", - "de:6:c:ab5:6a::9:252.06.06.249", - "f9:5f7:fa38:9:b::b6:09.255.248.252", + "de:6:c:ab5:6a::9:252.6.6.249", + "f9:5f7:fa38:9:b::b6:9.255.248.252", "97:c:5b:81:8a::f5dd:144.252.250.9", - "9:8:cd:8:a9::f:247.255.09.255", + "9:8:cd:8:a9::f:247.255.9.255", "18:1:8c:2:3::9:8.254.252.139", - "e:c298:3:e:a::bb12:254.246.05.250", + "e:c298:3:e:a::bb12:254.246.5.250", "e:e:c:8e:fd::8:253.8.49.231", "9:97f:f:e929:8a::c9:0.8.252.10", - "0df:b24:7:89:c::2b:16.249.240.092", + "0df:b24:7:89:c::2b:16.249.240.92", "b:8f5f:485:c:9a::84c:178.7.249.34", + "::3:e:a:bc:091.19.2.9", + "::a:f8:77.08.243.232", + "::8c:2:99.251.029.3", + "::8:c:a:f:8.35.8.096", + "d:ffa8:9:a:879:3:0202.39.8.245", + "139c:9e::f8:254.07.21.249", + "f0eb:0:e8:b:c:a:254.233.043.17", + "::a:b:85:e4d9:252.9.229.056", ] -proc ok(pos: openarray[string]) = +proc ok(pos: openArray[string]) = for p in pos: if not isIpAddress(p): echo "failure ", p -proc notok(neg: openarray[string]) = +proc notok(neg: openArray[string]) = for n in neg: if isIpAddress(n): echo "failure ", n diff --git a/tests/stdlib/tparsesql.nim b/tests/stdlib/tparsesql.nim index ba9e601a1..cd582551d 100644 --- a/tests/stdlib/tparsesql.nim +++ b/tests/stdlib/tparsesql.nim @@ -1,10 +1,23 @@ discard """ + matrix: "--mm:refc; --mm:orc" targets: "c js" """ import parsesql +import std/assertions -doAssert $parseSQL("SELECT foo FROM table;") == "select foo from table;" -doAssert $parseSQL(""" +doAssert treeRepr(parseSql("INSERT INTO STATS VALUES (10, 5.5); ") +) == """ + +nkStmtList + nkInsert + nkIdent STATS + nkNone + nkValueList + nkIntegerLit 10 + nkNumericLit 5.5""" + +doAssert $parseSql("SELECT foo FROM table;") == "select foo from table;" +doAssert $parseSql(""" SELECT CustomerName, ContactName, @@ -20,26 +33,26 @@ SELECT Country FROM table;""") == "select CustomerName, ContactName, Address, City, PostalCode, Country, CustomerName, ContactName, Address, City, PostalCode, Country from table;" -doAssert $parseSQL("SELECT foo FROM table limit 10") == "select foo from table limit 10;" -doAssert $parseSQL("SELECT foo, bar, baz FROM table limit 10") == "select foo, bar, baz from table limit 10;" -doAssert $parseSQL("SELECT foo AS bar FROM table") == "select foo as bar from table;" -doAssert $parseSQL("SELECT foo AS foo_prime, bar AS bar_prime, baz AS baz_prime FROM table") == "select foo as foo_prime, bar as bar_prime, baz as baz_prime from table;" -doAssert $parseSQL("SELECT * FROM table") == "select * from table;" -doAssert $parseSQL("SELECT count(*) FROM table") == "select count(*) from table;" -doAssert $parseSQL("SELECT count(*) as 'Total' FROM table") == "select count(*) as 'Total' from table;" -doAssert $parseSQL("SELECT count(*) as 'Total', sum(a) as 'Aggr' FROM table") == "select count(*) as 'Total', sum(a) as 'Aggr' from table;" +doAssert $parseSql("SELECT foo FROM table limit 10") == "select foo from table limit 10;" +doAssert $parseSql("SELECT foo, bar, baz FROM table limit 10") == "select foo, bar, baz from table limit 10;" +doAssert $parseSql("SELECT foo AS bar FROM table") == "select foo as bar from table;" +doAssert $parseSql("SELECT foo AS foo_prime, bar AS bar_prime, baz AS baz_prime FROM table") == "select foo as foo_prime, bar as bar_prime, baz as baz_prime from table;" +doAssert $parseSql("SELECT * FROM table") == "select * from table;" +doAssert $parseSql("SELECT count(*) FROM table") == "select count(*) from table;" +doAssert $parseSql("SELECT count(*) as 'Total' FROM table") == "select count(*) as 'Total' from table;" +doAssert $parseSql("SELECT count(*) as 'Total', sum(a) as 'Aggr' FROM table") == "select count(*) as 'Total', sum(a) as 'Aggr' from table;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table WHERE a = b and c = d """) == "select * from table where a = b and c = d;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table WHERE not b """) == "select * from table where not b;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM @@ -48,63 +61,63 @@ WHERE a and not b """) == "select * from table where a and not b;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table ORDER BY 1 """) == "select * from table order by 1;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table GROUP BY 1 ORDER BY 1 """) == "select * from table group by 1 order by 1;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table ORDER BY 1 LIMIT 100 """) == "select * from table order by 1 limit 100;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table WHERE a = b and c = d or n is null and not b + 1 = 3 """) == "select * from table where a = b and c = d or n is null and not b + 1 = 3;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table WHERE (a = b and c = d) or (n is null and not b + 1 = 3) """) == "select * from table where(a = b and c = d) or (n is null and not b + 1 = 3);" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table HAVING a = b and c = d """) == "select * from table having a = b and c = d;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM table GROUP BY a """) == "select a, b from table group by a;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM table GROUP BY 1, 2 """) == "select a, b from table group by 1, 2;" -doAssert $parseSQL("SELECT t.a FROM t as t") == "select t.a from t as t;" +doAssert $parseSql("SELECT t.a FROM t as t") == "select t.a from t as t;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM ( SELECT * FROM t ) """) == "select a, b from(select * from t);" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM ( SELECT * FROM t ) as foo """) == "select a, b from(select * from t) as foo;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM ( SELECT * FROM ( SELECT * FROM ( @@ -116,49 +129,49 @@ SELECT a, b FROM ( ) as inner5 """) == "select a, b from(select * from(select * from(select * from(select * from innerTable as inner1) as inner2) as inner3) as inner4) as inner5;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM (SELECT * FROM a), (SELECT * FROM b), (SELECT * FROM c) """) == "select a, b from(select * from a),(select * from b),(select * from c);" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM Products WHERE Price BETWEEN 10 AND 20; """) == "select * from Products where Price between 10 and 20;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT id FROM a JOIN b ON a.id == b.id """) == "select id from a join b on a.id == b.id;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT id FROM a JOIN (SELECT id from c) as b ON a.id == b.id """) == "select id from a join(select id from c) as b on a.id == b.id;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT id FROM a INNER JOIN b ON a.id == b.id """) == "select id from a inner join b on a.id == b.id;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT id FROM a OUTER JOIN b ON a.id == b.id """) == "select id from a outer join b on a.id == b.id;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT id FROM a CROSS JOIN b ON a.id == b.id """) == "select id from a cross join b on a.id == b.id;" -doAssert $parseSQL(""" +doAssert $parseSql(""" CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic'); CREATE TABLE holidays ( num_weeks int, @@ -168,31 +181,54 @@ CREATE INDEX table1_attr1 ON table1(attr1); SELECT * FROM myTab WHERE col1 = 'happy'; """) == "create type happiness as enum ('happy' , 'very happy' , 'ecstatic' ); create table holidays(num_weeks int , happiness happiness );; create index table1_attr1 on table1(attr1 );; select * from myTab where col1 = 'happy';" -doAssert $parseSQL(""" +doAssert $parseSql(""" INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country) VALUES ('Cardinal', 'Tom B. Erichsen', 'Skagen 21', 'Stavanger', '4006', 'Norway'); """) == "insert into Customers (CustomerName , ContactName , Address , City , PostalCode , Country ) values ('Cardinal' , 'Tom B. Erichsen' , 'Skagen 21' , 'Stavanger' , '4006' , 'Norway' );" -doAssert $parseSQL(""" +doAssert $parseSql(""" INSERT INTO TableName DEFAULT VALUES """) == "insert into TableName default values;" -doAssert $parseSQL(""" +doAssert $parseSql(""" UPDATE Customers SET ContactName = 'Alfred Schmidt', City= 'Frankfurt' WHERE CustomerID = 1; """) == "update Customers set ContactName = 'Alfred Schmidt' , City = 'Frankfurt' where CustomerID = 1;" -doAssert $parseSQL("DELETE FROM table_name;") == "delete from table_name;" +doAssert treeRepr(parseSql("""UPDATE Customers + SET ContactName = 'Alice', City= 'Frankfurt';""") +) == """ + +nkStmtList + nkUpdate + nkIdent Customers + nkAsgn + nkIdent ContactName + nkStringLit Alice + nkAsgn + nkIdent City + nkStringLit Frankfurt + nkNone""" + +doAssert $parseSql("DELETE FROM table_name;") == "delete from table_name;" + +doAssert treeRepr(parseSql("DELETE FROM table_name;") +) == """ + +nkStmtList + nkDelete + nkIdent table_name + nkNone""" -doAssert $parseSQL("DELETE * FROM table_name;") == "delete from table_name;" +doAssert $parseSql("DELETE * FROM table_name;") == "delete from table_name;" -doAssert $parseSQL(""" +doAssert $parseSql(""" --Select all: SELECT * FROM Customers; """) == "select * from Customers;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM Customers WHERE (CustomerName LIKE 'L%' OR CustomerName LIKE 'R%' /*OR CustomerName LIKE 'S%' OR CustomerName LIKE 'T%'*/ OR CustomerName LIKE 'W%') @@ -201,9 +237,9 @@ ORDER BY CustomerName; """) == "select * from Customers where(CustomerName like 'L%' or CustomerName like 'R%' or CustomerName like 'W%') and Country = 'USA' order by CustomerName;" # parse quoted keywords as identifires -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT `SELECT`, `FROM` as `GROUP` FROM `WHERE`; """) == """select "SELECT", "FROM" as "GROUP" from "WHERE";""" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT "SELECT", "FROM" as "GROUP" FROM "WHERE"; """) == """select "SELECT", "FROM" as "GROUP" from "WHERE";""" diff --git a/tests/stdlib/tparseuints.nim b/tests/stdlib/tparseuints.nim index 6b228d933..9c71a27d6 100644 --- a/tests/stdlib/tparseuints.nim +++ b/tests/stdlib/tparseuints.nim @@ -1,13 +1,11 @@ discard """ - action: run - output: ''' -[Suite] parseutils''' + matrix: "--mm:refc; --mm:orc" """ + import unittest, strutils -suite "parseutils": - test "uint": - check: parseBiggestUInt("0") == 0'u64 - check: parseBiggestUInt("18446744073709551615") == 0xFFFF_FFFF_FFFF_FFFF'u64 - expect(ValueError): - discard parseBiggestUInt("18446744073709551616") +block: # parseutils + check: parseBiggestUInt("0") == 0'u64 + check: parseBiggestUInt("18446744073709551615") == 0xFFFF_FFFF_FFFF_FFFF'u64 + expect(ValueError): + discard parseBiggestUInt("18446744073709551616") diff --git a/tests/stdlib/tparseutils.nim b/tests/stdlib/tparseutils.nim new file mode 100644 index 000000000..b69900864 --- /dev/null +++ b/tests/stdlib/tparseutils.nim @@ -0,0 +1,112 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp" +""" + +import std/[parseutils, sequtils, sugar, formatfloat] +import std/assertions + +proc test() = + let input = "$test{} $this is ${an{ example}} " + let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"), + (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")] + doAssert toSeq(interpolatedFragments(input)) == expected + + var value = 0 + discard parseHex("0x38", value) + doAssert value == 56 + + value = -1 + doAssert(parseSaturatedNatural("848", value) == 3) + doAssert value == 848 + + value = -1 + discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value) + doAssert value == high(int) + + value = -1 + discard parseSaturatedNatural("9223372036854775808", value) + doAssert value == high(int) + + value = -1 + discard parseSaturatedNatural("9223372036854775807", value) + doAssert value == high(int) + + value = -1 + discard parseSaturatedNatural("18446744073709551616", value) + doAssert value == high(int) + + value = -1 + discard parseSaturatedNatural("18446744073709551615", value) + doAssert value == high(int) + + value = -1 + doAssert(parseSaturatedNatural("1_000_000", value) == 9) + doAssert value == 1_000_000 + + var i64Value: int64 + discard parseBiggestInt("9223372036854775807", i64Value) + doAssert i64Value == 9223372036854775807 + + block: + var f: float + let res = collect: + for x in ["9.123456789012345+","11.123456789012345+","9.123456789012345-","8.123456789012345+","9.12345678901234-","9.123456789012345"]: + (parseFloat(x, f, 0), $f) + doAssert res == @[(17, "9.123456789012344"), (18, "11.123456789012344"), + (17, "9.123456789012344"), (17, "8.123456789012344"), + (16, "9.12345678901234"), (17, "9.123456789012344")] + +test() +static: test() + +block: # With this included, static: test() crashes the compiler (from a + # VM problem with parseSize calling parseFloat). + var sz: int64 + template checkParseSize(s, expectLen, expectVal) = + if (let got = parseSize(s, sz); got != expectLen): + raise newException(IOError, "got len " & $got & " != " & $expectLen) + if sz != expectVal: + raise newException(IOError, "got sz " & $sz & " != " & $expectVal) + # STRING LEN SZ + # Good, complete parses + checkParseSize "1 b" , 4, 1 + checkParseSize "1 B" , 4, 1 + checkParseSize "1k" , 2, 1000 + checkParseSize "1 kib" , 5, 1024 + checkParseSize "1 ki" , 4, 1024 + checkParseSize "1mi" , 3, 1048576 + checkParseSize "1 mi" , 4, 1048576 + checkParseSize "1 mib" , 5, 1048576 + checkParseSize "1 Mib" , 5, 1048576 + checkParseSize "1 MiB" , 5, 1048576 + checkParseSize "1.23GiB", 7, 1320702444 # 1320702443.52 rounded + checkParseSize "0.001k" , 6, 1 + checkParseSize "0.0004k", 7, 0 + checkParseSize "0.0006k", 7, 1 + # Incomplete parses + checkParseSize "1 " , 1, 1 # Trailing white IGNORED + checkParseSize "1 B " , 4, 1 # Trailing white IGNORED + checkParseSize "1 B/s" , 4, 1 # Trailing junk IGNORED + checkParseSize "1 kX" , 3, 1000 + checkParseSize "1 kiX" , 4, 1024 + checkParseSize "1j" , 1, 1 # Unknown prefix IGNORED + checkParseSize "1 jib" , 2, 1 # Unknown prefix post space + checkParseSize "1 ji" , 3, 1 + # Bad parses; `sz` should stay last good|incomplete value + checkParseSize "-1b" , 0, 1 # Negative numbers + checkParseSize "abc" , 0, 1 # Non-numeric + checkParseSize " 12" , 0, 1 # Leading white + # Value Edge cases + checkParseSize "9223372036854775807", 19, int64.high + +block: # bug #23936 + func parsePyFloat( + a: openArray[char], # here must be openArray instead of string to reproduce this bug + res: var BiggestFloat): int = + result = parseFloat(a, res) + + static: + var f = 0.0 + doAssert "1.0".parsePyFloat(f) == 3 + doAssert f == 1.0 diff --git a/tests/stdlib/tparsopt.nim b/tests/stdlib/tparsopt.nim index 948bc8d5f..f3a9a9798 100644 --- a/tests/stdlib/tparsopt.nim +++ b/tests/stdlib/tparsopt.nim @@ -9,6 +9,8 @@ disabled: true import parseopt +import std/[assertions, syncio] + proc writeHelp() = writeLine(stdout, "Usage: tparsopt [options] filename [options]") @@ -27,7 +29,7 @@ for kind, key, val in getopt(): of "version", "v": writeVersion() else: writeLine(stdout, "Unknown command line option: ", key, ": ", val) - of cmdEnd: assert(false) # cannot happen + of cmdEnd: doAssert(false) # cannot happen if filename == "": # no filename has been given, so we show the help: writeHelp() diff --git a/tests/stdlib/tpathnorm.nim b/tests/stdlib/tpathnorm.nim new file mode 100644 index 000000000..3dd287a77 --- /dev/null +++ b/tests/stdlib/tpathnorm.nim @@ -0,0 +1,36 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/os +import std/assertions + +when doslikeFileSystem: + import std/pathnorm + + template initVars = + var state {.inject.} = 0 + var result {.inject.}: string + + block: # / -> / + initVars + addNormalizePath("//?/c:/./foo//bar/../baz", result, state, '/') + doAssert result == "//?/c:/foo/baz" + addNormalizePath("me", result, state, '/') + doAssert result == "//?/c:/foo/baz/me" + + block: # / -> \ + initVars + addNormalizePath(r"//?/c:/./foo//bar/../baz", result, state, '\\') + doAssert result == r"\\?\c:\foo\baz" + addNormalizePath("me", result, state, '\\') + doAssert result == r"\\?\c:\foo\baz\me" + + block: # Append path component to UNC drive + initVars + addNormalizePath(r"//?/c:", result, state, '\\') + doAssert result == r"\\?\c:" + addNormalizePath("Users", result, state, '\\') + doAssert result == r"\\?\c:\Users" + addNormalizePath("me", result, state, '\\') + doAssert result == r"\\?\c:\Users\me" diff --git a/tests/stdlib/tpaths.nim b/tests/stdlib/tpaths.nim new file mode 100644 index 000000000..edb56209a --- /dev/null +++ b/tests/stdlib/tpaths.nim @@ -0,0 +1,238 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/paths +import std/assertions +import pathnorm +from std/private/ospaths2 {.all.} import joinPathImpl +import std/[sugar, sets] + + +proc normalizePath*(path: Path; dirSep = DirSep): Path = + result = Path(pathnorm.normalizePath(path.string, dirSep)) + +func joinPath*(parts: varargs[Path]): Path = + var estimatedLen = 0 + var state = 0 + for p in parts: estimatedLen += p.string.len + var res = newStringOfCap(estimatedLen) + for i in 0..high(parts): + joinPathImpl(res, state, parts[i].string) + result = Path(res) + + +func joinPath(head, tail: Path): Path {.inline.} = + head / tail + +block absolutePath: + doAssertRaises(ValueError): discard absolutePath(Path"a", Path"b") + doAssert absolutePath(Path"a") == getCurrentDir() / Path"a" + doAssert absolutePath(Path"a", Path"/b") == Path"/b" / Path"a" + when defined(posix): + doAssert absolutePath(Path"a", Path"/b/") == Path"/b" / Path"a" + doAssert absolutePath(Path"a", Path"/b/c") == Path"/b/c" / Path"a" + doAssert absolutePath(Path"/a", Path"b/") == Path"/a" + +block splitFile: + doAssert splitFile(Path"") == (Path"", Path"", "") + doAssert splitFile(Path"abc/") == (Path"abc", Path"", "") + doAssert splitFile(Path"/") == (Path"/", Path"", "") + doAssert splitFile(Path"./abc") == (Path".", Path"abc", "") + doAssert splitFile(Path".txt") == (Path"", Path".txt", "") + doAssert splitFile(Path"abc/.txt") == (Path"abc", Path".txt", "") + doAssert splitFile(Path"abc") == (Path"", Path"abc", "") + doAssert splitFile(Path"abc.txt") == (Path"", Path"abc", ".txt") + doAssert splitFile(Path"/abc.txt") == (Path"/", Path"abc", ".txt") + doAssert splitFile(Path"/foo/abc.txt") == (Path"/foo", Path"abc", ".txt") + doAssert splitFile(Path"/foo/abc.txt.gz") == (Path"/foo", Path"abc.txt", ".gz") + doAssert splitFile(Path".") == (Path"", Path".", "") + doAssert splitFile(Path"abc/.") == (Path"abc", Path".", "") + doAssert splitFile(Path"..") == (Path"", Path"..", "") + doAssert splitFile(Path"a/..") == (Path"a", Path"..", "") + doAssert splitFile(Path"/foo/abc....txt") == (Path"/foo", Path"abc...", ".txt") + +# execShellCmd is tested in tosproc + +block ospaths: + doAssert unixToNativePath(Path"") == Path"" + doAssert unixToNativePath(Path".") == Path($CurDir) + doAssert unixToNativePath(Path"..") == Path($ParDir) + doAssert isAbsolute(unixToNativePath(Path"/")) + doAssert isAbsolute(unixToNativePath(Path"/", Path"a")) + doAssert isAbsolute(unixToNativePath(Path"/a")) + doAssert isAbsolute(unixToNativePath(Path"/a", Path"a")) + doAssert isAbsolute(unixToNativePath(Path"/a/b")) + doAssert isAbsolute(unixToNativePath(Path"/a/b", Path"a")) + doAssert unixToNativePath(Path"a/b") == joinPath(Path"a", Path"b") + + when defined(macos): + doAssert unixToNativePath(Path"./") == Path":" + doAssert unixToNativePath(Path"./abc") == Path":abc" + doAssert unixToNativePath(Path"../abc") == Path"::abc" + doAssert unixToNativePath(Path"../../abc") == Path":::abc" + doAssert unixToNativePath(Path"/abc", Path"a") == Path"abc" + doAssert unixToNativePath(Path"/abc/def", Path"a") == Path"abc:def" + elif doslikeFileSystem: + doAssert unixToNativePath(Path"./") == Path(".\\") + doAssert unixToNativePath(Path"./abc") == Path(".\\abc") + doAssert unixToNativePath(Path"../abc") == Path("..\\abc") + doAssert unixToNativePath(Path"../../abc") == Path("..\\..\\abc") + doAssert unixToNativePath(Path"/abc", Path"a") == Path("a:\\abc") + doAssert unixToNativePath(Path"/abc/def", Path"a") == Path("a:\\abc\\def") + else: + #Tests for unix + doAssert unixToNativePath(Path"./") == Path"./" + doAssert unixToNativePath(Path"./abc") == Path"./abc" + doAssert unixToNativePath(Path"../abc") == Path"../abc" + doAssert unixToNativePath(Path"../../abc") == Path"../../abc" + doAssert unixToNativePath(Path"/abc", Path"a") == Path"/abc" + doAssert unixToNativePath(Path"/abc/def", Path"a") == Path"/abc/def" + + block extractFilenameTest: + doAssert extractFilename(Path"") == Path"" + when defined(posix): + doAssert extractFilename(Path"foo/bar") == Path"bar" + doAssert extractFilename(Path"foo/bar.txt") == Path"bar.txt" + doAssert extractFilename(Path"foo/") == Path"" + doAssert extractFilename(Path"/") == Path"" + when doslikeFileSystem: + doAssert extractFilename(Path(r"foo\bar")) == Path"bar" + doAssert extractFilename(Path(r"foo\bar.txt")) == Path"bar.txt" + doAssert extractFilename(Path(r"foo\")) == Path"" + doAssert extractFilename(Path(r"C:\")) == Path"" + + block lastPathPartTest: + doAssert lastPathPart(Path"") == Path"" + when defined(posix): + doAssert lastPathPart(Path"foo/bar.txt") == Path"bar.txt" + doAssert lastPathPart(Path"foo/") == Path"foo" + doAssert lastPathPart(Path"/") == Path"" + when doslikeFileSystem: + doAssert lastPathPart(Path(r"foo\bar.txt")) == Path"bar.txt" + doAssert lastPathPart(Path(r"foo\")) == Path"foo" + + template canon(x): Path = normalizePath(Path(x), '/') + doAssert canon"/foo/../bar" == Path"/bar" + doAssert canon"foo/../bar" == Path"bar" + + doAssert canon"/f/../bar///" == Path"/bar" + doAssert canon"f/..////bar" == Path"bar" + + doAssert canon"../bar" == Path"../bar" + doAssert canon"/../bar" == Path"/../bar" + + doAssert canon("foo/../../bar/") == Path"../bar" + doAssert canon("./bla/blob/") == Path"bla/blob" + doAssert canon(".hiddenFile") == Path".hiddenFile" + doAssert canon("./bla/../../blob/./zoo.nim") == Path"../blob/zoo.nim" + + doAssert canon("C:/file/to/this/long") == Path"C:/file/to/this/long" + doAssert canon("") == Path"" + doAssert canon("foobar") == Path"foobar" + doAssert canon("f/////////") == Path"f" + + doAssert relativePath(Path"/foo/bar//baz.nim", Path"/foo", '/') == Path"bar/baz.nim" + doAssert normalizePath(Path"./foo//bar/../baz", '/') == Path"foo/baz" + + doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/other/bad", '/') == Path"../../me/bar/z.nim" + + doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/other", '/') == Path"../me/bar/z.nim" + + # `//` is a UNC path, `/` is the current working directory's drive, so can't + # run this test on Windows. + when not doslikeFileSystem: + doAssert relativePath(Path"/Users///me/bar//z.nim", Path"//Users/", '/') == Path"me/bar/z.nim" + doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/me", '/') == Path"bar/z.nim" + doAssert relativePath(Path"", Path"/users/moo", '/') == Path"" + doAssert relativePath(Path"foo", Path"", '/') == Path"foo" + doAssert relativePath(Path"/foo", Path"/Foo", '/') == (when FileSystemCaseSensitive: Path"../foo" else: Path".") + doAssert relativePath(Path"/Foo", Path"/foo", '/') == (when FileSystemCaseSensitive: Path"../Foo" else: Path".") + doAssert relativePath(Path"/foo", Path"/fOO", '/') == (when FileSystemCaseSensitive: Path"../foo" else: Path".") + doAssert relativePath(Path"/foO", Path"/foo", '/') == (when FileSystemCaseSensitive: Path"../foO" else: Path".") + + doAssert relativePath(Path"foo", Path".", '/') == Path"foo" + doAssert relativePath(Path".", Path".", '/') == Path"." + doAssert relativePath(Path"..", Path".", '/') == Path".." + + doAssert relativePath(Path"foo", Path"foo") == Path"." + doAssert relativePath(Path"", Path"foo") == Path"" + doAssert relativePath(Path"././/foo", Path"foo//./") == Path"." + + doAssert relativePath(getCurrentDir() / Path"bar", Path"foo") == Path"../bar".unixToNativePath + doAssert relativePath(Path"bar", getCurrentDir() / Path"foo") == Path"../bar".unixToNativePath + + when doslikeFileSystem: + doAssert relativePath(r"c:\foo.nim".Path, r"C:\".Path) == r"foo.nim".Path + doAssert relativePath(r"c:\foo\bar\baz.nim".Path, r"c:\foo".Path) == r"bar\baz.nim".Path + doAssert relativePath(r"c:\foo\bar\baz.nim".Path, r"d:\foo".Path) == r"c:\foo\bar\baz.nim".Path + doAssert relativePath(r"\foo\baz.nim".Path, r"\foo".Path) == r"baz.nim".Path + doAssert relativePath(r"\foo\bar\baz.nim".Path, r"\bar".Path) == r"..\foo\bar\baz.nim".Path + doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foo\bar".Path) == r"baz.nim".Path + doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foO\bar".Path) == r"baz.nim".Path + doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\bar\bar".Path) == r"\\foo\bar\baz.nim".Path + doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foo\car".Path) == r"\\foo\bar\baz.nim".Path + doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\goo\bar".Path) == r"\\foo\bar\baz.nim".Path + doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"c:\".Path) == r"\\foo\bar\baz.nim".Path + doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\foo".Path) == r"\\foo\bar\baz.nim".Path + doAssert relativePath(r"c:\foo.nim".Path, r"\foo".Path) == r"c:\foo.nim".Path + + doAssert joinPath(Path"usr", Path"") == unixToNativePath(Path"usr") + doAssert joinPath(Path"usr", Path"") == (Path"usr").dup(add Path"") + doAssert joinPath(Path"", Path"lib") == Path"lib" + doAssert joinPath(Path"", Path"lib") == Path"".dup(add Path"lib") + doAssert joinPath(Path"", Path"/lib") == unixToNativePath(Path"/lib") + doAssert joinPath(Path"", Path"/lib") == unixToNativePath(Path"/lib") + doAssert joinPath(Path"usr/", Path"/lib") == Path"usr/".dup(add Path"/lib") + doAssert joinPath(Path"", Path"") == unixToNativePath(Path"") # issue #13455 + doAssert joinPath(Path"", Path"") == Path"".dup(add Path"") + doAssert joinPath(Path"", Path"/") == unixToNativePath(Path"/") + doAssert joinPath(Path"", Path"/") == Path"".dup(add Path"/") + doAssert joinPath(Path"/", Path"/") == unixToNativePath(Path"/") + doAssert joinPath(Path"/", Path"/") == Path"/".dup(add Path"/") + doAssert joinPath(Path"/", Path"") == unixToNativePath(Path"/") + doAssert joinPath(Path"/" / Path"") == unixToNativePath(Path"/") # weird test case... + doAssert joinPath(Path"/", Path"/a/b/c") == unixToNativePath(Path"/a/b/c") + doAssert joinPath(Path"foo/", Path"") == unixToNativePath(Path"foo/") + doAssert joinPath(Path"foo/", Path"abc") == unixToNativePath(Path"foo/abc") + doAssert joinPath(Path"foo//./", Path"abc/.//") == unixToNativePath(Path"foo/abc/") + doAssert Path"foo//./".dup(add Path"abc/.//") == unixToNativePath(Path"foo/abc/") + doAssert joinPath(Path"foo", Path"abc") == unixToNativePath(Path"foo/abc") + doAssert Path"foo".dup(add Path"abc") == unixToNativePath(Path"foo/abc") + doAssert joinPath(Path"", Path"abc") == unixToNativePath(Path"abc") + + doAssert joinPath(Path"zook/.", Path"abc") == unixToNativePath(Path"zook/abc") + + # controversial: inconsistent with `joinPath("zook/.","abc")` + # on linux, `./foo` and `foo` are treated a bit differently for executables + # but not `./foo/bar` and `foo/bar` + doAssert joinPath(Path".", Path"/lib") == unixToNativePath(Path"./lib") + doAssert joinPath(Path".", Path"abc") == unixToNativePath(Path"./abc") + + # cases related to issue #13455 + doAssert joinPath(Path"foo", Path"", Path"") == Path"foo" + doAssert joinPath(Path"foo", Path"") == Path"foo" + doAssert joinPath(Path"foo/", Path"") == unixToNativePath(Path"foo/") + doAssert joinPath(Path"foo/", Path".") == Path"foo" + doAssert joinPath(Path"foo", Path"./") == unixToNativePath(Path"foo/") + doAssert joinPath(Path"foo", Path"", Path"bar/") == unixToNativePath(Path"foo/bar/") + + # issue #13579 + doAssert joinPath(Path"/foo", Path"../a") == unixToNativePath(Path"/a") + doAssert joinPath(Path"/foo/", Path"../a") == unixToNativePath(Path"/a") + doAssert joinPath(Path"/foo/.", Path"../a") == unixToNativePath(Path"/a") + doAssert joinPath(Path"/foo/.b", Path"../a") == unixToNativePath(Path"/foo/a") + doAssert joinPath(Path"/foo///", Path"..//a/") == unixToNativePath(Path"/a/") + doAssert joinPath(Path"foo/", Path"../a") == unixToNativePath(Path"a") + + when doslikeFileSystem: + doAssert joinPath(Path"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\", Path"..\\..\\VC\\vcvarsall.bat") == r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat".Path + doAssert joinPath(Path"C:\\foo", Path"..\\a") == r"C:\a".Path + doAssert joinPath(Path"C:\\foo\\", Path"..\\a") == r"C:\a".Path + + +block: # bug #23663 + var s: HashSet[Path] + s.incl("/a/b/c/..".Path) + doAssert "/a/b/".Path in s + doAssert "/a/b/c".Path notin s diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index 2990a44a5..da3fc14b7 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -1,5 +1,6 @@ discard """ - targets: "c js" + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" output: ''' PEG AST traversal output ------------------------ @@ -51,8 +52,10 @@ Event parser output ''' """ -import strutils, streams -import pegs +when defined(nimHasEffectsOf): + {.experimental: "strictEffects".} + +import std/[strutils, streams, pegs, assertions] const indent = " " @@ -104,9 +107,9 @@ block: block: var - pStack: seq[string] = @[] - valStack: seq[float] = @[] - opStack = "" + pStack {.threadvar.}: seq[string] + valStack {.threadvar.}: seq[float] + opStack {.threadvar.}: string let parseArithExpr = pegAst.eventParser: pkNonTerminal: @@ -125,7 +128,7 @@ block: discard of "Sum", "Product": try: - let val = matchStr.parseFloat + let val {.used.} = matchStr.parseFloat except ValueError: if valStack.len > 1 and opStack.len > 0: valStack[^2] = case opStack[^1] @@ -146,4 +149,196 @@ block: echo "Event parser output" echo "-------------------" let pLen = parseArithExpr(txt) - assert txt.len == pLen + doAssert txt.len == pLen + + +import std/importutils + +block: + proc pegsTest() = + privateAccess(NonTerminal) + privateAccess(Captures) + + if "test" =~ peg"s <- {{\ident}}": # bug #19104 + doAssert matches[0] == "test" + doAssert matches[1] == "test", $matches[1] + + doAssert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27" + doAssert match("(a b c)", peg"'(' @ ')'") + doAssert match("W_HI_Le", peg"\y 'while'") + doAssert(not match("W_HI_L", peg"\y 'while'")) + doAssert(not match("W_HI_Le", peg"\y v'while'")) + doAssert match("W_HI_Le", peg"y'while'") + + doAssert($ +digits == $peg"\d+") + doAssert "0158787".match(peg"\d+") + doAssert "ABC 0232".match(peg"\w+\s+\d+") + doAssert "ABC".match(peg"\d+ / \w+") + + var accum: seq[string] = @[] + for word in split("00232this02939is39an22example111", peg"\d+"): + accum.add(word) + doAssert(accum == @["this", "is", "an", "example"]) + + doAssert matchLen("key", ident) == 3 + + var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) + doAssert matchLen("key1= cal9", pattern) == 11 + + var ws = newNonTerminal("ws", 1, 1) + ws.rule = *whitespace + + var expr = newNonTerminal("expr", 1, 1) + expr.rule = sequence(capture(ident), *sequence( + nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) + + var c: Captures + var s = "a+b + c +d+e+f" + doAssert rawMatch(s, expr.rule, 0, c) == len(s) + var a = "" + for i in 0..c.ml-1: + a.add(substr(s, c.matches[i][0], c.matches[i][1])) + doAssert a == "abcdef" + #echo expr.rule + + #const filename = "lib/devel/peg/grammar.txt" + #var grammar = parsePeg(newFileStream(filename, fmRead), filename) + #echo "a <- [abc]*?".match(grammar) + doAssert find("_____abc_______", term("abc"), 2) == 5 + doAssert match("_______ana", peg"A <- 'ana' / . A") + doAssert match("abcs%%%", peg"A <- ..A / .A / '%'") + + var matches: array[0..MaxSubpatterns-1, string] + if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}": + doAssert matches[0] == "abc" + else: + doAssert false + + var g2 = peg"""S <- A B / C D + A <- 'a'+ + B <- 'b'+ + C <- 'c'+ + D <- 'd'+ + """ + doAssert($g2 == "((A B) / (C D))") + doAssert match("cccccdddddd", g2) + doAssert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") == + "var1<-keykey; var2<-key2key2") + doAssert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") == + "$1<-$2$2; $1<-$2$2") + doAssert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}") + + if "aaaaaa" =~ peg"'aa' !. / ({'a'})+": + doAssert matches[0] == "a" + else: + doAssert false + + if match("abcdefg", peg"c {d} ef {g}", matches, 2): + doAssert matches[0] == "d" + doAssert matches[1] == "g" + else: + doAssert false + + accum = @[] + for x in findAll("abcdef", peg".", 3): + accum.add(x) + doAssert(accum == @["d", "e", "f"]) + + for x in findAll("abcdef", peg"^{.}", 3): + doAssert x == "d" + + if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')": + doAssert matches[0] == "f" + doAssert matches[1] == "a, b" + else: + doAssert false + + doAssert match("eine übersicht und außerdem", peg"(\letter \white*)+") + # ß is not a lower cased letter?! + doAssert match("eine übersicht und auerdem", peg"(\lower \white*)+") + doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+") + doAssert(not match("456678", peg"(\letter)+")) + + doAssert("var1 = key; var2 = key2".replacef( + peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") == + "var1<-keykey;var2<-key2key2") + + doAssert match("prefix/start", peg"^start$", 7) + + if "foo" =~ peg"{'a'}?.*": + doAssert matches[0].len == 0 + else: doAssert false + + if "foo" =~ peg"{''}.*": + doAssert matches[0] == "" + else: doAssert false + + if "foo" =~ peg"{'foo'}": + doAssert matches[0] == "foo" + else: doAssert false + + let empty_test = peg"^\d*" + let str = "XYZ" + + doAssert(str.find(empty_test) == 0) + doAssert(str.match(empty_test)) + + proc handleMatches(m: int, n: int, c: openArray[string]): string = + result = "" + + if m > 0: + result.add ", " + + result.add case n: + of 2: toLowerAscii(c[0]) & ": '" & c[1] & "'" + of 1: toLowerAscii(c[0]) & ": ''" + else: "" + + doAssert("Var1=key1;var2=Key2; VAR3". + replace(peg"{\ident}('='{\ident})* ';'* \s*", + handleMatches) == "var1: 'key1', var2: 'Key2', var3: ''") + + + doAssert "test1".match(peg"""{@}$""") + doAssert "test2".match(peg"""{(!$ .)*} $""") + + doAssert "abbb".match(peg"{a} {b} $2 $^1") + doAssert "abBA".match(peg"{a} {b} i$2 i$^2") + + doAssert "abba".match(peg"{a} {b} $^1 {} $^1") + + block: + let grammar = peg""" +program <- {''} stmt* $ +stmt <- call / block +call <- 'call()' EOL +EOL <- \n / $ +block <- 'block:' \n indBody +indBody <- {$^1 ' '+} stmt ($^1 stmt)* {} +""" + let program = """ +call() +block: + block: + call() + call() + call() +call() +""" + var c: Captures + doAssert program.len == program.rawMatch(grammar, 0, c) + doAssert c.ml == 1 + + block: + # bug #21632 + + let p = peg""" + atext <- \w / \d + """ + + doAssert "a".match(p) + doAssert "1".match(p) + + pegsTest() + static: + pegsTest() diff --git a/tests/stdlib/tposix.nim b/tests/stdlib/tposix.nim index 14f1fd6e2..060482229 100644 --- a/tests/stdlib/tposix.nim +++ b/tests/stdlib/tposix.nim @@ -1,5 +1,6 @@ discard """ -outputsub: "" + matrix: "--mm:refc; --mm:orc" + disabled: windows """ # Test Posix interface @@ -7,6 +8,7 @@ outputsub: "" when not defined(windows): import posix + import std/[assertions, syncio] var u: Utsname @@ -17,3 +19,70 @@ when not defined(windows): writeLine(stdout, u.nodename) writeLine(stdout, u.release) writeLine(stdout, u.machine) + + when not (defined(nintendoswitch) or defined(macos) or defined(macosx)): + block: + type Message = object + value: int + + const MQ_PATH: cstring = "/top_level_file" + const MQ_PRIORITY: cuint = 170 + const MQ_MESSAGE_SIZE: csize_t = csize_t(sizeof(Message)) + + let mqd_a: posix.MqAttr = MqAttr(mq_maxmsg: 10, mq_msgsize: clong(MQ_MESSAGE_SIZE)) + let writable: posix.Mqd = posix.mq_open( + MQ_PATH, + posix.O_CREAT or posix.O_WRONLY or posix.O_NONBLOCK, + posix.S_IRWXU, + addr(mqd_a) + ) + let readable: posix.Mqd = posix.mq_open( + MQ_PATH, + posix.O_RDONLY or posix.O_NONBLOCK, + posix.S_IRWXU, + addr(mqd_a) + ) + + let sent: Message = Message(value: 88) + block: + let success: int = writable.mq_send( + cast[cstring](sent.addr), + MQ_MESSAGE_SIZE, + MQ_PRIORITY + ) + doAssert success == 0, $success + + block: + var buffer: Message + var priority: cuint + let bytesRead: int = readable.mq_receive( + cast[cstring](buffer.addr), + MQ_MESSAGE_SIZE, + priority + ) + doAssert buffer == sent + doAssert bytesRead == int(MQ_MESSAGE_SIZE) + + block: + var rl: RLimit + var res = getrlimit(RLIMIT_STACK, rl) + doAssert res == 0 + + # save old value + let oldrlim = rl.rlim_cur + + # set new value + rl.rlim_cur = rl.rlim_max - 1 + res = setrlimit(RLIMIT_STACK, rl) + doAssert res == 0 + + # get new value + var rl1: RLimit + res = getrlimit(RLIMIT_STACK, rl1) + doAssert res == 0 + doAssert rl1.rlim_cur == rl.rlim_max - 1 + + # restore old value + rl.rlim_cur = oldrlim + res = setrlimit(RLIMIT_STACK, rl) + doAssert res == 0 diff --git a/tests/stdlib/tprelude.nim b/tests/stdlib/tprelude.nim new file mode 100644 index 000000000..47f46b511 --- /dev/null +++ b/tests/stdlib/tprelude.nim @@ -0,0 +1,16 @@ +discard """ + targets: "c js" + matrix: "; -d:nimTestTpreludeCase1" +""" + +when defined nimTestTpreludeCase1: + include std/prelude +else: + include prelude + +import std/assertions + +template main() = + doAssert toSeq(1..3) == @[1,2,3] +static: main() +main() diff --git a/tests/stdlib/tquit.nim b/tests/stdlib/tquit.nim deleted file mode 100644 index 1f9283ec4..000000000 --- a/tests/stdlib/tquit.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ -output: ''' -just exiting... -''' -joinable: false -""" - -# Test the new beforeQuit variable: - -proc myExit() {.noconv.} = - write(stdout, "just exiting...\n") - -addQuitProc(myExit) diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim new file mode 100644 index 000000000..eb32f7757 --- /dev/null +++ b/tests/stdlib/trandom.nim @@ -0,0 +1,310 @@ +discard """ + joinable: false # to avoid messing with global rand state + matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on" +""" +import std/[assertions, formatfloat] +import std/[random, math, stats, sets, tables] +import std/private/jsutils +when not defined(js): + import std/os + +randomize(233) + +proc main() = + var occur: array[1000, int] + + for i in 0..100_000: + let x = rand(high(occur)) + inc occur[x] + + doAssert max(occur) <= 140 and min(occur) >= 60 # gives some slack + + var a = [0, 1] + shuffle(a) + doAssert a in [[0,1], [1,0]] + + doAssert rand(0) == 0 + when not defined(nimscript): + doAssert sample("a") == 'a' + + when compileOption("rangeChecks") and not defined(nimscript): + doAssertRaises(RangeDefect): + discard rand(-1) + + doAssertRaises(RangeDefect): + discard rand(-1.0) + + # don't use causes integer overflow + doAssert compiles(rand[int](low(int) .. high(int))) + +main() + +block: + when not defined(js): + doAssert almostEqual(rand(12.5), 7.355175342026979) + doAssert almostEqual(rand(2233.3322), 499.342386778917) + + type DiceRoll = range[0..6] + when not defined(js): + doAssert rand(DiceRoll).int == 3 + elif compileOption("jsbigint64"): + doAssert rand(DiceRoll).int == 1 + else: + doAssert rand(DiceRoll).int == 6 + +var rs: RunningStat +for j in 1..5: + for i in 1 .. 100_000: + rs.push(gauss()) + doAssert abs(rs.mean-0) < 0.08, $rs.mean + doAssert abs(rs.standardDeviation()-1.0) < 0.1 + let bounds = [3.5, 5.0] + for a in [rs.max, -rs.min]: + doAssert a >= bounds[0] and a <= bounds[1] + rs.clear() + +block: + type DiceRoll = range[3..6] + var flag = false + for i in 0..<100: + if rand(5.DiceRoll) < 3: + flag = true + doAssert flag # because of: rand(max: int): int + + +block: # random int + block: # there might be some randomness + var set = initHashSet[int](128) + + for i in 1..1000: + incl(set, rand(high(int))) + doAssert len(set) == 1000 + + block: # single number bounds work + var rand: int + for i in 1..1000: + rand = rand(1000) + doAssert rand <= 1000 + doAssert rand >= 0 + + block: # slice bounds work + var rand: int + for i in 1..1000: + rand = rand(100..1000) + doAssert rand <= 1000 + doAssert rand >= 100 + + block: # again gives new numbers + var rand1 = rand(1000000) + when not (defined(js) or defined(nimscript)): + os.sleep(200) + + var rand2 = rand(1000000) + doAssert rand1 != rand2 + +block: # random float + block: # there might be some randomness + var set = initHashSet[float](128) + + for i in 1..100: + incl(set, rand(1.0)) + doAssert len(set) == 100 + + block: # single number bounds work + var rand: float + for i in 1..1000: + rand = rand(1000.0) + doAssert rand <= 1000.0 + doAssert rand >= 0.0 + + block: # slice bounds work + var rand: float + for i in 1..1000: + rand = rand(100.0..1000.0) + doAssert rand <= 1000.0 + doAssert rand >= 100.0 + + block: # again gives new numbers + var rand1: float = rand(1000000.0) + when not (defined(js) or defined(nimscript)): + os.sleep(200) + + var rand2: float = rand(1000000.0) + doAssert rand1 != rand2 + +block: # random sample + block: # "non-uniform array sample unnormalized int CDF + let values = [10, 20, 30, 40, 50] # values + let counts = [4, 3, 2, 1, 0] # weights aka unnormalized probabilities + var histo = initCountTable[int]() + let cdf = counts.cumsummed # unnormalized CDF + for i in 0 ..< 5000: + histo.inc(sample(values, cdf)) + doAssert histo.len == 4 # number of non-zero in `counts` + # Any one bin is a binomial random var for n samples, each with prob p of + # adding a count to k; E[k]=p*n, Var k=p*(1-p)*n, approximately Normal for + # big n. So, P(abs(k - p*n)/sqrt(p*(1-p)*n))>3.0) =~ 0.0027, while + # P(wholeTestFails) =~ 1 - P(binPasses)^4 =~ 1 - (1-0.0027)^4 =~ 0.01. + for i, c in counts: + if c == 0: + doAssert values[i] notin histo + continue + let p = float(c) / float(cdf[^1]) + let n = 5000.0 + let expected = p * n + let stdDev = sqrt(n * p * (1.0 - p)) + doAssert abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev + + block: # non-uniform array sample normalized float CDF + let values = [10, 20, 30, 40, 50] # values + let counts = [0.4, 0.3, 0.2, 0.1, 0] # probabilities + var histo = initCountTable[int]() + let cdf = counts.cumsummed # normalized CDF + for i in 0 ..< 5000: + histo.inc(sample(values, cdf)) + doAssert histo.len == 4 # number of non-zero in ``counts`` + for i, c in counts: + if c == 0: + doAssert values[i] notin histo + continue + let p = float(c) / float(cdf[^1]) + let n = 5000.0 + let expected = p * n + let stdDev = sqrt(n * p * (1.0 - p)) + # NOTE: like unnormalized int CDF test, P(wholeTestFails) =~ 0.01. + doAssert abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev + +block: + # 0 is a valid seed + var r = initRand(0) + doAssert r.rand(1.0) != r.rand(1.0) + r = initRand(10) + doAssert r.rand(1.0) != r.rand(1.0) + # changing the seed changes the sequence + var r1 = initRand(123) + var r2 = initRand(124) + doAssert r1.rand(1.0) != r2.rand(1.0) + +block: # bug #17467 + let n = 1000 + for i in -n .. n: + var r = initRand(i) + let x = r.rand(1.0) + doAssert x > 1e-4, $(x, i) + # This used to fail for each i in 0..<26844, i.e. the 1st produced value + # was predictable and < 1e-4, skewing distributions. + +block: # bug #16360, Natural overload + var r = initRand() + template test(a) = + let a2 = a + block: + let a3 = r.rand(a2) + doAssert a3 <= a2 + doAssert a3.type is a2.type + block: + let a3 = rand(a2) + doAssert a3 <= a2 + doAssert a3.type is a2.type + test int.high + test int.high - 1 + test int.high - 2 + test 0 + +block: # same as above but use slice overload + var r = initRand() + template test[T](a: T) = + let a2: T = a + block: + let a3 = r.rand(T(0) .. a2) + doAssert a3 <= a2 + doAssert a3.type is a2.type + block: + let a3 = rand(T(0) .. a2) + doAssert a3 <= a2 + doAssert a3.type is a2.type + test cast[uint](int.high) + test cast[uint](int.high) + 1 + whenJsNoBigInt64: discard + do: + test uint64.high + test uint64.high - 1 + test uint.high - 2 + test uint.high - 1 + test uint.high + test int.high + test int.high - 1 + test int.high - 2 + test 0 + test 0'u + test 0'u64 + +block: # bug #16296 + var r = initRand() + template test(x) = + let a2 = x + let a3 = r.rand(a2) + doAssert a3 <= a2.b + doAssert a3 >= a2.a + doAssert a3.type is a2.a.type + test(-2 .. int.high-1) + test(int.low .. int.high) + test(int.low+1 .. int.high) + test(int.low .. int.high-1) + test(int.low .. 0) + test(int.low .. -1) + test(int.low .. 1) + test(int64.low .. 1'i64) + test(10'u64 .. uint64.high) + +block: # bug #17670 + type UInt48 = range[0'u64..2'u64^48-1] + let x = rand(UInt48) + doAssert x is UInt48 + +block: # bug #17898 + # Checks whether `initRand()` generates unique states. + # size should be 2^64, but we don't have time and space. + + # Disable this test for js until js gets proper skipRandomNumbers. + when not defined(js): + const size = 1000 + var + rands: array[size, Rand] + randSet: HashSet[Rand] + for i in 0..<size: + rands[i] = initRand() + randSet.incl rands[i] + + doAssert randSet.len == size + + # Checks random number sequences overlapping. + const numRepeat = 100 + for i in 0..<size: + for j in 0..<numRepeat: + discard rands[i].next + doAssert rands[i] notin randSet + +block: # bug #22360 + const size = 1000 + var fc = 0 + var tc = 0 + + for _ in 1..size: + let s = rand(bool) + + if s: + inc tc + else: + inc fc + + when defined(js) and not compileOption("jsbigint64"): + doAssert (tc, fc) == (515, 485), $(tc, fc) + else: + doAssert (tc, fc) == (510, 490), $(tc, fc) + +block: + when defined(js) and not compileOption("jsbigint64"): + doAssert rand(int32.high) == 335507522 + else: + doAssert rand(int32.high) == 607539621 diff --git a/tests/stdlib/trat_float.nim b/tests/stdlib/trat_float.nim new file mode 100644 index 000000000..663973bf9 --- /dev/null +++ b/tests/stdlib/trat_float.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: '''type mismatch: got''' + file: "trat_float.nim" + line: "9,19" +""" +import rationals +var + # this fails - no floats as num or den + r = initRational(1.0'f, 1.0'f) diff --git a/tests/stdlib/trat_init.nim b/tests/stdlib/trat_init.nim new file mode 100644 index 000000000..2be0c0099 --- /dev/null +++ b/tests/stdlib/trat_init.nim @@ -0,0 +1,14 @@ +discard """ + output: '''true''' +""" +import rationals +var + z = Rational[int](num: 0, den: 1) + o = initRational(num=1, den=1) + a = initRational(1, 2) + +try: + var + r = initRational(1, 0) # this fails - no zero denominator +except AssertionDefect: + echo "true" diff --git a/tests/stdlib/trationals.nim b/tests/stdlib/trationals.nim new file mode 100644 index 000000000..22d7f5c2d --- /dev/null +++ b/tests/stdlib/trationals.nim @@ -0,0 +1,117 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/[rationals, math] +import std/assertions + +template main() = + var + z = Rational[int](num: 0, den: 1) + o = initRational(num = 1, den = 1) + a = initRational(1, 2) + u = 3u // 2 + b = -1 // -2 + m1 = -1 // 1 + tt = 10 // 2 + + doAssert a == a + doAssert a - a == z + doAssert a + b == o + doAssert a / b == o + doAssert a * b == 1 // 4 + doAssert 3 / a == 6 // 1 + doAssert a / 3 == 1 // 6 + doAssert tt * z == z + doAssert 10 * a == tt + doAssert a * 10 == tt + doAssert tt / 10 == a + doAssert a - m1 == 3 // 2 + doAssert a + m1 == -1 // 2 + doAssert m1 + tt == 16 // 4 + doAssert m1 - tt == 6 // -1 + + doAssert z < o + doAssert z <= o + doAssert z == z + doAssert cmp(z, o) < 0 + doAssert cmp(o, z) > 0 + + doAssert o == o + doAssert o >= o + doAssert not(o > o) + doAssert cmp(o, o) == 0 + doAssert cmp(z, z) == 0 + doAssert hash(o) == hash(o) + + doAssert a == b + doAssert a >= b + doAssert not(b > a) + doAssert cmp(a, b) == 0 + doAssert hash(a) == hash(b) + + var x = 1 // 3 + + x *= 5 // 1 + doAssert x == 5 // 3 + x += 2 // 9 + doAssert x == 17 // 9 + x -= 9 // 18 + doAssert x == 25 // 18 + x /= 1 // 2 + doAssert x == 50 // 18 + + var y = 1 // 3 + + y *= 4 + doAssert y == 4 // 3 + y += 5 + doAssert y == 19 // 3 + y -= 2 + doAssert y == 13 // 3 + y /= 9 + doAssert y == 13 // 27 + + doAssert toRational(5) == 5 // 1 + doAssert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 + doAssert toInt(z) == 0 + + when sizeof(int) == 8: + doAssert toRational(0.98765432) == 2111111029 // 2137499919 + doAssert toRational(PI) == 817696623 // 260280919 + when sizeof(int) == 4: + doAssert toRational(0.98765432) == 80 // 81 + doAssert toRational(PI) == 355 // 113 + + doAssert toRational(0.1) == 1 // 10 + doAssert toRational(0.9) == 9 // 10 + + doAssert toRational(0.0) == 0 // 1 + doAssert toRational(-0.25) == 1 // -4 + doAssert toRational(3.2) == 16 // 5 + doAssert toRational(0.33) == 33 // 100 + doAssert toRational(0.22) == 11 // 50 + doAssert toRational(10.0) == 10 // 1 + + doAssert (1 // 1) div (3 // 10) == 3 + doAssert (-1 // 1) div (3 // 10) == -3 + doAssert (3 // 10) mod (1 // 1) == 3 // 10 + doAssert (-3 // 10) mod (1 // 1) == -3 // 10 + doAssert floorDiv(1 // 1, 3 // 10) == 3 + doAssert floorDiv(-1 // 1, 3 // 10) == -4 + doAssert floorMod(3 // 10, 1 // 1) == 3 // 10 + doAssert floorMod(-3 // 10, 1 // 1) == 7 // 10 + + when sizeof(int) == 8: + doAssert almostEqual(PI.toRational.toFloat, PI) + + # unsigned + doAssert u == u + doAssert u + u == 3u // 1 + doAssert 3u.toRational - u == u + doAssert u * 2 == 3u // 1 + + + +static: main() +main() diff --git a/tests/stdlib/tre.nim b/tests/stdlib/tre.nim index ea1b5af32..39637434d 100644 --- a/tests/stdlib/tre.nim +++ b/tests/stdlib/tre.nim @@ -1,4 +1,9 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + import std/re +import std/assertions proc testAll() = doAssert match("(a b c)", rex"\( .* \)") @@ -99,10 +104,19 @@ proc testAll() = accum.add($x) doAssert(accum == @["a","b","c"]) - block: - # bug #9306 + block: # bug #9306 doAssert replace("bar", re"^", "foo") == "foobar" - doAssert replace("foo", re"", "-") == "-foo" doAssert replace("foo", re"$", "bar") == "foobar" + + block: # bug #9437 + doAssert replace("foo", re"", "-") == "-f-o-o-" + doAssert replace("ooo", re"o", "-") == "---" + + block: # bug #14468 + accum = @[] + for word in split("this is an example", re"\b"): + accum.add(word) + doAssert(accum == @["this", " ", "is", " ", "an", " ", "example"]) + testAll() diff --git a/tests/stdlib/treadln.nim b/tests/stdlib/treadln.nim new file mode 100644 index 000000000..4a070e848 --- /dev/null +++ b/tests/stdlib/treadln.nim @@ -0,0 +1,23 @@ + +discard """ +output: ''' +test the improved readline handling that does not care whether its +Macintosh, Unix or Windows text format. +''' +""" + +import std/syncio + +# test the improved readline handling that does not care whether its +# Macintosh, Unix or Windows text format. + +var + inp: File + line: string + +if open(inp, "tests/stdlib/treadln.nim"): + while not endOfFile(inp): + line = readLine(inp) + if line.len >= 2 and line[0] == '#' and line[1] == ' ': + echo line[2..^1] + close(inp) diff --git a/tests/stdlib/tregex.nim b/tests/stdlib/tregex.nim index 21f4e6743..9dd66cd60 100644 --- a/tests/stdlib/tregex.nim +++ b/tests/stdlib/tregex.nim @@ -1,5 +1,6 @@ discard """ output: "key: keyAYes!" + matrix: "--mm:refc; --mm:orc" """ # Test the new regular expression module # which is based on the PCRE library @@ -11,7 +12,7 @@ when defined(powerpc64): else: import re - + import std/syncio if "keyA = valueA" =~ re"\s*(\w+)\s*\=\s*(\w+)": write(stdout, "key: ", matches[0]) elif "# comment!" =~ re.re"\s*(\#.*)": diff --git a/tests/stdlib/tregistry.nim b/tests/stdlib/tregistry.nim new file mode 100644 index 000000000..25aed8df8 --- /dev/null +++ b/tests/stdlib/tregistry.nim @@ -0,0 +1,16 @@ +discard """ + disabled: "unix" + matrix: "--mm:refc; --mm:orc" +""" + +when defined(windows): + import std/registry + import std/assertions + + block: # bug #14010 + let path = "Environment" + let key = "D20210328T202842_key" + let val = "D20210328T202842_val" + let handle = HKEY_CURRENT_USER + setUnicodeValue("Environment", key, val, handle) + doAssert getUnicodeValue(path, key, handle) == val diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index c1941bd38..3956b98f9 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -1,36 +1,328 @@ discard """ - output: '''{a, b}{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'} -[1, 2, 3, 4, 5, 6]''' + targets: "c cpp js" + matrix: "--mm:refc;--mm:arc" """ -type - TEnum = enum - a, b - -var val = {a, b} -stdout.write(repr(val)) -stdout.writeLine(repr({'a'..'z', 'A'..'Z'})) - -type - TObj {.pure, inheritable.} = object - data: int - TFoo = ref object of TObj - d2: float -var foo: TFoo -new(foo) - -when false: - # cannot capture this output as it contains a memory address :-/ - echo foo.repr -#var testseq: seq[string] = @[ -# "a", "b", "c", "d", "e" -#] -#echo(repr(testseq)) - -# bug #7878 -proc test(variable: var openarray[int]) = - echo repr(variable) - -var arr = [1, 2, 3, 4, 5, 6] - -test(arr) +# if excessive, could remove 'cpp' from targets + +from strutils import endsWith, contains, strip +from std/macros import newLit +import std/assertions + +macro deb(a): string = newLit a.repr.strip +macro debTyped(a: typed): string = newLit a.repr.strip + +template main() = + doAssert repr({3,5}) == "{3, 5}" + + block: + type TEnum = enum a, b + var val = {a, b} + when nimvm: + discard + #[ + # BUG: + {0, 1} + {97..99, 65..67} + ]# + else: + doAssert repr(val) == "{a, b}" + doAssert repr({'a'..'c', 'A'..'C'}) == "{'A', 'B', 'C', 'a', 'b', 'c'}" + + type + TObj {.pure, inheritable.} = object + data: int + TFoo = ref object of TObj + d2: float + var foo: TFoo + new(foo) + + #[ + BUG: + --gc:arc returns `"abc"` + regular gc returns with address, e.g. 0x1068aae60"abc", but only + for c,cpp backends (not js, vm) + ]# + block: + doAssert repr("abc").endsWith "\"abc\"" + var b: cstring = "def" + doAssert repr(b).endsWith "\"def\"" + + block: + var c = @[1,2] + when nimvm: + discard # BUG: this shows [1, 2] instead of @[1, 2] + else: + # BUG (already mentioned above): some backends / gc show address, others don't + doAssert repr(c).endsWith "@[1, 2]" + + let d = @["foo", "bar"] + let s = repr(d) + # depending on backend/gc, we get 0x106a1c350@[0x106a1c390"foo", 0x106a1c3c0"bar"] + doAssert "\"foo\"," in s + + var arr = [1, 2, 3] + doAssert repr(arr) == "[1, 2, 3]" + + block: # bug #7878 + proc reprOpenarray(variable: var openArray[int]): string = repr(variable) + when defined(js): discard # BUG: doesn't work + else: + doAssert reprOpenarray(arr) == "[1, 2, 3]" + + block: # bug #17292 repr with `do` + template foo(a, b, c, d) = discard + block: + let a = deb: + foo(1, 2, 3, 4) + doAssert a == "foo(1, 2, 3, 4)" + block: + let a = deb: + foo(1, 2, 3): 4 + doAssert a == """ +foo(1, 2, 3): + 4""" + + block: + let a = deb: + foo(1, 2): 3 + do: 4 + doAssert a == """ +foo(1, 2): + 3 +do: + 4""" + + block: + let a = deb: + foo(1): 3 + do: 3 + do: 4 + doAssert a == """ +foo(1): + 3 +do: + 3 +do: + 4""" + + block: + let a = deb: + foo(1): + 3 + do: + discard + 3 + do: + discard + 4 + + doAssert a == """ +foo(1): + 3 +do: + discard + 3 +do: + discard + 4""" + + block: + let a = deb: + foo: 1 + do: 2 + do: 3 + do: 4 + doAssert a == """ +foo: + 1 +do: + 2 +do: + 3 +do: + 4""" + + block: # bug #17292 repr with `(discard)` (`discard` would result in illegal code) + let a = deb: + let f {.inject.} = () => (discard) + doAssert a == """ +let f {.inject.} = () => + (discard )""" + + let a2 = deb: + block: + discard + discard + + block: + when true: discard + + # let a = b => discard # illegal + discard b => (discard) # legal + + block: + return + doAssert a2 == """ +block: + discard +discard +block: + when true: + discard +discard b => + (discard ) +block: + return""" + + block: # bug #17292 (bug 4) + let a = deb: + proc `=destroy`() = discard + proc `'foo`(): int = discard + proc `foo bar baz`(): int = discard + let a2 = """ +proc `=destroy`() = + discard + +proc `'foo`(): int = + discard + +proc `foo bar baz`(): int = + discard""" + doAssert a2 == a + + block: # setters: `foo=` + let a = deb: + proc `foo=`() = discard + doAssert a == """ +proc `foo=`() = + discard""" + + block: # bug #14850 + block: + let a = deb: + template bar(): untyped = + foo1: + discard + 4 + foo2(1): + discard + 4 + foo3(1): + discard + 4 + do: 1 + do: 2 + x.add foo4 + x.add: foo5: 3 + x.add foo6 do: 4 + a.add(foo7 do: + echo "baz" + 4) + + doAssert a == """ +template bar(): untyped = + foo1: + discard + 4 + foo2(1): + discard + 4 + foo3(1): + discard + 4 + do: + 1 + do: + 2 + x.add foo4 + x.add: + foo5: + 3 + x.add foo6 do: + 4 + a.add(foo7 do: + echo "baz" + 4)""" + + block: # one liner doc comments + let a1 = deb: + func fn1(): int = 1 ## comment + func fn2(): int = 1 + ## comment + let a2 = debTyped: + func fn1(): int = 1 ## comment + func fn2(): int = 1 + ## comment + doAssert a1 == """ +func fn1(): int = + ## comment + 1 + +func fn2(): int = + ## comment + 1""" + doAssert a2 == """ +func fn1(): int = + ## comment + result = 1 + +func fn2(): int = + ## comment + result = 1""" + + block: # block calls + let a = deb: + foo(a, b, (c, d)): + e + f + do: g + of h: i + elif j: k + except m: n + do () -> u: v + finally: o + + a + b: + c + d + do: + e + f + else: g + + *a: b + do: c + + doAssert a == """foo(a, b, (c, d)): + e + f +do: + g +of h: + i +elif j: + k +except m: + n +do -> u: + v +finally: + o +a + b: + c + d +do: + e + f +else: + g +*a: + b +do: + c""" + + doAssert repr(1..2) == "1 .. 2" + +static: main() +main() diff --git a/tests/stdlib/trlocks.nim b/tests/stdlib/trlocks.nim new file mode 100644 index 000000000..135d9b028 --- /dev/null +++ b/tests/stdlib/trlocks.nim @@ -0,0 +1,14 @@ +discard """ + action: "compile" + # Disallow joining to ensure it can compile in isolation. + # See #15584 + joinable: false + cmd: "nim $target --threads:on $options $file" +""" + +# bugfix #15584 + +import rlocks + +var r: RLock +r.initRLock() diff --git a/tests/stdlib/tropes.nim b/tests/stdlib/tropes.nim new file mode 100644 index 000000000..eb0edc364 --- /dev/null +++ b/tests/stdlib/tropes.nim @@ -0,0 +1,104 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + +import std/ropes +import std/assertions + +template main() = + block: + let r: Rope = nil + doAssert r[0] == '\0' + doAssert $r == "" + + block: + var + r1 = rope("Hello, ") + r2 = rope("Nim-Lang") + + let r = r1 & r2 + let s = $r + doAssert s == "Hello, Nim-Lang" + for i in 0 ..< r.len: + doAssert r[i] == s[i] + + doAssert r[66] == '\0' + + block: + let r = rope("Hello, Nim-Lang") + + let s = $r + doAssert s == "Hello, Nim-Lang" + for i in 0 ..< r.len: + doAssert r[i] == s[i] + + doAssert r[66] == '\0' + + block: + var r: Rope + r.add rope("Nim ") + r.add rope("is ") + r.add rope("a ") + r.add rope("great ") + r.add rope("language") + + let s = $r + doAssert s == "Nim is a great language" + for i in 0 ..< r.len: + doAssert r[i] == s[i] + + doAssert r[66] == '\0' + + block: + var r: Rope + r.add rope("My Conquest") + r.add rope(" is ") + r.add rope("the Sea of Stars") + + let s = $r + doAssert s == "My Conquest is the Sea of Stars" + for i in 0 ..< r.len: + doAssert r[i] == s[i] + + doAssert r[66] == '\0' + + block: + var r: Rope + r.add rope("My Conquest") + r.add rope(" is ") + r.add rope("the Sea of Stars") + + doAssert $r == "My Conquest is the Sea of Stars" + + var i: int + for item in r: + doAssert r[i] == item + inc i + + doAssert r[66] == '\0' + + block: + let r1 = "$1 $2 $3" % [rope("Nim"), rope("is"), rope("a great language")] + doAssert $r1 == "Nim is a great language" + + let r2 = "$# $# $#" % [rope("Nim"), rope("is"), rope("a great language")] + doAssert $r2 == "Nim is a great language" + + block: # `[]` + let r1 = rope("Hello, Nim!") + + doAssert r1[-2] == '\0' + doAssert r1[0] == 'H' + doAssert r1[7] == 'N' + doAssert r1[22] == '\0' + + let r2 = rope("Hello") & rope(", Nim!") + + doAssert r2[-2] == '\0' + doAssert r2[0] == 'H' + doAssert r2[7] == 'N' + doAssert r2[22] == '\0' + +static: main() +main() diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 797010a22..ceab34bc9 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -1,21 +1,1565 @@ discard """ output: ''' + +[Suite] RST parsing + +[Suite] RST tables + +[Suite] RST indentation + +[Suite] Markdown indentation + +[Suite] Warnings + [Suite] RST include directive + +[Suite] RST escaping + +[Suite] RST inline markup + +[Suite] Misc isssues ''' +matrix: "--mm:refc; --mm:orc" """ # tests for rst module -import ../../lib/packages/docutils/rstgen -import ../../lib/packages/docutils/rst -import unittest +import ../../lib/packages/docutils/[rstgen, rst, rstast] +import unittest, strutils +import std/private/miscdollars import os +import std/[assertions, syncio] + +const preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} +# legacy nimforum / old default mode: +const preferRst = {roSupportMarkdown, roNimFile, roSandboxDisabled} +const pureRst = {roNimFile, roSandboxDisabled} + +proc toAst(input: string, + rstOptions: RstParseOptions = preferMarkdown, + error: ref string = nil, + warnings: ref seq[string] = nil): string = + ## If `error` is nil then no errors should be generated. + ## The same goes for `warnings`. + proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind, + arg: string) = + let mc = msgkind.whichMsgClass + let a = $msgkind % arg + var message: string + toLocation(message, filename, line, col + ColRstOffset) + message.add " $1: $2" % [$mc, a] + if mc == mcError: + if error == nil: + raise newException(EParseError, "[unexpected error] " & message) + error[] = message + # we check only first error because subsequent ones may be meaningless + raise newException(EParseError, "") + else: + doAssert warnings != nil, "unexpected RST warning '" & message & "'" + warnings[].add message + try: + const filen = "input" + + proc myFindFile(filename: string): string = + # we don't find any files in online mode: + result = "" + + var (rst, _, _) = rstParse(input, filen, line=LineRstInit, column=ColRstInit, + rstOptions, myFindFile, nil, testMsgHandler) + result = treeRepr(rst) + except EParseError as e: + if e.msg != "": + result = e.msg + +suite "RST parsing": + test "Standalone punctuation is not parsed as heading overlines": + check(dedent""" + Paragraph + + !""".toAst == + dedent""" + rnInner + rnParagraph + rnLeaf 'Paragraph' + rnParagraph + rnLeaf '!' + """) + + check(dedent""" + Paragraph1 + + ... + + Paragraph2""".toAst == + dedent""" + rnInner + rnParagraph + rnLeaf 'Paragraph1' + rnParagraph + rnLeaf '...' + rnParagraph + rnLeaf 'Paragraph2' + """) + + check(dedent""" + --- + Paragraph""".toAst == + dedent""" + rnInner + rnLeaf '---' + rnLeaf ' ' + rnLeaf 'Paragraph' + """) + + test "References are whitespace-neutral and case-insensitive": + # refname is 'lexical-analysis', the same for all the 3 variants: + check(dedent""" + Lexical Analysis + ================ + + Ref. `Lexical Analysis`_ or `Lexical analysis`_ or `lexical analysis`_. + """.toAst == + dedent""" + rnInner + rnHeadline level=1 anchor='lexical-analysis' + rnLeaf 'Lexical' + rnLeaf ' ' + rnLeaf 'Analysis' + rnParagraph + rnLeaf 'Ref' + rnLeaf '.' + rnLeaf ' ' + rnInternalRef + rnInner + rnLeaf 'Lexical' + rnLeaf ' ' + rnLeaf 'Analysis' + rnLeaf 'lexical-analysis' + rnLeaf ' ' + rnLeaf 'or' + rnLeaf ' ' + rnInternalRef + rnInner + rnLeaf 'Lexical' + rnLeaf ' ' + rnLeaf 'analysis' + rnLeaf 'lexical-analysis' + rnLeaf ' ' + rnLeaf 'or' + rnLeaf ' ' + rnInternalRef + rnInner + rnLeaf 'lexical' + rnLeaf ' ' + rnLeaf 'analysis' + rnLeaf 'lexical-analysis' + rnLeaf '.' + rnLeaf ' ' + """) + + test "RST quoted literal blocks": + let expected = + dedent""" + rnInner + rnLeaf 'Paragraph' + rnLeaf ':' + rnLiteralBlock + rnLeaf '>x' + """ + + check(dedent""" + Paragraph:: + + >x""".toAst(rstOptions = preferRst) == expected) + + check(dedent""" + Paragraph:: + + >x""".toAst(rstOptions = preferRst) == expected) + + test "RST quoted literal blocks, :: at a separate line": + let expected = + dedent""" + rnInner + rnInner + rnLeaf 'Paragraph' + rnLiteralBlock + rnLeaf '>x + >>y' + """ + + check(dedent""" + Paragraph + + :: + + >x + >>y""".toAst(rstOptions = preferRst) == expected) + + check(dedent""" + Paragraph + + :: + + >x + >>y""".toAst(rstOptions = preferRst) == expected) + + test "Markdown quoted blocks": + check(dedent""" + Paragraph. + >x""".toAst == + dedent""" + rnInner + rnLeaf 'Paragraph' + rnLeaf '.' + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnLeaf 'x' + """) + + # bug #17987 + check(dedent""" + foo https://github.com/nim-lang/Nim/issues/8258 + + > bar""".toAst == + dedent""" + rnInner + rnInner + rnLeaf 'foo' + rnLeaf ' ' + rnStandaloneHyperlink + rnLeaf 'https://github.com/nim-lang/Nim/issues/8258' + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnLeaf 'bar' + """) + + let expected = dedent""" + rnInner + rnLeaf 'Paragraph' + rnLeaf '.' + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnLeaf 'x1' + rnLeaf ' ' + rnLeaf 'x2' + rnMarkdownBlockQuoteItem quotationDepth=2 + rnInner + rnLeaf 'y1' + rnLeaf ' ' + rnLeaf 'y2' + rnMarkdownBlockQuoteItem quotationDepth=1 + rnLeaf 'z' + """ + + check(dedent""" + Paragraph. + >x1 x2 + >>y1 y2 + >z""".toAst == expected) + + check(dedent""" + Paragraph. + > x1 x2 + >> y1 y2 + > z""".toAst == expected) + + check(dedent""" + >x + >y + >z""".toAst == + dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnLeaf 'x' + rnLeaf ' ' + rnLeaf 'y' + rnLeaf ' ' + rnLeaf 'z' + """) + + check(dedent""" + > z + > > >y + """.toAst == + dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnLeaf 'z' + rnMarkdownBlockQuoteItem quotationDepth=3 + rnLeaf 'y' + """) + + test "Markdown quoted blocks: lazy": + let expected = dedent""" + rnInner + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=2 + rnInner + rnLeaf 'x' + rnLeaf ' ' + rnLeaf 'continuation1' + rnLeaf ' ' + rnLeaf 'continuation2' + rnParagraph + rnLeaf 'newParagraph' + """ + check(dedent""" + >>x + continuation1 + continuation2 + + newParagraph""".toAst == expected) + + check(dedent""" + >> x + continuation1 + continuation2 + + newParagraph""".toAst == expected) + + # however mixing more than 1 non-lazy line and lazy one(s) splits quote + # in our parser, which appeared the easiest way to handle such cases: + var warnings = new seq[string] + check(dedent""" + >> x + >> continuation1 + continuation2 + + newParagraph""".toAst(warnings=warnings) == + dedent""" + rnInner + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=2 + rnLeaf 'x' + rnMarkdownBlockQuoteItem quotationDepth=2 + rnInner + rnLeaf 'continuation1' + rnLeaf ' ' + rnLeaf 'continuation2' + rnParagraph + rnLeaf 'newParagraph' + """) + check(warnings[] == @[ + "input(2, 1) Warning: RST style: two or more quoted lines " & + "are followed by unquoted line 3"]) + + test "Markdown quoted blocks: not lazy": + # here is where we deviate from CommonMark specification: 'bar' below is + # not considered as continuation of 2-level '>> foo' quote. + check(dedent""" + >>> foo + > bar + >> baz + """.toAst() == + dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=3 + rnLeaf 'foo' + rnMarkdownBlockQuoteItem quotationDepth=1 + rnLeaf 'bar' + rnMarkdownBlockQuoteItem quotationDepth=2 + rnLeaf 'baz' + """) + + + test "Markdown quoted blocks: inline markup works": + check(dedent""" + > hi **bold** text + """.toAst == dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnLeaf 'hi' + rnLeaf ' ' + rnStrongEmphasis + rnLeaf 'bold' + rnLeaf ' ' + rnLeaf 'text' + """) + + test "Markdown quoted blocks: blank line separator": + let expected = dedent""" + rnInner + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnLeaf 'x' + rnLeaf ' ' + rnLeaf 'y' + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnLeaf 'z' + rnLeaf ' ' + rnLeaf 't' + """ + check(dedent""" + >x + >y + + > z + > t""".toAst == expected) + + check(dedent""" + >x + y + + > z + t""".toAst == expected) + + test "Markdown quoted blocks: nested body blocks/elements work #1": + let expected = dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnBulletList + rnBulletItem + rnInner + rnLeaf 'x' + rnBulletItem + rnInner + rnLeaf 'y' + """ + + check(dedent""" + > - x + - y + """.toAst == expected) + + # TODO: if bug #17340 point 28 is resolved then this may work: + # check(dedent""" + # > - x + # - y + # """.toAst == expected) + + check(dedent""" + > - x + > - y + """.toAst == expected) + + check(dedent""" + > + > - x + > + > - y + > + """.toAst == expected) + + test "Markdown quoted blocks: nested body blocks/elements work #2": + let expected = dedent""" + rnAdmonition adType=note + [nil] + [nil] + rnDefList + rnDefItem + rnDefName + rnLeaf 'deflist' + rnLeaf ':' + rnDefBody + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=2 + rnInner + rnLeaf 'quote' + rnLeaf ' ' + rnLeaf 'continuation' + """ + + check(dedent""" + .. Note:: deflist: + >> quote + continuation + """.toAst(rstOptions = preferRst) == expected) + + check(dedent""" + .. Note:: + deflist: + >> quote + continuation + """.toAst(rstOptions = preferRst) == expected) + + check(dedent""" + .. Note:: + deflist: + >> quote + >> continuation + """.toAst(rstOptions = preferRst) == expected) + + # spaces are not significant between `>`: + check(dedent""" + .. Note:: + deflist: + > > quote + > > continuation + """.toAst(rstOptions = preferRst) == expected) + + test "Markdown quoted blocks: de-indent handled well": + check(dedent""" + > + > - x + > - y + > + > Paragraph. + """.toAst(rstOptions = preferRst) == dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnBlockQuote + rnBulletList + rnBulletItem + rnInner + rnLeaf 'x' + rnBulletItem + rnInner + rnLeaf 'y' + rnParagraph + rnLeaf 'Paragraph' + rnLeaf '.' + """) + + let expectCodeBlock = dedent""" + rnCodeBlock + [nil] + rnFieldList + rnField + rnFieldName + rnLeaf 'default-language' + rnFieldBody + rnLeaf 'Nim' + rnLiteralBlock + rnLeaf 'let a = 1 + ```' + """ + + test "Markdown footnotes": + # Testing also 1) correct order of manually-numbered and automatically- + # numbered footnotes; 2) no spaces between references (html & 3 below): + + check(dedent""" + Paragraph [^1] [^html-hyphen][^3] and [^latex] + + [^1]: footnote1 + + [^html-hyphen]: footnote2 + continuation2 + + [^latex]: footnote4 + + [^3]: footnote3 + continuation3 + """.toAst == + dedent""" + rnInner + rnInner + rnLeaf 'Paragraph' + rnLeaf ' ' + rnFootnoteRef + rnInner + rnLeaf '1' + rnLeaf 'footnote-1' + rnLeaf ' ' + rnFootnoteRef + rnInner + rnLeaf '2' + rnLeaf 'footnote-htmlminushyphen' + rnFootnoteRef + rnInner + rnLeaf '3' + rnLeaf 'footnote-3' + rnLeaf ' ' + rnLeaf 'and' + rnLeaf ' ' + rnFootnoteRef + rnInner + rnLeaf '4' + rnLeaf 'footnote-latex' + rnFootnoteGroup + rnFootnote anchor='footnote-1' + rnInner + rnLeaf '1' + rnLeaf 'footnote1' + rnFootnote anchor='footnote-htmlminushyphen' + rnInner + rnLeaf '2' + rnInner + rnLeaf 'footnote2' + rnLeaf ' ' + rnLeaf 'continuation2' + rnFootnote anchor='footnote-latex' + rnInner + rnLeaf '4' + rnLeaf 'footnote4' + rnFootnote anchor='footnote-3' + rnInner + rnLeaf '3' + rnInner + rnLeaf 'footnote3' + rnLeaf ' ' + rnLeaf 'continuation3' + """) + + test "Markdown code blocks with more > 3 backticks": + check(dedent""" + ```` + let a = 1 + ``` + ````""".toAst == expectCodeBlock) + + test "Markdown code blocks with ~~~": + check(dedent""" + ~~~ + let a = 1 + ``` + ~~~""".toAst == expectCodeBlock) + check(dedent""" + ~~~~~ + let a = 1 + ``` + ~~~~~""".toAst == expectCodeBlock) + + test "Markdown code blocks with Nim-specific arguments": + check(dedent""" + ```nim number-lines=1 test + let a = 1 + ```""".toAst == + dedent""" + rnCodeBlock + rnDirArg + rnLeaf 'nim' + rnFieldList + rnField + rnFieldName + rnLeaf 'number-lines' + rnFieldBody + rnLeaf '1' + rnField + rnFieldName + rnLeaf 'test' + rnFieldBody + rnLiteralBlock + rnLeaf 'let a = 1' + """) + + check(dedent""" + ```nim test = "nim c $1" number-lines = 1 + let a = 1 + ```""".toAst == + dedent""" + rnCodeBlock + rnDirArg + rnLeaf 'nim' + rnFieldList + rnField + rnFieldName + rnLeaf 'test' + rnFieldBody + rnLeaf '"nim c $1"' + rnField + rnFieldName + rnLeaf 'number-lines' + rnFieldBody + rnLeaf '1' + rnLiteralBlock + rnLeaf 'let a = 1' + """) + + test "additional indentation < 4 spaces is handled fine": + check(dedent""" + Indentation + + ```nim + let a = 1 + ```""".toAst == + dedent""" + rnInner + rnParagraph + rnLeaf 'Indentation' + rnParagraph + rnCodeBlock + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf ' let a = 1' + """) + # | | + # | \ indentation of exactly two spaces before 'let a = 1' + + test "no blank line is required before or after Markdown code block": + let inputBacktick = dedent""" + Some text + ``` + CodeBlock() + ``` + Other text""" + let inputTilde = dedent""" + Some text + ~~~~~~~~~ + CodeBlock() + ~~~~~~~~~ + Other text""" + let expected = dedent""" + rnInner + rnParagraph + rnLeaf 'Some' + rnLeaf ' ' + rnLeaf 'text' + rnParagraph + rnCodeBlock + [nil] + rnFieldList + rnField + rnFieldName + rnLeaf 'default-language' + rnFieldBody + rnLeaf 'Nim' + rnLiteralBlock + rnLeaf 'CodeBlock()' + rnLeaf ' ' + rnLeaf 'Other' + rnLeaf ' ' + rnLeaf 'text' + """ + check inputBacktick.toAst == expected + check inputTilde.toAst == expected + + test "option list has priority over definition list": + for opt in [preferMarkdown, preferRst]: + check(dedent""" + --defusages + file + -o set + """.toAst(rstOptions = opt) == + dedent""" + rnOptionList + rnOptionListItem order=1 + rnOptionGroup + rnLeaf '--' + rnLeaf 'defusages' + rnDescription + rnInner + rnLeaf 'file' + rnOptionListItem order=2 + rnOptionGroup + rnLeaf '-' + rnLeaf 'o' + rnDescription + rnLeaf 'set' + """) + + test "items of 1 option list can be separated by blank lines": + check(dedent""" + -a desc1 + + -b desc2 + """.toAst == + dedent""" + rnOptionList + rnOptionListItem order=1 + rnOptionGroup + rnLeaf '-' + rnLeaf 'a' + rnDescription + rnLeaf 'desc1' + rnOptionListItem order=2 + rnOptionGroup + rnLeaf '-' + rnLeaf 'b' + rnDescription + rnLeaf 'desc2' + """) + + test "definition list does not gobble up the following blocks": + check(dedent""" + defName + defBody + + -b desc2 + """.toAst(rstOptions = preferRst) == + dedent""" + rnInner + rnDefList + rnDefItem + rnDefName + rnLeaf 'defName' + rnDefBody + rnInner + rnLeaf 'defBody' + rnOptionList + rnOptionListItem order=1 + rnOptionGroup + rnLeaf '-' + rnLeaf 'b' + rnDescription + rnLeaf 'desc2' + """) + + test "definition lists work correctly with additional indentation in Markdown": + check(dedent""" + Paragraph: + -c desc1 + -b desc2 + """.toAst() == + dedent""" + rnInner + rnInner + rnLeaf 'Paragraph' + rnLeaf ':' + rnOptionList + rnOptionListItem order=1 + rnOptionGroup + rnLeaf '-' + rnLeaf 'c' + rnDescription + rnLeaf 'desc1' + rnOptionListItem order=2 + rnOptionGroup + rnLeaf '-' + rnLeaf 'b' + rnDescription + rnLeaf 'desc2' + """) + + test "RST comment": + check(dedent""" + .. comment1 + comment2 + someParagraph""".toAst == + dedent""" + rnLeaf 'someParagraph' + """) + + check(dedent""" + .. + comment1 + comment2 + someParagraph""".toAst == + dedent""" + rnLeaf 'someParagraph' + """) + + test "check that additional line right after .. ends comment": + check(dedent""" + .. + + notAcomment1 + notAcomment2 + someParagraph""".toAst(rstOptions = preferRst) == + dedent""" + rnInner + rnBlockQuote + rnInner + rnLeaf 'notAcomment1' + rnLeaf ' ' + rnLeaf 'notAcomment2' + rnParagraph + rnLeaf 'someParagraph' + """) + + test "check that additional line right after .. ends comment (Markdown mode)": + # in Markdown small indentation does not matter so this should + # just be split to 2 paragraphs. + check(dedent""" + .. + + notAcomment1 + notAcomment2 + someParagraph""".toAst == + dedent""" + rnInner + rnInner + rnLeaf 'notAcomment1' + rnLeaf ' ' + rnLeaf 'notAcomment2' + rnParagraph + rnLeaf 'someParagraph' + """) + + test "but blank lines after 2nd non-empty line don't end the comment": + check(dedent""" + .. + comment1 + + + comment2 + someParagraph""".toAst == + dedent""" + rnLeaf 'someParagraph' + """) + + test "using .. as separator b/w directives and block quotes": + check(dedent""" + .. note:: someNote + + .. + + someBlockQuote""".toAst(rstOptions = preferRst) == + dedent""" + rnInner + rnAdmonition adType=note + [nil] + [nil] + rnLeaf 'someNote' + rnBlockQuote + rnInner + rnLeaf 'someBlockQuote' + """) + + test "no redundant blank lines in literal blocks": + check(dedent""" + Check:: + + + code + + """.toAst(rstOptions = preferRst) == + dedent""" + rnInner + rnLeaf 'Check' + rnLeaf ':' + rnLiteralBlock + rnLeaf 'code' + """) + + test "Markdown indented code blocks": + check(dedent""" + See + + some code""".toAst == + dedent""" + rnInner + rnInner + rnLeaf 'See' + rnLiteralBlock + rnLeaf 'some code' + """) + + # not a code block -- no blank line before: + check(dedent""" + See + some code""".toAst == + dedent""" + rnInner + rnLeaf 'See' + rnLeaf ' ' + rnLeaf 'some' + rnLeaf ' ' + rnLeaf 'code' + """) + +suite "RST tables": + + test "formatting in tables works": + check( + dedent""" + ========= === + `build` `a` + ========= === + """.toAst == + dedent""" + rnTable colCount=2 + rnTableRow + rnTableDataCell + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'build' + rnTableDataCell + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'a' + """) + + test "tables with slightly overflowed cells cause an error (1)": + var error = new string + check( + dedent""" + ====== ====== + Inputs Output + ====== ====== + """.toAst(rstOptions = pureRst, error = error) == "") + check(error[] == "input(2, 2) Error: Illformed table: " & + "this word crosses table column from the right") + + # In nimforum compatibility mode & Markdown we raise a warning instead: + let expected = dedent""" + rnTable colCount=2 + rnTableRow + rnTableDataCell + rnLeaf 'Inputs' + rnTableDataCell + rnLeaf 'Output' + """ + for opt in [preferRst, preferMarkdown]: + var warnings = new seq[string] + + check( + dedent""" + ====== ====== + Inputs Output + ====== ====== + """.toAst(rstOptions = opt, warnings = warnings) == expected) + check(warnings[] == @[ + "input(2, 2) Warning: RST style: this word crosses table column from the right"]) + + test "tables with slightly overflowed cells cause an error (2)": + var error = new string + check("" == dedent""" + ===== ===== ====== + Input Output + ===== ===== ====== + False False False + ===== ===== ====== + """.toAst(rstOptions = pureRst, error = error)) + check(error[] == "input(2, 8) Error: Illformed table: " & + "this word crosses table column from the right") + + test "tables with slightly underflowed cells cause an error": + var error = new string + check("" == dedent""" + ===== ===== ====== + Input Output + ===== ===== ====== + False False False + ===== ===== ====== + """.toAst(rstOptions = pureRst, error = error)) + check(error[] == "input(2, 7) Error: Illformed table: " & + "this word crosses table column from the left") + + test "tables with unequal underlines should be reported (1)": + var error = new string + error[] = "none" + check("" == dedent""" + ===== ====== + Input Output + ===== ====== + False False + ===== ======= + """.toAst(rstOptions = pureRst, error = error)) + check(error[] == "input(5, 14) Error: Illformed table: " & + "end of table column #2 should end at position 13") + + test "tables with unequal underlines should be reported (2)": + var error = new string + check("" == dedent""" + ===== ====== + Input Output + ===== ======= + False False + ===== ====== + """.toAst(rstOptions = pureRst, error = error)) + check(error[] == "input(3, 14) Error: Illformed table: " & + "end of table column #2 should end at position 13") + + test "tables with empty first cells": + check( + dedent""" + = = = + x y z + t + = = = + """.toAst == + dedent""" + rnTable colCount=3 + rnTableRow + rnTableDataCell + rnLeaf 'x' + rnTableDataCell + rnInner + rnLeaf 'y' + rnLeaf ' ' + rnTableDataCell + rnInner + rnLeaf 'z' + rnLeaf ' ' + rnLeaf 't' + """) + + test "tables with spanning cells & separators": + check( + dedent""" + ===== ===== ====== + Inputs Output + ------------ ------ + A B A or B + ===== ===== ====== + False False False + True False True + ----- ----- ------ + False True True + True True True + ===== ===== ====== + """.toAst == + dedent""" + rnTable colCount=3 + rnTableRow + rnTableHeaderCell span=2 + rnLeaf 'Inputs' + rnTableHeaderCell span=1 + rnLeaf 'Output' + rnTableRow endsHeader + rnTableHeaderCell + rnLeaf 'A' + rnTableHeaderCell + rnLeaf 'B' + rnTableHeaderCell + rnInner + rnLeaf 'A' + rnLeaf ' ' + rnLeaf 'or' + rnLeaf ' ' + rnLeaf 'B' + rnTableRow + rnTableDataCell + rnLeaf 'False' + rnTableDataCell + rnLeaf 'False' + rnTableDataCell + rnLeaf 'False' + rnTableRow + rnTableDataCell span=1 + rnLeaf 'True' + rnTableDataCell span=1 + rnLeaf 'False' + rnTableDataCell span=1 + rnLeaf 'True' + rnTableRow + rnTableDataCell + rnLeaf 'False' + rnTableDataCell + rnLeaf 'True' + rnTableDataCell + rnLeaf 'True' + rnTableRow + rnTableDataCell + rnLeaf 'True' + rnTableDataCell + rnLeaf 'True' + rnTableDataCell + rnLeaf 'True' + """) + + test "tables with spanning cells with uneqal underlines cause an error": + var error = new string + check( + dedent""" + ===== ===== ====== + Inputs Output + ------------- ------ + A B A or B + ===== ===== ====== + """.toAst(error=error) == "") + check(error[] == "input(3, 1) Error: Illformed table: " & + "spanning underline does not match main table columns") + + let expTable = dedent""" + rnTable colCount=2 + rnTableRow + rnTableDataCell + rnLeaf 'Inputs' + rnTableDataCell + rnLeaf 'Output' + """ + + test "only tables with `=` columns specs are allowed (1)": + var warnings = new seq[string] + check( + dedent""" + ------ ------ + Inputs Output + ------ ------ + """.toAst(warnings=warnings) == + expTable) + check(warnings[] == + @["input(1, 1) Warning: RST style: " & + "only tables with `=` columns specification are allowed", + "input(3, 1) Warning: RST style: " & + "only tables with `=` columns specification are allowed"]) + + test "only tables with `=` columns specs are allowed (2)": + var warnings = new seq[string] + check( + dedent""" + ====== ====== + Inputs Output + ~~~~~~ ~~~~~~ + """.toAst(warnings=warnings) == + expTable) + check(warnings[] == + @["input(3, 1) Warning: RST style: "& + "only tables with `=` columns specification are allowed"]) + + +suite "RST indentation": + test "nested bullet lists": + let input = dedent """ + * - bullet1 + - bullet2 + * - bullet3 + - bullet4 + """ + let output = input.toAst + check(output == dedent""" + rnBulletList + rnBulletItem + rnBulletList + rnBulletItem + rnInner + rnLeaf 'bullet1' + rnBulletItem + rnInner + rnLeaf 'bullet2' + rnBulletItem + rnBulletList + rnBulletItem + rnInner + rnLeaf 'bullet3' + rnBulletItem + rnInner + rnLeaf 'bullet4' + """) + + test "nested markup blocks": + let input = dedent""" + #) .. Hint:: .. Error:: none + #) .. Warning:: term0 + Definition0 + #) some + paragraph1 + #) term1 + Definition1 + term2 + Definition2 + """ + check(input.toAst(rstOptions = preferRst) == dedent""" + rnEnumList labelFmt=1) + rnEnumItem + rnAdmonition adType=hint + [nil] + [nil] + rnAdmonition adType=error + [nil] + [nil] + rnLeaf 'none' + rnEnumItem + rnAdmonition adType=warning + [nil] + [nil] + rnDefList + rnDefItem + rnDefName + rnLeaf 'term0' + rnDefBody + rnInner + rnLeaf 'Definition0' + rnEnumItem + rnInner + rnLeaf 'some' + rnLeaf ' ' + rnLeaf 'paragraph1' + rnEnumItem + rnDefList + rnDefItem + rnDefName + rnLeaf 'term1' + rnDefBody + rnInner + rnLeaf 'Definition1' + rnDefItem + rnDefName + rnLeaf 'term2' + rnDefBody + rnInner + rnLeaf 'Definition2' + """) + + test "code-block parsing": + let input1 = dedent""" + .. code-block:: nim + :test: "nim c $1" + + template additive(typ: typedesc) = + discard + """ + let input2 = dedent""" + .. code-block:: nim + :test: "nim c $1" + + template additive(typ: typedesc) = + discard + """ + let input3 = dedent""" + .. code-block:: nim + :test: "nim c $1" + template additive(typ: typedesc) = + discard + """ + let inputWrong = dedent""" + .. code-block:: nim + :test: "nim c $1" + + template additive(typ: typedesc) = + discard + """ + let ast = dedent""" + rnCodeBlock + rnDirArg + rnLeaf 'nim' + rnFieldList + rnField + rnFieldName + rnLeaf 'test' + rnFieldBody + rnInner + rnLeaf '"' + rnLeaf 'nim' + rnLeaf ' ' + rnLeaf 'c' + rnLeaf ' ' + rnLeaf '$' + rnLeaf '1' + rnLeaf '"' + rnField + rnFieldName + rnLeaf 'default-language' + rnFieldBody + rnLeaf 'Nim' + rnLiteralBlock + rnLeaf 'template additive(typ: typedesc) = + discard' + """ + check input1.toAst == ast + check input2.toAst == ast + check input3.toAst == ast + # "template..." should be parsed as a definition list attached to ":test:": + check inputWrong.toAst != ast + + test "Markdown definition lists work in conjunction with bullet lists": + check(dedent""" + * some term + : the definition + + Paragraph.""".toAst == + dedent""" + rnInner + rnBulletList + rnBulletItem + rnMdDefList + rnDefItem + rnDefName + rnLeaf 'some' + rnLeaf ' ' + rnLeaf 'term' + rnDefBody + rnInner + rnLeaf 'the' + rnLeaf ' ' + rnLeaf 'definition' + rnParagraph + rnLeaf 'Paragraph' + rnLeaf '.' + """) + + test "Markdown definition lists work with blank lines and extra paragraphs": + check(dedent""" + Term1 + + : Definition1 + + Term2 *inline markup* + + : Definition2 + + Paragraph2 + + Term3 + : * point1 + * point2 + : term3definition2 + """.toAst == dedent""" + rnMdDefList + rnDefItem + rnDefName + rnLeaf 'Term1' + rnDefBody + rnInner + rnLeaf 'Definition1' + rnDefItem + rnDefName + rnLeaf 'Term2' + rnLeaf ' ' + rnEmphasis + rnLeaf 'inline' + rnLeaf ' ' + rnLeaf 'markup' + rnDefBody + rnParagraph + rnLeaf 'Definition2' + rnParagraph + rnLeaf 'Paragraph2' + rnDefItem + rnDefName + rnLeaf 'Term3' + rnDefBody + rnBulletList + rnBulletItem + rnInner + rnLeaf 'point1' + rnBulletItem + rnInner + rnLeaf 'point2' + rnDefBody + rnInner + rnLeaf 'term3definition2' + """) + +suite "Markdown indentation": + test "Markdown paragraph indentation": + # Additional spaces (<=3) of indentation does not break the paragraph. + # TODO: in 2nd case de-indentation causes paragraph to break, this is + # reasonable but does not seem to conform the Markdown spec. + check(dedent""" + Start1 + stop1 + + Start2 + stop2 + """.toAst == + dedent""" + rnInner + rnParagraph + rnLeaf 'Start1' + rnLeaf ' ' + rnLeaf 'stop1' + rnParagraph + rnLeaf 'Start2' + rnParagraph + rnLeaf 'stop2' + rnLeaf ' ' + """) + +suite "Warnings": + test "warnings for broken footnotes/links/substitutions": + let input = dedent""" + firstParagraph + + footnoteRef [som]_ + + link `a broken Link`_ + + substitution |undefined subst| + + link short.link_ + + lastParagraph + """ + var warnings = new seq[string] + let output = input.toAst(rstOptions=preferRst, warnings=warnings) + check(warnings[] == @[ + "input(3, 14) Warning: broken link 'citation-som'", + "input(5, 7) Warning: broken link 'a broken Link'", + "input(7, 15) Warning: unknown substitution 'undefined subst'", + "input(9, 6) Warning: broken link 'short.link'" + ]) + + test "Pandoc Markdown concise link warning points to target": + var warnings = new seq[string] + check( + "ref [here][target]".toAst(warnings=warnings) == + dedent""" + rnInner + rnLeaf 'ref' + rnLeaf ' ' + rnPandocRef + rnInner + rnLeaf 'here' + rnInner + rnLeaf 'target' + """) + check warnings[] == @["input(1, 12) Warning: broken link 'target'"] + + test "With include directive and blank lines at the beginning": + "other.rst".writeFile(dedent""" + + + firstParagraph + + here brokenLink_""") + let input = ".. include:: other.rst" + var warnings = new seq[string] + let output = input.toAst(warnings=warnings) + check warnings[] == @["other.rst(5, 6) Warning: broken link 'brokenLink'"] + check(output == dedent""" + rnInner + rnParagraph + rnLeaf 'firstParagraph' + rnParagraph + rnLeaf 'here' + rnLeaf ' ' + rnRstRef + rnLeaf 'brokenLink' + """) + removeFile("other.rst") + + test "warnings for ambiguous links (references + anchors)": + # Reference like `x`_ generates a link alias x that may clash with others + let input = dedent""" + Manual reference: `foo <#foo,string,string>`_ + + .. _foo: + + Paragraph. + + Ref foo_ + """ + var warnings = new seq[string] + let output = input.toAst(warnings=warnings) + check(warnings[] == @[ + dedent """ + input(7, 5) Warning: ambiguous doc link `foo` + clash: + (3, 8): (manual directive anchor) + (1, 45): (implicitly-generated hyperlink alias)""" + ]) + # reference should be resolved to the manually set anchor: + check(output == + dedent""" + rnInner + rnParagraph + rnLeaf 'Manual' + rnLeaf ' ' + rnLeaf 'reference' + rnLeaf ':' + rnLeaf ' ' + rnHyperlink + rnInner + rnLeaf 'foo' + rnInner + rnLeaf '#foo,string,string' + rnParagraph anchor='foo' + rnLeaf 'Paragraph' + rnLeaf '.' + rnParagraph + rnLeaf 'Ref' + rnLeaf ' ' + rnInternalRef + rnInner + rnLeaf 'foo' + rnLeaf 'foo' + rnLeaf ' ' + """) suite "RST include directive": test "Include whole": "other.rst".writeFile("**test1**") let input = ".. include:: other.rst" - assert "<strong>test1</strong>" == rstTohtml(input, {}, defaultConfig()) + doAssert "<strong>test1</strong>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") test "Include starting from": @@ -29,7 +1573,7 @@ OtherStart .. include:: other.rst :start-after: OtherStart """ - assert "<em>Visible</em>" == rstTohtml(input, {}, defaultConfig()) + check "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") test "Include everything before": @@ -43,7 +1587,7 @@ And this should **NOT** be visible in `docs.html` .. include:: other.rst :end-before: OtherEnd """ - assert "<em>Visible</em>" == rstTohtml(input, {}, defaultConfig()) + doAssert "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") @@ -61,7 +1605,7 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - assert "<em>Visible</em>" == rstTohtml(input, {}, defaultConfig()) + check "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") @@ -81,5 +1625,370 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - assert "<em>Visible</em>" == rstTohtml(input, {}, defaultConfig()) + doAssert "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") + +suite "RST escaping": + test "backspaces": + check("""\ this""".toAst == dedent""" + rnLeaf 'this' + """) + + check("""\\ this""".toAst == dedent""" + rnInner + rnLeaf '\' + rnLeaf ' ' + rnLeaf 'this' + """) + + check("""\\\ this""".toAst == dedent""" + rnInner + rnLeaf '\' + rnLeaf 'this' + """) + + check("""\\\\ this""".toAst == dedent""" + rnInner + rnLeaf '\' + rnLeaf '\' + rnLeaf ' ' + rnLeaf 'this' + """) + +suite "RST inline markup": + test "* and ** surrounded by spaces are not inline markup": + check("a * b * c ** d ** e".toAst == dedent""" + rnInner + rnLeaf 'a' + rnLeaf ' ' + rnLeaf '*' + rnLeaf ' ' + rnLeaf 'b' + rnLeaf ' ' + rnLeaf '*' + rnLeaf ' ' + rnLeaf 'c' + rnLeaf ' ' + rnLeaf '**' + rnLeaf ' ' + rnLeaf 'd' + rnLeaf ' ' + rnLeaf '**' + rnLeaf ' ' + rnLeaf 'e' + """) + + test "end-string has repeating symbols": + check("*emphasis content****".toAst == dedent""" + rnEmphasis + rnLeaf 'emphasis' + rnLeaf ' ' + rnLeaf 'content' + rnLeaf '***' + """) + + check("""*emphasis content\****""".toAst == dedent""" + rnEmphasis + rnLeaf 'emphasis' + rnLeaf ' ' + rnLeaf 'content' + rnLeaf '*' + rnLeaf '**' + """) # exact configuration of leafs with * is not really essential, + # only total number of * is essential + + check("**strong content****".toAst == dedent""" + rnStrongEmphasis + rnLeaf 'strong' + rnLeaf ' ' + rnLeaf 'content' + rnLeaf '**' + """) + + check("""**strong content*\****""".toAst == dedent""" + rnStrongEmphasis + rnLeaf 'strong' + rnLeaf ' ' + rnLeaf 'content' + rnLeaf '*' + rnLeaf '*' + rnLeaf '*' + """) + + check("``lit content`````".toAst == dedent""" + rnInlineLiteral + rnLeaf 'lit' + rnLeaf ' ' + rnLeaf 'content' + rnLeaf '```' + """) + + test "interpreted text parsing: code fragments": + check(dedent""" + .. default-role:: option + + `--gc:refc`""".toAst == + dedent""" + rnInner + rnDefaultRole + rnDirArg + rnLeaf 'option' + [nil] + [nil] + rnParagraph + rnCodeFragment + rnInner + rnLeaf '--' + rnLeaf 'gc' + rnLeaf ':' + rnLeaf 'refc' + rnLeaf 'option' + """) + + test """interpreted text can be ended with \` """: + let output = (".. default-role:: literal\n" & """`\``""").toAst + check(output.endsWith """ + rnParagraph + rnInlineLiteral + rnLeaf '`'""" & "\n") + + let output2 = """`\``""".toAst + check(output2 == dedent""" + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf '`' + """) + + let output3 = """`proc \`+\``""".toAst + check(output3 == dedent""" + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'proc `+`' + """) + + check("""`\\`""".toAst == + dedent""" + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf '\\' + """) + + test "Markdown-style code/backtick": + # no whitespace is required before ` + check("`try`...`except`".toAst == + dedent""" + rnInner + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'try' + rnLeaf '...' + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'except' + """) + + + test """inline literals can contain \ anywhere""": + check("""``\``""".toAst == dedent""" + rnInlineLiteral + rnLeaf '\' + """) + + check("""``\\``""".toAst == dedent""" + rnInlineLiteral + rnLeaf '\' + rnLeaf '\' + """) + + check("""``\```""".toAst == dedent""" + rnInlineLiteral + rnLeaf '\' + rnLeaf '`' + """) + + check("""``\\```""".toAst == dedent""" + rnInlineLiteral + rnLeaf '\' + rnLeaf '\' + rnLeaf '`' + """) + + check("""``\````""".toAst == dedent""" + rnInlineLiteral + rnLeaf '\' + rnLeaf '`' + rnLeaf '`' + """) + + test "references with _ at the end": + check(dedent""" + .. _lnk: https + + lnk_""".toAst == + dedent""" + rnHyperlink + rnInner + rnLeaf 'lnk' + rnInner + rnLeaf 'https' + """) + + test "not a hyper link": + check(dedent""" + .. _lnk: https + + lnk___""".toAst == + dedent""" + rnInner + rnLeaf 'lnk' + rnLeaf '___' + """) + + test "no punctuation in the end of a standalone URI is allowed": + check(dedent""" + [see (http://no.org)], end""".toAst(rstOptions = preferRst) == + dedent""" + rnInner + rnLeaf '[' + rnLeaf 'see' + rnLeaf ' ' + rnLeaf '(' + rnStandaloneHyperlink + rnLeaf 'http://no.org' + rnLeaf ')' + rnLeaf ']' + rnLeaf ',' + rnLeaf ' ' + rnLeaf 'end' + """) + + # but `/` at the end is OK + check( + dedent""" + See http://no.org/ end""".toAst == + dedent""" + rnInner + rnLeaf 'See' + rnLeaf ' ' + rnStandaloneHyperlink + rnLeaf 'http://no.org/' + rnLeaf ' ' + rnLeaf 'end' + """) + + # a more complex URL with some made-up ending '&='. + # Github Markdown would include final &= and + # so would rst2html.py in contradiction with RST spec. + check( + dedent""" + See https://www.google.com/url?sa=t&source=web&cd=&cad=rja&url=https%3A%2F%2Fnim-lang.github.io%2FNim%2Frst.html%23features&usg=AO&= end""".toAst == + dedent""" + rnInner + rnLeaf 'See' + rnLeaf ' ' + rnStandaloneHyperlink + rnLeaf 'https://www.google.com/url?sa=t&source=web&cd=&cad=rja&url=https%3A%2F%2Fnim-lang.github.io%2FNim%2Frst.html%23features&usg=AO' + rnLeaf '&' + rnLeaf '=' + rnLeaf ' ' + rnLeaf 'end' + """) + + test "Markdown-style link can be split to a few lines": + check(dedent""" + is [term-rewriting + macros](manual.html#term-rewriting-macros)""".toAst == + dedent""" + rnInner + rnLeaf 'is' + rnLeaf ' ' + rnHyperlink + rnLeaf 'term-rewriting macros' + rnLeaf 'manual.html#term-rewriting-macros' + """) + + test "URL with balanced parentheses (Markdown rule)": + # 2 balanced parens, 1 unbalanced: + check(dedent""" + https://en.wikipedia.org/wiki/APL_((programming_language)))""".toAst == + dedent""" + rnInner + rnStandaloneHyperlink + rnLeaf 'https://en.wikipedia.org/wiki/APL_((programming_language))' + rnLeaf ')' + """) + + # the same for Markdown-style link: + check(dedent""" + [foo [bar]](https://en.wikipedia.org/wiki/APL_((programming_language))))""".toAst == + dedent""" + rnInner + rnHyperlink + rnLeaf 'foo [bar]' + rnLeaf 'https://en.wikipedia.org/wiki/APL_((programming_language))' + rnLeaf ')' + """) + + # unbalanced (here behavior is more RST-like actually): + check(dedent""" + https://en.wikipedia.org/wiki/APL_(programming_language(""".toAst == + dedent""" + rnInner + rnStandaloneHyperlink + rnLeaf 'https://en.wikipedia.org/wiki/APL_(programming_language' + rnLeaf '(' + """) + + # unbalanced [, but still acceptable: + check(dedent""" + [my {link example](http://example.com/bracket_(symbol_[))""".toAst == + dedent""" + rnHyperlink + rnLeaf 'my {link example' + rnLeaf 'http://example.com/bracket_(symbol_[)' + """) + + test "not a Markdown link": + # bug #17340 (27) `f` will be considered as a protocol and blocked as unsafe + var warnings = new seq[string] + check("[T](f: var Foo)".toAst(warnings = warnings) == + dedent""" + rnInner + rnLeaf '[' + rnLeaf 'T' + rnLeaf ']' + rnLeaf '(' + rnLeaf 'f' + rnLeaf ':' + rnLeaf ' ' + rnLeaf 'var' + rnLeaf ' ' + rnLeaf 'Foo' + rnLeaf ')' + """) + check(warnings[] == @["input(1, 5) Warning: broken link 'f'"]) + +suite "Misc isssues": + test "Markdown CodeblockFields in one line (lacking enclosing ```)": + let message = """ + ```llvm-profdata merge first.profraw second.profraw third.profraw <more stuff maybe> -output data.profdata```""" + + try: + echo rstgen.rstToHtml(message, {roSupportMarkdown}, nil) + except EParseError: + discard diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 8fdbf3911..6253e7146 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc; --mm:orc" outputsub: "" """ @@ -6,7 +7,53 @@ outputsub: "" import ../../lib/packages/docutils/rstgen import ../../lib/packages/docutils/rst -import unittest +import unittest, strutils, strtabs +import std/private/miscdollars +import std/assertions + +const + NoSandboxOpts = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} + preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile} + preferRst = {roSupportMarkdown, roNimFile} + +proc toHtml(input: string, + rstOptions: RstParseOptions = preferMarkdown, + error: ref string = nil, + warnings: ref seq[string] = nil): string = + ## If `error` is nil then no errors should be generated. + ## The same goes for `warnings`. + proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind, + arg: string) = + let mc = msgkind.whichMsgClass + let a = $msgkind % arg + var message: string + toLocation(message, filename, line, col + ColRstOffset) + message.add " $1: $2" % [$mc, a] + if mc == mcError: + if error == nil: + raise newException(EParseError, "[unexpected error] " & message) + error[] = message + # we check only first error because subsequent ones may be meaningless + raise newException(EParseError, "") + else: + doAssert warnings != nil, "unexpected RST warning '" & message & "'" + warnings[].add message + try: + result = rstToHtml(input, rstOptions, defaultConfig(), + msgHandler=testMsgHandler) + except EParseError as e: + if e.msg != "": + result = e.msg + +# inline code tags (for parsing originated from highlite.nim) +proc id(str: string): string = """<span class="Identifier">""" & str & "</span>" +proc op(str: string): string = """<span class="Operator">""" & str & "</span>" +proc pu(str: string): string = """<span class="Punctuation">""" & str & "</span>" +proc optionListLabel(opt: string): string = + """<div class="option-list-label"><tt><span class="option">""" & + opt & + "</span></tt></div>" + suite "YAML syntax highlighting": test "Basics": @@ -21,8 +68,8 @@ suite "YAML syntax highlighting": ? key : value ...""" - let output = rstTohtml(input, {}, defaultConfig()) - assert output == """<pre class = "listing"><span class="Directive">%YAML 1.2</span> + let output = input.toHtml({}) + doAssert output == """<pre class = "listing"><span class="Directive">%YAML 1.2</span> <span class="Keyword">---</span> <span class="StringLit">a string</span><span class="Punctuation">:</span> <span class="StringLit">string</span> <span class="StringLit">a list</span><span class="Punctuation">:</span> @@ -47,8 +94,8 @@ suite "YAML syntax highlighting": another literal block scalar: |+ # comment after header allowed, since more indented than parent""" - let output = rstToHtml(input, {}, defaultConfig()) - assert output == """<pre class = "listing"><span class="StringLit">a literal block scalar</span><span class="Punctuation">:</span> <span class="Command">|</span><span class="Command"></span><span class="LongStringLit"> + let output = input.toHtml({}) + doAssert output == """<pre class = "listing"><span class="StringLit">a literal block scalar</span><span class="Punctuation">:</span> <span class="Command">|</span><span class="Command"></span><span class="LongStringLit"> some text # not a comment </span><span class="Comment"># a comment, since less indented</span> @@ -73,8 +120,8 @@ suite "YAML syntax highlighting": % not a directive ... %TAG ! !foo:""" - let output = rstToHtml(input, {}, defaultConfig()) - assert output == """<pre class = "listing"><span class="Directive">%YAML 1.2</span> + let output = input.toHtml({}) + doAssert output == """<pre class = "listing"><span class="Directive">%YAML 1.2</span> <span class="Keyword">---</span> <span class="StringLit">%not a directive</span> <span class="Keyword">...</span> @@ -94,8 +141,8 @@ suite "YAML syntax highlighting": more numbers: [-783, 11e78], not numbers: [ 42e, 0023, +32.37, 8 ball] }""" - let output = rstToHtml(input, {}, defaultConfig()) - assert output == """<pre class = "listing"><span class="Punctuation">{</span> + let output = input.toHtml({}) + doAssert output == """<pre class = "listing"><span class="Punctuation">{</span> <span class="StringLit">"</span><span class="StringLit">quoted string"</span><span class="Punctuation">:</span> <span class="DecNumber">42</span><span class="Punctuation">,</span> <span class="StringLit">'single quoted string'</span><span class="Punctuation">:</span> <span class="StringLit">false</span><span class="Punctuation">,</span> <span class="Punctuation">[</span> <span class="StringLit">list</span><span class="Punctuation">,</span> <span class="StringLit">"</span><span class="StringLit">with"</span><span class="Punctuation">,</span> <span class="StringLit">'entries'</span> <span class="Punctuation">]</span><span class="Punctuation">:</span> <span class="FloatNumber">73.32e-73</span><span class="Punctuation">,</span> @@ -103,6 +150,25 @@ suite "YAML syntax highlighting": <span class="StringLit">not numbers</span><span class="Punctuation">:</span> <span class="Punctuation">[</span> <span class="StringLit">42e</span><span class="Punctuation">,</span> <span class="StringLit">0023</span><span class="Punctuation">,</span> <span class="StringLit">+32.37</span><span class="Punctuation">,</span> <span class="StringLit">8 ball</span><span class="Punctuation">]</span> <span class="Punctuation">}</span></pre>""" + test "Directives: warnings": + let input = dedent""" + .. non-existent-warning: Paragraph. + + .. another.wrong:warning::: Paragraph. + """ + var warnings = new seq[string] + let output = input.toHtml(warnings=warnings) + check output == "" + doAssert warnings[].len == 2 + check "(1, 24) Warning: RST style:" in warnings[0] + check "double colon :: may be missing at end of 'non-existent-warning'" in warnings[0] + check "(3, 25) Warning: RST style:" in warnings[1] + check "RST style: too many colons for a directive (should be ::)" in warnings[1] + + test "not a directive": + let input = "..warning:: I am not a warning." + check input.toHtml == input + test "Anchors, Aliases, Tags": let input = """.. code-block:: yaml --- !!map @@ -111,8 +177,8 @@ suite "YAML syntax highlighting": : !localtag foo alias: *anchor """ - let output = rstToHtml(input, {}, defaultConfig()) - assert output == """<pre class = "listing"><span class="Keyword">---</span> <span class="TagStart">!!map</span> + let output = input.toHtml({}) + doAssert output == """<pre class = "listing"><span class="Keyword">---</span> <span class="TagStart">!!map</span> <span class="TagStart">!!str</span> <span class="StringLit">string</span><span class="Punctuation">:</span> <span class="TagStart">!<tag:yaml.org,2002:int></span> <span class="DecNumber">42</span> <span class="Punctuation">?</span> <span class="Label">&anchor</span> <span class="TagStart">!!seq</span> <span class="Punctuation">[</span><span class="Punctuation">]</span><span class="Punctuation">:</span> <span class="Punctuation">:</span> <span class="TagStart">!localtag</span> <span class="StringLit">foo</span> @@ -131,8 +197,8 @@ suite "YAML syntax highlighting": example.com/not/a#comment: ?not a map key """ - let output = rstToHtml(input, {}, defaultConfig()) - assert output == """<pre class = "listing"><span class="Keyword">...</span> + let output = input.toHtml({}) + doAssert output == """<pre class = "listing"><span class="Keyword">...</span> <span class="StringLit">%a string</span><span class="Punctuation">:</span> <span class="StringLit">a:string:not:a:map</span> <span class="Keyword">...</span> @@ -144,12 +210,1483 @@ suite "YAML syntax highlighting": <span class="StringLit">?not a map key</span></pre>""" +suite "RST/Markdown general": + test "RST emphasis": + doAssert rstToHtml("*Hello* **world**!", {}, + newStringTable(modeStyleInsensitive)) == + "<em>Hello</em> <strong>world</strong>!" + test "Markdown links": - let - a = rstToHtml("(( [Nim](https://nim-lang.org/) ))", {roSupportMarkdown}, defaultConfig()) - b = rstToHtml("(([Nim](https://nim-lang.org/)))", {roSupportMarkdown}, defaultConfig()) - c = rstToHtml("[[Nim](https://nim-lang.org/)]", {roSupportMarkdown}, defaultConfig()) - - assert a == """(( <a class="reference external" href="https://nim-lang.org/">Nim</a> ))""" - assert b == """((<a class="reference external" href="https://nim-lang.org/">Nim</a>))""" - assert c == """[<a class="reference external" href="https://nim-lang.org/">Nim</a>]""" + check("(( [Nim](https://nim-lang.org/) ))".toHtml == + """(( <a class="reference external" href="https://nim-lang.org/">Nim</a> ))""") + check("(([Nim](https://nim-lang.org/)))".toHtml == + """((<a class="reference external" href="https://nim-lang.org/">Nim</a>))""") + check("[[Nim](https://nim-lang.org/)]".toHtml == + """[<a class="reference external" href="https://nim-lang.org/">Nim</a>]""") + + test "Markdown tables": + let input1 = """ +| A1 header | A2 \| not fooled +| :--- | ----: | +| C1 | C2 **bold** | ignored | +| D1 `code \|` | D2 | also ignored +| E1 \| text | +| | F2 without pipe +not in table""" + let output1 = input1.toHtml + #[ + TODO: `\|` inside a table cell should render as `|` + `|` outside a table cell should render as `\|` + consistently with markdown, see https://stackoverflow.com/a/66557930/1426932 + ]# + check(output1 == """ +<table border="1" class="docutils"><tr><th>A1 header</th><th>A2 | not fooled</th></tr> +<tr><td>C1</td><td>C2 <strong>bold</strong></td></tr> +<tr><td>D1 <tt class="docutils literal"><span class="pre">""" & id"code" & " " & op"\|" & """</span></tt></td><td>D2</td></tr> +<tr><td>E1 | text</td><td></td></tr> +<tr><td></td><td>F2 without pipe</td></tr> +</table><p>not in table</p>""") + let input2 = """ +| A1 header | A2 | +| --- | --- |""" + let output2 = input2.toHtml + doAssert output2 == """<table border="1" class="docutils"><tr><th>A1 header</th><th>A2</th></tr> +</table>""" + + test "RST tables": + let input1 = """ +Test 2 column/4 rows table: +==== === +H0 H1 +==== === +A0 A1 +==== === +A2 A3 +==== === +A4 A5 +==== === """ + let output1 = rstToLatex(input1, {}) + doAssert "{LL}" in output1 # 2 columns + doAssert count(output1, "\\\\") == 4 # 4 rows + for cell in ["H0", "H1", "A0", "A1", "A2", "A3", "A4", "A5"]: + doAssert cell in output1 + + let input2 = """ +Now test 3 columns / 2 rows, and also borders containing 4 =, 3 =, 1 = signs: + +==== === = +H0 H1 H +==== === = +A0 A1 X + Ax Y +==== === = """ + let output2 = rstToLatex(input2, {}) + doAssert "{LLL}" in output2 # 3 columns + doAssert count(output2, "\\\\") == 2 # 2 rows + for cell in ["H0", "H1", "H", "A0", "A1", "X", "Ax", "Y"]: + doAssert cell in output2 + + + test "RST adornments": + let input1 = """ +Check that a few punctuation symbols are not parsed as adornments: +:word1: word2 .... word3 """ + let output1 = input1.toHtml + discard output1 + + test "RST sections": + let input1 = """ +Long chapter name +''''''''''''''''''' +""" + let output1 = input1.toHtml + doAssert "Long chapter name" in output1 and "<h1" in output1 + + let input2 = """ +Short chapter name: + +ChA +=== +""" + let output2 = input2.toHtml + doAssert "ChA" in output2 and "<h1" in output2 + + let input3 = """ +Very short chapter name: + +X +~ +""" + let output3 = input3.toHtml + doAssert "X" in output3 and "<h1" in output3 + + let input4 = """ +Check that short underline is not enough to make section: + +Wrong chapter +------------ + +""" + var error4 = new string + let output4 = input4.toHtml(error = error4) + check(error4[] == "input(3, 1) Error: new section expected (underline " & + "\'------------\' is too short)") + + let input5 = """ +Check that punctuation after adornment and indent are not detected as adornment. + +Some chapter +-------------- + + "punctuation symbols" """ + let output5 = input5.toHtml + doAssert ""punctuation symbols"" in output5 and "<h1" in output5 + + # check that EOF after adornment does not prevent it parsing as heading + let input6 = dedent """ + Some chapter + ------------""" + let output6 = input6.toHtml + doAssert "<h1 id=\"some-chapter\">Some chapter</h1>" in output6 + + # check that overline and underline match + let input7 = dedent """ + ------------ + Some chapter + ----------- + """ + var error7 = new string + let output7 = input7.toHtml(error=error7) + check(error7[] == "input(1, 1) Error: new section expected (underline " & + "\'-----------\' does not match overline \'------------\')") + + let input8 = dedent """ + ----------- + Overflow + ----------- + """ + var error8 = new string + let output8 = input8.toHtml(error=error8) + check(error8[] == "input(1, 1) Error: new section expected (overline " & + "\'-----------\' is too short)") + + # check that hierarchy of title styles works + let input9good = dedent """ + Level1 + ====== + + Level2 + ------ + + Level3 + ~~~~~~ + + L1 + == + + Another2 + -------- + + More3 + ~~~~~ + + """ + let output9good = input9good.toHtml(preferRst) + doAssert "<h1 id=\"level1\">Level1</h1>" in output9good + doAssert "<h2 id=\"level2\">Level2</h2>" in output9good + doAssert "<h3 id=\"level3\">Level3</h3>" in output9good + doAssert "<h1 id=\"l1\">L1</h1>" in output9good + doAssert "<h2 id=\"another2\">Another2</h2>" in output9good + doAssert "<h3 id=\"more3\">More3</h3>" in output9good + + # check that swap causes an exception + let input9Bad = dedent """ + Level1 + ====== + + Level2 + ------ + + Level3 + ~~~~~~ + + L1 + == + + More + ~~~~ + + Another + ------- + + """ + var error9Bad = new string + let output9Bad = input9Bad.toHtml(preferRst, error=error9Bad) + check(error9Bad[] == "input(15, 1) Error: new section expected (section " & + "level inconsistent: underline ~~~~~ unexpectedly found, while " & + "the following intermediate section level(s) are missing on " & + "lines 12..15: underline -----)") + + test "RST sections overline": + # the same as input9good but with overline headings + # first overline heading has a special meaning: document title + let input = dedent """ + ====== + Title0 + ====== + + +++++++++ + SubTitle0 + +++++++++ + + ------ + Level1 + ------ + + Level2 + ------ + + ~~~~~~ + Level3 + ~~~~~~ + + -- + L1 + -- + + Another2 + -------- + + ~~~~~ + More3 + ~~~~~ + + """ + var rstGenera: RstGenerator + var output: string + let (rst, files, _) = rstParse(input, "", 1, 1, {}) + rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames = files) + rstGenera.renderRstToOut(rst, output) + doAssert rstGenera.meta[metaTitle] == "Title0" + doAssert rstGenera.meta[metaSubtitle] == "SubTitle0" + doAssert "<h1 id=\"level1\"><center>Level1</center></h1>" in output + doAssert "<h2 id=\"level2\">Level2</h2>" in output + doAssert "<h3 id=\"level3\"><center>Level3</center></h3>" in output + doAssert "<h1 id=\"l1\"><center>L1</center></h1>" in output + doAssert "<h2 id=\"another2\">Another2</h2>" in output + doAssert "<h3 id=\"more3\"><center>More3</center></h3>" in output + + test "RST sections overline 2": + # check that a paragraph prevents interpreting overlines as document titles + let input = dedent """ + Paragraph + + ====== + Title0 + ====== + + +++++++++ + SubTitle0 + +++++++++ + """ + var rstGenera: RstGenerator + var output: string + let (rst, files, _) = rstParse(input, "", 1, 1, {}) + rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) + rstGenera.renderRstToOut(rst, output) + doAssert rstGenera.meta[metaTitle] == "" + doAssert rstGenera.meta[metaSubtitle] == "" + doAssert "<h1 id=\"title0\"><center>Title0</center></h1>" in output + doAssert "<h2 id=\"subtitle0\"><center>SubTitle0</center></h2>" in output + + test "RST+Markdown sections": + # check that RST and Markdown headings don't interfere + let input = dedent """ + ====== + Title0 + ====== + + MySection1a + +++++++++++ + + # MySection1b + + MySection1c + +++++++++++ + + ##### MySection5a + + MySection2a + ----------- + """ + var rstGenera: RstGenerator + var output: string + let (rst, files, _) = rstParse(input, "", 1, 1, {roSupportMarkdown}) + rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) + rstGenera.renderRstToOut(rst, output) + doAssert rstGenera.meta[metaTitle] == "Title0" + doAssert rstGenera.meta[metaSubtitle] == "" + doAssert output == + "\n<h1 id=\"mysection1a\">MySection1a</h1>" & # RST + "\n<h1 id=\"mysection1b\">MySection1b</h1>" & # Markdown + "\n<h1 id=\"mysection1c\">MySection1c</h1>" & # RST + "\n<h5 id=\"mysection5a\">MySection5a</h5>" & # Markdown + "\n<h2 id=\"mysection2a\">MySection2a</h2>" # RST + + test "RST inline text": + let input1 = "GC_step" + let output1 = input1.toHtml + doAssert output1 == "GC_step" + + test "RST anchors/links to headings": + # Currently in TOC mode anchors are modified (for making links from + # the TOC unique) + let inputNoToc = dedent""" + Type relations + ============== + + Convertible relation + -------------------- + + Ref. `Convertible relation`_ + """ + let outputNoToc = inputNoToc.toHtml + check outputNoToc.count("id=\"type-relations\"") == 1 + check outputNoToc.count("id=\"convertible-relation\"") == 1 + check outputNoToc.count("href=\"#convertible-relation\"") == 1 + + let inputTocCases = @[ + dedent""" + .. contents:: + + Type relations + ============== + + Convertible relation + -------------------- + + Ref. `Convertible relation`_ + + Guards and locks + ================ + """, + dedent""" + Ref. `Convertible relation`_ + + .. contents:: + + Type relations + ============== + + Convertible relation + -------------------- + + Guards and locks + ================ + """ + ] + for inputToc in inputTocCases: + let outputToc = inputToc.toHtml + check outputToc.count("id=\"type-relations\"") == 1 + check outputToc.count("id=\"type-relations-convertible-relation\"") == 1 + check outputToc.count("id=\"convertible-relation\">") == 0 + # Besides "Ref.", heading also contains link to itself: + check outputToc.count( + "href=\"#type-relations-convertible-relation\">") == 2 + check outputToc.count("href=\"#convertible-relation\"") == 0 + + test "RST links": + let input1 = """ +Want to learn about `my favorite programming language`_? + +.. _my favorite programming language: https://nim-lang.org""" + let output1 = input1.toHtml + doAssert "<a" in output1 and "href=\"https://nim-lang.org\"" in output1 + + test "RST transitions": + let input1 = """ +context1 + +~~~~ + +context2 +""" + let output1 = input1.toHtml(preferRst) + doAssert "<hr" in output1 + + let input2 = """ +This is too short to be a transition: + +--- +context2 +--- +""" + var error2 = new string + let output2 = input2.toHtml(error=error2) + check(error2[] == "input(3, 1) Error: new section expected (overline " & + "\'---\' is too short)") + + test "RST literal block": + let input1 = """ +Test literal block + +:: + + check """ + let output1 = input1.toHtml(preferRst) + doAssert "<pre>" in output1 + + test "Markdown code block": + let input1 = """ +``` +let x = 1 +``` """ + let output1 = input1.toHtml({roSupportMarkdown, roPreferMarkdown}) + doAssert "<pre" in output1 and "class=\"Keyword\"" notin output1 + + # Check Nim highlighting by default in .nim files: + let output1nim = input1.toHtml({roSupportMarkdown, roPreferMarkdown, + roNimFile}) + doAssert "<pre" in output1nim and "class=\"Keyword\"" in output1nim + + let input2 = """ +Parse the block with language specifier: +```Nim +let x = 1 +``` """ + let output2 = input2.toHtml + doAssert "<pre" in output2 and "class=\"Keyword\"" in output2 + + test "interpreted text": + check("""`foo.bar`""".toHtml == + """<tt class="docutils literal"><span class="pre">""" & + id"foo" & op"." & id"bar" & "</span></tt>") + check("""`foo\`\`bar`""".toHtml == + """<tt class="docutils literal"><span class="pre">""" & + id"foo" & pu"`" & pu"`" & id"bar" & "</span></tt>") + check("""`foo\`bar`""".toHtml == + """<tt class="docutils literal"><span class="pre">""" & + id"foo" & pu"`" & id"bar" & "</span></tt>") + check("""`\`bar`""".toHtml == + """<tt class="docutils literal"><span class="pre">""" & + pu"`" & id"bar" & "</span></tt>") + check("""`a\b\x\\ar`""".toHtml == + """<tt class="docutils literal"><span class="pre">""" & + id"a" & op"""\""" & id"b" & op"""\""" & id"x" & op"""\\""" & id"ar" & + "</span></tt>") + + test "inline literal": + check """``foo.bar``""".toHtml == """<tt class="docutils literal"><span class="pre">foo.bar</span></tt>""" + check """``foo\bar``""".toHtml == """<tt class="docutils literal"><span class="pre">foo\bar</span></tt>""" + check """``f\`o\\o\b`ar``""".toHtml == """<tt class="docutils literal"><span class="pre">f\`o\\o\b`ar</span></tt>""" + + test "default-role": + # nim(default) -> literal -> nim -> code(=literal) + let input = dedent""" + Par1 `value1`. + + .. default-role:: literal + + Par2 `value2`. + + .. default-role:: nim + + Par3 `value3`. + + .. default-role:: code + + Par4 `value4`.""" + let p1 = """Par1 <tt class="docutils literal"><span class="pre">""" & id"value1" & "</span></tt>." + let p2 = """<p>Par2 <tt class="docutils literal"><span class="pre">value2</span></tt>.</p>""" + let p3 = """<p>Par3 <tt class="docutils literal"><span class="pre">""" & id"value3" & "</span></tt>.</p>" + let p4 = """<p>Par4 <tt class="docutils literal"><span class="pre">value4</span></tt>.</p>""" + let expected = p1 & p2 & "\n" & p3 & "\n" & p4 + check( + input.toHtml(NoSandboxOpts) == expected + ) + + test "role directive": + let input = dedent""" + .. role:: y(code) + :language: yaml + + .. role:: brainhelp(code) + :language: brainhelp + """ + var warnings = new seq[string] + let output = input.toHtml( + NoSandboxOpts, + warnings=warnings + ) + check(warnings[].len == 1 and "language 'brainhelp' not supported" in warnings[0]) + + test "RST comments": + let input1 = """ + +Check that comment disappears: + +.. + some comment """ + let output1 = input1.toHtml + doAssert output1 == "Check that comment disappears:" + + test "RST line blocks + headings": + let input = """ +===== +Test1 +===== + +| +| +| line block +| other line + +""" + var rstGenera: RstGenerator + var output: string + let (rst, files, _) = rstParse(input, "", 1, 1, {}) + rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) + rstGenera.renderRstToOut(rst, output) + doAssert rstGenera.meta[metaTitle] == "Test1" + # check that title was not overwritten to '|' + doAssert output == "<p><br/><br/>line block<br/>other line<br/></p>" + let output1l = rstToLatex(input, {}) + doAssert "line block\n\n" in output1l + doAssert "other line\n\n" in output1l + doAssert output1l.count("\\vspace") == 2 + 2 # +2 surrounding paddings + + test "RST line blocks": + let input2 = dedent""" + Paragraph1 + + | + + Paragraph2""" + + let output2 = input2.toHtml + doAssert "Paragraph1<p><br/></p> <p>Paragraph2</p>" == output2 + + let input3 = dedent""" + | xxx + | yyy + | zzz""" + + let output3 = input3.toHtml + doAssert "xxx<br/>" in output3 + doAssert "<span style=\"margin-left: 1.0em\">yyy</span><br/>" in output3 + doAssert "<span style=\"margin-left: 2.0em\">zzz</span><br/>" in output3 + + # check that '| ' with a few spaces is still parsed as new line + let input4 = dedent""" + | xxx + | + | zzz""" + + let output4 = input4.toHtml + doAssert "xxx<br/><br/>" in output4 + doAssert "<span style=\"margin-left: 2.0em\">zzz</span><br/>" in output4 + + test "RST enumerated lists": + let input1 = dedent """ + 1. line1 + 1 + 2. line2 + 2 + + 3. line3 + 3 + + + 4. line4 + 4 + + + + 5. line5 + 5 + """ + let output1 = input1.toHtml + for i in 1..5: + doAssert ($i & ". line" & $i) notin output1 + doAssert ("<li>line" & $i & " " & $i & "</li>") in output1 + + let input2 = dedent """ + 3. line3 + + 4. line4 + + + 5. line5 + + + + 7. line7 + + + + + 8. line8 + """ + let output2 = input2.toHtml + for i in [3, 4, 5, 7, 8]: + doAssert ($i & ". line" & $i) notin output2 + doAssert ("<li>line" & $i & "</li>") in output2 + + # check that nested enumerated lists work + let input3 = dedent """ + 1. a) string1 + 2. string2 + """ + let output3 = input3.toHtml + doAssert count(output3, "<ol ") == 2 + doAssert count(output3, "</ol>") == 2 + doAssert "<li>string1</li>" in output3 and "<li>string2</li>" in output3 + + let input4 = dedent """ + Check that enumeration specifiers are respected + + 9. string1 + 10. string2 + 12. string3 + + b) string4 + c) string5 + e) string6 + """ + let output4 = input4.toHtml + doAssert count(output4, "<ol ") == 4 + doAssert count(output4, "</ol>") == 4 + for enumerator in [9, 12]: + doAssert "start=\"$1\"" % [$enumerator] in output4 + for enumerator in [2, 5]: # 2=b, 5=e + doAssert "start=\"$1\"" % [$enumerator] in output4 + + let input5 = dedent """ + Check that auto-numbered enumeration lists work. + + #. string1 + + #. string2 + + #. string3 + + #) string5 + #) string6 + """ + let output5 = input5.toHtml + doAssert count(output5, "<ol ") == 2 + doAssert count(output5, "</ol>") == 2 + doAssert count(output5, "<li>") == 5 + + let input5a = dedent """ + Auto-numbered RST list can start with 1 even when Markdown support is on. + + 1. string1 + #. string2 + #. string3 + """ + let output5a = input5a.toHtml + doAssert count(output5a, "<ol ") == 1 + doAssert count(output5a, "</ol>") == 1 + doAssert count(output5a, "<li>") == 3 + + let input6 = dedent """ + ... And for alphabetic enumerators too! + + b. string1 + #. string2 + #. string3 + """ + let output6 = input6.toHtml + doAssert count(output6, "<ol ") == 1 + doAssert count(output6, "</ol>") == 1 + doAssert count(output6, "<li>") == 3 + doAssert "start=\"2\"" in output6 and "class=\"loweralpha simple\"" in output6 + + let input7 = dedent """ + ... And for uppercase alphabetic enumerators. + + C. string1 + #. string2 + #. string3 + """ + let output7 = input7.toHtml + doAssert count(output7, "<ol ") == 1 + doAssert count(output7, "</ol>") == 1 + doAssert count(output7, "<li>") == 3 + doAssert "start=\"3\"" in output7 and "class=\"upperalpha simple\"" in output7 + + # check that it's not recognized as enum.list without indentation on 2nd line + let input8 = dedent """ + Paragraph. + + A. stringA + B. stringB + C. string1 + string2 + """ + var warnings8 = new seq[string] + let output8 = input8.toHtml(warnings = warnings8) + check(warnings8[].len == 1) + check("input(6, 1) Warning: RST style: \n" & + "not enough indentation on line 6" in warnings8[0]) + doAssert output8 == "Paragraph.<ol class=\"upperalpha simple\">" & + "<li>stringA</li>\n<li>stringB</li>\n</ol>\n<p>C. string1 string2 </p>" + + test "Markdown enumerated lists": + let input1 = dedent """ + Below are 2 enumerated lists: Markdown-style (5 items) and RST (1 item) + 1. line1 + 1. line2 + 1. line3 + 1. line4 + + 1. line5 + + #. lineA + """ + let output1 = input1.toHtml + for i in 1..5: + doAssert ($i & ". line" & $i) notin output1 + doAssert ("<li>line" & $i & "</li>") in output1 + doAssert count(output1, "<ol ") == 2 + doAssert count(output1, "</ol>") == 2 + + test "RST bullet lists": + let input1 = dedent """ + * line1 + 1 + * line2 + 2 + + * line3 + 3 + + + * line4 + 4 + + + + * line5 + 5 + """ + let output1 = input1.toHtml + for i in 1..5: + doAssert ("<li>line" & $i & " " & $i & "</li>") in output1 + doAssert count(output1, "<ul ") == 1 + doAssert count(output1, "</ul>") == 1 + + test "Nim RST footnotes and citations": + # check that auto-label footnote enumerated properly after a manual one + let input1 = dedent """ + .. [1] Body1. + .. [#note] Body2 + + Ref. [#note]_ + """ + let output1 = input1.toHtml(preferRst) + doAssert output1.count(">[1]</a>") == 1 + doAssert output1.count(">[2]</a>") == 2 + doAssert "href=\"#footnote-note\"" in output1 + doAssert ">[-1]" notin output1 + doAssert "Body1." in output1 + doAssert "Body2" in output1 + + # check that there are NO footnotes/citations, only comments: + let input2 = dedent """ + .. [1 #] Body1. + .. [# note] Body2. + .. [wrong citation] That gives you a comment. + + .. [not&allowed] That gives you a comment. + + Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_. + """ + let output2 = input2.toHtml(preferRst) + doAssert output2 == "Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_." + + # check that auto-symbol footnotes work: + let input3 = dedent """ + Ref. [*]_ and [*]_ and [*]_. + + .. [*] Body1 + .. [*] Body2. + + + .. [*] Body3. + .. [*] Body4 + + And [*]_. + """ + let output3 = input3.toHtml(preferRst) + # both references and footnotes. Footnotes have link to themselves. + doAssert output3.count("href=\"#footnotesym-1\">[*]</a>") == 2 + doAssert output3.count("href=\"#footnotesym-2\">[**]</a>") == 2 + doAssert output3.count("href=\"#footnotesym-3\">[***]</a>") == 2 + doAssert output3.count("href=\"#footnotesym-4\">[^]</a>") == 2 + # footnote group + doAssert output3.count("<hr class=\"footnote\">" & + "<div class=\"footnote-group\">") == 1 + # footnotes + doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" & + "<a href=\"#footnotesym-1\">[*]</a></strong></sup></div>") == 1 + doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" & + "<a href=\"#footnotesym-2\">[**]</a></strong></sup></div>") == 1 + doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" & + "<a href=\"#footnotesym-3\">[***]</a></strong></sup></div>") == 1 + doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" & + "<a href=\"#footnotesym-4\">[^]</a></strong></sup></div>") == 1 + for i in 1 .. 4: doAssert ("Body" & $i) in output3 + + # check manual, auto-number and auto-label footnote enumeration + let input4 = dedent """ + .. [3] Manual1. + .. [#] Auto-number1. + .. [#mylabel] Auto-label1. + .. [#note] Auto-label2. + .. [#] Auto-number2. + + Ref. [#note]_ and [#]_ and [#]_. + """ + let output4 = input4.toHtml(preferRst) + doAssert ">[-1]" notin output1 + let order = @[ + "footnote-3", "[3]", "Manual1.", + "footnoteauto-1", "[1]", "Auto-number1", + "footnote-mylabel", "[2]", "Auto-label1", + "footnote-note", "[4]", "Auto-label2", + "footnoteauto-2", "[5]", "Auto-number2", + ] + for i in 0 .. order.len-2: + let pos1 = output4.find(order[i]) + let pos2 = output4.find(order[i+1]) + doAssert pos1 >= 0 + doAssert pos2 >= 0 + doAssert pos1 < pos2 + + # forgot [#]_ + let input5 = dedent """ + .. [3] Manual1. + .. [#] Auto-number1. + .. [#note] Auto-label2. + + Ref. [#note]_ + """ + var error5 = new string + let output5 = input5.toHtml(preferRst, error=error5) + check(error5[] == "input(1, 1) Error: mismatch in number of footnotes " & + "and their refs: 1 (lines 2) != 0 (lines ) for auto-numbered " & + "footnotes") + + # extra [*]_ + let input6 = dedent """ + Ref. [*]_ + + .. [*] Auto-Symbol. + + Ref. [*]_ + """ + var error6 = new string + let output6 = input6.toHtml(preferRst, error=error6) + check(error6[] == "input(1, 1) Error: mismatch in number of footnotes " & + "and their refs: 1 (lines 3) != 2 (lines 2, 6) for auto-symbol " & + "footnotes") + + let input7 = dedent """ + .. [Some:CITATION-2020] Citation. + + Ref. [some:citation-2020]_. + """ + let output7 = input7.toHtml(preferRst) + doAssert output7.count("href=\"#citation-somecoloncitationminus2020\"") == 2 + doAssert output7.count("[Some:CITATION-2020]") == 1 + doAssert output7.count("[some:citation-2020]") == 1 + doAssert output3.count("<hr class=\"footnote\">" & + "<div class=\"footnote-group\">") == 1 + + let input8 = dedent """ + .. [Some] Citation. + + Ref. [som]_. + """ + var warnings8 = new seq[string] + let output8 = input8.toHtml(preferRst, warnings=warnings8) + check(warnings8[] == @["input(3, 7) Warning: broken link 'citation-som'"]) + + # check that footnote group does not break parsing of other directives: + let input9 = dedent """ + .. [Some] Citation. + + .. _`internal anchor`: + + .. [Another] Citation. + .. just comment. + .. [Third] Citation. + + Paragraph1. + + Paragraph2 ref `internal anchor`_. + """ + let output9 = input9.toHtml(preferRst) + # _`internal anchor` got erased: + check "href=\"#internal-anchor\"" notin output9 + check "href=\"#citation-another\"" in output9 + doAssert output9.count("<hr class=\"footnote\">" & + "<div class=\"footnote-group\">") == 1 + doAssert output9.count("<div class=\"footnote-label\">") == 3 + doAssert "just comment" notin output9 + + # check that nested citations/footnotes work + let input10 = dedent """ + Paragraph1 [#]_. + + .. [First] Citation. + + .. [#] Footnote. + + .. [Third] Citation. + """ + let output10 = input10.toHtml(preferRst) + doAssert output10.count("<hr class=\"footnote\">" & + "<div class=\"footnote-group\">") == 3 + doAssert output10.count("<div class=\"footnote-label\">") == 3 + doAssert "<a href=\"#citation-first\">[First]</a>" in output10 + doAssert "<a href=\"#footnoteauto-1\">[1]</a>" in output10 + doAssert "<a href=\"#citation-third\">[Third]</a>" in output10 + + let input11 = ".. [note]\n" # should not crash + let output11 = input11.toHtml(preferRst) + doAssert "<a href=\"#citation-note\">[note]</a>" in output11 + + # check that references to auto-numbered footnotes work + let input12 = dedent """ + Ref. [#]_ and [#]_ STOP. + + .. [#] Body1. + .. [#] Body3 + .. [2] Body2. + """ + let output12 = input12.toHtml(preferRst) + let orderAuto = @[ + "#footnoteauto-1", "[1]", + "#footnoteauto-2", "[3]", + "STOP.", + "Body1.", "Body3", "Body2." + ] + for i in 0 .. orderAuto.len-2: + let pos1 = output12.find(orderAuto[i]) + let pos2 = output12.find(orderAuto[i+1]) + doAssert pos1 >= 0 + doAssert pos2 >= 0 + doAssert pos1 < pos2 + + test "Nim (RST extension) code-block": + # check that presence of fields doesn't consume the following text as + # its code (which is a literal block) + let input0 = dedent """ + .. code-block:: nim + :number-lines: 0 + + Paragraph1""" + let output0 = input0.toHtml + doAssert "<p>Paragraph1</p>" in output0 + + test "Nim code-block :number-lines:": + let input = dedent """ + .. code-block:: nim + :number-lines: 55 + + x + y + """ + check "<pre class=\"line-nums\">55\n56\n</pre>" in input.toHtml + + test "Nim code-block indentation": + let input = dedent """ + .. code-block:: nim + :number-lines: 55 + + x + """ + let output = input.toHtml + check "<pre class=\"line-nums\">55\n</pre>" in output + check "<span class=\"Identifier\">x</span>" in output + + test "Nim code-block indentation": + let input = dedent """ + .. code-block:: nim + :number-lines: 55 + let a = 1 + """ + var error = new string + let output = input.toHtml(error=error) + check(error[] == "input(2, 3) Error: invalid field: " & + "extra arguments were given to number-lines: ' let a = 1'") + check "" == output + + test "code-block warning": + let input = dedent """ + .. code:: Nim + :unsupportedField: anything + + .. code:: unsupportedLang + + anything + + ```anotherLang + someCode + ``` + """ + let warnings = new seq[string] + let output = input.toHtml(warnings=warnings) + check(warnings[] == @[ + "input(2, 4) Warning: field 'unsupportedField' not supported", + "input(4, 11) Warning: language 'unsupportedLang' not supported", + "input(8, 4) Warning: language 'anotherLang' not supported" + ]) + check(output == "<pre class = \"listing\">anything</pre>" & + "<p><pre class = \"listing\">someCode</pre> </p>") + + test "RST admonitions": + # check that all admonitions are implemented + let input0 = dedent """ + .. admonition:: endOf admonition + .. attention:: endOf attention + .. caution:: endOf caution + .. danger:: endOf danger + .. error:: endOf error + .. hint:: endOf hint + .. important:: endOf important + .. note:: endOf note + .. tip:: endOf tip + .. warning:: endOf warning + """ + let output0 = input0.toHtml( + NoSandboxOpts + ) + for a in ["admonition", "attention", "caution", "danger", "error", "hint", + "important", "note", "tip", "warning" ]: + doAssert "endOf " & a & "</div>" in output0 + + # Test that admonition does not swallow up the next paragraph. + let input1 = dedent """ + .. error:: endOfError + + Test paragraph. + """ + let output1 = input1.toHtml( + NoSandboxOpts + ) + doAssert "endOfError</div>" in output1 + doAssert "<p>Test paragraph. </p>" in output1 + doAssert "class=\"admonition admonition-error\"" in output1 + + # Test that second line is parsed as continuation of the first line. + let input2 = dedent """ + .. error:: endOfError + Test2p. + + Test paragraph. + """ + let output2 = input2.toHtml( + NoSandboxOpts + ) + doAssert "endOfError Test2p.</div>" in output2 + doAssert "<p>Test paragraph. </p>" in output2 + doAssert "class=\"admonition admonition-error\"" in output2 + + let input3 = dedent """ + .. note:: endOfNote + """ + let output3 = input3.toHtml( + NoSandboxOpts + ) + doAssert "endOfNote</div>" in output3 + doAssert "class=\"admonition admonition-info\"" in output3 + + test "RST internal links": + let input1 = dedent """ + Start. + + .. _target000: + + Paragraph. + + .. _target001: + + * bullet list + * Y + + .. _target002: + + 1. enumeration list + 2. Y + + .. _target003: + + term 1 + Definition list 1. + + .. _target004: + + | line block + + .. _target005: + + :a: field list value + + .. _target006: + + -a option description + + .. _target007: + + :: + + Literal block + + .. _target008: + + Doctest blocks are not implemented. + + .. _target009: + + block quote + + .. _target010: + + ===== ===== ======= + A B A and B + ===== ===== ======= + False False False + ===== ===== ======= + + .. _target100: + + .. CAUTION:: admonition + + .. _target101: + + .. code:: nim + + const pi = 3.14 + + .. _target102: + + .. code-block:: + + const pi = 3.14 + + Paragraph2. + + .. _target202: + + ---- + + That was a transition. + """ + let output1 = input1.toHtml( + preferRst + ) + doAssert "<p id=\"target000\"" in output1 + doAssert "<ul id=\"target001\"" in output1 + doAssert "<ol id=\"target002\"" in output1 + doAssert "<dl id=\"target003\"" in output1 + doAssert "<p id=\"target004\"" in output1 + doAssert "<table id=\"target005\"" in output1 # field list + doAssert "<div id=\"target006\"" in output1 # option list + doAssert "<pre id=\"target007\"" in output1 + doAssert "<blockquote id=\"target009\"" in output1 + doAssert "<table id=\"target010\"" in output1 # just table + doAssert "<span id=\"target100\"" in output1 + doAssert "<pre id=\"target101\"" in output1 # code + doAssert "<pre id=\"target102\"" in output1 # code-block + doAssert "<hr id=\"target202\"" in output1 + + test "RST internal links for sections": + let input1 = dedent """ + .. _target101: + .. _target102: + + Section xyz + ----------- + + Ref. target101_ + """ + let output1 = input1.toHtml + # "target101" should be erased and changed to "section-xyz": + check "href=\"#target101\"" notin output1 + check "id=\"target101\"" notin output1 + check "href=\"#target102\"" notin output1 + check "id=\"target102\"" notin output1 + check "id=\"section-xyz\"" in output1 + check "href=\"#section-xyz\"" in output1 + + let input2 = dedent """ + .. _target300: + + Section xyz + =========== + + .. _target301: + + SubsectionA + ----------- + + Ref. target300_ and target301_. + + .. _target103: + + .. [cit2020] note. + + Ref. target103_. + + """ + let output2 = input2.toHtml(preferRst) + # "target101" should be erased and changed to "section-xyz": + doAssert "href=\"#target300\"" notin output2 + doAssert "id=\"target300\"" notin output2 + doAssert "href=\"#target301\"" notin output2 + doAssert "id=\"target301\"" notin output2 + doAssert "<h1 id=\"section-xyz\"" in output2 + doAssert "<h2 id=\"subsectiona\"" in output2 + # links should preserve their original names but point to section labels: + doAssert "href=\"#section-xyz\">target300" in output2 + doAssert "href=\"#subsectiona\">target301" in output2 + doAssert "href=\"#citation-cit2020\">target103" in output2 + + let output2l = rstToLatex(input2, {}) + doAssert "\\label{section-xyz}\\hypertarget{section-xyz}{}" in output2l + doAssert "\\hyperlink{section-xyz}{target300}" in output2l + doAssert "\\hyperlink{subsectiona}{target301}" in output2l + + test "RST internal links (inline)": + let input1 = dedent """ + Paragraph with _`some definition`. + + Ref. `some definition`_. + """ + let output1 = input1.toHtml + doAssert "<span class=\"target\" " & + "id=\"some-definition\">some definition</span>" in output1 + doAssert "Ref. <a class=\"reference internal\" " & + "href=\"#some-definition\">some definition</a>" in output1 + + test "RST references (additional symbols)": + # check that ., _, -, +, : are allowed symbols in references without ` ` + let input1 = dedent """ + sec.1 + ----- + + 2-other:sec+c_2 + ^^^^^^^^^^^^^^^ + + .. _link.1_2021: + + Paragraph + + Ref. sec.1_! and 2-other:sec+c_2_;and link.1_2021_. + """ + let output1 = input1.toHtml + doAssert "id=\"secdot1\"" in output1 + doAssert "id=\"Z2minusothercolonsecplusc-2\"" in output1 + check "id=\"linkdot1-2021\"" in output1 + let ref1 = "<a class=\"reference internal\" href=\"#secdot1\">sec.1</a>" + let ref2 = "<a class=\"reference internal\" href=\"#Z2minusothercolonsecplusc-2\">2-other:sec+c_2</a>" + let ref3 = "<a class=\"reference internal\" href=\"#linkdot1-2021\">link.1_2021</a>" + let refline = "Ref. " & ref1 & "! and " & ref2 & ";and " & ref3 & "." + doAssert refline in output1 + + test "Option lists 1": + # check that "* b" is not consumed by previous bullet item because of + # incorrect indentation handling in option lists + let input = dedent """ + * a + -m desc + -n very long + desc + * b""" + let output = input.toHtml + check(output.count("<ul") == 1) + check(output.count("<li>") == 2) + check(output.count("<div class=\"option-list\"") == 1) + check(optionListLabel("-m") & + """<div class="option-list-description">desc</div></div>""" in + output) + check(optionListLabel("-n") & + """<div class="option-list-description">very long desc</div></div>""" in + output) + + test "Option lists 2": + # check that 2nd option list is not united with the 1st + let input = dedent """ + * a + -m desc + -n very long + desc + -d option""" + let output = input.toHtml + check(output.count("<ul") == 1) + check output.count("<div class=\"option-list\"") == 2 + check(optionListLabel("-m") & + """<div class="option-list-description">desc</div></div>""" in + output) + check(optionListLabel("-n") & + """<div class="option-list-description">very long desc</div></div>""" in + output) + check(optionListLabel("-d") & + """<div class="option-list-description">option</div></div>""" in + output) + check "<p>option</p>" notin output + + test "Option list 3 (double /)": + let input = dedent """ + * a + //compile compile1 + //doc doc1 + cont + -d option""" + let output = input.toHtml + check(output.count("<ul") == 1) + check output.count("<div class=\"option-list\"") == 2 + check(optionListLabel("compile") & + """<div class="option-list-description">compile1</div></div>""" in + output) + check(optionListLabel("doc") & + """<div class="option-list-description">doc1 cont</div></div>""" in + output) + check(optionListLabel("-d") & + """<div class="option-list-description">option</div></div>""" in + output) + check "<p>option</p>" notin output + + test "Roles: subscript prefix/postfix": + let expected = "See <sub>some text</sub>." + check "See :subscript:`some text`.".toHtml == expected + check "See `some text`:subscript:.".toHtml == expected + + test "Roles: correct parsing from beginning of line": + let expected = "<sup>3</sup>He is an isotope of helium." + check """:superscript:`3`\ He is an isotope of helium.""".toHtml == expected + check """:sup:`3`\ He is an isotope of helium.""".toHtml == expected + check """`3`:sup:\ He is an isotope of helium.""".toHtml == expected + check """`3`:superscript:\ He is an isotope of helium.""".toHtml == expected + + test "Roles: warnings": + let input = dedent""" + See function :py:func:`spam`. + + See also `egg`:py:class:. + """ + var warnings = new seq[string] + let output = input.toHtml(warnings=warnings) + doAssert warnings[].len == 2 + check "(1, 14) Warning: " in warnings[0] + check "language 'py:func' not supported" in warnings[0] + check "(3, 15) Warning: " in warnings[1] + check "language 'py:class' not supported" in warnings[1] + check("""<p>See function <span class="py:func">spam</span>.</p>""" & "\n" & + """<p>See also <span class="py:class">egg</span>. </p>""" == + output) + + test "(not) Roles: check escaping 1": + let expected = """See :subscript:<tt class="docutils literal">""" & + """<span class="pre">""" & id"some" & " " & id"text" & + "</span></tt>." + check """See \:subscript:`some text`.""".toHtml == expected + check """See :subscript\:`some text`.""".toHtml == expected + + test "(not) Roles: check escaping 2": + check("""See :subscript:\`some text\`.""".toHtml == + "See :subscript:`some text`.") + + test "Field list": + check(":field: text".toHtml == + """<table class="docinfo" frame="void" rules="none">""" & + """<col class="docinfo-name" /><col class="docinfo-content" />""" & + """<tbody valign="top"><tr><th class="docinfo-name">field:</th>""" & + """<td>text</td></tr>""" & "\n</tbody></table>") + + test "Field list: body after newline": + let output = dedent""" + :field: + text1""".toHtml + check "<table class=\"docinfo\"" in output + check ">field:</th>" in output + check "<td>text1</td>" in output + + test "Field list (incorrect)": + check ":field:text".toHtml == ":field:text" + +suite "RST/Code highlight": + test "Basic Python code highlight": + let pythonCode = """ + .. code-block:: python + + def f_name(arg=42): + print(f"{arg}") + + """ + + let expected = """<blockquote><p><span class="Keyword">def</span> f_name<span class="Punctuation">(</span><span class="Punctuation">arg</span><span class="Operator">=</span><span class="DecNumber">42</span><span class="Punctuation">)</span><span class="Punctuation">:</span> + print<span class="Punctuation">(</span><span class="RawData">f"{arg}"</span><span class="Punctuation">)</span></p></blockquote>""" + + check strip(rstToHtml(pythonCode, {}, newStringTable(modeCaseSensitive))) == + strip(expected) + + +suite "invalid targets": + test "invalid image target": + let input1 = dedent """.. image:: /images/myimage.jpg + :target: https://bar.com + :alt: Alt text for the image""" + let output1 = input1.toHtml + check output1 == """<a class="reference external" href="https://bar.com"><img src="/images/myimage.jpg" alt="Alt text for the image"/></a>""" + + let input2 = dedent """.. image:: /images/myimage.jpg + :target: javascript://bar.com + :alt: Alt text for the image""" + let output2 = input2.toHtml + check output2 == """<img src="/images/myimage.jpg" alt="Alt text for the image"/>""" + + let input3 = dedent """.. image:: /images/myimage.jpg + :target: bar.com + :alt: Alt text for the image""" + let output3 = input3.toHtml + check output3 == """<a class="reference external" href="bar.com"><img src="/images/myimage.jpg" alt="Alt text for the image"/></a>""" + + test "invalid links": + check("(([Nim](https://nim-lang.org/)))".toHtml == + """((<a class="reference external" href="https://nim-lang.org/">Nim</a>))""") + # unknown protocol is treated just like plain text, not a link + var warnings = new seq[string] + check("(([Nim](javascript://nim-lang.org/)))".toHtml(warnings=warnings) == + """(([Nim](javascript://nim-lang.org/)))""") + check(warnings[] == @["input(1, 9) Warning: broken link 'javascript'"]) + warnings[].setLen 0 + check("`Nim <javascript://nim-lang.org/>`_".toHtml(warnings=warnings) == + """Nim <javascript://nim-lang.org/>""") + check(warnings[] == @["input(1, 33) Warning: broken link 'javascript'"]) + +suite "local file inclusion": + test "cannot include files in sandboxed mode": + var error = new string + discard ".. include:: ./readme.md".toHtml(error=error) + check(error[] == "input(1, 11) Error: disabled directive: 'include'") + + test "code-block file directive is disabled": + var error = new string + discard ".. code-block:: nim\n :file: ./readme.md".toHtml(error=error) + check(error[] == "input(2, 20) Error: disabled directive: 'file'") + + test "code-block file directive is disabled - Markdown": + var error = new string + discard "```nim file = ./readme.md\n```".toHtml(error=error) + check(error[] == "input(1, 23) Error: disabled directive: 'file'") + +proc documentToHtml*(doc: string, isMarkdown: bool = false): string {.gcsafe.} = + var options = {roSupportMarkdown} + if isMarkdown: + options.incl roPreferMarkdown + result = rstToHtml(doc, options, defaultConfig()) diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim index ea3f06982..1094ae233 100644 --- a/tests/stdlib/tsequtils.nim +++ b/tests/stdlib/tsequtils.nim @@ -1,6 +1,18 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + +# xxx move all tests under `main` + import std/sequtils import strutils from algorithm import sorted +import std/assertions + +{.experimental: "strictEffects".} +{.push warningAsError[Effect]: on.} +{.experimental: "strictFuncs".} # helper for testing double substitution side effects which are handled # by `evalOnceAs` @@ -15,7 +27,7 @@ block: # concat test s2 = @[4, 5] s3 = @[6, 7] total = concat(s1, s2, s3) - assert total == @[1, 2, 3, 4, 5, 6, 7] + doAssert total == @[1, 2, 3, 4, 5, 6, 7] block: # count test let @@ -35,18 +47,18 @@ block: # count test ar3 = count(a2, 'y') ar4 = count(a2, 'x') ar5 = count(a2, 'a') - assert r0 == 0 - assert r1 == 1 - assert r2 == 2 - assert r3 == 0 - assert r4 == 1 - assert r5 == 2 - assert ar0 == 0 - assert ar1 == 1 - assert ar2 == 2 - assert ar3 == 0 - assert ar4 == 1 - assert ar5 == 2 + doAssert r0 == 0 + doAssert r1 == 1 + doAssert r2 == 2 + doAssert r3 == 0 + doAssert r4 == 1 + doAssert r5 == 2 + doAssert ar0 == 0 + doAssert ar1 == 1 + doAssert ar2 == 2 + doAssert ar3 == 0 + doAssert ar4 == 1 + doAssert ar5 == 2 block: # cycle tests let @@ -62,9 +74,9 @@ block: # cycle tests doAssert c.cycle(0) == @[] block: # repeat tests - assert repeat(10, 5) == @[10, 10, 10, 10, 10] - assert repeat(@[1, 2, 3], 2) == @[@[1, 2, 3], @[1, 2, 3]] - assert repeat([1, 2, 3], 2) == @[[1, 2, 3], [1, 2, 3]] + doAssert repeat(10, 5) == @[10, 10, 10, 10, 10] + doAssert repeat(@[1, 2, 3], 2) == @[@[1, 2, 3], @[1, 2, 3]] + doAssert repeat([1, 2, 3], 2) == @[[1, 2, 3], [1, 2, 3]] block: # deduplicates test let @@ -80,14 +92,14 @@ block: # deduplicates test unique6 = deduplicate(dup2, true) unique7 = deduplicate(dup3.sorted, true) unique8 = deduplicate(dup4, true) - assert unique1 == @[1, 3, 4, 2, 8] - assert unique2 == @["a", "c", "d"] - assert unique3 == @[1, 3, 4, 2, 8] - assert unique4 == @["a", "c", "d"] - assert unique5 == @[1, 2, 3, 4, 8] - assert unique6 == @["a", "c", "d"] - assert unique7 == @[1, 2, 3, 4, 8] - assert unique8 == @["a", "c", "d"] + doAssert unique1 == @[1, 3, 4, 2, 8] + doAssert unique2 == @["a", "c", "d"] + doAssert unique3 == @[1, 3, 4, 2, 8] + doAssert unique4 == @["a", "c", "d"] + doAssert unique5 == @[1, 2, 3, 4, 8] + doAssert unique6 == @["a", "c", "d"] + doAssert unique7 == @[1, 2, 3, 4, 8] + doAssert unique8 == @["a", "c", "d"] block: # zip test let @@ -100,29 +112,29 @@ block: # zip test zip1 = zip(short, long) zip2 = zip(short, words) zip3 = zip(ashort, along) - assert zip1 == @[(1, 6), (2, 5), (3, 4)] - assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] - assert zip3 == @[(1, 6), (2, 5), (3, 4)] - assert zip1[2][1] == 4 - assert zip2[2][1] == "three" - assert zip3[2][1] == 4 + doAssert zip1 == @[(1, 6), (2, 5), (3, 4)] + doAssert zip2 == @[(1, "one"), (2, "two"), (3, "three")] + doAssert zip3 == @[(1, 6), (2, 5), (3, 4)] + doAssert zip1[2][1] == 4 + doAssert zip2[2][1] == "three" + doAssert zip3[2][1] == 4 when (NimMajor, NimMinor) <= (1, 0): let # In Nim 1.0.x and older, zip returned a seq of tuple strictly # with fields named "a" and "b". zipAb = zip(ashort, awords) - assert zipAb == @[(a: 1, b: "one"), (2, "two"), (3, "three")] - assert zipAb[2].b == "three" + doAssert zipAb == @[(a: 1, b: "one"), (2, "two"), (3, "three")] + doAssert zipAb[2].b == "three" else: let # As zip returns seq of anonymous tuples, they can be assigned # to any variable that's a sequence of named tuples too. zipXy: seq[tuple[x: int, y: string]] = zip(ashort, awords) zipMn: seq[tuple[m: int, n: string]] = zip(ashort, words) - assert zipXy == @[(x: 1, y: "one"), (2, "two"), (3, "three")] - assert zipMn == @[(m: 1, n: "one"), (2, "two"), (3, "three")] - assert zipXy[2].y == "three" - assert zipMn[2].n == "three" + doAssert zipXy == @[(x: 1, y: "one"), (2, "two"), (3, "three")] + doAssert zipMn == @[(m: 1, n: "one"), (2, "two"), (3, "three")] + doAssert zipXy[2].y == "three" + doAssert zipMn[2].n == "three" block: # distribute tests let numbers = @[1, 2, 3, 4, 5, 6, 7] @@ -156,13 +168,13 @@ block: # map test anumbers = [1, 4, 5, 8, 9, 7, 4] m1 = map(numbers, proc(x: int): int = 2*x) m2 = map(anumbers, proc(x: int): int = 2*x) - assert m1 == @[2, 8, 10, 16, 18, 14, 8] - assert m2 == @[2, 8, 10, 16, 18, 14, 8] + doAssert m1 == @[2, 8, 10, 16, 18, 14, 8] + doAssert m2 == @[2, 8, 10, 16, 18, 14, 8] block: # apply test var a = @["1", "2", "3", "4"] apply(a, proc(x: var string) = x &= "42") - assert a == @["142", "242", "342", "442"] + doAssert a == @["142", "242", "342", "442"] block: # filter proc test let @@ -172,34 +184,23 @@ block: # filter proc test f2 = filter(colors) do (x: string) -> bool: x.len > 5 f3 = filter(acolors, proc(x: string): bool = x.len < 6) f4 = filter(acolors) do (x: string) -> bool: x.len > 5 - assert f1 == @["red", "black"] - assert f2 == @["yellow"] - assert f3 == @["red", "black"] - assert f4 == @["yellow"] + doAssert f1 == @["red", "black"] + doAssert f2 == @["yellow"] + doAssert f3 == @["red", "black"] + doAssert f4 == @["yellow"] block: # filter iterator test let numbers = @[1, 4, 5, 8, 9, 7, 4] let anumbers = [1, 4, 5, 8, 9, 7, 4] - assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == + doAssert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == @[4, 8, 4] - assert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) == + doAssert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) == @[4, 8, 4] block: # keepIf test var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] keepIf(floats, proc(x: float): bool = x > 10) - assert floats == @[13.0, 12.5, 10.1] - -block: # delete tests - let outcome = @[1, 1, 1, 1, 1, 1, 1, 1] - var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] - dest.delete(3, 8) - assert outcome == dest, """\ - Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1] - is [1,1,1,1,1,1,1,1]""" - var x = @[1, 2, 3] - x.delete(100, 100) - assert x == @[1, 2, 3] + doAssert floats == @[13.0, 12.5, 10.1] block: # insert tests var dest = @[1, 1, 1, 1, 1, 1, 1, 1] @@ -207,7 +208,7 @@ block: # insert tests src = @[2, 2, 2, 2, 2, 2] outcome = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] dest.insert(src, 3) - assert dest == outcome, """\ + doAssert dest == outcome, """\ Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1] at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]""" @@ -216,57 +217,57 @@ block: # filterIt test temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] acceptable = filterIt(temperatures, it < 50 and it > -10) notAcceptable = filterIt(temperatures, it > 50 or it < -10) - assert acceptable == @[-2.0, 24.5, 44.31] - assert notAcceptable == @[-272.15, 99.9, -113.44] + doAssert acceptable == @[-2.0, 24.5, 44.31] + doAssert notAcceptable == @[-272.15, 99.9, -113.44] block: # keepItIf test var candidates = @["foo", "bar", "baz", "foobar"] keepItIf(candidates, it.len == 3 and it[0] == 'b') - assert candidates == @["bar", "baz"] + doAssert candidates == @["bar", "baz"] block: # all let numbers = @[1, 4, 5, 8, 9, 7, 4] anumbers = [1, 4, 5, 8, 9, 7, 4] len0seq: seq[int] = @[] - assert all(numbers, proc (x: int): bool = return x < 10) == true - assert all(numbers, proc (x: int): bool = return x < 9) == false - assert all(len0seq, proc (x: int): bool = return false) == true - assert all(anumbers, proc (x: int): bool = return x < 10) == true - assert all(anumbers, proc (x: int): bool = return x < 9) == false + doAssert all(numbers, proc (x: int): bool = return x < 10) == true + doAssert all(numbers, proc (x: int): bool = return x < 9) == false + doAssert all(len0seq, proc (x: int): bool = return false) == true + doAssert all(anumbers, proc (x: int): bool = return x < 10) == true + doAssert all(anumbers, proc (x: int): bool = return x < 9) == false block: # allIt let numbers = @[1, 4, 5, 8, 9, 7, 4] anumbers = [1, 4, 5, 8, 9, 7, 4] len0seq: seq[int] = @[] - assert allIt(numbers, it < 10) == true - assert allIt(numbers, it < 9) == false - assert allIt(len0seq, false) == true - assert allIt(anumbers, it < 10) == true - assert allIt(anumbers, it < 9) == false + doAssert allIt(numbers, it < 10) == true + doAssert allIt(numbers, it < 9) == false + doAssert allIt(len0seq, false) == true + doAssert allIt(anumbers, it < 10) == true + doAssert allIt(anumbers, it < 9) == false block: # any let numbers = @[1, 4, 5, 8, 9, 7, 4] anumbers = [1, 4, 5, 8, 9, 7, 4] len0seq: seq[int] = @[] - assert any(numbers, proc (x: int): bool = return x > 8) == true - assert any(numbers, proc (x: int): bool = return x > 9) == false - assert any(len0seq, proc (x: int): bool = return true) == false - assert any(anumbers, proc (x: int): bool = return x > 8) == true - assert any(anumbers, proc (x: int): bool = return x > 9) == false + doAssert any(numbers, proc (x: int): bool = return x > 8) == true + doAssert any(numbers, proc (x: int): bool = return x > 9) == false + doAssert any(len0seq, proc (x: int): bool = return true) == false + doAssert any(anumbers, proc (x: int): bool = return x > 8) == true + doAssert any(anumbers, proc (x: int): bool = return x > 9) == false block: # anyIt let numbers = @[1, 4, 5, 8, 9, 7, 4] anumbers = [1, 4, 5, 8, 9, 7, 4] len0seq: seq[int] = @[] - assert anyIt(numbers, it > 8) == true - assert anyIt(numbers, it > 9) == false - assert anyIt(len0seq, true) == false - assert anyIt(anumbers, it > 8) == true - assert anyIt(anumbers, it > 9) == false + doAssert anyIt(numbers, it > 8) == true + doAssert anyIt(numbers, it > 9) == false + doAssert anyIt(len0seq, true) == false + doAssert anyIt(anumbers, it > 8) == true + doAssert anyIt(anumbers, it > 9) == false block: # toSeq test block: @@ -275,7 +276,7 @@ block: # toSeq test oddNumbers = toSeq(filter(numeric) do (x: int) -> bool: if x mod 2 == 1: result = true) - assert oddNumbers == @[1, 3, 5, 7, 9] + doAssert oddNumbers == @[1, 3, 5, 7, 9] block: doAssert [1, 2].toSeq == @[1, 2] @@ -298,43 +299,45 @@ block: # toSeq test doAssert myIter.toSeq == @[1, 2] doAssert toSeq(myIter) == @[1, 2] - block: - iterator myIter(): int {.closure.} = - yield 1 - yield 2 - - doAssert myIter.toSeq == @[1, 2] - doAssert toSeq(myIter) == @[1, 2] + when not defined(js): + # pending #4695 + block: + iterator myIter(): int {.closure.} = + yield 1 + yield 2 - block: - proc myIter(): auto = - iterator ret(): int {.closure.} = - yield 1 - yield 2 - result = ret + doAssert myIter.toSeq == @[1, 2] + doAssert toSeq(myIter) == @[1, 2] - doAssert myIter().toSeq == @[1, 2] - doAssert toSeq(myIter()) == @[1, 2] + block: + proc myIter(): auto = + iterator ret(): int {.closure.} = + yield 1 + yield 2 + result = ret - block: - proc myIter(n: int): auto = - var counter = 0 - iterator ret(): int {.closure.} = - while counter < n: - yield counter - counter.inc - result = ret + doAssert myIter().toSeq == @[1, 2] + doAssert toSeq(myIter()) == @[1, 2] block: - let myIter3 = myIter(3) - doAssert myIter3.toSeq == @[0, 1, 2] - block: - let myIter3 = myIter(3) - doAssert toSeq(myIter3) == @[0, 1, 2] - block: - # makes sure this does not hang forever - doAssert myIter(3).toSeq == @[0, 1, 2] - doAssert toSeq(myIter(3)) == @[0, 1, 2] + proc myIter(n: int): auto = + var counter = 0 + iterator ret(): int {.closure.} = + while counter < n: + yield counter + counter.inc + result = ret + + block: + let myIter3 = myIter(3) + doAssert myIter3.toSeq == @[0, 1, 2] + block: + let myIter3 = myIter(3) + doAssert toSeq(myIter3) == @[0, 1, 2] + block: + # makes sure this does not hang forever + doAssert myIter(3).toSeq == @[0, 1, 2] + doAssert toSeq(myIter(3)) == @[0, 1, 2] block: # tests https://github.com/nim-lang/Nim/issues/7187 @@ -350,10 +353,10 @@ block: # foldl tests multiplication = foldl(numbers, a * b) words = @["nim", "is", "cool"] concatenation = foldl(words, a & b) - assert addition == 25, "Addition is (((5)+9)+11)" - assert subtraction == -15, "Subtraction is (((5)-9)-11)" - assert multiplication == 495, "Multiplication is (((5)*9)*11)" - assert concatenation == "nimiscool" + doAssert addition == 25, "Addition is (((5)+9)+11)" + doAssert subtraction == -15, "Subtraction is (((5)-9)-11)" + doAssert multiplication == 495, "Multiplication is (((5)*9)*11)" + doAssert concatenation == "nimiscool" block: # foldr tests let @@ -363,10 +366,10 @@ block: # foldr tests multiplication = foldr(numbers, a * b) words = @["nim", "is", "cool"] concatenation = foldr(words, a & b) - assert addition == 25, "Addition is (5+(9+(11)))" - assert subtraction == 7, "Subtraction is (5-(9-(11)))" - assert multiplication == 495, "Multiplication is (5*(9*(11)))" - assert concatenation == "nimiscool" + doAssert addition == 25, "Addition is (5+(9+(11)))" + doAssert subtraction == 7, "Subtraction is (5-(9-(11)))" + doAssert multiplication == 495, "Multiplication is (5*(9*(11)))" + doAssert concatenation == "nimiscool" doAssert toSeq(1..3).foldr(a + b) == 6 # issue #14404 block: # mapIt + applyIt test @@ -376,8 +379,8 @@ block: # mapIt + applyIt test strings = nums.identity.mapIt($(4 * it)) doAssert counter == 1 nums.applyIt(it * 3) - assert nums[0] + nums[3] == 15 - assert strings[2] == "12" + doAssert nums[0] + nums[3] == 15 + doAssert strings[2] == "12" block: # newSeqWith tests var seq2D = newSeqWith(4, newSeq[bool](2)) @@ -386,6 +389,11 @@ block: # newSeqWith tests seq2D[0][1] = true doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] +block: # bug #21538 + var x: seq[int] = @[2, 4] + var y = newSeqWith(x.pop(), true) + doAssert y == @[true, true, true, true] + block: # mapLiterals tests let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int) doAssert x is array[4, int] @@ -410,13 +418,11 @@ block: # mapIt with direct openArray template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10) counter = 0 doAssert foo2(openArray[int]([identity(1), identity(2)])) == @[10, 20] - # TODO: this fails; not sure how to fix this case - # doAssert counter == 2 + doAssert counter == 2 counter = 0 doAssert openArray[int]([identity(1), identity(2)]).mapIt(it) == @[1, 2] - # ditto - # doAssert counter == 2 + doAssert counter == 2 block: # mapIt empty test, see https://github.com/nim-lang/Nim/pull/8584#pullrequestreview-144723468 # NOTE: `[].mapIt(it)` is illegal, just as `let a = @[]` is (lacks type @@ -454,7 +460,88 @@ block: for i in 0..<len: yield i - doAssert: iter(3).mapIt(2*it).foldl(a + b) == 6 - -when not defined(testing): - echo "Finished doc tests" + # xxx: obscure CT error: basic_types.nim(16, 16) Error: internal error: symbol has no generated name: true + when not defined(js): + doAssert: iter(3).mapIt(2*it).foldl(a + b) == 6 + +block: # strictFuncs tests with ref object + type Foo = ref object + + let foo1 = Foo() + let foo2 = Foo() + let foos = @[foo1, foo2] + + # Procedures that are `func` + discard concat(foos, foos) + discard count(foos, foo1) + discard cycle(foos, 3) + discard deduplicate(foos) + discard minIndex(foos) + discard maxIndex(foos) + discard distribute(foos, 2) + var mutableFoos = foos + mutableFoos.delete(0..1) + mutableFoos.insert(foos) + + # Some procedures that are `proc`, but were reverted from `func` + discard repeat(foo1, 3) + discard zip(foos, foos) + let fooTuples = @[(foo1, 1), (foo2, 2)] + discard unzip(fooTuples) + +template main = + # xxx move all tests here + block: # delete tests + let outcome = @[1, 1, 1, 1, 1, 1, 1, 1] + var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] + dest.delete(3, 8) + doAssert outcome == dest, """\ + Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1] + is [1,1,1,1,1,1,1,1]""" + var x = @[1, 2, 3] + x.delete(100, 100) + doAssert x == @[1, 2, 3] + + block: # delete tests + var a = @[10, 11, 12, 13, 14] + doAssertRaises(IndexDefect): a.delete(4..5) + doAssertRaises(IndexDefect): a.delete(4..<6) + doAssertRaises(IndexDefect): a.delete(-1..1) + doAssertRaises(IndexDefect): a.delete(-1 .. -1) + doAssertRaises(IndexDefect): a.delete(5..5) + doAssertRaises(IndexDefect): a.delete(5..3) + doAssertRaises(IndexDefect): a.delete(5..<5) # edge case + doAssert a == @[10, 11, 12, 13, 14] + a.delete(4..4) + doAssert a == @[10, 11, 12, 13] + a.delete(1..2) + doAssert a == @[10, 13] + a.delete(1..<1) # empty slice + doAssert a == @[10, 13] + a.delete(0..<0) + doAssert a == @[10, 13] + a.delete(0..0) + doAssert a == @[13] + a.delete(0..0) + doAssert a == @[] + doAssertRaises(IndexDefect): a.delete(0..0) + doAssertRaises(IndexDefect): a.delete(0..<0) # edge case + block: + type A = object + a0: int + var a = @[A(a0: 10), A(a0: 11), A(a0: 12)] + a.delete(0..1) + doAssert a == @[A(a0: 12)] + block: + type A = ref object + let a0 = A() + let a1 = A() + let a2 = A() + var a = @[a0, a1, a2] + a.delete(0..1) + doAssert a == @[a2] + +static: main() +main() + +{.pop.} diff --git a/tests/stdlib/tsetutils.nim b/tests/stdlib/tsetutils.nim new file mode 100644 index 000000000..c8498f23e --- /dev/null +++ b/tests/stdlib/tsetutils.nim @@ -0,0 +1,49 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + +import std/setutils +import std/assertions + +type + Colors = enum + red, green = 5, blue = 10 + Bar = enum + bar0 = -1, bar1, bar2 + +template main = + block: # toSet + doAssert "abcbb".toSet == {'a', 'b', 'c'} + doAssert toSet([10u8, 12, 13]) == {10u8, 12, 13} + doAssert toSet(0u16..30) == {0u16..30} + type A = distinct char + doAssert [A('x')].toSet == {A('x')} + + block: # fullSet + doAssert fullSet(Colors) == {red, green, blue} + doAssert fullSet(char) == {0.chr..255.chr} + doAssert fullSet(Bar) == {bar0, bar1, bar2} + doAssert fullSet(bool) == {true, false} + + block: # complement + doAssert {red, blue}.complement == {green} + doAssert (complement {red, green, blue}).card == 0 + doAssert (complement {false}) == {true} + doAssert {bar0}.complement == {bar1, bar2} + doAssert {range[0..10](0), 1, 2, 3}.complement == {range[0..10](4), 5, 6, 7, 8, 9, 10} + doAssert {'0'..'9'}.complement == {0.char..255.char} - {'0'..'9'} + + block: # `[]=` + type A = enum + a0, a1, a2, a3 + var s = {a0, a3} + s[a0] = false + s[a1] = false + doAssert s == {a3} + s[a2] = true + s[a3] = true + doAssert s == {a2, a3} + +main() +static: main() diff --git a/tests/stdlib/tsharedlist.nim b/tests/stdlib/tsharedlist.nim new file mode 100644 index 000000000..b91302d19 --- /dev/null +++ b/tests/stdlib/tsharedlist.nim @@ -0,0 +1,49 @@ +discard """ + matrix: "--mm:orc; --mm:refc" +""" + +import std/sharedlist +import std/assertions + +block: + var + list: SharedList[int] + count: int + + init(list) + + for i in 1 .. 250: + list.add i + + for i in list: + inc count + + doAssert count == 250 + + deinitSharedList(list) + + +block: # bug #17696 + var keysList = SharedList[string]() + init(keysList) + + keysList.add("a") + keysList.add("b") + keysList.add("c") + keysList.add("d") + keysList.add("e") + keysList.add("f") + + + # Remove element "b" and "d" from the list. + keysList.iterAndMutate(proc (key: string): bool = + if key == "b" or key == "d": # remove only "b" and "d" + return true + return false + ) + + var results: seq[string] + for key in keysList.items: + results.add key + + doAssert results == @["a", "f", "c", "e"] diff --git a/tests/stdlib/tsharedtable.nim b/tests/stdlib/tsharedtable.nim index ce6aa96df..10ad5f658 100644 --- a/tests/stdlib/tsharedtable.nim +++ b/tests/stdlib/tsharedtable.nim @@ -1,19 +1,20 @@ discard """ -cmd: "nim $target --threads:on $options $file" +matrix: "--mm:refc; --mm:orc" output: ''' ''' """ import sharedtables +import std/assertions block: var table: SharedTable[int, int] init(table) table[1] = 10 - assert table.mget(1) == 10 - assert table.mgetOrPut(3, 7) == 7 - assert table.mgetOrPut(3, 99) == 7 + doAssert table.mget(1) == 10 + doAssert table.mgetOrPut(3, 7) == 7 + doAssert table.mgetOrPut(3, 99) == 7 deinitSharedTable(table) import sequtils, algorithm diff --git a/tests/stdlib/tsince.nim b/tests/stdlib/tsince.nim index 14dd09c15..a0a4229cb 100644 --- a/tests/stdlib/tsince.nim +++ b/tests/stdlib/tsince.nim @@ -1,4 +1,5 @@ import std/private/since +import std/assertions proc fun1(): int {.since: (1, 3).} = 12 proc fun1Bad(): int {.since: (99, 3).} = 12 @@ -26,7 +27,6 @@ doAssert ok since (99, 3): doAssert false -when false: - # pending https://github.com/timotheecour/Nim/issues/129 - # Error: cannot attach a custom pragma to 'fun3' - template fun3(): int {.since: (1, 3).} = 12 +template fun3(): int {.since: (1, 3).} = 12 + +doAssert declared(fun3) diff --git a/tests/stdlib/tsocketstreams.nim b/tests/stdlib/tsocketstreams.nim new file mode 100644 index 000000000..a37e7c34c --- /dev/null +++ b/tests/stdlib/tsocketstreams.nim @@ -0,0 +1,65 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + output: ''' +OM +NIM +3 +NIM +NIM +Hello server! +Hi there client! +'''""" +import std/socketstreams, net, streams + +block UDP: + var recvSocket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) + var recvStream = newReadSocketStream(recvSocket) + recvSocket.bindAddr(Port(12345), "127.0.0.1") + + var sendSocket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) + sendSocket.connect("127.0.0.1", Port(12345)) + var sendStream = newWriteSocketStream(sendSocket) + sendStream.write "NOM\n" + sendStream.setPosition(1) + echo sendStream.peekStr(2) + sendStream.write "I" + sendStream.setPosition(0) + echo sendStream.readStr(3) + echo sendStream.getPosition() + sendStream.flush() + + echo recvStream.readLine() + recvStream.setPosition(0) + echo recvStream.readLine() + recvStream.close() + +block TCP: + var server = newSocket() + server.setSockOpt(OptReusePort, true) + server.bindAddr(Port(12345)) + server.listen() + + var + client = newSocket() + clientRequestStream = newWriteSocketStream(client) + clientResponseStream = newReadSocketStream(client) + client.connect("127.0.0.1", Port(12345)) + clientRequestStream.writeLine("Hello server!") + clientRequestStream.flush() + + var + incoming: Socket + address: string + server.acceptAddr(incoming, address) + var + serverRequestStream = newReadSocketStream(incoming) + serverResponseStream = newWriteSocketStream(incoming) + echo serverRequestStream.readLine() + serverResponseStream.writeLine("Hi there client!") + serverResponseStream.flush() + serverResponseStream.close() + serverRequestStream.close() + + echo clientResponseStream.readLine() + clientResponseStream.close() + clientRequestStream.close() diff --git a/tests/stdlib/tsortcall.nim b/tests/stdlib/tsortcall.nim index 242e3fe4c..32e004921 100644 --- a/tests/stdlib/tsortcall.nim +++ b/tests/stdlib/tsortcall.nim @@ -1,5 +1,5 @@ discard """ -outputsub: "" + matrix: "--mm:refc; --mm:orc" """ import algorithm diff --git a/tests/stdlib/tsqlitebindatas.nim b/tests/stdlib/tsqlitebindatas.nim new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/stdlib/tsqlitebindatas.nim diff --git a/tests/stdlib/tsqlparser.nim b/tests/stdlib/tsqlparser.nim index 11ee22e2b..6f123f21d 100644 --- a/tests/stdlib/tsqlparser.nim +++ b/tests/stdlib/tsqlparser.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc" output: '''true''' """ diff --git a/tests/stdlib/tssl.nim b/tests/stdlib/tssl.nim new file mode 100644 index 000000000..1628b9326 --- /dev/null +++ b/tests/stdlib/tssl.nim @@ -0,0 +1,138 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + joinable: false + disabled: "freebsd" # see #15713 + disabled: "openbsd" # see #15713 + disabled: "netbsd" # see #15713 +""" + +import std/[net, nativesockets, assertions, typedthreads] + +when defined(posix): import os, posix +else: + import winlean + const SD_SEND = 1 + +when not defined(ssl): + {.error: "This test must be compiled with -d:ssl".} + +const DummyData = "dummy data\n" + +proc abruptShutdown(port: Port) {.thread.} = + let clientContext = newContext(verifyMode = CVerifyNone) + var client = newSocket(buffered = false) + clientContext.wrapSocket(client) + client.connect("localhost", port) + + discard client.recvLine() + client.getFd.close() + +proc notifiedShutdown(port: Port) {.thread.} = + let clientContext = newContext(verifyMode = CVerifyNone) + var client = newSocket(buffered = false) + clientContext.wrapSocket(client) + client.connect("localhost", port) + + discard client.recvLine() + client.close() + +proc main() = + when defined(posix): + var + ignoreAction = Sigaction(sa_handler: SIG_IGN) + oldSigPipeHandler: Sigaction + if sigemptyset(ignoreAction.sa_mask) == -1: + raiseOSError(osLastError(), "Couldn't create an empty signal set") + if sigaction(SIGPIPE, ignoreAction, oldSigPipeHandler) == -1: + raiseOSError(osLastError(), "Couldn't ignore SIGPIPE") + + let serverContext = newContext(verifyMode = CVerifyNone, + certFile = "tests/testdata/mycert.pem", + keyFile = "tests/testdata/mycert.pem") + + block peer_close_during_write_without_shutdown: + var server = newSocket(buffered = false) + defer: server.close() + serverContext.wrapSocket(server) + server.bindAddr(address = "localhost") + let (_, port) = server.getLocalAddr() + server.listen() + + var clientThread: Thread[Port] + createThread(clientThread, abruptShutdown, port) + + var peer: Socket + try: + server.accept(peer) + peer.send(DummyData) + + joinThread clientThread + + while true: + # Send data until we get EPIPE. + peer.send(DummyData, {}) + except OSError: + discard + finally: + peer.close() + + when defined(posix): + if sigaction(SIGPIPE, oldSigPipeHandler, nil) == -1: + raiseOSError(osLastError(), "Couldn't restore SIGPIPE handler") + + block peer_close_before_received_shutdown: + var server = newSocket(buffered = false) + defer: server.close() + serverContext.wrapSocket(server) + server.bindAddr(address = "localhost") + let (_, port) = server.getLocalAddr() + server.listen() + + var clientThread: Thread[Port] + createThread(clientThread, abruptShutdown, port) + + var peer: Socket + try: + server.accept(peer) + peer.send(DummyData) + + joinThread clientThread + + # Tell the OS to close off the write side so shutdown attempts will + # be met with SIGPIPE. + when defined(posix): + discard peer.getFd.shutdown(SHUT_WR) + else: + discard peer.getFd.shutdown(SD_SEND) + finally: + peer.close() + + block peer_close_after_received_shutdown: + var server = newSocket(buffered = false) + defer: server.close() + serverContext.wrapSocket(server) + server.bindAddr(address = "localhost") + let (_, port) = server.getLocalAddr() + server.listen() + + var clientThread: Thread[Port] + createThread(clientThread, notifiedShutdown, port) + + var peer: Socket + try: + server.accept(peer) + peer.send(DummyData) + + doAssert peer.recv(1024) == "" # Get the shutdown notification + joinThread clientThread + + # Tell the OS to close off the write side so shutdown attempts will + # be met with SIGPIPE. + when defined(posix): + discard peer.getFd.shutdown(SHUT_WR) + else: + discard peer.getFd.shutdown(SD_SEND) + finally: + peer.close() + +when isMainModule: main() diff --git a/tests/stdlib/tssl.nims b/tests/stdlib/tssl.nims new file mode 100644 index 000000000..4739e7f07 --- /dev/null +++ b/tests/stdlib/tssl.nims @@ -0,0 +1,5 @@ +--threads:on +--d:ssl +when defined(freebsd) or defined(netbsd): + # See https://github.com/nim-lang/Nim/pull/15066#issuecomment-665541265 and https://github.com/nim-lang/Nim/issues/15493 + --tlsEmulation:off diff --git a/tests/stdlib/tstackframes.nim b/tests/stdlib/tstackframes.nim index 618ff7b92..b0f05d51d 100644 --- a/tests/stdlib/tstackframes.nim +++ b/tests/stdlib/tstackframes.nim @@ -1,4 +1,4 @@ -import std/[strformat,os,osproc] +import std/[strformat,os,osproc,assertions] import stdtest/unittest_light proc main(opt: string, expected: string) = diff --git a/tests/stdlib/tstaticos.nim b/tests/stdlib/tstaticos.nim new file mode 100644 index 000000000..41ab995dd --- /dev/null +++ b/tests/stdlib/tstaticos.nim @@ -0,0 +1,8 @@ +import std/[assertions, staticos, os] + +block: + static: + doAssert staticDirExists("MISSINGFILE") == false + doAssert staticFileExists("MISSINGDIR") == false + doAssert staticDirExists(currentSourcePath().parentDir) + doAssert staticFileExists(currentSourcePath()) diff --git a/tests/stdlib/tstats.nim b/tests/stdlib/tstats.nim new file mode 100644 index 000000000..728d93d09 --- /dev/null +++ b/tests/stdlib/tstats.nim @@ -0,0 +1,61 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/[stats, assertions] +import std/math + + +func `~=`(x, y: float32): bool = + math.almostEqual(x, y) + +template main() = + var rs: RunningStat + rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0]) + doAssert(rs.n == 8) + doAssert rs.mean ~= 2.0 + doAssert rs.variance() ~= 1.5 + doAssert rs.varianceS() ~= 1.71428571 + doAssert rs.skewness() ~= 0.81649658 + doAssert rs.skewnessS() ~= 1.01835015 + doAssert rs.kurtosis() ~= -1.0 + doAssert rs.kurtosisS() ~= -0.7000000000000001 + + var rs1, rs2: RunningStat + rs1.push(@[1.0, 2.0, 1.0, 4.0]) + rs2.push(@[1.0, 4.0, 1.0, 2.0]) + let rs3 = rs1 + rs2 + doAssert rs3.variance ~= rs.variance + doAssert rs3.skewness ~= rs.skewness + doAssert rs3.kurtosis ~= rs.kurtosis + rs1 += rs2 + doAssert rs1.variance ~= rs.variance + doAssert rs1.skewness ~= rs.skewness + doAssert rs1.kurtosis ~= rs.kurtosis + rs1.clear() + rs1.push(@[1.0, 2.2, 1.4, 4.9]) + doAssert rs1.sum ~= 9.5 + doAssert rs1.mean() ~= 2.375 + + when not defined(cpu32): + # XXX For some reason on 32bit CPUs these results differ + var rr: RunningRegress + rr.push(@[0.0, 1.0, 2.8, 3.0, 4.0], @[0.0, 1.0, 2.3, 3.0, 4.0]) + doAssert rr.slope() ~= 0.9695585996955861 + doAssert rr.intercept() ~= -0.03424657534246611 + doAssert rr.correlation() ~= 0.9905100362239381 + var rr1, rr2: RunningRegress + rr1.push(@[0.0, 1.0], @[0.0, 1.0]) + rr2.push(@[2.8, 3.0, 4.0], @[2.3, 3.0, 4.0]) + let rr3 = rr1 + rr2 + doAssert rr3.correlation() ~= rr.correlation() + doAssert rr3.slope() ~= rr.slope() + doAssert rr3.intercept() ~= rr.intercept() + + block: # bug #18718 + var rs: RunningStat + rs.push(-1.0) + doAssert rs.max == -1.0 + +static: main() +main() diff --git a/tests/stdlib/tstdlib_issues.nim b/tests/stdlib/tstdlib_issues.nim index 323bf09c6..b7b806db8 100644 --- a/tests/stdlib/tstdlib_issues.nim +++ b/tests/stdlib/tstdlib_issues.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc; --mm:orc" output: ''' 02 1 @@ -17,7 +18,7 @@ Second readLine raised an exception ''' """ -import terminal, colors, re, encodings, strutils, os +import std/[terminal, colors, re, encodings, strutils, os, assertions, syncio] block t9394: @@ -77,7 +78,7 @@ block t5349: const fn = "file9char.txt" writeFile(fn, "123456789") - var f = system.open(fn) + var f = syncio.open(fn) echo getFileSize(f) var line = newString(10) diff --git a/tests/stdlib/tstdlib_various.nim b/tests/stdlib/tstdlib_various.nim index cddd43f6e..bac5018fa 100644 --- a/tests/stdlib/tstdlib_various.nim +++ b/tests/stdlib/tstdlib_various.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc" output: ''' abc def @@ -20,27 +21,20 @@ Hi Andreas! How do you feel, Rumpf? @[0, 2, 1] @[0, 1, 2] 055this should be the casehugh@["(", "+", " 1", " 2", ")"] -caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash! [5] [4, 5] [3, 4, 5] [2, 3, 4, 5] [2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6] -true <h1><a href="http://force7.de/nim">Nim</a></h1> ''' """ import - critbits, cstrutils, sets, strutils, tables, random, algorithm, re, ropes, - segfaults, lists, parsesql, streams, os, htmlgen, xmltree, strtabs - + std/[critbits, sets, strutils, tables, random, algorithm, re, ropes, + segfaults, lists, parsesql, streams, os, htmlgen, xmltree, strtabs] +import std/[syncio, assertions] block tcritbits: var r: CritBitTree[void] @@ -161,18 +155,21 @@ block tropes: block tsegfaults: - proc main = - try: - var x: ptr int - echo x[] + when not defined(arm64): + var crashes = 0 + proc main = try: - raise newException(ValueError, "not a crash") - except ValueError: - discard - except NilAccessDefect: - echo "caught a crash!" - for i in 0..5: - main() + var x: ptr int + echo x[] + try: + raise newException(ValueError, "not a crash") + except ValueError: + discard + except NilAccessDefect: + inc crashes + for i in 0..5: + main() + assert crashes == 6 @@ -209,11 +206,7 @@ block tsplit2: s.add("#") s.add(w) - try: - discard "hello".split("") - echo "false" - except AssertionDefect: - echo "true" + doAssert "true".split("") == @["true"] @@ -245,24 +238,3 @@ block txmltree: ]) ]) doAssert(y.innerText == "foobar") - - -block tcstrutils: - let s = cstring "abcdef" - doAssert s.startsWith("a") - doAssert not s.startsWith("b") - doAssert s.endsWith("f") - doAssert not s.endsWith("a") - - let a = cstring "abracadabra" - doAssert a.startsWith("abra") - doAssert not a.startsWith("bra") - doAssert a.endsWith("abra") - doAssert not a.endsWith("dab") - - doAssert cmpIgnoreCase(cstring "FooBar", "foobar") == 0 - doAssert cmpIgnoreCase(cstring "bar", "Foo") < 0 - doAssert cmpIgnoreCase(cstring "Foo5", "foo4") > 0 - - doAssert cmpIgnoreStyle(cstring "foo_bar", "FooBar") == 0 - doAssert cmpIgnoreStyle(cstring "foo_bar_5", "FooBar4") > 0 diff --git a/tests/stdlib/tstrbasics.nim b/tests/stdlib/tstrbasics.nim new file mode 100644 index 000000000..a965ff15f --- /dev/null +++ b/tests/stdlib/tstrbasics.nim @@ -0,0 +1,103 @@ +discard """ + targets: "c cpp js" + matrix: "--mm:refc; --mm:orc" +""" + +import std/[strbasics, sugar, assertions] + +template strip2(input: string, args: varargs[untyped]): untyped = + var a = input + when varargsLen(args) > 0: + strip(a, args) + else: + strip(a) + a + +proc main() = + block: # strip + block: # bug #17173 + var a = " vhellov " + strip(a) + doAssert a == "vhellov" + + doAssert strip2(" vhellov ") == "vhellov" + doAssert strip2(" vhellov ", leading = false) == " vhellov" + doAssert strip2(" vhellov ", trailing = false) == "vhellov " + doAssert strip2("vhellov", chars = {'v'}) == "hello" + doAssert strip2("vhellov", leading = false, chars = {'v'}) == "vhello" + doAssert strip2("blaXbla", chars = {'b', 'a'}) == "laXbl" + doAssert strip2("blaXbla", chars = {'b', 'a', 'l'}) == "X" + doAssert strip2("xxxxxx", chars={'x'}) == "" + doAssert strip2("x", chars={'x'}) == "" + doAssert strip2("x", chars={'1'}) == "x" + doAssert strip2("", chars={'x'}) == "" + doAssert strip2("xxx xxx", chars={'x'}) == " " + doAssert strip2("xxx wind", chars={'x'}) == " wind" + doAssert strip2("xxx iii", chars={'i'}) == "xxx " + + block: + var a = "xxx iii" + doAssert a.dup(strip(chars = {'i'})) == "xxx " + doAssert a.dup(strip(chars = {' '})) == "xxx iii" + doAssert a.dup(strip(chars = {'x'})) == " iii" + doAssert a.dup(strip(chars = {'x', ' '})) == "iii" + doAssert a.dup(strip(chars = {'x', 'i'})) == " " + doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 + + block: + var a = "x i" + doAssert a.dup(strip(chars = {'i'})) == "x " + doAssert a.dup(strip(chars = {' '})) == "x i" + doAssert a.dup(strip(chars = {'x'})) == " i" + doAssert a.dup(strip(chars = {'x', ' '})) == "i" + doAssert a.dup(strip(chars = {'x', 'i'})) == " " + doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 + + block: + var a = "" + doAssert a.dup(strip(chars = {'i'})).len == 0 + doAssert a.dup(strip(chars = {' '})).len == 0 + doAssert a.dup(strip(chars = {'x'})).len == 0 + doAssert a.dup(strip(chars = {'x', ' '})).len == 0 + doAssert a.dup(strip(chars = {'x', 'i'})).len == 0 + doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 + + block: + var a = " " + doAssert a.dup(strip(chars = {'i'})) == " " + doAssert a.dup(strip(chars = {' '})).len == 0 + doAssert a.dup(strip(chars = {'x'})) == " " + doAssert a.dup(strip(chars = {'x', ' '})).len == 0 + doAssert a.dup(strip(chars = {'x', 'i'})) == " " + doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 + + block: # setSlice + var a = "Hello, Nim!" + doAssert a.dup(setSlice(7 .. 9)) == "Nim" + doAssert a.dup(setSlice(0 .. 0)) == "H" + doAssert a.dup(setSlice(0 .. 1)) == "He" + doAssert a.dup(setSlice(0 .. 10)) == a + doAssert a.dup(setSlice(1 .. 0)).len == 0 + doAssert a.dup(setSlice(20 .. -1)).len == 0 + + doAssertRaises(AssertionDefect): + discard a.dup(setSlice(-1 .. 1)) + + doAssertRaises(AssertionDefect): + discard a.dup(setSlice(1 .. 11)) + + block: # add + var a0 = "hi" + var b0 = "foobar" + when nimvm: + discard # pending bug #15952 + else: + a0.add b0.toOpenArray(1,3) + doAssert a0 == "hioob" + proc fn(c: openArray[char]): string = + result.add c + doAssert fn("def") == "def" + doAssert fn(['d','\0', 'f'])[2] == 'f' + +static: main() +main() diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index 354bdf60f..60c63b450 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -1,22 +1,22 @@ discard """ + matrix: "--mm:refc; --mm:orc" input: "Arne" output: ''' Hello! What is your name? Nice name: Arne fs is: nil - threw exception +_heh_ ''' nimout: ''' I AM GROOT ''' -disabled: "windows" """ -import streams +import std/[syncio, streams, assertions] block tstreams: @@ -41,7 +41,7 @@ block tstreams2: block tstreams3: try: var fs = openFileStream("shouldneverexist.txt") - except IoError: + except IOError: echo "threw exception" static: @@ -49,3 +49,59 @@ block tstreams3: for line in s.lines: echo line s.close + + +block: + let fs = newFileStream("amissingfile.txt") + defer: fs.close() + doAssert isNil(fs) + +# bug #12410 + +var a = newStringStream "hehohihahuhyh" +a.readDataStrImpl = nil + +var buffer = "_ooo_" + +doAssert a.readDataStr(buffer, 1..3) == 3 + +echo buffer + + +block: + var ss = newStringStream("The quick brown fox jumped over the lazy dog.\nThe lazy dog ran") + doAssert(ss.getPosition == 0) + doAssert(ss.peekStr(5) == "The q") + doAssert(ss.getPosition == 0) # haven't moved + doAssert(ss.readStr(5) == "The q") + doAssert(ss.getPosition == 5) # did move + doAssert(ss.peekLine() == "uick brown fox jumped over the lazy dog.") + doAssert(ss.getPosition == 5) # haven't moved + var str = newString(100) + doAssert(ss.peekLine(str)) + doAssert(str == "uick brown fox jumped over the lazy dog.") + doAssert(ss.getPosition == 5) # haven't moved + # bug #19707 - Ensure we dont error with writing over literals on arc/orc + ss.setPosition(0) + ss.write("hello") + ss.setPosition(0) + doAssert(ss.peekStr(5) == "hello") + +# bug #19716 +static: # Ensure streams it doesnt break with nimscript on arc/orc #19716 + let s = newStringStream("a") + doAssert s.data == "a" + +static: # issue #24054, readStr + var s = newStringStream("foo bar baz") + doAssert s.readStr(3) == "foo" + +template main = + var strm = newStringStream("abcde") + var buffer = "12345" + doAssert strm.readDataStr(buffer, 0..3) == 4 + doAssert buffer == "abcd5" + strm.close() + +static: main() +main() diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index c65165946..ff406f898 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -1,311 +1,590 @@ discard """ -action: "run" -output: '''Received (name: "Foo", species: "Bar")''' + matrix: "--mm:refc; --mm:orc" """ -# issue #7632 - import genericstrformat -import strutils - - -doAssert works(5) == "formatted 5" -doAssert fails0(6) == "formatted 6" -doAssert fails(7) == "formatted 7" -doAssert fails2[0](8) == "formatted 8" - -# other tests - -import strformat - -type Obj = object - -proc `$`(o: Obj): string = "foobar" - -# for custom types, formatValue needs to be overloaded. -template formatValue(result: var string; value: Obj; specifier: string) = - result.formatValue($value, specifier) - -var o: Obj -doAssert fmt"{o}" == "foobar" -doAssert fmt"{o:10}" == "foobar " - -# see issue #7933 -var str = "abc" -doAssert fmt">7.1 :: {str:>7.1}" == ">7.1 :: a" -doAssert fmt">7.2 :: {str:>7.2}" == ">7.2 :: ab" -doAssert fmt">7.3 :: {str:>7.3}" == ">7.3 :: abc" -doAssert fmt">7.9 :: {str:>7.9}" == ">7.9 :: abc" -doAssert fmt">7.0 :: {str:>7.0}" == ">7.0 :: " -doAssert fmt" 7.1 :: {str:7.1}" == " 7.1 :: a " -doAssert fmt" 7.2 :: {str:7.2}" == " 7.2 :: ab " -doAssert fmt" 7.3 :: {str:7.3}" == " 7.3 :: abc " -doAssert fmt" 7.9 :: {str:7.9}" == " 7.9 :: abc " -doAssert fmt" 7.0 :: {str:7.0}" == " 7.0 :: " -doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: a " -doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: ab " -doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: abc " -doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: abc " -doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: " -str = "äöüe\u0309\u0319o\u0307\u0359" -doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: ä " -doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: äö " -doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: äöü " -doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: " -# this is actually wrong, but the unicode module has no support for graphemes -doAssert fmt"^7.4 :: {str:^7.4}" == "^7.4 :: äöüe " -doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: äöüe\u0309\u0319o\u0307\u0359" - -# see issue #7932 -doAssert fmt"{15:08}" == "00000015" # int, works -doAssert fmt"{1.5:08}" == "000001.5" # float, works -doAssert fmt"{1.5:0>8}" == "000001.5" # workaround using fill char works for positive floats -doAssert fmt"{-1.5:0>8}" == "0000-1.5" # even that does not work for negative floats -doAssert fmt"{-1.5:08}" == "-00001.5" # works -doAssert fmt"{1.5:+08}" == "+00001.5" # works -doAssert fmt"{1.5: 08}" == " 00001.5" # works - -# only add explicitly requested sign if value != -0.0 (neg zero) -doAssert fmt"{-0.0:g}" == "-0" -doAssert fmt"{-0.0:+g}" == "-0" -doAssert fmt"{-0.0: g}" == "-0" -doAssert fmt"{0.0:g}" == "0" -doAssert fmt"{0.0:+g}" == "+0" -doAssert fmt"{0.0: g}" == " 0" - -# seq format - -let data1 = [1'i64, 10000'i64, 10000000'i64] -let data2 = [10000000'i64, 100'i64, 1'i64] - -proc formatValue(result: var string; value: (array|seq|openArray); specifier: string) = - result.add "[" - for i, it in value: - if i != 0: +import std/[strformat, strutils, times, tables, json] + +import std/[assertions, formatfloat] +import std/objectdollar + +proc main() = + block: # issue #7632 + doAssert works(5) == "formatted 5" + doAssert fails0(6) == "formatted 6" + doAssert fails(7) == "formatted 7" + doAssert fails2[0](8) == "formatted 8" + + block: # other tests + type Obj = object + + proc `$`(o: Obj): string = "foobar" + + # for custom types, formatValue needs to be overloaded. + template formatValue(result: var string; value: Obj; specifier: string) = + result.formatValue($value, specifier) + + var o: Obj + doAssert fmt"{o}" == "foobar" + doAssert fmt"{o:10}" == "foobar " + + doAssert fmt"{o=}" == "o=foobar" + doAssert fmt"{o=:10}" == "o=foobar " + + block: # see issue #7933 + var str = "abc" + doAssert fmt">7.1 :: {str:>7.1}" == ">7.1 :: a" + doAssert fmt">7.2 :: {str:>7.2}" == ">7.2 :: ab" + doAssert fmt">7.3 :: {str:>7.3}" == ">7.3 :: abc" + doAssert fmt">7.9 :: {str:>7.9}" == ">7.9 :: abc" + doAssert fmt">7.0 :: {str:>7.0}" == ">7.0 :: " + doAssert fmt" 7.1 :: {str:7.1}" == " 7.1 :: a " + doAssert fmt" 7.2 :: {str:7.2}" == " 7.2 :: ab " + doAssert fmt" 7.3 :: {str:7.3}" == " 7.3 :: abc " + doAssert fmt" 7.9 :: {str:7.9}" == " 7.9 :: abc " + doAssert fmt" 7.0 :: {str:7.0}" == " 7.0 :: " + doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: a " + doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: ab " + doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: abc " + doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: abc " + doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: " + + doAssert fmt">7.1 :: {str=:>7.1}" == ">7.1 :: str= a" + doAssert fmt">7.2 :: {str=:>7.2}" == ">7.2 :: str= ab" + doAssert fmt">7.3 :: {str=:>7.3}" == ">7.3 :: str= abc" + doAssert fmt">7.9 :: {str=:>7.9}" == ">7.9 :: str= abc" + doAssert fmt">7.0 :: {str=:>7.0}" == ">7.0 :: str= " + doAssert fmt" 7.1 :: {str=:7.1}" == " 7.1 :: str=a " + doAssert fmt" 7.2 :: {str=:7.2}" == " 7.2 :: str=ab " + doAssert fmt" 7.3 :: {str=:7.3}" == " 7.3 :: str=abc " + doAssert fmt" 7.9 :: {str=:7.9}" == " 7.9 :: str=abc " + doAssert fmt" 7.0 :: {str=:7.0}" == " 7.0 :: str= " + doAssert fmt"^7.1 :: {str=:^7.1}" == "^7.1 :: str= a " + doAssert fmt"^7.2 :: {str=:^7.2}" == "^7.2 :: str= ab " + doAssert fmt"^7.3 :: {str=:^7.3}" == "^7.3 :: str= abc " + doAssert fmt"^7.9 :: {str=:^7.9}" == "^7.9 :: str= abc " + doAssert fmt"^7.0 :: {str=:^7.0}" == "^7.0 :: str= " + str = "äöüe\u0309\u0319o\u0307\u0359" + doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: ä " + doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: äö " + doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: äöü " + doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: " + + doAssert fmt"^7.1 :: {str=:^7.1}" == "^7.1 :: str= ä " + doAssert fmt"^7.2 :: {str=:^7.2}" == "^7.2 :: str= äö " + doAssert fmt"^7.3 :: {str=:^7.3}" == "^7.3 :: str= äöü " + doAssert fmt"^7.0 :: {str=:^7.0}" == "^7.0 :: str= " + # this is actually wrong, but the unicode module has no support for graphemes + doAssert fmt"^7.4 :: {str:^7.4}" == "^7.4 :: äöüe " + doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: äöüe\u0309\u0319o\u0307\u0359" + + doAssert fmt"^7.4 :: {str=:^7.4}" == "^7.4 :: str= äöüe " + doAssert fmt"^7.9 :: {str=:^7.9}" == "^7.9 :: str=äöüe\u0309\u0319o\u0307\u0359" + + block: # see issue #7932 + doAssert fmt"{15:08}" == "00000015" # int, works + doAssert fmt"{1.5:08}" == "000001.5" # float, works + doAssert fmt"{1.5:0>8}" == "000001.5" # workaround using fill char works for positive floats + doAssert fmt"{-1.5:0>8}" == "0000-1.5" # even that does not work for negative floats + doAssert fmt"{-1.5:08}" == "-00001.5" # works + doAssert fmt"{1.5:+08}" == "+00001.5" # works + doAssert fmt"{1.5: 08}" == " 00001.5" # works + + doAssert fmt"{15=:08}" == "15=00000015" # int, works + doAssert fmt"{1.5=:08}" == "1.5=000001.5" # float, works + doAssert fmt"{1.5=:0>8}" == "1.5=000001.5" # workaround using fill char works for positive floats + doAssert fmt"{-1.5=:0>8}" == "-1.5=0000-1.5" # even that does not work for negative floats + doAssert fmt"{-1.5=:08}" == "-1.5=-00001.5" # works + doAssert fmt"{1.5=:+08}" == "1.5=+00001.5" # works + doAssert fmt"{1.5=: 08}" == "1.5= 00001.5" # works + + block: # only add explicitly requested sign if value != -0.0 (neg zero) + doAssert fmt"{-0.0:g}" == "-0" + doAssert fmt"{-0.0:+g}" == "-0" + doAssert fmt"{-0.0: g}" == "-0" + doAssert fmt"{0.0:g}" == "0" + doAssert fmt"{0.0:+g}" == "+0" + doAssert fmt"{0.0: g}" == " 0" + + doAssert fmt"{-0.0=:g}" == "-0.0=-0" + doAssert fmt"{-0.0=:+g}" == "-0.0=-0" + doAssert fmt"{-0.0=: g}" == "-0.0=-0" + doAssert fmt"{0.0=:g}" == "0.0=0" + doAssert fmt"{0.0=:+g}" == "0.0=+0" + doAssert fmt"{0.0=: g}" == "0.0= 0" + + block: # seq format + let data1 = [1'i64, 10000'i64, 10000000'i64] + let data2 = [10000000'i64, 100'i64, 1'i64] + + proc formatValue(result: var string; value: (array|seq|openArray); specifier: string) = + result.add "[" + for i, it in value: + if i != 0: + result.add ", " + result.formatValue(it, specifier) + result.add "]" + + doAssert fmt"data1: {data1:8} #" == "data1: [ 1, 10000, 10000000] #" + doAssert fmt"data2: {data2:8} =" == "data2: [10000000, 100, 1] =" + + doAssert fmt"data1: {data1=:8} #" == "data1: data1=[ 1, 10000, 10000000] #" + doAssert fmt"data2: {data2=:8} =" == "data2: data2=[10000000, 100, 1] =" + + block: # custom format Value + type + Vec2[T] = object + x,y: T + + proc formatValue[T](result: var string; value: Vec2[T]; specifier: string) = + result.add '[' + result.formatValue value.x, specifier result.add ", " - result.formatValue(it, specifier) - result.add "]" - -doAssert fmt"data1: {data1:8} #" == "data1: [ 1, 10000, 10000000] #" -doAssert fmt"data2: {data2:8} =" == "data2: [10000000, 100, 1] =" -doAssert fmt"data1: {data1=:8} #" == "data1: data1=[ 1, 10000, 10000000] #" -doAssert fmt"data2: {data2=:8} =" == "data2: data2=[10000000, 100, 1] =" - -# custom format Value - -type - Vec2[T] = object - x,y: T - -proc formatValue[T](result: var string; value: Vec2[T]; specifier: string) = - result.add '[' - result.formatValue value.x, specifier - result.add ", " - result.formatValue value.y, specifier - result.add "]" - -let v1 = Vec2[float32](x:1.0, y: 2.0) -let v2 = Vec2[int32](x:1, y: 1337) -doAssert fmt"v1: {v1:+08} v2: {v2:>4}" == "v1: [+0000001, +0000002] v2: [ 1, 1337]" -doAssert fmt"v1: {v1=:+08} v2: {v2=:>4}" == "v1: v1=[+0000001, +0000002] v2: v2=[ 1, 1337]" - -# bug #11012 - -type - Animal = object - name, species: string - AnimalRef = ref Animal - -proc print_object(animalAddr: AnimalRef) = - echo fmt"Received {animalAddr[]}" - -print_object(AnimalRef(name: "Foo", species: "Bar")) - -# bug #11723 - -let pos: Positive = 64 -doAssert fmt"{pos:3}" == " 64" -doAssert fmt"{pos:3b}" == "1000000" -doAssert fmt"{pos:3d}" == " 64" -doAssert fmt"{pos:3o}" == "100" -doAssert fmt"{pos:3x}" == " 40" -doAssert fmt"{pos:3X}" == " 40" - -doAssert fmt"{pos=:3}" == "pos= 64" -doAssert fmt"{pos=:3b}" == "pos=1000000" -doAssert fmt"{pos=:3d}" == "pos= 64" -doAssert fmt"{pos=:3o}" == "pos=100" -doAssert fmt"{pos=:3x}" == "pos= 40" -doAssert fmt"{pos=:3X}" == "pos= 40" - - -let nat: Natural = 64 -doAssert fmt"{nat:3}" == " 64" -doAssert fmt"{nat:3b}" == "1000000" -doAssert fmt"{nat:3d}" == " 64" -doAssert fmt"{nat:3o}" == "100" -doAssert fmt"{nat:3x}" == " 40" -doAssert fmt"{nat:3X}" == " 40" - -doAssert fmt"{nat=:3}" == "nat= 64" -doAssert fmt"{nat=:3b}" == "nat=1000000" -doAssert fmt"{nat=:3d}" == "nat= 64" -doAssert fmt"{nat=:3o}" == "nat=100" -doAssert fmt"{nat=:3x}" == "nat= 40" -doAssert fmt"{nat=:3X}" == "nat= 40" - -# bug #12612 -proc my_proc = - const value = "value" - const a = &"{value}" - assert a == value - -my_proc() - -block: - template fmt(pattern: string; openCloseChar: char): untyped = - fmt(pattern, openCloseChar, openCloseChar) - - let - testInt = 123 - testStr = "foobar" - testFlt = 3.141592 - doAssert ">><<".fmt('<', '>') == "><" - doAssert " >> << ".fmt('<', '>') == " > < " - doAssert "<<>>".fmt('<', '>') == "<>" - doAssert " << >> ".fmt('<', '>') == " < > " - doAssert "''".fmt('\'') == "'" - doAssert "''''".fmt('\'') == "''" - doAssert "'' ''".fmt('\'') == "' '" - doAssert "<testInt>".fmt('<', '>') == "123" - doAssert "<testInt>".fmt('<', '>') == "123" - doAssert "'testFlt:1.2f'".fmt('\'') == "3.14" - doAssert "<testInt><testStr>".fmt('<', '>') == "123foobar" - doAssert """ ""{"123+123"}"" """.fmt('"') == " \"{246}\" " - doAssert "(((testFlt:1.2f)))((111))".fmt('(', ')') == "(3.14)(111)" - doAssert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)" - doAssert "{}abc`testStr' `testFlt:1.2f' `1+1' ``".fmt('`', '\'') == "{}abcfoobar 3.14 2 `" - doAssert """x = '"foo" & "bar"' - y = '123 + 111' - z = '3 in {2..7}' - """.fmt('\'') == - """x = foobar - y = 234 - z = true - """ - - -# tests from the very own strformat documentation! - -let msg = "hello" -doAssert fmt"{msg}\n" == "hello\\n" - -doAssert &"{msg}\n" == "hello\n" - -doAssert fmt"{msg}{'\n'}" == "hello\n" -doAssert fmt("{msg}\n") == "hello\n" -doAssert "{msg}\n".fmt == "hello\n" - -doAssert fmt"{msg=}\n" == "msg=hello\\n" - -doAssert &"{msg=}\n" == "msg=hello\n" - -doAssert fmt"{msg=}{'\n'}" == "msg=hello\n" -doAssert fmt("{msg=}\n") == "msg=hello\n" -doAssert "{msg=}\n".fmt == "msg=hello\n" - -doAssert &"""{"abc":>4}""" == " abc" -doAssert &"""{"abc":<4}""" == "abc " - -doAssert fmt"{-12345:08}" == "-0012345" -doAssert fmt"{-1:3}" == " -1" -doAssert fmt"{-1:03}" == "-01" -doAssert fmt"{16:#X}" == "0x10" - -doAssert fmt"{123.456}" == "123.456" -doAssert fmt"{123.456:>9.3f}" == " 123.456" -doAssert fmt"{123.456:9.3f}" == " 123.456" -doAssert fmt"{123.456:9.4f}" == " 123.4560" -doAssert fmt"{123.456:>9.0f}" == " 123." -doAssert fmt"{123.456:<9.4f}" == "123.4560 " - -doAssert fmt"{123.456:e}" == "1.234560e+02" -doAssert fmt"{123.456:>13e}" == " 1.234560e+02" -doAssert fmt"{123.456:13e}" == " 1.234560e+02" - - -doAssert &"""{"abc"=:>4}""" == "\"abc\"= abc" -doAssert &"""{"abc"=:<4}""" == "\"abc\"=abc " - -doAssert fmt"{-12345=:08}" == "-12345=-0012345" -doAssert fmt"{-1=:3}" == "-1= -1" -doAssert fmt"{-1=:03}" == "-1=-01" -doAssert fmt"{16=:#X}" == "16=0x10" - -doAssert fmt"{123.456=}" == "123.456=123.456" -doAssert fmt"{123.456=:>9.3f}" == "123.456= 123.456" -doAssert fmt"{123.456=:9.3f}" == "123.456= 123.456" -doAssert fmt"{123.456=:9.4f}" == "123.456= 123.4560" -doAssert fmt"{123.456=:>9.0f}" == "123.456= 123." -doAssert fmt"{123.456=:<9.4f}" == "123.456=123.4560 " - -doAssert fmt"{123.456=:e}" == "123.456=1.234560e+02" -doAssert fmt"{123.456=:>13e}" == "123.456= 1.234560e+02" -doAssert fmt"{123.456=:13e}" == "123.456= 1.234560e+02" - -## tests for debug format string -block: - var name = "hello" - let age = 21 - const hobby = "swim" - doAssert fmt"{age*9 + 16=}" == "age*9 + 16=205" - doAssert &"name: {name =}\nage: { age =: >7}\nhobby: { hobby= : 8}" == - "name: name =hello\nage: age = 21\nhobby: hobby= swim " - doAssert fmt"{age == 12}" == "false" - doAssert fmt"{name.toUpperAscii( ) = }" == "name.toUpperAscii( ) = HELLO" - doAssert fmt"{ toUpperAscii( s = name ) = }" == " toUpperAscii( s = name ) = HELLO" - doAssert fmt"{ strutils.toUpperAscii( s = name ) = }" == " strutils.toUpperAscii( s = name ) = HELLO" - doAssert fmt"{age==12}" == "false" - doAssert fmt"{age!= 12}" == "true" - doAssert fmt"{age <= 12}" == "false" - for i in 1 .. 10: - doAssert fmt"{age.float =: .2f}" == "age.float = 21.00" - doAssert fmt"{age.float() =:.3f}" == "age.float() =21.000" - doAssert fmt"{float age= :.3f}" == "float age= 21.000" - doAssert fmt"{12 == int(`!=`(age, 12))}" == "false" - doAssert fmt"{0==1}" == "false" - - -block: - let x = "hello" - doAssert fmt"{x=}" == "x=" & $x - doAssert fmt"{x =}" == "x =" & $x - - - let y = 3.1415926 - doAssert fmt"{y=:.2f}" == fmt"y={y:.2f}" - doAssert fmt"{y=}" == fmt"y={y}" - doAssert fmt"{y =: <16}" == fmt"y ={y: <16}" - - proc hello(a: string, b: float): int = 12 - template foo(a: string, b: float): int = 18 - - doAssert fmt"{hello(x, y)=}" == "hello(x, y)=12" - doAssert fmt"{hello(x, y) =}" == "hello(x, y) =12" - doAssert fmt"{hello(x, y)= }" == "hello(x, y)= 12" - doAssert fmt"{hello(x, y) = }" == "hello(x, y) = 12" - - doAssert fmt"{x.hello(y)=}" == "x.hello(y)=12" - doAssert fmt"{x.hello(y) =}" == "x.hello(y) =12" - doAssert fmt"{x.hello(y)= }" == "x.hello(y)= 12" - doAssert fmt"{x.hello(y) = }" == "x.hello(y) = 12" - - doAssert fmt"{foo(x, y)=}" == "foo(x, y)=18" - doAssert fmt"{foo(x, y) =}" == "foo(x, y) =18" - doAssert fmt"{foo(x, y)= }" == "foo(x, y)= 18" - doAssert fmt"{foo(x, y) = }" == "foo(x, y) = 18" - - doAssert fmt"{x.foo(y)=}" == "x.foo(y)=18" - doAssert fmt"{x.foo(y) =}" == "x.foo(y) =18" - doAssert fmt"{x.foo(y)= }" == "x.foo(y)= 18" - doAssert fmt"{x.foo(y) = }" == "x.foo(y) = 18" + result.formatValue value.y, specifier + result.add "]" + + let v1 = Vec2[float32](x:1.0, y: 2.0) + let v2 = Vec2[int32](x:1, y: 1337) + doAssert fmt"v1: {v1:+08} v2: {v2:>4}" == "v1: [+0000001, +0000002] v2: [ 1, 1337]" + doAssert fmt"v1: {v1=:+08} v2: {v2=:>4}" == "v1: v1=[+0000001, +0000002] v2: v2=[ 1, 1337]" + + block: # bug #11012 + type + Animal = object + name, species: string + AnimalRef = ref Animal + + proc print_object(animalAddr: AnimalRef): string = + fmt"Received {animalAddr[]}" + + doAssert print_object(AnimalRef(name: "Foo", species: "Bar")) == """Received (name: "Foo", species: "Bar")""" + + block: # bug #11723 + let pos: Positive = 64 + doAssert fmt"{pos:3}" == " 64" + doAssert fmt"{pos:3b}" == "1000000" + doAssert fmt"{pos:3d}" == " 64" + doAssert fmt"{pos:3o}" == "100" + doAssert fmt"{pos:3x}" == " 40" + doAssert fmt"{pos:3X}" == " 40" + + doAssert fmt"{pos=:3}" == "pos= 64" + doAssert fmt"{pos=:3b}" == "pos=1000000" + doAssert fmt"{pos=:3d}" == "pos= 64" + doAssert fmt"{pos=:3o}" == "pos=100" + doAssert fmt"{pos=:3x}" == "pos= 40" + doAssert fmt"{pos=:3X}" == "pos= 40" + + let nat: Natural = 64 + doAssert fmt"{nat:3}" == " 64" + doAssert fmt"{nat:3b}" == "1000000" + doAssert fmt"{nat:3d}" == " 64" + doAssert fmt"{nat:3o}" == "100" + doAssert fmt"{nat:3x}" == " 40" + doAssert fmt"{nat:3X}" == " 40" + + doAssert fmt"{nat=:3}" == "nat= 64" + doAssert fmt"{nat=:3b}" == "nat=1000000" + doAssert fmt"{nat=:3d}" == "nat= 64" + doAssert fmt"{nat=:3o}" == "nat=100" + doAssert fmt"{nat=:3x}" == "nat= 40" + doAssert fmt"{nat=:3X}" == "nat= 40" + + block: # bug #12612 + proc my_proc() = + const value = "value" + const a = &"{value}" + doAssert a == value + + const b = &"{value=}" + doAssert b == "value=" & value + + my_proc() + + block: + template fmt(pattern: string; openCloseChar: char): untyped = + fmt(pattern, openCloseChar, openCloseChar) + + let + testInt = 123 + testStr = "foobar" + testFlt = 3.141592 + doAssert ">><<".fmt('<', '>') == "><" + doAssert " >> << ".fmt('<', '>') == " > < " + doAssert "<<>>".fmt('<', '>') == "<>" + doAssert " << >> ".fmt('<', '>') == " < > " + doAssert "''".fmt('\'') == "'" + doAssert "''''".fmt('\'') == "''" + doAssert "'' ''".fmt('\'') == "' '" + doAssert "<testInt>".fmt('<', '>') == "123" + doAssert "<testInt>".fmt('<', '>') == "123" + doAssert "'testFlt:1.2f'".fmt('\'') == "3.14" + doAssert "<testInt><testStr>".fmt('<', '>') == "123foobar" + doAssert """ ""{"123+123"}"" """.fmt('"') == " \"{246}\" " + doAssert "(((testFlt:1.2f)))((111))".fmt('(', ')') == "(3.14)(111)" + doAssert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)" + doAssert "{}abc`testStr' `testFlt:1.2f' `1+1' ``".fmt('`', '\'') == "{}abcfoobar 3.14 2 `" + doAssert """x = '"foo" & "bar"' + y = '123 + 111' + z = '3 in {2..7}' + """.fmt('\'') == + """x = foobar + y = 234 + z = true + """ + + block: # tests from the very own strformat documentation! + let msg = "hello" + doAssert fmt"{msg}\n" == "hello\\n" + + doAssert &"{msg}\n" == "hello\n" + + doAssert fmt"{msg}{'\n'}" == "hello\n" + doAssert fmt("{msg}\n") == "hello\n" + doAssert "{msg}\n".fmt == "hello\n" + + doAssert fmt"{msg=}\n" == "msg=hello\\n" + + doAssert &"{msg=}\n" == "msg=hello\n" + + doAssert fmt"{msg=}{'\n'}" == "msg=hello\n" + doAssert fmt("{msg=}\n") == "msg=hello\n" + doAssert "{msg=}\n".fmt == "msg=hello\n" + + doAssert &"""{"abc":>4}""" == " abc" + doAssert &"""{"abc":<4}""" == "abc " + + doAssert fmt"{-12345:08}" == "-0012345" + doAssert fmt"{-1:3}" == " -1" + doAssert fmt"{-1:03}" == "-01" + doAssert fmt"{16:#X}" == "0x10" + + doAssert fmt"{123.456}" == "123.456" + doAssert fmt"{123.456:>9.3f}" == " 123.456" + doAssert fmt"{123.456:9.3f}" == " 123.456" + doAssert fmt"{123.456:9.4f}" == " 123.4560" + doAssert fmt"{123.456:>9.0f}" == " 123." + doAssert fmt"{123.456:<9.4f}" == "123.4560 " + + doAssert fmt"{123.456:e}" == "1.234560e+02" + doAssert fmt"{123.456:>13e}" == " 1.234560e+02" + doAssert fmt"{123.456:13e}" == " 1.234560e+02" + + doAssert &"""{"abc"=:>4}""" == "\"abc\"= abc" + doAssert &"""{"abc"=:<4}""" == "\"abc\"=abc " + + doAssert fmt"{-12345=:08}" == "-12345=-0012345" + doAssert fmt"{-1=:3}" == "-1= -1" + doAssert fmt"{-1=:03}" == "-1=-01" + doAssert fmt"{16=:#X}" == "16=0x10" + + doAssert fmt"{123.456=}" == "123.456=123.456" + doAssert fmt"{123.456=:>9.3f}" == "123.456= 123.456" + doAssert fmt"{123.456=:9.3f}" == "123.456= 123.456" + doAssert fmt"{123.456=:9.4f}" == "123.456= 123.4560" + doAssert fmt"{123.456=:>9.0f}" == "123.456= 123." + doAssert fmt"{123.456=:<9.4f}" == "123.456=123.4560 " + + doAssert fmt"{123.456=:e}" == "123.456=1.234560e+02" + doAssert fmt"{123.456=:>13e}" == "123.456= 1.234560e+02" + doAssert fmt"{123.456=:13e}" == "123.456= 1.234560e+02" + + let x = 3.14 + doAssert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847" + doAssert fmt"""{(block: + var res: string + for i in 1..15: + res.add (if i mod 15 == 0: "FizzBuzz" + elif i mod 5 == 0: "Buzz" + elif i mod 3 == 0: "Fizz" + else: $i) & " " + res)}""" == "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz " + + doAssert fmt"""{ "\{(" & msg & ")\}" }""" == "{(hello)}" + doAssert fmt"""{{({ msg })}}""" == "{(hello)}" + doAssert fmt"""{ $(\{msg:1,"world":2\}) }""" == """[("hello", 1), ("world", 2)]""" + block: # tests for debug format string + var name = "hello" + let age = 21 + const hobby = "swim" + doAssert fmt"{age*9 + 16=}" == "age*9 + 16=205" + doAssert &"name: {name =}\nage: { age =: >7}\nhobby: { hobby= : 8}" == + "name: name =hello\nage: age = 21\nhobby: hobby= swim " + doAssert fmt"{age == 12}" == "false" + doAssert fmt"{name.toUpperAscii() = }" == "name.toUpperAscii() = HELLO" + doAssert fmt"{name.toUpperAscii( ) = }" == "name.toUpperAscii( ) = HELLO" + doAssert fmt"{ toUpperAscii( s = name ) = }" == " toUpperAscii( s = name ) = HELLO" + doAssert fmt"{ strutils.toUpperAscii( s = name ) = }" == " strutils.toUpperAscii( s = name ) = HELLO" + doAssert fmt"{age==12}" == "false" + doAssert fmt"{age!= 12}" == "true" + doAssert fmt"{age <= 12}" == "false" + for i in 1 .. 10: + doAssert fmt"{age.float =: .2f}" == "age.float = 21.00" + doAssert fmt"{age.float() =:.3f}" == "age.float() =21.000" + doAssert fmt"{float age= :.3f}" == "float age= 21.000" + doAssert fmt"{12 == int(`!=`(age, 12))}" == "false" + doAssert fmt"{0==1}" == "false" + + block: # It is space sensitive. + let x = "12" + doAssert fmt"{x=:}" == "x=12" + doAssert fmt"{x=}" == "x=12" + doAssert fmt"{x =:}" == "x =12" + doAssert fmt"{x =}" == "x =12" + doAssert fmt"{x= :}" == "x= 12" + doAssert fmt"{x= }" == "x= 12" + doAssert fmt"{x = :}" == "x = 12" + doAssert fmt"{x = }" == "x = 12" + doAssert fmt"{x = :}" == "x = 12" + doAssert fmt"{x = }" == "x = 12" + + block: + let x = "hello" + doAssert fmt"{x=}" == "x=hello" + doAssert fmt"{x =}" == "x =hello" + + let y = 3.1415926 + doAssert fmt"{y=:.2f}" == fmt"y={y:.2f}" + doAssert fmt"{y=}" == fmt"y={y}" + doAssert fmt"{y = : <8}" == fmt"y = 3.14159 " + + proc hello(a: string, b: float): int = 12 + template foo(a: string, b: float): int = 18 + + doAssert fmt"{hello(x, y)=}" == "hello(x, y)=12" + doAssert fmt"{hello(x, y) =}" == "hello(x, y) =12" + doAssert fmt"{hello(x, y)= }" == "hello(x, y)= 12" + doAssert fmt"{hello(x, y) = }" == "hello(x, y) = 12" + + doAssert fmt"{hello x, y=}" == "hello x, y=12" + doAssert fmt"{hello x, y =}" == "hello x, y =12" + doAssert fmt"{hello x, y= }" == "hello x, y= 12" + doAssert fmt"{hello x, y = }" == "hello x, y = 12" + + doAssert fmt"{x.hello(y)=}" == "x.hello(y)=12" + doAssert fmt"{x.hello(y) =}" == "x.hello(y) =12" + doAssert fmt"{x.hello(y)= }" == "x.hello(y)= 12" + doAssert fmt"{x.hello(y) = }" == "x.hello(y) = 12" + + doAssert fmt"{foo(x, y)=}" == "foo(x, y)=18" + doAssert fmt"{foo(x, y) =}" == "foo(x, y) =18" + doAssert fmt"{foo(x, y)= }" == "foo(x, y)= 18" + doAssert fmt"{foo(x, y) = }" == "foo(x, y) = 18" + + doAssert fmt"{x.foo(y)=}" == "x.foo(y)=18" + doAssert fmt"{x.foo(y) =}" == "x.foo(y) =18" + doAssert fmt"{x.foo(y)= }" == "x.foo(y)= 18" + doAssert fmt"{x.foo(y) = }" == "x.foo(y) = 18" + + block: + template check(actual, expected: string) = + doAssert actual == expected + + # Basic tests + let s = "string" + check &"{0} {s}", "0 string" + check &"{s[0..2].toUpperAscii}", "STR" + check &"{-10:04}", "-010" + check &"{-10:<04}", "-010" + check &"{-10:>04}", "-010" + check &"0x{10:02X}", "0x0A" + + check &"{10:#04X}", "0x0A" + + check &"""{"test":#>5}""", "#test" + check &"""{"test":>5}""", " test" + + check &"""{"test":#^7}""", "#test##" + + check &"""{"test": <5}""", "test " + check &"""{"test":<5}""", "test " + check &"{1f:.3f}", "1.000" + check &"Hello, {s}!", "Hello, string!" + + # Tests for identifiers without parenthesis + check &"{s} works{s}", "string worksstring" + check &"{s:>7}", " string" + doAssert(not compiles(&"{s_works}")) # parsed as identifier `s_works` + + # Misc general tests + check &"{{}}", "{}" + check &"{0}%", "0%" + check &"{0}%asdf", "0%asdf" + check &("\n{\"\\n\"}\n"), "\n\n\n" + check &"""{"abc"}s""", "abcs" + + # String tests + check &"""{"abc"}""", "abc" + check &"""{"abc":>4}""", " abc" + check &"""{"abc":<4}""", "abc " + check &"""{"":>4}""", " " + check &"""{"":<4}""", " " + + # Int tests + check &"{12345}", "12345" + check &"{ - 12345}", "-12345" + check &"{12345:6}", " 12345" + check &"{12345:>6}", " 12345" + check &"{12345:4}", "12345" + check &"{12345:08}", "00012345" + check &"{-12345:08}", "-0012345" + check &"{0:0}", "0" + check &"{0:02}", "00" + check &"{-1:3}", " -1" + check &"{-1:03}", "-01" + check &"{10}", "10" + check &"{16:#X}", "0x10" + check &"{16:^#7X}", " 0x10 " + check &"{16:^+#7X}", " +0x10 " + + # Hex tests + check &"{0:x}", "0" + check &"{-0:x}", "0" + check &"{255:x}", "ff" + check &"{255:X}", "FF" + check &"{-255:x}", "-ff" + check &"{-255:X}", "-FF" + check &"{255:x} uNaffeCteD CaSe", "ff uNaffeCteD CaSe" + check &"{255:X} uNaffeCteD CaSe", "FF uNaffeCteD CaSe" + check &"{255:4x}", " ff" + check &"{255:04x}", "00ff" + check &"{-255:4x}", " -ff" + check &"{-255:04x}", "-0ff" + + # Float tests + check &"{123.456}", "123.456" + check &"{-123.456}", "-123.456" + check &"{123.456:.3f}", "123.456" + check &"{123.456:+.3f}", "+123.456" + check &"{-123.456:+.3f}", "-123.456" + check &"{-123.456:.3f}", "-123.456" + check &"{123.456:1g}", "123.456" + check &"{123.456:.1f}", "123.5" + check &"{123.456:.0f}", "123." + check &"{123.456:>9.3f}", " 123.456" + check &"{123.456:9.3f}", " 123.456" + check &"{123.456:>9.4f}", " 123.4560" + check &"{123.456:>9.0f}", " 123." + check &"{123.456:<9.4f}", "123.4560 " + + # Float (scientific) tests + check &"{123.456:e}", "1.234560e+02" + check &"{123.456:>13e}", " 1.234560e+02" + check &"{123.456:<13e}", "1.234560e+02 " + check &"{123.456:.1e}", "1.2e+02" + check &"{123.456:.2e}", "1.23e+02" + check &"{123.456:.3e}", "1.235e+02" + + # Note: times.format adheres to the format protocol. Test that this + # works: + when nimvm: + discard + else: + var dt = dateTime(2000, mJan, 01, 00, 00, 00) + check &"{dt:yyyy-MM-dd}", "2000-01-01" + + var tm = fromUnix(0) + discard &"{tm}" + + var noww = now() + check &"{noww}", $noww + + # Unicode string tests + check &"""{"αβγ"}""", "αβγ" + check &"""{"αβγ":>5}""", " αβγ" + check &"""{"αβγ":<5}""", "αβγ " + check &"""a{"a"}α{"α"}€{"€"}𐍈{"𐍈"}""", "aaαα€€𐍈𐍈" + check &"""a{"a":2}α{"α":2}€{"€":2}𐍈{"𐍈":2}""", "aa αα €€ 𐍈𐍈 " + # Invalid unicode sequences should be handled as plain strings. + # Invalid examples taken from: https://stackoverflow.com/a/3886015/1804173 + let invalidUtf8 = [ + "\xc3\x28", "\xa0\xa1", + "\xe2\x28\xa1", "\xe2\x82\x28", + "\xf0\x28\x8c\xbc", "\xf0\x90\x28\xbc", "\xf0\x28\x8c\x28" + ] + for s in invalidUtf8: + check &"{s:>5}", repeat(" ", 5-s.len) & s + + # bug #11089 + let flfoo: float = 1.0 + check &"{flfoo}", "1.0" + + # bug #11092 + check &"{high(int64)}", "9223372036854775807" + check &"{low(int64)}", "-9223372036854775808" + + doAssert fmt"{'a'} {'b'}" == "a b" + + block: # test low(int64) + doAssert &"{low(int64):-}" == "-9223372036854775808" + block: #expressions plus formatting + doAssert fmt"{if true\: 123.456 else\: 0=:>9.3f}" == "if true: 123.456 else: 0= 123.456" + doAssert fmt"{(if true: 123.456 else: 0)=}" == "(if true: 123.456 else: 0)=123.456" + doAssert fmt"{if true\: 123.456 else\: 0=:9.3f}" == "if true: 123.456 else: 0= 123.456" + doAssert fmt"{(if true: 123.456 else: 0)=:9.4f}" == "(if true: 123.456 else: 0)= 123.4560" + doAssert fmt"{(if true: 123.456 else: 0)=:>9.0f}" == "(if true: 123.456 else: 0)= 123." + doAssert fmt"{if true\: 123.456 else\: 0=:<9.4f}" == "if true: 123.456 else: 0=123.4560 " + + doAssert fmt"""{(case true + of false: 0.0 + of true: 123.456)=:e}""" == """(case true + of false: 0.0 + of true: 123.456)=1.234560e+02""" + + doAssert fmt"""{block\: + var res = 0.000123456 + for _ in 0..5\: + res *= 10 + res=:>13e}""" == """block: + var res = 0.000123456 + for _ in 0..5: + res *= 10 + res= 1.234560e+02""" + #side effects + var x = 5 + doAssert fmt"{(x=7;123.456)=:13e}" == "(x=7;123.456)= 1.234560e+02" + doAssert x==7 + block: #curly bracket expressions and tuples + proc formatValue(result: var string; value:Table|bool|JsonNode; specifier:string) = result.add $value + + doAssert fmt"""{\{"a"\:1,"b"\:2\}.toTable() = }""" == """{"a":1,"b":2}.toTable() = {"a": 1, "b": 2}""" + doAssert fmt"""{(\{3: (1,"hi",0.9),4: (4,"lo",1.1)\}).toTable()}""" == """{3: (1, "hi", 0.9), 4: (4, "lo", 1.1)}""" + doAssert fmt"""{ (%* \{"name": "Isaac", "books": ["Robot Dreams"]\}) }""" == """{"name":"Isaac","books":["Robot Dreams"]}""" + doAssert """%( \%\* {"name": "Isaac"})*""".fmt('%','*') == """{"name":"Isaac"}""" + block: #parens in quotes that fool my syntax highlighter + doAssert fmt"{(if true: ')' else: '(')}" == ")" + doAssert fmt"{(if true: ']' else: ')')}" == "]" + doAssert fmt"""{(if true: "\")\"" else: "\"(")}""" == """")"""" + doAssert &"""{(if true: "\")" else: "")}""" == "\")" + doAssert &"{(if true: \"\\\")\" else: \"\")}" == "\")" + doAssert fmt"""{(if true: "')" else: "")}""" == "')" + doAssert fmt"""{(if true: "'" & "'" & ')' else: "")}""" == "'')" + doAssert &"""{(if true: "'" & "'" & ')' else: "")}""" == "'')" + doAssert &"{(if true: \"\'\" & \"'\" & ')' else: \"\")}" == "'')" + doAssert fmt"""{(if true: "'" & ')' else: "")}""" == "')" + + block: # issue #20381 + var ss: seq[string] + template myTemplate(s: string) = + ss.add s + ss.add s + proc foo() = + myTemplate fmt"hello" + foo() + doAssert ss == @["hello", "hello"] + + block: + proc noraises() {.raises: [].} = + const + flt = 0.0 + str = "str" + + doAssert fmt"{flt} {str}" == "0.0 str" + + noraises() + + block: + doAssert not compiles(fmt"{formatting errors detected at compile time") + +static: main() +main() diff --git a/tests/stdlib/tstrformatlineinfo.nim b/tests/stdlib/tstrformatlineinfo.nim new file mode 100644 index 000000000..3a7bf0d33 --- /dev/null +++ b/tests/stdlib/tstrformatlineinfo.nim @@ -0,0 +1,8 @@ +# issue #21759 + +{.hint[ConvFromXToItselfNotNeeded]: on.} + +import std/strformat + +echo fmt"{string ""abc""}" #[tt.Hint + ^ conversion from string to itself is pointless]# diff --git a/tests/stdlib/tstrimpl.nim b/tests/stdlib/tstrimpl.nim new file mode 100644 index 000000000..a8933e53f --- /dev/null +++ b/tests/stdlib/tstrimpl.nim @@ -0,0 +1,12 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/private/strimpl + +import std/assertions + +doAssert find(cstring"Hello Nim", cstring"Nim") == 6 +doAssert find(cstring"Hello Nim", cstring"N") == 6 +doAssert find(cstring"Hello Nim", cstring"I") == -1 +doAssert find(cstring"Hello Nim", cstring"O") == -1 diff --git a/tests/stdlib/tstring.nim b/tests/stdlib/tstring.nim index 0a7bf0511..b9b3c78a3 100644 --- a/tests/stdlib/tstring.nim +++ b/tests/stdlib/tstring.nim @@ -1,93 +1,124 @@ discard """ - output: '''OK -@[@[], @[], @[], @[], @[]] -''' + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" """ -const characters = "abcdefghijklmnopqrstuvwxyz" -const numbers = "1234567890" - -var s: string - -proc test_string_slice() = - # test "slice of length == len(characters)": - # replace characters completely by numbers - s = characters - s[0..^1] = numbers - doAssert s == numbers - - # test "slice of length > len(numbers)": - # replace characters by slice of same length - s = characters - s[1..16] = numbers - doAssert s == "a1234567890rstuvwxyz" - - # test "slice of length == len(numbers)": - # replace characters by slice of same length - s = characters - s[1..10] = numbers - doAssert s == "a1234567890lmnopqrstuvwxyz" - - # test "slice of length < len(numbers)": - # replace slice of length. and insert remaining chars - s = characters - s[1..4] = numbers - doAssert s == "a1234567890fghijklmnopqrstuvwxyz" - - # test "slice of length == 1": - # replace first character. and insert remaining 9 chars - s = characters - s[1..1] = numbers - doAssert s == "a1234567890cdefghijklmnopqrstuvwxyz" - - # test "slice of length == 0": - # insert chars at slice start index - s = characters - s[2..1] = numbers - doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz" - - # test "slice of negative length": - # same as slice of zero length - s = characters - s[2..0] = numbers - doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz" - - # bug #6223 - doAssertRaises(IndexDefect): - discard s[0..999] - - echo("OK") - -proc test_string_cmp() = - let world = "hello\0world" - let earth = "hello\0earth" - let short = "hello\0" - let hello = "hello" - let goodbye = "goodbye" - - doAssert world == world - doAssert world != earth - doAssert world != short - doAssert world != hello - doAssert world != goodbye - - doAssert cmp(world, world) == 0 - doAssert cmp(world, earth) > 0 - doAssert cmp(world, short) > 0 - doAssert cmp(world, hello) > 0 - doAssert cmp(world, goodbye) > 0 - -test_string_slice() -test_string_cmp() - - -#-------------------------- -# bug #7816 -import sugar -import sequtils + +from std/sequtils import toSeq, map +from std/sugar import `=>` +import std/assertions proc tester[T](x: T) = let test = toSeq(0..4).map(i => newSeq[int]()) - echo test - -tester(1) - + doAssert $test == "@[@[], @[], @[], @[], @[]]" + +func reverse*(a: string): string = + result = a + for i in 0 ..< a.len div 2: + swap(result[i], result[^(i + 1)]) + +proc main() = + block: # .. + const + characters = "abcdefghijklmnopqrstuvwxyz" + numbers = "1234567890" + + # test "slice of length == len(characters)": + # replace characters completely by numbers + var s: string + s = characters + s[0..^1] = numbers + doAssert s == numbers + + # test "slice of length > len(numbers)": + # replace characters by slice of same length + s = characters + s[1..16] = numbers + doAssert s == "a1234567890rstuvwxyz" + + # test "slice of length == len(numbers)": + # replace characters by slice of same length + s = characters + s[1..10] = numbers + doAssert s == "a1234567890lmnopqrstuvwxyz" + + # test "slice of length < len(numbers)": + # replace slice of length. and insert remaining chars + s = characters + s[1..4] = numbers + doAssert s == "a1234567890fghijklmnopqrstuvwxyz" + + # test "slice of length == 1": + # replace first character. and insert remaining 9 chars + s = characters + s[1..1] = numbers + doAssert s == "a1234567890cdefghijklmnopqrstuvwxyz" + + # test "slice of length == 0": + # insert chars at slice start index + s = characters + s[2..1] = numbers + doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz" + + # test "slice of negative length": + # same as slice of zero length + s = characters + s[2..0] = numbers + doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz" + + when nimvm: + discard + else: + # bug #6223 + doAssertRaises(IndexDefect): + discard s[0..999] + + block: # ==, cmp + let world = "hello\0world" + let earth = "hello\0earth" + let short = "hello\0" + let hello = "hello" + let goodbye = "goodbye" + + doAssert world == world + doAssert world != earth + doAssert world != short + doAssert world != hello + doAssert world != goodbye + + doAssert cmp(world, world) == 0 + doAssert cmp(world, earth) > 0 + doAssert cmp(world, short) > 0 + doAssert cmp(world, hello) > 0 + doAssert cmp(world, goodbye) > 0 + + block: # bug #7816 + tester(1) + + block: # bug #14497, reverse + doAssert reverse("hello") == "olleh" + + block: # len, high + var a = "ab\0cd" + var b = a.cstring + doAssert a.len == 5 + block: # bug #16405 + when defined(js): + when nimvm: doAssert b.len == 2 + else: doAssert b.len == 5 + else: doAssert b.len == 2 + + doAssert a.high == a.len - 1 + doAssert b.high == b.len - 1 + + doAssert "".len == 0 + doAssert "".high == -1 + doAssert "".cstring.len == 0 + doAssert "".cstring.high == -1 + + block: # bug #16674 + var c: cstring = nil + doAssert c.len == 0 + doAssert c.high == -1 + +static: main() +main() diff --git a/tests/stdlib/tstrmiscs.nim b/tests/stdlib/tstrmiscs.nim new file mode 100644 index 000000000..b42f2e1fe --- /dev/null +++ b/tests/stdlib/tstrmiscs.nim @@ -0,0 +1,27 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/strmisc +import std/assertions + + +doAssert expandTabs("\t", 4) == " " +doAssert expandTabs("\tfoo\t", 4) == " foo " +doAssert expandTabs("\tfoo\tbar", 4) == " foo bar" +doAssert expandTabs("\tfoo\tbar\t", 4) == " foo bar " +doAssert expandTabs("", 4) == "" +doAssert expandTabs("", 0) == "" +doAssert expandTabs("\t\t\t", 0) == "" + +doAssert partition("foo:bar", ":") == ("foo", ":", "bar") +doAssert partition("foobarbar", "bar") == ("foo", "bar", "bar") +doAssert partition("foobarbar", "bank") == ("foobarbar", "", "") +doAssert partition("foobarbar", "foo") == ("", "foo", "barbar") +doAssert partition("foofoobar", "bar") == ("foofoo", "bar", "") + +doAssert rpartition("foo:bar", ":") == ("foo", ":", "bar") +doAssert rpartition("foobarbar", "bar") == ("foobar", "bar", "") +doAssert rpartition("foobarbar", "bank") == ("", "", "foobarbar") +doAssert rpartition("foobarbar", "foo") == ("", "foo", "barbar") +doAssert rpartition("foofoobar", "bar") == ("foofoo", "bar", "") diff --git a/tests/stdlib/tstrscans.nim b/tests/stdlib/tstrscans.nim index 08fc14e45..ae7fd98ca 100644 --- a/tests/stdlib/tstrscans.nim +++ b/tests/stdlib/tstrscans.nim @@ -1,8 +1,8 @@ discard """ - output: "" + matrix: "--mm:refc; --mm:orc" """ -import strscans +import std/[strscans, strutils, assertions] block ParsePasswd: proc parsePasswd(content: string): seq[string] = @@ -15,7 +15,7 @@ block ParsePasswd: else: break - const etc_passwd = """root:x:0:0:root:/root:/bin/bash + const etcPasswd = """root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh @@ -23,7 +23,7 @@ nobody:x:65534:65534:nobody:/nonexistent:/bin/sh messagebus:x:103:107::/var/run/dbus:/bin/false """ - const parsed_etc_passwd = @[ + const parsedEtcPasswd = @[ "root:x:0:0:root:/root:/bin/bash", "daemon:x:1:1:daemon:/usr/sbin:/bin/sh", "bin:x:2:2:bin:/bin:/bin/sh", @@ -31,7 +31,7 @@ messagebus:x:103:107::/var/run/dbus:/bin/false "nobody:x:65534:65534:nobody:/nonexistent:/bin/sh", "messagebus:x:103:107::/var/run/dbus:/bin/false", ] - doAssert etc_passwd.parsePasswd == parsed_etc_passwd + doAssert etcPasswd.parsePasswd == parsedEtcPasswd block LastNot: var idx : int @@ -79,8 +79,210 @@ block EmptyTuple: block Arrow: let text = "foo;bar;baz;" var idx = 0 - var res = "" doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';') doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';') doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';') doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';') == false + + +block issue15064: + var nick1, msg1: string + doAssert scanf("<abcd> a", "<$+> $+", nick1, msg1) + doAssert nick1 == "abcd" + doAssert msg1 == "a" + + var nick2, msg2: string + doAssert(not scanf("<abcd> ", "<$+> $+", nick2, msg2)) + + var nick3, msg3: string + doAssert scanf("<abcd> ", "<$+> $*", nick3, msg3) + doAssert nick3 == "abcd" + doAssert msg3 == "" + + +block: + proc twoDigits(input: string; x: var int; start: int): int = + if start+1 < input.len and input[start] == '0' and input[start+1] == '0': + result = 2 + x = 13 + else: + result = 0 + + proc someSep(input: string; start: int; seps: set[char] = {';', ',', '-', '.'}): int = + result = 0 + while start+result < input.len and input[start+result] in seps: inc result + + proc demangle(s: string; res: var string; start: int): int = + while result+start < s.len and s[result+start] in {'_', '@'}: inc result + res = "" + while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_': + res.add s[result+start] + inc result + while result+start < s.len and s[result+start] > ' ': + inc result + + proc parseGDB(resp: string): seq[string] = + const + digits = {'0'..'9'} + hexdigits = digits + {'a'..'f', 'A'..'F'} + whites = {' ', '\t', '\C', '\L'} + result = @[] + var idx = 0 + while true: + var prc = "" + var info = "" + if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "), + demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')', + *`whites`, "at ", +(~{'\C', '\L'} -> info.add($_))): + result.add prc & " " & info + else: + break + + var key, val: string + var intVal: int + var floatVal: float + doAssert scanf("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f", key, val, intVal, floatVal) + doAssert key == "abc" + doAssert val == "xyz" + doAssert intVal == 89 + doAssert floatVal == 33.25 + + var binVal: int + var octVal: int + var hexVal: int + doAssert scanf("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binVal, octVal, hexVal) + doAssert binVal == 0b0101 + doAssert octVal == 0o1234 + doAssert hexVal == 0xabcd + + let xx = scanf("$abc", "$$$i", intVal) + doAssert xx == false + + + let xx2 = scanf("$1234", "$$$i", intVal) + doAssert xx2 + + let yy = scanf(";.--Breakpoint00 [output]", + "$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.", + intVal, key) + doAssert yy + doAssert key == "output" + doAssert intVal == 13 + + var ident = "" + var idx = 0 + let zz = scanp("foobar x x x xWZ", idx, +{'a'..'z'} -> add(ident, $_), *(*{ + ' ', '\t'}, "x"), ~'U', "Z") + doAssert zz + doAssert ident == "foobar" + + const digits = {'0'..'9'} + var year = 0 + var idx2 = 0 + if scanp("201655-8-9", idx2, `digits`{4, 6} -> (year = year * 10 + ord($_) - + ord('0')), "-8", "-9"): + doAssert year == 201655 + + const gdbOut = """ + #0 @foo_96013_1208911747@8 (x0=...) + at c:/users/anwender/projects/nim/temp.nim:11 + #1 0x00417754 in tempInit000 () at c:/users/anwender/projects/nim/temp.nim:13 + #2 0x0041768d in NimMainInner () + at c:/users/anwender/projects/nim/lib/system.nim:2605 + #3 0x004176b1 in NimMain () + at c:/users/anwender/projects/nim/lib/system.nim:2613 + #4 0x004176db in main (argc=1, args=0x712cc8, env=0x711ca8) + at c:/users/anwender/projects/nim/lib/system.nim:2620""" + const result = @["foo c:/users/anwender/projects/nim/temp.nim:11", + "tempInit000 c:/users/anwender/projects/nim/temp.nim:13", + "NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605", + "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613", + "main c:/users/anwender/projects/nim/lib/system.nim:2620"] + doAssert parseGDB(gdbOut) == result + + # bug #6487 + var count = 0 + + proc test(): string = + inc count + result = ",123123" + + var a: int + discard scanf(test(), ",$i", a) + doAssert count == 1 + + +block: + let input = """1-3 s: abc +15-18 9: def +15-18 A: ghi +15-18 _: jkl +""" + var + lo, hi: int + w: string + c: char + res: int + for line in input.splitLines: + if line.scanf("$i-$i $c: $w", lo, hi, c, w): + inc res + doAssert res == 4 + +block: + #whenscanf testing + let input = """1-3 s: abc +15-18 9: def +15-18 A: ghi +15-18 _: jkl +""" + proc twoDigits(input: string; x: var int; start: int): int = + if start+1 < input.len and input[start] == '0' and input[start+1] == '0': + result = 2 + x = 13 + else: + result = 0 + + proc someSep(input: string; start: int; seps: set[char] = {';', ',', '-', '.'}): int = + result = 0 + while start+result < input.len and input[start+result] in seps: inc result + + type + ScanRetType = tuple + success: bool + lo: int + hi: int + ch: char + word: string + + var res = 0 + for line in input.splitLines: + let ret: ScanRetType = scanTuple(line, "$i-$i $c: $w") + if ret.success: + inc res + doAssert res == 4 + + let (_, key, val, intVal, floatVal) = scanTuple("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f") + doAssert key == "abc" + doAssert val == "xyz" + doAssert intVal == 89 + doAssert floatVal == 33.25 + + + let (_, binVal, octVal, hexVal) = scanTuple("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binVal, octVal, hexVal) + doAssert binVal == 0b0101 + doAssert octVal == 0o1234 + doAssert hexVal == 0xabcd + + var (xx,_) = scanTuple("$abc", "$$$i") + doAssert xx == false + + + let (xx2, _) = block: scanTuple("$1234", "$$$i") + doAssert xx2 + + var (yy, intVal2, key2) = scanTuple(";.--Breakpoint00 [output]", + "$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.", + int) + doAssert yy + doAssert key2 == "output" + doAssert intVal2 == 13 diff --git a/tests/stdlib/tstrset.nim b/tests/stdlib/tstrset.nim index 1b253f862..bbb6c2677 100644 --- a/tests/stdlib/tstrset.nim +++ b/tests/stdlib/tstrset.nim @@ -1,12 +1,16 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + # test a simple yet highly efficient set of strings type TRadixNodeKind = enum rnLinear, rnFull, rnLeaf PRadixNode = ref TRadixNode - TRadixNode = object {.inheritable.} + TRadixNode {.inheritable.} = object kind: TRadixNodeKind TRadixNodeLinear = object of TRadixNode - len: int8 + len: uint8 keys: array[0..31, char] vals: array[0..31, PRadixNode] TRadixNodeFull = object of TRadixNode @@ -24,7 +28,7 @@ proc search(r: PRadixNode, s: string): PRadixNode = case r.kind of rnLinear: var x = PRadixNodeLinear(r) - for j in 0..ze(x.len)-1: + for j in 0..int(x.len)-1: if x.keys[j] == s[i]: if s[i] == '\0': return r r = x.vals[j] @@ -50,7 +54,7 @@ proc search(r: PRadixNode, s: string): PRadixNode = proc contains*(r: PRadixNode, s: string): bool = return search(r, s) != nil -proc testOrincl*(r: var PRadixNode, s: string): bool = +proc testOrIncl*(r: var PRadixNode, s: string): bool = nil proc incl*(r: var PRadixNode, s: string) = discard testOrIncl(r, s) @@ -63,9 +67,9 @@ proc excl*(r: var PRadixNode, s: string) = of rnFull: PRadixNodeFull(x).b['\0'] = nil of rnLinear: var x = PRadixNodeLinear(x) - for i in 0..ze(x.len)-1: + for i in 0..int(x.len)-1: if x.keys[i] == '\0': - swap(x.keys[i], x.keys[ze(x.len)-1]) + swap(x.keys[i], x.keys[int(x.len)-1]) dec(x.len) break diff --git a/tests/stdlib/tstrtabs.nim b/tests/stdlib/tstrtabs.nim index 5d7e8c762..d261abe76 100644 --- a/tests/stdlib/tstrtabs.nim +++ b/tests/stdlib/tstrtabs.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--mm:refc; --mm:orc" sortoutput: true output: ''' key1: value1 @@ -88,7 +89,7 @@ value1 = value2 ''' """ -import strtabs +import std/[strtabs, assertions, syncio] var tab = newStringTable({"key1": "val1", "key2": "val2"}, modeStyleInsensitive) @@ -102,3 +103,15 @@ writeLine(stdout, "length of table ", $tab.len) writeLine(stdout, `%`("$key1 = $key2", tab, {useEnvironment})) tab.clear writeLine(stdout, "length of table ", $tab.len) + +block: + var x = {"k": "v", "11": "22", "565": "67"}.newStringTable + doAssert x["k"] == "v" + doAssert x["11"] == "22" + doAssert x["565"] == "67" + x["11"] = "23" + doAssert x["11"] == "23" + + x.clear(modeCaseInsensitive) + x["11"] = "22" + doAssert x["11"] == "22" diff --git a/tests/stdlib/tstrtabs.nims b/tests/stdlib/tstrtabs.nims new file mode 100644 index 000000000..3563ad0ad --- /dev/null +++ b/tests/stdlib/tstrtabs.nims @@ -0,0 +1,5 @@ +import std/[strtabs, assertions] + +static: + let t = {"name": "John", "city": "Monaco"}.newStringTable + doAssert "${name} lives in ${city}" % t == "John lives in Monaco" diff --git a/tests/stdlib/tstrtabs2.nim b/tests/stdlib/tstrtabs2.nim new file mode 100644 index 000000000..a4030ec77 --- /dev/null +++ b/tests/stdlib/tstrtabs2.nim @@ -0,0 +1,32 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" +""" + +import std/strtabs +import std/assertions + +macro m = + var t = {"name": "John"}.newStringTable + doAssert t["name"] == "John" + +block: + var t = {"name": "John"}.newStringTable + doAssert t["name"] == "John" + +m() + +proc fun()= + let ret = newStringTable(modeCaseSensitive) + ret["foo"] = "bar" + + doAssert $ret == "{foo: bar}" + + let b = ret["foo"] + doAssert b == "bar" + +proc main()= + static: fun() + fun() + +main() diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim deleted file mode 100644 index 5702b7341..000000000 --- a/tests/stdlib/tstrutil.nim +++ /dev/null @@ -1,435 +0,0 @@ -# test the new strutils module - -import - strutils - -import macros - -template rejectParse(e) = - try: - discard e - raise newException(AssertionDefect, "This was supposed to fail: $#!" % astToStr(e)) - except ValueError: discard - -proc testStrip() = - doAssert strip(" ha ") == "ha" - -proc testRemoveSuffix = - var s = "hello\n\r" - s.removeSuffix - assert s == "hello" - s.removeSuffix - assert s == "hello" - - s = "hello\n\n" - s.removeSuffix - assert s == "hello" - - s = "hello\r" - s.removeSuffix - assert s == "hello" - - s = "hello \n there" - s.removeSuffix - assert s == "hello \n there" - - s = "hello" - s.removeSuffix("llo") - assert s == "he" - s.removeSuffix('e') - assert s == "h" - - s = "hellos" - s.removeSuffix({'s','z'}) - assert s == "hello" - s.removeSuffix({'l','o'}) - assert s == "he" - - s = "aeiou" - s.removeSuffix("") - assert s == "aeiou" - - s = "" - s.removeSuffix("") - assert s == "" - - s = " " - s.removeSuffix - assert s == " " - - s = " " - s.removeSuffix("") - assert s == " " - - s = " " - s.removeSuffix(" ") - assert s == " " - - s = " " - s.removeSuffix(' ') - assert s == "" - - # Contrary to Chomp in other languages - # empty string does not change behaviour - s = "hello\r\n\r\n" - s.removeSuffix("") - assert s == "hello\r\n\r\n" - -proc testRemovePrefix = - var s = "\n\rhello" - s.removePrefix - assert s == "hello" - s.removePrefix - assert s == "hello" - - s = "\n\nhello" - s.removePrefix - assert s == "hello" - - s = "\rhello" - s.removePrefix - assert s == "hello" - - s = "hello \n there" - s.removePrefix - assert s == "hello \n there" - - s = "hello" - s.removePrefix("hel") - assert s == "lo" - s.removePrefix('l') - assert s == "o" - - s = "hellos" - s.removePrefix({'h','e'}) - assert s == "llos" - s.removePrefix({'l','o'}) - assert s == "s" - - s = "aeiou" - s.removePrefix("") - assert s == "aeiou" - - s = "" - s.removePrefix("") - assert s == "" - - s = " " - s.removePrefix - assert s == " " - - s = " " - s.removePrefix("") - assert s == " " - - s = " " - s.removePrefix(" ") - assert s == " " - - s = " " - s.removePrefix(' ') - assert s == "" - - # Contrary to Chomp in other languages - # empty string does not change behaviour - s = "\r\n\r\nhello" - s.removePrefix("") - assert s == "\r\n\r\nhello" - -proc main() = - testStrip() - testRemoveSuffix() - testRemovePrefix() - var ret: seq[string] # or use `toSeq` - for p in split("/home/a1:xyz:/usr/bin", {':'}): ret.add p - doAssert ret == @["/home/a1", "xyz", "/usr/bin"] - -proc testDelete = - var s = "0123456789ABCDEFGH" - delete(s, 4, 5) - assert s == "01236789ABCDEFGH" - delete(s, s.len-1, s.len-1) - assert s == "01236789ABCDEFG" - delete(s, 0, 0) - assert s == "1236789ABCDEFG" - -proc testFind = - assert "0123456789ABCDEFGH".find('A') == 10 - assert "0123456789ABCDEFGH".find('A', 5) == 10 - assert "0123456789ABCDEFGH".find('A', 5, 10) == 10 - assert "0123456789ABCDEFGH".find('A', 5, 9) == -1 - assert "0123456789ABCDEFGH".find("A") == 10 - assert "0123456789ABCDEFGH".find("A", 5) == 10 - assert "0123456789ABCDEFGH".find("A", 5, 10) == 10 - assert "0123456789ABCDEFGH".find("A", 5, 9) == -1 - assert "0123456789ABCDEFGH".find({'A'..'C'}) == 10 - assert "0123456789ABCDEFGH".find({'A'..'C'}, 5) == 10 - assert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 10) == 10 - assert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 9) == -1 - -proc testRFind = - assert "0123456789ABCDEFGAH".rfind('A') == 17 - assert "0123456789ABCDEFGAH".rfind('A', last=13) == 10 - assert "0123456789ABCDEFGAH".rfind('H', last=13) == -1 - assert "0123456789ABCDEFGAH".rfind("A") == 17 - assert "0123456789ABCDEFGAH".rfind("A", last=13) == 10 - assert "0123456789ABCDEFGAH".rfind("H", last=13) == -1 - assert "0123456789ABCDEFGAH".rfind({'A'..'C'}) == 17 - assert "0123456789ABCDEFGAH".rfind({'A'..'C'}, last=13) == 12 - assert "0123456789ABCDEFGAH".rfind({'G'..'H'}, last=13) == -1 - assert "0123456789ABCDEFGAH".rfind('A', start=18) == -1 - assert "0123456789ABCDEFGAH".rfind('A', start=11, last=17) == 17 - assert "0123456789ABCDEFGAH".rfind("0", start=0) == 0 - assert "0123456789ABCDEFGAH".rfind("0", start=1) == -1 - assert "0123456789ABCDEFGAH".rfind("H", start=11) == 18 - assert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=5) == 9 - assert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=10) == -1 - -proc testTrimZeros() = - var x = "1200" - x.trimZeros() - assert x == "1200" - x = "120.0" - x.trimZeros() - assert x == "120" - x = "0." - x.trimZeros() - assert x == "0" - x = "1.0e2" - x.trimZeros() - assert x == "1e2" - x = "78.90" - x.trimZeros() - assert x == "78.9" - x = "1.23e4" - x.trimZeros() - assert x == "1.23e4" - x = "1.01" - x.trimZeros() - assert x == "1.01" - x = "1.1001" - x.trimZeros() - assert x == "1.1001" - x = "0.0" - x.trimZeros() - assert x == "0" - x = "0.01" - x.trimZeros() - assert x == "0.01" - x = "1e0" - x.trimZeros() - assert x == "1e0" - -proc testSplitLines() = - let fixture = "a\nb\rc\r\nd" - assert len(fixture.splitLines) == 4 - assert splitLines(fixture) == @["a", "b", "c", "d"] - assert splitLines(fixture, keepEol=true) == @["a\n", "b\r", "c\r\n", "d"] - -proc testCountLines = - proc assertCountLines(s: string) = assert s.countLines == s.splitLines.len - assertCountLines("") - assertCountLines("\n") - assertCountLines("\n\n") - assertCountLines("abc") - assertCountLines("abc\n123") - assertCountLines("abc\n123\n") - assertCountLines("\nabc\n123") - assertCountLines("\nabc\n123\n") - -proc testParseInts = - # binary - assert "0b1111".parseBinInt == 15 - assert "0B1111".parseBinInt == 15 - assert "1111".parseBinInt == 15 - assert "1110".parseBinInt == 14 - assert "1_1_1_1".parseBinInt == 15 - assert "0b1_1_1_1".parseBinInt == 15 - rejectParse "".parseBinInt - rejectParse "_".parseBinInt - rejectParse "0b".parseBinInt - rejectParse "0b1234".parseBinInt - # hex - assert "0x72".parseHexInt == 114 - assert "0X72".parseHexInt == 114 - assert "#72".parseHexInt == 114 - assert "72".parseHexInt == 114 - assert "FF".parseHexInt == 255 - assert "ff".parseHexInt == 255 - assert "fF".parseHexInt == 255 - assert "0x7_2".parseHexInt == 114 - rejectParse "".parseHexInt - rejectParse "_".parseHexInt - rejectParse "0x".parseHexInt - rejectParse "0xFFG".parseHexInt - rejectParse "reject".parseHexInt - # octal - assert "0o17".parseOctInt == 15 - assert "0O17".parseOctInt == 15 - assert "17".parseOctInt == 15 - assert "10".parseOctInt == 8 - assert "0o1_0_0".parseOctInt == 64 - rejectParse "".parseOctInt - rejectParse "_".parseOctInt - rejectParse "0o".parseOctInt - rejectParse "9".parseOctInt - rejectParse "0o9".parseOctInt - rejectParse "reject".parseOctInt - -testDelete() -testFind() -testRFind() -testTrimZeros() -testSplitLines() -testCountLines() -testParseInts() - -assert(insertSep($1000_000) == "1_000_000") -assert(insertSep($232) == "232") -assert(insertSep($12345, ',') == "12,345") -assert(insertSep($0) == "0") - -assert "/1/2/3".rfind('/') == 4 -assert "/1/2/3".rfind('/', last=1) == 0 -assert "/1/2/3".rfind('0') == -1 - -assert(toHex(100i16, 32) == "00000000000000000000000000000064") -assert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C") - -assert "".parseHexStr == "" -assert "00Ff80".parseHexStr == "\0\xFF\x80" -try: - discard "00Ff8".parseHexStr - assert false, "Should raise ValueError" -except ValueError: - discard - -try: - discard "0k".parseHexStr - assert false, "Should raise ValueError" -except ValueError: - discard - -assert "".toHex == "" -assert "\x00\xFF\x80".toHex == "00FF80" -assert "0123456789abcdef".parseHexStr.toHex == "0123456789ABCDEF" - -assert(' '.repeat(8) == " ") -assert(" ".repeat(8) == " ") -assert(spaces(8) == " ") - -assert(' '.repeat(0) == "") -assert(" ".repeat(0) == "") -assert(spaces(0) == "") - -# bug #11369 - -var num: int64 = -1 -assert num.toBin(64) == "1111111111111111111111111111111111111111111111111111111111111111" -assert num.toOct(24) == "001777777777777777777777" - - -# bug #8911 -when true: - static: - let a = "" - let a2 = a.replace("\n", "\\n") - -when true: - static: - let b = "b" - let b2 = b.replace("\n", "\\n") - -when true: - let c = "" - let c2 = c.replace("\n", "\\n") - -main() -#OUT ha/home/a1xyz/usr/bin - - -# `parseEnum`, ref issue #14030 -# check enum defined at top level -type - Foo = enum - A = -10 - B = "bb" - C = (-5, "ccc") - D = 15 - E = "ee" # check that we count enum fields correctly - -block: - let a = parseEnum[Foo]("A") - let b = parseEnum[Foo]("bb") - let c = parseEnum[Foo]("ccc") - let d = parseEnum[Foo]("D") - let e = parseEnum[Foo]("ee") - doAssert a == A - doAssert b == B - doAssert c == C - doAssert d == D - doAssert e == E - try: - let f = parseEnum[Foo]("Bar") - doAssert false - except ValueError: - discard - - # finally using default - let g = parseEnum[Foo]("Bar", A) - doAssert g == A - -block: - # check enum defined in block - type - Bar = enum - V - W = "ww" - X = (3, "xx") - Y = 10 - Z = "zz" # check that we count enum fields correctly - - let a = parseEnum[Bar]("V") - let b = parseEnum[Bar]("ww") - let c = parseEnum[Bar]("xx") - let d = parseEnum[Bar]("Y") - let e = parseEnum[Bar]("zz") - doAssert a == V - doAssert b == W - doAssert c == X - doAssert d == Y - doAssert e == Z - try: - let f = parseEnum[Bar]("Baz") - doAssert false - except ValueError: - discard - - # finally using default - let g = parseEnum[Bar]("Baz", V) - doAssert g == V - -block: - # check ambiguous enum fails to parse - type - Ambig = enum - f1 = "A" - f2 = "B" - f3 = "A" - - doAssert not compiles((let a = parseEnum[Ambig]("A"))) - -block: - # check almost ambiguous enum - type - AlmostAmbig = enum - f1 = "someA" - f2 = "someB" - f3 = "SomeA" - - let a = parseEnum[AlmostAmbig]("someA") - let b = parseEnum[AlmostAmbig]("someB") - let c = parseEnum[AlmostAmbig]("SomeA") - doAssert a == f1 - doAssert b == f2 - doAssert c == f3 diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim new file mode 100644 index 000000000..35f6bc669 --- /dev/null +++ b/tests/stdlib/tstrutils.nim @@ -0,0 +1,913 @@ +discard """ + matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on" +""" + +import std/strutils +from stdtest/testutils import disableVm +import std/assertions +import std/private/jsutils +# xxx each instance of `disableVm` and `when not defined js:` should eventually be fixed + +template rejectParse(e) = + try: + discard e + raise newException(AssertionDefect, "This was supposed to fail: $#!" % astToStr(e)) + except ValueError: discard + +template main() = + block: # strip + doAssert strip(" ha ") == "ha" + doAssert strip(" foofoofoo ") == "foofoofoo" + doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo" + doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo" + doAssert strip("stripme but don't strip this stripme", + chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == + " but don't strip this " + doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo" + doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" + + block: + let a = "xxxxxx" + doAssert a.strip(chars={'x'}).len == 0 + + doAssert "".strip(chars={'x'}).len == 0 + doAssert " ".strip(chars={'x'}) == " " + doAssert "xxx xxx".strip(chars={'x'}) == " " + doAssert "xxx wind".strip(chars={'x'}) == " wind" + doAssert "xxx iii".strip(chars={'i'}) == "xxx " + doAssert "x".strip(leading = false, chars={'x'}).len == 0 + doAssert "x".strip(trailing = false, chars={'x'}).len == 0 + doAssert "x".strip(leading = false, trailing = false, chars={'x'}) == "x" + + block: # split + var ret: seq[string] # or use `toSeq` or `collect` + for p in split("/home/a1:xyz:/usr/bin", {':'}): ret.add p + doAssert ret == @["/home/a1", "xyz", "/usr/bin"] + + let s = " this is an example " + let s2 = ":this;is;an:example;;" + + doAssert s.split() == @["", "this", "is", "an", "example", "", ""] + doAssert s2.split(seps = {':', ';'}) == @["", "this", "is", "an", "example", + "", ""] + doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example "] + doAssert s.split(' ', maxsplit = 1) == @["", "this is an example "] + doAssert s.split(" ", maxsplit = 4) == @["", "this", "is", "an", "example "] + # Empty string: + doAssert "".split() == @[""] + doAssert "".split(" ") == @[""] + doAssert "".split({' '}) == @[""] + # Empty separators: + doAssert "".split({}) == @[""] + doAssert "".split("") == @[""] + doAssert s.split({}) == @[s] + doAssert s.split("") == @[s] + + block: # splitLines + let fixture = "a\nb\rc\r\nd" + doAssert len(fixture.splitLines) == 4 + doAssert splitLines(fixture) == @["a", "b", "c", "d"] + doAssert splitLines(fixture, keepEol=true) == @["a\n", "b\r", "c\r\n", "d"] + + block: # rsplit + doAssert rsplit("foo bar", seps = Whitespace) == @["foo", "bar"] + doAssert rsplit(" foo bar", seps = Whitespace, maxsplit = 1) == @[" foo", "bar"] + doAssert rsplit(" foo bar ", seps = Whitespace, maxsplit = 1) == @[" foo bar", ""] + doAssert rsplit(":foo:bar", sep = ':') == @["", "foo", "bar"] + doAssert rsplit(":foo:bar", sep = ':', maxsplit = 2) == @["", "foo", "bar"] + doAssert rsplit(":foo:bar", sep = ':', maxsplit = 3) == @["", "foo", "bar"] + doAssert rsplit("foothebar", sep = "the") == @["foo", "bar"] + # Empty string: + doAssert "".rsplit() == @[""] + doAssert "".rsplit(" ") == @[""] + doAssert "".rsplit({' '}) == @[""] + # Empty separators: + let s = " this is an example " + doAssert "".rsplit({}) == @[""] + doAssert "".rsplit("") == @[""] + doAssert s.rsplit({}) == @[s] + doAssert s.rsplit("") == @[s] + + block: # splitWhitespace + let s = " this is an example " + doAssert s.splitWhitespace() == @["this", "is", "an", "example"] + doAssert s.splitWhitespace(maxsplit = 1) == @["this", "is an example "] + doAssert s.splitWhitespace(maxsplit = 2) == @["this", "is", "an example "] + doAssert s.splitWhitespace(maxsplit = 3) == @["this", "is", "an", "example "] + doAssert s.splitWhitespace(maxsplit = 4) == @["this", "is", "an", "example"] + + block: # removeSuffix + var s = "hello\n\r" + s.removeSuffix + doAssert s == "hello" + s.removeSuffix + doAssert s == "hello" + + s = "hello\n\n" + s.removeSuffix + doAssert s == "hello" + + s = "hello\r" + s.removeSuffix + doAssert s == "hello" + + s = "hello \n there" + s.removeSuffix + doAssert s == "hello \n there" + + s = "hello" + s.removeSuffix("llo") + doAssert s == "he" + s.removeSuffix('e') + doAssert s == "h" + + s = "hellos" + s.removeSuffix({'s','z'}) + doAssert s == "hello" + s.removeSuffix({'l','o'}) + doAssert s == "he" + + s = "aeiou" + s.removeSuffix("") + doAssert s == "aeiou" + + s = "" + s.removeSuffix("") + doAssert s == "" + + s = " " + s.removeSuffix + doAssert s == " " + + s = " " + s.removeSuffix("") + doAssert s == " " + + s = " " + s.removeSuffix(" ") + doAssert s == " " + + s = " " + s.removeSuffix(' ') + doAssert s == "" + + # Contrary to Chomp in other languages + # empty string does not change behaviour + s = "hello\r\n\r\n" + s.removeSuffix("") + doAssert s == "hello\r\n\r\n" + + block: # removePrefix + var s = "\n\rhello" + s.removePrefix + doAssert s == "hello" + s.removePrefix + doAssert s == "hello" + + s = "\n\nhello" + s.removePrefix + doAssert s == "hello" + + s = "\rhello" + s.removePrefix + doAssert s == "hello" + + s = "hello \n there" + s.removePrefix + doAssert s == "hello \n there" + + s = "hello" + s.removePrefix("hel") + doAssert s == "lo" + s.removePrefix('l') + doAssert s == "o" + + s = "hellos" + s.removePrefix({'h','e'}) + doAssert s == "llos" + s.removePrefix({'l','o'}) + doAssert s == "s" + + s = "aeiou" + s.removePrefix("") + doAssert s == "aeiou" + + s = "" + s.removePrefix("") + doAssert s == "" + + s = " " + s.removePrefix + doAssert s == " " + + s = " " + s.removePrefix("") + doAssert s == " " + + s = " " + s.removePrefix(" ") + doAssert s == " " + + s = " " + s.removePrefix(' ') + doAssert s == "" + + # Contrary to Chomp in other languages + # empty string does not change behaviour + s = "\r\n\r\nhello" + s.removePrefix("") + doAssert s == "\r\n\r\nhello" + + block: # delete(slice) + var s = "0123456789ABCDEFGH" + delete(s, 4 .. 5) + doAssert s == "01236789ABCDEFGH" + delete(s, s.len-1 .. s.len-1) + doAssert s == "01236789ABCDEFG" + delete(s, 0..0) + doAssert s == "1236789ABCDEFG" + s = "" + doAssertRaises(IndexDefect): delete(s, 0..0) + doAssert s == "" + s = "abc" + doAssertRaises(IndexDefect): delete(s, -1 .. -2) + doAssertRaises(IndexDefect): delete(s, 2..3) + doAssertRaises(IndexDefect): delete(s, 3..2) + delete(s, 2..2) + doAssert s == "ab" + delete(s, 1..0) + doAssert s == "ab" + delete(s, 0..0) + doAssert s == "b" + + block: # delete(first, last) + {.push warning[deprecated]:off.} + var s = "0123456789ABCDEFGH" + delete(s, 4, 5) + doAssert s == "01236789ABCDEFGH" + delete(s, s.len-1, s.len-1) + doAssert s == "01236789ABCDEFG" + delete(s, 0, 0) + doAssert s == "1236789ABCDEFG" + {.pop.} + + block: # find + const haystack: string = "0123456789ABCDEFGH" + doAssert haystack.find('A') == 10 + doAssert haystack.find('A', 5) == 10 + doAssert haystack.find('A', 5, 10) == 10 + doAssert haystack.find('A', 5, 9) == -1 + doAssert haystack.find("A") == 10 + doAssert haystack.find("A", 5) == 10 + doAssert haystack.find("A", 5, 10) == 10 + doAssert haystack.find("A", 5, 9) == -1 + doAssert haystack.find({'A'..'C'}) == 10 + doAssert haystack.find({'A'..'C'}, 5) == 10 + doAssert haystack.find({'A'..'C'}, 5, 10) == 10 + doAssert haystack.find({'A'..'C'}, 5, 9) == -1 + doAssert haystack.find('A', 0, 0) == -1 # search limited to the first char + doAssert haystack.find('A', 5, 0) == -1 # last < start + doAssert haystack.find('A', 5, 4) == -1 # last < start + + block: + const haystack: string = "ABCABABABABCAB" + doAssert haystack.len == 14 + + # only last argument + doAssert haystack.find("ABC") == 0 + doAssert haystack.find("ABC", last=13) == 0 # after the second ABC + doAssert haystack.find("ABC", last=5) == 0 # before the second ABC + + # only start argument + doAssert haystack.find("ABC", start=0) == 0 + doAssert haystack.find("ABC", start=1) == 9 + doAssert haystack.find("ABC", start=9) == 9 + doAssert haystack.find("ABC", start=10) == -1 + + # both start and last arguments + doAssert haystack.find("ABC", start=0, last=14) == 0 + doAssert haystack.find("ABC", start=0, last=13) == 0 + doAssert haystack.find("ABC", start=0, last=12) == 0 + doAssert haystack.find("ABC", start=1, last=13) == 9 + doAssert haystack.find("ABC", start=1, last=12) == 9 + doAssert haystack.find("ABC", start=1, last=11) == 9 + doAssert haystack.find("ABC", start=1, last=10) == -1 + + doAssert "".find("/") == -1 + doAssert "/".find("/") == 0 + doAssert "/".find("//") == -1 + doAssert "///".find("//", start=3) == -1 + + # searching for empty string + doAssert "".find("") == 0 + doAssert "abc".find("") == 0 + doAssert "abc".find("", start=1) == 1 + doAssert "abc".find("", start=2) == 2 + doAssert "abc".find("", start=3) == 3 + doAssert "abc".find("", start=4) == -1 + doAssert "abc".find("", start=400) == -1 + doAssert "abc".find("", start=1, last=3) == 1 + doAssert "abc".find("", start=1, last=2) == 1 + doAssert "abc".find("", start=1, last=1) == 1 + doAssert "abc".find("", start=1, last=0) == 1 + doAssert "abc".find("", start=1, last = -1) == 1 + + # when last <= start, searching for non-empty string + block: + let last: int = -1 # searching through whole line + doAssert "abcd".find("ab", start=0, last=last) == 0 + doAssert "abcd".find("ab", start=1, last=last) == -1 + doAssert "abcd".find("bc", start=1, last=last) == 1 + doAssert "abcd".find("bc", start=2, last=last) == -1 + block: + let last: int = 0 + doAssert "abcd".find("ab", start=0, last=last) == -1 + doAssert "abcd".find("ab", start=1, last=last) == -1 + doAssert "abcd".find("bc", start=1, last=last) == -1 + doAssert "abcd".find("bc", start=2, last=last) == -1 + block: + let last: int = 1 + doAssert "abcd".find("ab", start=0, last=last) == 0 + doAssert "abcd".find("ab", start=1, last=last) == -1 + doAssert "abcd".find("bc", start=1, last=last) == -1 + doAssert "abcd".find("bc", start=2, last=last) == -1 + + block: # rfind + doAssert "0123456789ABCDEFGAH".rfind('A') == 17 + doAssert "0123456789ABCDEFGAH".rfind('A', last=13) == 10 + doAssert "0123456789ABCDEFGAH".rfind('H', last=13) == -1 + doAssert "0123456789ABCDEFGAH".rfind("A") == 17 + doAssert "0123456789ABCDEFGAH".rfind("A", last=13) == 10 + doAssert "0123456789ABCDEFGAH".rfind("H", last=13) == -1 + doAssert "0123456789ABCDEFGAH".rfind({'A'..'C'}) == 17 + doAssert "0123456789ABCDEFGAH".rfind({'A'..'C'}, last=13) == 12 + doAssert "0123456789ABCDEFGAH".rfind({'G'..'H'}, last=13) == -1 + doAssert "0123456789ABCDEFGAH".rfind('A', start=18) == -1 + doAssert "0123456789ABCDEFGAH".rfind('A', start=11, last=17) == 17 + doAssert "0123456789ABCDEFGAH".rfind("0", start=0) == 0 + doAssert "0123456789ABCDEFGAH".rfind("0", start=1) == -1 + doAssert "0123456789ABCDEFGAH".rfind("H", start=11) == 18 + doAssert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=5) == 9 + doAssert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=10) == -1 + + doAssert "/1/2/3".rfind('/') == 4 + doAssert "/1/2/3".rfind('/', last=1) == 0 + doAssert "/1/2/3".rfind('0') == -1 + + block: + const haystack: string = "ABCABABABABCAB" + doAssert haystack.len == 14 + doAssert haystack.rfind("ABC") == 9 + doAssert haystack.rfind("ABC", last=13) == 9 + doAssert haystack.rfind("ABC", last=12) == 9 + doAssert haystack.rfind("ABC", last=11) == 9 + doAssert haystack.rfind("ABC", last=10) == 0 + + doAssert haystack.rfind("ABC", start=0) == 9 + doAssert haystack.rfind("ABC", start=1) == 9 + doAssert haystack.rfind("ABC", start=9) == 9 + doAssert haystack.rfind("ABC", start=10) == -1 + + doAssert haystack.rfind("ABC", start=0, last=13) == 9 + doAssert haystack.rfind("ABC", start=0, last=12) == 9 + doAssert haystack.rfind("ABC", start=0, last=11) == 9 + doAssert haystack.rfind("ABC", start=0, last=10) == 0 + doAssert haystack.rfind("ABC", start=1, last=10) == -1 + + doAssert "".rfind("/") == -1 + doAssert "/".rfind("/") == 0 + doAssert "/".rfind("//") == -1 + doAssert "///".rfind("//", start=3) == -1 + + # searching for empty string + doAssert "".rfind("") == 0 + doAssert "abc".rfind("") == 3 + doAssert "abc".rfind("", start=1) == 3 + doAssert "abc".rfind("", start=2) == 3 + doAssert "abc".rfind("", start=3) == 3 + doAssert "abc".rfind("", start=4) == 4 + doAssert "abc".rfind("", start=400) == 400 + + doAssert "abc".rfind("", start=1, last=3) == 3 + doAssert "abc".rfind("", start=1, last=2) == 2 + doAssert "abc".rfind("", start=1, last=1) == 1 + # This returns the start index instead of the last index + # because start > last + doAssert "abc".rfind("", start=1, last=0) == 1 + doAssert "abc".rfind("", start=1, last = -1) == 3 + + doAssert "abc".rfind("", start=0, last=0) == 0 + + # when last <= start, searching for non-empty string + block: + let last: int = -1 + doAssert "abcd".rfind("ab", start=0, last=last) == 0 + doAssert "abcd".rfind("ab", start=1, last=last) == -1 + doAssert "abcd".rfind("bc", start=1, last=last) == 1 + doAssert "abcd".rfind("bc", start=2, last=last) == -1 + block: + let last: int = 0 + doAssert "abcd".rfind("ab", start=0, last=last) == -1 + doAssert "abcd".rfind("ab", start=1, last=last) == -1 + doAssert "abcd".rfind("bc", start=1, last=last) == -1 + doAssert "abcd".rfind("bc", start=2, last=last) == -1 + block: + let last: int = 1 + doAssert "abcd".rfind("ab", start=0, last=last) == 0 + doAssert "abcd".rfind("ab", start=1, last=last) == -1 + doAssert "abcd".rfind("bc", start=1, last=last) == -1 + doAssert "abcd".rfind("bc", start=2, last=last) == -1 + + block: # trimZeros + var x = "1200" + x.trimZeros() + doAssert x == "1200" + x = "120.0" + x.trimZeros() + doAssert x == "120" + x = "0." + x.trimZeros() + doAssert x == "0" + x = "1.0e2" + x.trimZeros() + doAssert x == "1e2" + x = "78.90" + x.trimZeros() + doAssert x == "78.9" + x = "1.23e4" + x.trimZeros() + doAssert x == "1.23e4" + x = "1.01" + x.trimZeros() + doAssert x == "1.01" + x = "1.1001" + x.trimZeros() + doAssert x == "1.1001" + x = "0.0" + x.trimZeros() + doAssert x == "0" + x = "0.01" + x.trimZeros() + doAssert x == "0.01" + x = "1e0" + x.trimZeros() + doAssert x == "1e0" + x = "1.23" + x.trimZeros() + doAssert x == "1.23" + + block: # countLines + proc assertCountLines(s: string) = doAssert s.countLines == s.splitLines.len + assertCountLines("") + assertCountLines("\n") + assertCountLines("\n\n") + assertCountLines("abc") + assertCountLines("abc\n123") + assertCountLines("abc\n123\n") + assertCountLines("\nabc\n123") + assertCountLines("\nabc\n123\n") + + block: # parseBinInt, parseHexInt, parseOctInt + # binary + doAssert "0b1111".parseBinInt == 15 + doAssert "0B1111".parseBinInt == 15 + doAssert "1111".parseBinInt == 15 + doAssert "1110".parseBinInt == 14 + doAssert "1_1_1_1".parseBinInt == 15 + doAssert "0b1_1_1_1".parseBinInt == 15 + rejectParse "".parseBinInt + rejectParse "_".parseBinInt + rejectParse "0b".parseBinInt + rejectParse "0b1234".parseBinInt + # hex + doAssert "0x72".parseHexInt == 114 + doAssert "0X72".parseHexInt == 114 + doAssert "#72".parseHexInt == 114 + doAssert "72".parseHexInt == 114 + doAssert "FF".parseHexInt == 255 + doAssert "ff".parseHexInt == 255 + doAssert "fF".parseHexInt == 255 + doAssert "0x7_2".parseHexInt == 114 + rejectParse "".parseHexInt + rejectParse "_".parseHexInt + rejectParse "0x".parseHexInt + rejectParse "0xFFG".parseHexInt + rejectParse "reject".parseHexInt + # octal + doAssert "0o17".parseOctInt == 15 + doAssert "0O17".parseOctInt == 15 + doAssert "17".parseOctInt == 15 + doAssert "10".parseOctInt == 8 + doAssert "0o1_0_0".parseOctInt == 64 + rejectParse "".parseOctInt + rejectParse "_".parseOctInt + rejectParse "0o".parseOctInt + rejectParse "9".parseOctInt + rejectParse "0o9".parseOctInt + rejectParse "reject".parseOctInt + + block: # parseHexStr + doAssert "".parseHexStr == "" + doAssert "00Ff80".parseHexStr == "\0\xFF\x80" + try: + discard "00Ff8".parseHexStr + doAssert false, "Should raise ValueError" + except ValueError: + discard + + try: + discard "0k".parseHexStr + doAssert false, "Should raise ValueError" + except ValueError: + discard + + doAssert "".toHex == "" + doAssert "\x00\xFF\x80".toHex == "00FF80" + doAssert "0123456789abcdef".parseHexStr.toHex == "0123456789ABCDEF" + + block: # toHex + doAssert(toHex(100i16, 32) == "00000000000000000000000000000064") + whenJsNoBigInt64: discard + do: + doAssert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C") + doAssert(toHex(high(uint64)) == "FFFFFFFFFFFFFFFF") + doAssert(toHex(high(uint64), 16) == "FFFFFFFFFFFFFFFF") + doAssert(toHex(high(uint64), 32) == "0000000000000000FFFFFFFFFFFFFFFF") + + block: # insertSep + doAssert(insertSep($1000_000) == "1_000_000") + doAssert(insertSep($232) == "232") + doAssert(insertSep($12345, ',') == "12,345") + doAssert(insertSep($0) == "0") + + block: # repeat, spaces + doAssert(' '.repeat(8) == " ") + doAssert(" ".repeat(8) == " ") + doAssert(spaces(8) == " ") + + doAssert(' '.repeat(0) == "") + doAssert(" ".repeat(0) == "") + doAssert(spaces(0) == "") + + block: # toBin, toOct + whenJsNoBigInt64: # bug #11369 + discard + do: + var num: int64 = -1 + doAssert num.toBin(64) == "1111111111111111111111111111111111111111111111111111111111111111" + doAssert num.toOct(24) == "001777777777777777777777" + + block: # replace + doAssert "oo".replace("", "abc") == "oo" + # bug #8911 + static: + let a = "" + let a2 = a.replace("\n", "\\n") + + static: + let b = "b" + let b2 = b.replace("\n", "\\n") + + block: + let c = "" + let c2 = c.replace("\n", "\\n") + + block: # replaceWord + doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " + doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" + doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc" + + block: # multiReplace + doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab" + doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", + "PEOPLE!")) == "HELLO PEOPLE!" + doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa" + + # `parseEnum`, ref issue #14030 + # check enum defined at top level # xxx this is probably irrelevant, and pollutes scope + # for remaining tests + type + Foo = enum + A = -10 + B = "bb" + C = (-5, "ccc") + D = 15 + E = "ee" # check that we count enum fields correctly + + block: # parseEnum + block: + let a = parseEnum[Foo]("A") + let b = parseEnum[Foo]("bb") + let c = parseEnum[Foo]("ccc") + let d = parseEnum[Foo]("D") + let e = parseEnum[Foo]("ee") + doAssert a == A + doAssert b == B + doAssert c == C + doAssert d == D + doAssert e == E + try: + let f = parseEnum[Foo]("Bar") + doAssert false + except ValueError: + discard + + # finally using default + let g = parseEnum[Foo]("Bar", A) + doAssert g == A + + block: # bug #19463 + const CAMPAIGN_TABLE = "wikientries_campaign" + const CHARACTER_TABLE = "wikientries_character" + + type Tables = enum + a = CAMPAIGN_TABLE, + b = CHARACTER_TABLE, + + let myA = CAMPAIGN_TABLE + doAssert $parseEnum[Tables](myA) == "wikientries_campaign" + + block: # check enum defined in block + type + Bar = enum + V + W = "ww" + X = (3, "xx") + Y = 10 + Z = "zz" # check that we count enum fields correctly + + let a = parseEnum[Bar]("V") + let b = parseEnum[Bar]("ww") + let c = parseEnum[Bar]("xx") + let d = parseEnum[Bar]("Y") + let e = parseEnum[Bar]("zz") + doAssert a == V + doAssert b == W + doAssert c == X + doAssert d == Y + doAssert e == Z + try: + let f = parseEnum[Bar]("Baz") + doAssert false + except ValueError: + discard + + # finally using default + let g = parseEnum[Bar]("Baz", V) + doAssert g == V + + block: # check ambiguous enum fails to parse + type + Ambig = enum + f1 = "A" + f2 = "B" + f3 = "A" + + doAssert not compiles((let a = parseEnum[Ambig]("A"))) + + block: # check almost ambiguous enum + type + AlmostAmbig = enum + f1 = "someA" + f2 = "someB" + f3 = "SomeA" + + let a = parseEnum[AlmostAmbig]("someA") + let b = parseEnum[AlmostAmbig]("someB") + let c = parseEnum[AlmostAmbig]("SomeA") + doAssert a == f1 + doAssert b == f2 + doAssert c == f3 + + block: + type MyEnum = enum enA, enB, enC, enuD, enE + doAssert parseEnum[MyEnum]("enu_D") == enuD + + doAssert parseEnum("invalid enum value", enC) == enC + + block: # issue #22726 + type SomeEnum = enum A, B, C + + proc assignEnum(dest: var enum, s: string) = + type ty = typeof(dest) + dest = parseEnum[ty](s) + + var v: SomeEnum + v.assignEnum("A") + doAssert v == A + + block: # indentation + doAssert 0 == indentation """ +hey + low + there +""" + doAssert 2 == indentation """ + hey + low + there +""" + doAssert 2 == indentation """ hey + low + there +""" + doAssert 2 == indentation """ hey + low + there""" + doAssert 0 == indentation "" + doAssert 0 == indentation " \n \n" + doAssert 0 == indentation " " + + block: # indent + doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" + + block: # unindent + doAssert """~~!!foo +~~!!bar +~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz" + + doAssert """~~!!foo +~~!!bar +~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz" + doAssert """~~foo +~~ bar +~~ baz""".unindent(4, "~") == "foo\n bar\n baz" + doAssert """foo +bar + baz + """.unindent(4) == "foo\nbar\nbaz\n" + doAssert """foo + bar + baz + """.unindent(2) == "foo\n bar\n baz\n" + doAssert """foo + bar + baz + """.unindent(100) == "foo\nbar\nbaz\n" + + doAssert """foo + foo + bar + """.unindent() == "foo\nfoo\nbar\n" + + block: # formatBiggestFloat + disableVm: + doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000" + when not defined(js): + doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." # bugs 8242, 12586 + doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6" + doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" + doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in + ["1,0e-11", "1,0e-011"] + block: # formatFloat + disableVm: + # bug #6589 + when not defined(js): + doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02" + + block: # `%` + doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" + doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb" + doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] == + "The cat eats fish." + + block: # formatSize + disableVm: + whenJsNoBigInt64: discard + do: + doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" # <=== bug #8231 + doAssert formatSize((2.234*1024*1024).int) == "2.234MiB" + doAssert formatSize(4096) == "4KiB" + doAssert formatSize(4096, prefix = bpColloquial, includeSpace = true) == "4 kB" + doAssert formatSize(4096, includeSpace = true) == "4 KiB" + doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,13MB" + + block: # formatEng + disableVm: + doAssert formatEng(0, 2, trim = false) == "0.00" + doAssert formatEng(0, 2) == "0" + doAssert formatEng(53, 2, trim = false) == "53.00" + doAssert formatEng(0.053, 2, trim = false) == "53.00e-3" + doAssert formatEng(0.053, 4, trim = false) == "53.0000e-3" + doAssert formatEng(0.053, 4, trim = true) == "53e-3" + doAssert formatEng(0.053, 0) == "53e-3" + doAssert formatEng(52731234) == "52.731234e6" + doAssert formatEng(-52731234) == "-52.731234e6" + doAssert formatEng(52731234, 1) == "52.7e6" + doAssert formatEng(-52731234, 1) == "-52.7e6" + doAssert formatEng(52731234, 1, decimalSep = ',') == "52,7e6" + doAssert formatEng(-52731234, 1, decimalSep = ',') == "-52,7e6" + + doAssert formatEng(4100, siPrefix = true, unit = "V") == "4.1 kV" + doAssert formatEng(4.1, siPrefix = true, unit = "V", + useUnitSpace = true) == "4.1 V" + doAssert formatEng(4.1, siPrefix = true) == "4.1" # Note lack of space + doAssert formatEng(4100, siPrefix = true) == "4.1 k" + doAssert formatEng(4.1, siPrefix = true, unit = "", + useUnitSpace = true) == "4.1 " # Includes space + doAssert formatEng(4100, siPrefix = true, unit = "") == "4.1 k" + doAssert formatEng(4100) == "4.1e3" + doAssert formatEng(4100, unit = "V", useUnitSpace = true) == "4.1e3 V" + doAssert formatEng(4100, unit = "", useUnitSpace = true) == "4.1e3 " + # Don't use SI prefix as number is too big + doAssert formatEng(3.1e22, siPrefix = true, unit = "a", + useUnitSpace = true) == "31e21 a" + # Don't use SI prefix as number is too small + doAssert formatEng(3.1e-25, siPrefix = true, unit = "A", + useUnitSpace = true) == "310e-27 A" + + block: # align + doAssert align("abc", 4) == " abc" + doAssert align("a", 0) == "a" + doAssert align("1232", 6) == " 1232" + doAssert align("1232", 6, '#') == "##1232" + + block: # alignLeft + doAssert alignLeft("abc", 4) == "abc " + doAssert alignLeft("a", 0) == "a" + doAssert alignLeft("1232", 6) == "1232 " + doAssert alignLeft("1232", 6, '#') == "1232##" + + block: # center + doAssert center("foo", 13) == " foo " + doAssert center("foo", 0) == "foo" + doAssert center("foo", 3, fillChar = 'a') == "foo" + doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t" + + block: # count + doAssert count("foofoofoo", "foofoo") == 1 + doAssert count("foofoofoo", "foofoo", overlapping = true) == 2 + doAssert count("foofoofoo", 'f') == 3 + doAssert count("foofoofoobar", {'f', 'b'}) == 4 + + block: # isAlphaAscii + doAssert isAlphaAscii('r') + doAssert isAlphaAscii('A') + doAssert(not isAlphaAscii('$')) + + block: # isAlphaNumeric + doAssert isAlphaNumeric('3') + doAssert isAlphaNumeric('R') + doAssert(not isAlphaNumeric('!')) + + block: # isDigit + doAssert isDigit('3') + doAssert(not isDigit('a')) + doAssert(not isDigit('%')) + + block: # isSpaceAscii + doAssert isSpaceAscii('\t') + doAssert isSpaceAscii('\l') + doAssert(not isSpaceAscii('A')) + + block: # isEmptyOrWhitespace + doAssert(isEmptyOrWhitespace("")) + doAssert(isEmptyOrWhitespace(" ")) + doAssert(isEmptyOrWhitespace("\t\l \v\r\f")) + doAssert(not isEmptyOrWhitespace("ABc \td")) + + block: # isLowerAscii + doAssert isLowerAscii('a') + doAssert isLowerAscii('z') + doAssert(not isLowerAscii('A')) + doAssert(not isLowerAscii('5')) + doAssert(not isLowerAscii('&')) + doAssert(not isLowerAscii(' ')) + + block: # isUpperAscii + doAssert isUpperAscii('A') + doAssert(not isUpperAscii('b')) + doAssert(not isUpperAscii('5')) + doAssert(not isUpperAscii('%')) + + block: # unescape + doAssert(unescape(r"\x013", "", "") == "\x013") + + block: # join + doAssert join(["foo", "bar", "baz"]) == "foobarbaz" + doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz" + doAssert join([1, 2, 3]) == "123" + doAssert join(@[1, 2, 3], ", ") == "1, 2, 3" + + block: # startsWith / endsWith + var s = "abcdef" + doAssert s.startsWith('a') + doAssert s.startsWith('b') == false + doAssert s.endsWith('f') + doAssert s.endsWith('a') == false + doAssert s.endsWith('\0') == false + + block: # nimIdentNormalize + doAssert nimIdentNormalize("") == "" + doAssert nimIdentNormalize("foo") == "foo" + doAssert nimIdentNormalize("foo_bar") == "foobar" + doAssert nimIdentNormalize("Foo_bar") == "Foobar" + doAssert nimIdentNormalize("_Foo_bar") == "_foobar" + + block: # bug #19500 + doAssert "abc \0 def".find("def") == 6 + doAssert "abc \0 def".find('d') == 6 + + +static: main() +main() diff --git a/tests/stdlib/tstrutils2.nim b/tests/stdlib/tstrutils2.nim deleted file mode 100644 index 881817f90..000000000 --- a/tests/stdlib/tstrutils2.nim +++ /dev/null @@ -1,32 +0,0 @@ -import "$lib/.." / compiler/strutils2 - -block: # setLen - var a = "abc" - a.setLen 0 - a.setLen 3, isInit = false - doAssert a[1] == 'b' - a.setLen 0 - a.setLen 3, isInit = true - doAssert a[1] == '\0' - -block: # forceCopy - var a: string - a = "foo" - shallow(a) - var b: string - b = a - doAssert b[0].addr == a[0].addr - var c: string - c.forceCopy a - doAssert c == a - doAssert c[0].addr != a[0].addr - -block: # toLowerAscii - var a = "fooBAr" - a.toLowerAscii - doAssert a == "foobar" - -block: # dataPointer - var a: string - discard a.dataPointer - # doAssert a.dataPointer == nil # not guaranteed diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim new file mode 100644 index 000000000..2ea96cfbb --- /dev/null +++ b/tests/stdlib/tsugar.nim @@ -0,0 +1,310 @@ +discard """ + targets: "c js" + matrix: "--mm:refc; --mm:orc" + output: ''' +x + y = 30 +''' +""" +import std/[sugar, algorithm, random, sets, tables, strutils, sequtils] +import std/[syncio, assertions] + +type # for capture test, ref #20679 + FooCapture = ref object + x: int + +proc mainProc() = + block: # bug #16967 + var s = newSeq[proc (): int](5) + {.push exportc.} + proc bar() = + for i in 0 ..< s.len: + let foo = i + 1 + capture foo: + s[i] = proc(): int = foo + {.pop.} + + bar() + + for i, p in s.pairs: + let foo = i + 1 + doAssert p() == foo + +template main() = + block: # `=>` + block: + let f1 = () => 42 + doAssert f1() == 42 + + let f2 = (x: int) => x + 1 + doAssert f2(42) == 43 + + let f3 = (x, y: int) => x + y + doAssert f3(1, 2) == 3 + + var x = 0 + let f4 = () => (x = 12) + f4() + doAssert x == 12 + + let f5 = () => (discard) # simplest proc that returns void + f5() + + block: + proc call1(f: () -> int): int = f() + doAssert call1(() => 12) == 12 + + proc call2(f: int -> int): int = f(42) + doAssert call2(x => x) == 42 + doAssert call2((x) => x) == 42 + doAssert call2((x: int) => x) == 42 + + proc call3(f: (int, int) -> int): int = f(1, 2) + doAssert call3((x, y) => x + y) == 3 + doAssert call3((x, y: int) => x + y) == 3 + doAssert call3((x: int, y: int) => x + y) == 3 + + var a = 0 + proc call4(f: int -> void) = f(42) + call4((x: int) => (a = x)) + doAssert a == 42 + + proc call5(f: (int {.noSideEffect.} -> int)): int = f(42) + doAssert call5(x {.noSideEffect.} => x + 1) == 43 + + block: # `->` + doAssert $(() -> int) == "proc (): int{.closure.}" + doAssert $(float -> int) == "proc (i0: float): int{.closure.}" + doAssert $((float) -> int) == "proc (i0: float): int{.closure.}" + doAssert $((float, bool) -> int) == "proc (i0: float, i1: bool): int{.closure.}" + + doAssert $(() -> void) == "proc (){.closure.}" + doAssert $(float -> void) == "proc (i0: float){.closure.}" + doAssert $((float) -> void) == "proc (i0: float){.closure.}" + doAssert $((float, bool) -> void) == "proc (i0: float, i1: bool){.closure.}" + + doAssert $(() {.inline.} -> int) == "proc (): int{.inline.}" + doAssert $(float {.inline.} -> int) == "proc (i0: float): int{.inline.}" + doAssert $((float) {.inline.} -> int) == "proc (i0: float): int{.inline.}" + doAssert $((float, bool) {.inline.} -> int) == "proc (i0: float, i1: bool): int{.inline.}" + + block: # capture + var closure1: () -> int + for i in 0 .. 10: + if i == 5: + capture i: + closure1 = () => i + doAssert closure1() == 5 + + var closure2: () -> (int, int) + for i in 0 .. 10: + for j in 0 .. 10: + if i == 5 and j == 3: + capture i, j: + closure2 = () => (i, j) + doAssert closure2() == (5, 3) + + block: # issue #20679 + # this should compile. Previously was broken as `var int` is an `nnkHiddenDeref` + # which was not handled correctly + + block: + var x = 5 + var s1 = newSeq[proc (): int](2) + proc function(data: var int) = + for i in 0 ..< 2: + data = (i+1) * data + capture data: + s1[i] = proc(): int = data + function(x) + doAssert s1[0]() == 5 + doAssert s1[1]() == 10 + + + block: + var y = @[5, 10] + var s2 = newSeq[proc (): seq[int]](2) + proc functionS(data: var seq[int]) = + for i in 0 ..< 2: + data.add (i+1) * 5 + capture data: + s2[i] = proc(): seq[int] = data + functionS(y) + doAssert s2[0]() == @[5, 10, 5] + doAssert s2[1]() == @[5, 10, 5, 10] + + + template typeT(typ, val: untyped): untyped = + var x = val + var s = newSeq[proc (): typ](2) + + proc functionT[T](data: var T) = + for i in 0 ..< 2: + if i == 1: + data = default(T) + capture data: + s[i] = proc (): T = data + + functionT(x) + doAssert s[0]() == val + doAssert s[1]() == x # == default + doAssert s[1]() == default(typ) + + block: + var x = 1.1 + typeT(float, x) + block: + var x = "hello" + typeT(string, x) + block: + var f = FooCapture(x: 5) + typeT(FooCapture, f) + + block: # dup + block dup_with_field: + type + Foo = object + col, pos: int + name: string + + proc inc_col(foo: var Foo) = inc(foo.col) + proc inc_pos(foo: var Foo) = inc(foo.pos) + proc name_append(foo: var Foo, s: string) = foo.name &= s + + let a = Foo(col: 1, pos: 2, name: "foo") + block: + let b = a.dup(inc_col, inc_pos): + _.pos = 3 + name_append("bar") + inc_pos + + doAssert(b == Foo(col: 2, pos: 4, name: "foobar")) + + block: + let b = a.dup(inc_col, pos = 3, name = "bar"): + name_append("bar") + inc_pos + + doAssert(b == Foo(col: 2, pos: 4, name: "barbar")) + + block: + var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + doAssert dup(a, sort(_)) == sorted(a) + doAssert a.dup(sort) == sorted(a) + # Chaining: + var aCopy = a + aCopy.insert(10) + doAssert a.dup(insert(10)).dup(sort()) == sorted(aCopy) + + block: + when nimvm: discard + else: + const b = @[0, 1, 2] + discard b.dup shuffle() + doAssert b[0] == 0 + doAssert b[1] == 1 + + block: # collect + let data = @["bird", "word"] # if this gets stuck in your head, its not my fault + + doAssert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"] + doAssert collect(initTable(2), for (i, d) in data.pairs: {i: d}) == + {0: "bird", 1: "word"}.toTable + doAssert collect(initHashSet(), for d in data.items: {d}) == data.toHashSet + + block: + let x = collect(newSeqOfCap(4)): + for (i, d) in data.pairs: + if i mod 2 == 0: d + doAssert x == @["bird"] + + block: # bug #12874 + let bug = collect( + newSeq, + for (i, d) in data.pairs:( + block: + if i mod 2 == 0: + d + else: + d & d + ) + ) + doAssert bug == @["bird", "wordword"] + + block: + let y = collect(newSeq): + for (i, d) in data.pairs: + try: parseInt(d) except: 0 + doAssert y == @[0, 0] + + block: + let z = collect(newSeq): + for (i, d) in data.pairs: + case d + of "bird": "word" + else: d + doAssert z == @["word", "word"] + + block: + proc tforum(): seq[int] = + collect(newSeq): + for y in 0..10: + if y mod 5 == 2: + for x in 0..y: + x + doAssert tforum() == @[0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7] + + block: + let x = collect: + for d in data.items: + when d is int: "word" + else: d + doAssert x == @["bird", "word"] + + block: + doAssert collect(for (i, d) in pairs(data): (i, d)) == @[(0, "bird"), (1, "word")] + doAssert collect(for d in data.items: (try: parseInt(d) except: 0)) == @[0, 0] + doAssert collect(for (i, d) in pairs(data): {i: d}) == + {1: "word", 0: "bird"}.toTable + doAssert collect(for d in data.items: {d}) == data.toHashSet + + block: # bug #14332 + template foo = + discard collect(newSeq, for i in 1..3: i) + foo() + + block: # dump + # symbols in templates are gensym'd + let + x {.inject.} = 10 + y {.inject.} = 20 + dump(x + y) # x + y = 30 + + block: # dumpToString + template square(x): untyped = x * x + let x {.inject.} = 10 + doAssert dumpToString(square(x)) == "square(x): x * x = 100" + let s = dumpToString(doAssert 1+1 == 2) + doAssert "failedAssertImpl" in s + let s2 = dumpToString: + doAssertRaises(AssertionDefect): doAssert false + doAssert "except AssertionDefect" in s2 + + block: # bug #20704 + proc test() = + var xs, ys: seq[int] + for i in 0..5: + xs.add(i) + + xs.apply(proc (d: auto) = ys.add(d)) + # ^ can be turned into d => ys.add(d) when we can infer void return type, #16906 + doAssert ys == @[0, 1, 2, 3, 4, 5] + + test() + + mainProc() + +when not defined(js): # TODO fixme JS VM + static: + main() + +main() diff --git a/tests/stdlib/tsums.nim b/tests/stdlib/tsums.nim new file mode 100644 index 000000000..cf410cddf --- /dev/null +++ b/tests/stdlib/tsums.nim @@ -0,0 +1,27 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/sums +from math import pow +import std/assertions + +var epsilon = 1.0 +while 1.0 + epsilon != 1.0: + epsilon /= 2.0 +let data = @[1.0, epsilon, -epsilon] +doAssert sumKbn(data) == 1.0 +# doAssert sumPairs(data) != 1.0 # known to fail in 64 bits +doAssert (1.0 + epsilon) - epsilon != 1.0 + +var tc1: seq[float] +for n in 1 .. 1000: + tc1.add 1.0 / n.float +doAssert sumKbn(tc1) == 7.485470860550345 +doAssert sumPairs(tc1) == 7.485470860550345 + +var tc2: seq[float] +for n in 1 .. 1000: + tc2.add pow(-1.0, n.float) / n.float +doAssert sumKbn(tc2) == -0.6926474305598203 +doAssert sumPairs(tc2) == -0.6926474305598204 diff --git a/tests/stdlib/tsysrand.nim b/tests/stdlib/tsysrand.nim new file mode 100644 index 000000000..7b7a0fc34 --- /dev/null +++ b/tests/stdlib/tsysrand.nim @@ -0,0 +1,34 @@ +discard """ + targets: "c cpp js" + matrix: "--experimental:vmopsDanger; --experimental:vmopsDanger --mm:refc" +""" + +import std/sysrand +import std/assertions + +template main() = + block: + var x = array[5, byte].default + doAssert urandom(x) + + block: + var x = newSeq[byte](5) + doAssert urandom(x) + + block: + var x = @[byte(0), 0, 0, 0, 0] + doAssert urandom(x) + + block: + var x = @[byte(1), 2, 3, 4, 5] + doAssert urandom(x) + + block: + doAssert urandom(0).len == 0 + doAssert urandom(10).len == 10 + doAssert urandom(20).len == 20 + doAssert urandom(120).len == 120 + doAssert urandom(113).len == 113 + doAssert urandom(1234) != urandom(1234) # unlikely to fail in practice + +main() diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim new file mode 100644 index 000000000..f634ce0c2 --- /dev/null +++ b/tests/stdlib/tsystem.nim @@ -0,0 +1,200 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" +""" + +import stdtest/testutils +import std/[assertions, formatfloat] + +# TODO: in future work move existing `system` tests here, where they belong + + +template main = + block: # closure + proc outer() = + var a = 0 + proc inner1 = a.inc + proc inner2 = discard + doAssert inner1 is "closure" + doAssert inner2 isnot "closure" + doAssert inner1 is (proc) + doAssert inner2 is (proc) + let inner1b = inner1 + doAssert inner1b is "closure" + doAssert inner1b == inner1 + outer() + + block: # rawProc, rawProc, bug #17911 + proc outer() = + var a = 0 + var b = 0 + proc inner1() = a.inc + proc inner2() = a += 2 + proc inner3() = b.inc + let inner1b = inner1 + doAssert inner2 != inner1 + doAssert inner3 != inner1 + whenVMorJs: discard + do: + doAssert rawProc(inner1b) == rawProc(inner1) + doAssert rawProc(inner2) != rawProc(inner1) + doAssert rawProc(inner3) != rawProc(inner1) + + doAssert rawEnv(inner1b) == rawEnv(inner1) + doAssert rawEnv(inner2) == rawEnv(inner1) # because both use `a` + # doAssert rawEnv(inner3) != rawEnv(inner1) # because `a` vs `b` # this doesn't hold + outer() + + block: # system.delete + block: + var s = @[1] + s.delete(0) + doAssert s == @[] + + block: + var s = @["foo", "bar"] + s.delete(1) + doAssert s == @["foo"] + + when false: + var s: seq[string] + doAssertRaises(IndexDefect): + s.delete(0) + + block: + doAssert not compiles(@["foo"].delete(-1)) + + block: # bug #6710 + var s = @["foo"] + s.delete(0) + doAssert s == @[] + + when false: # bug #16544: deleting out of bounds index should raise + var s = @["foo"] + doAssertRaises(IndexDefect): + s.delete(1) + +static: main() +main() + +# bug #19967 +block: + type + X = object + a: string + b: set[char] + c: int + d: float + e: int64 + + + var x = X(b: {'a'}, e: 10) + + var y = move x + + doAssert x.a == "" + doAssert x.b == {} + doAssert x.c == 0 + doAssert x.d == 0.0 + doAssert x.e == 0 + + reset(y) + + doAssert y.a == "" + doAssert y.b == {} + doAssert y.c == 0 + doAssert y.d == 0.0 + doAssert y.e == 0 + +block: + var x = 2 + var y = move x + doAssert y == 2 + doAssert x == 0 + reset y + doAssert y == 0 + +block: + type + X = object + a: string + b: float + + var y = X(b: 1314.521) + + reset(y) + + doAssert y.b == 0.0 + +block: + type + X = object + a: string + b: string + + var y = X(b: "1314") + + reset(y) + + doAssert y.b == "" + +block: + type + X = object + a: string + b: seq[int] + + var y = X(b: @[1, 3]) + + reset(y) + + doAssert y.b == @[] + +block: + type + X = object + a: string + b: tuple[a: int, b: string] + + var y = X(b: (1, "cc")) + + reset(y) + + doAssert y.b == (0, "") + +block: + type + Color = enum + Red, Blue, Yellow + X = object + a: string + b: set[Color] + + var y = X(b: {Red, Blue}) + + reset(y) + doAssert y.b == {} + +block: # bug #20516 + type Foo = object + x {.bitsize:4.}: uint + y {.bitsize:4.}: uint + + when not defined(js): + let a = create(Foo) + +block: # bug #6549 + when not defined(js): + block: + const v = 18446744073709551615'u64 + + doAssert $v == "18446744073709551615" + doAssert $float32(v) == "1.8446744e+19", $float32(v) + doAssert $float64(v) == "1.8446744073709552e+19", $float64(v) + + block: + let v = 18446744073709551615'u64 + + doAssert $v == "18446744073709551615" + doAssert $float32(v) == "1.8446744e+19" + doAssert $float64(v) == "1.8446744073709552e+19" diff --git a/tests/stdlib/ttables.nim b/tests/stdlib/ttables.nim new file mode 100644 index 000000000..c529aff9f --- /dev/null +++ b/tests/stdlib/ttables.nim @@ -0,0 +1,316 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import tables, hashes +import std/assertions + +type + Person = object + firstName, lastName: string + +proc hash(x: Person): Hash = + ## Piggyback on the already available string hash proc. + ## + ## Without this proc nothing works! + result = x.firstName.hash !& x.lastName.hash + result = !$result + +var + salaries = initTable[Person, int]() + p1, p2: Person +p1.firstName = "Jon" +p1.lastName = "Ross" +salaries[p1] = 30_000 +p2.firstName = "소진" +p2.lastName = "박" +salaries[p2] = 45_000 +var + s2 = initOrderedTable[Person, int]() + s3 = initCountTable[Person]() +s2[p1] = 30_000 +s2[p2] = 45_000 +s3[p1] = 30_000 +s3[p2] = 45_000 + +block: # Ordered table should preserve order after deletion + var + s4 = initOrderedTable[int, int]() + s4[1] = 1 + s4[2] = 2 + s4[3] = 3 + + var prev = 0 + for i in s4.values: + doAssert(prev < i) + prev = i + + s4.del(2) + doAssert(2 notin s4) + doAssert(s4.len == 2) + prev = 0 + for i in s4.values: + doAssert(prev < i) + prev = i + +block: # Deletion from OrderedTable should account for collision groups. See issue #5057. + # The bug is reproducible only with exact keys + const key1 = "boy_jackpot.inGamma" + const key2 = "boy_jackpot.outBlack" + + var t = { + key1: 0, + key2: 0 + }.toOrderedTable() + + t.del(key1) + doAssert(t.len == 1) + doAssert(key2 in t) + +var + t1 = initCountTable[string]() + t2 = initCountTable[string]() +t1.inc("foo") +t1.inc("bar", 2) +t1.inc("baz", 3) +t2.inc("foo", 4) +t2.inc("bar") +t2.inc("baz", 11) +merge(t1, t2) +doAssert(t1["foo"] == 5) +doAssert(t1["bar"] == 3) +doAssert(t1["baz"] == 14) + +let + t1r = newCountTable[string]() + t2r = newCountTable[string]() +t1r.inc("foo") +t1r.inc("bar", 2) +t1r.inc("baz", 3) +t2r.inc("foo", 4) +t2r.inc("bar") +t2r.inc("baz", 11) +merge(t1r, t2r) +doAssert(t1r["foo"] == 5) +doAssert(t1r["bar"] == 3) +doAssert(t1r["baz"] == 14) + +var + t1l = initCountTable[string]() + t2l = initCountTable[string]() +t1l.inc("foo") +t1l.inc("bar", 2) +t1l.inc("baz", 3) +t2l.inc("foo", 4) +t2l.inc("bar") +t2l.inc("baz", 11) + +block: + const testKey = "TESTKEY" + let t: CountTableRef[string] = newCountTable[string]() + + # Before, does not compile with error message: + #test_counttable.nim(7, 43) template/generic instantiation from here + #lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here + #lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode + doAssert 0 == t[testKey] + t.inc(testKey, 3) + doAssert 3 == t[testKey] + +block: + # Clear tests + var clearTable = newTable[int, string]() + clearTable[42] = "asd" + clearTable[123123] = "piuyqwb " + doAssert clearTable[42] == "asd" + clearTable.clear() + doAssert(not clearTable.hasKey(123123)) + doAssert clearTable.getOrDefault(42) == "" + +block: #5482 + var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() + var b = newOrderedTable[string, string](initialSize = 2) + b["wrong?"] = "foo" + b["wrong?"] = "foo2" + doAssert a == b + +block: #5482 + var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() + var b = newOrderedTable[string, string](initialSize = 2) + b["wrong?"] = "foo" + b["wrong?"] = "foo2" + doAssert a == b + +block: #5487 + var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() + var b = newOrderedTable[string, string]() # notice, default size! + b["wrong?"] = "foo" + b["wrong?"] = "foo2" + doAssert a == b + +block: #5487 + var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() + var b = newOrderedTable[string, string]() # notice, default size! + b["wrong?"] = "foo" + b["wrong?"] = "foo2" + doAssert a == b + +block: + var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() + var b = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() + var c = newOrderedTable[string, string]() # notice, default size! + c["wrong?"] = "foo" + c["wrong?"] = "foo2" + doAssert a == b + doAssert a == c + +block: #6250 + let + a = {3: 1}.toOrderedTable + b = {3: 2}.toOrderedTable + doAssert((a == b) == false) + doAssert((b == a) == false) + +block: #6250 + let + a = {3: 2}.toOrderedTable + b = {3: 2}.toOrderedTable + doAssert((a == b) == true) + doAssert((b == a) == true) + +block: # CountTable.smallest + let t = toCountTable([0, 0, 5, 5, 5]) + doAssert t.smallest == (0, 2) + +block: #10065 + let t = toCountTable("abracadabra") + doAssert t['z'] == 0 + + var t_mut = toCountTable("abracadabra") + doAssert t_mut['z'] == 0 + # the previous read may not have modified the table. + doAssert t_mut.hasKey('z') == false + t_mut['z'] = 1 + doAssert t_mut['z'] == 1 + doAssert t_mut.hasKey('z') == true + +block: #12813 #13079 + var t = toCountTable("abracadabra") + doAssert len(t) == 5 + + t['a'] = 0 # remove a key + doAssert len(t) == 4 + +block: + var tp: Table[string, string] = initTable[string, string]() + doAssert "test1" == tp.getOrDefault("test1", "test1") + tp["test2"] = "test2" + doAssert "test2" == tp.getOrDefault("test2", "test1") + var tr: TableRef[string, string] = newTable[string, string]() + doAssert "test1" == tr.getOrDefault("test1", "test1") + tr["test2"] = "test2" + doAssert "test2" == tr.getOrDefault("test2", "test1") + var op: OrderedTable[string, string] = initOrderedTable[string, string]() + doAssert "test1" == op.getOrDefault("test1", "test1") + op["test2"] = "test2" + doAssert "test2" == op.getOrDefault("test2", "test1") + var orf: OrderedTableRef[string, string] = newOrderedTable[string, string]() + doAssert "test1" == orf.getOrDefault("test1", "test1") + orf["test2"] = "test2" + doAssert "test2" == orf.getOrDefault("test2", "test1") + +block tableWithoutInit: + var + a: Table[string, int] + b: Table[string, int] + c: Table[string, int] + d: Table[string, int] + e: Table[string, int] + + a["a"] = 7 + doAssert a.hasKey("a") + doAssert a.len == 1 + doAssert a["a"] == 7 + a["a"] = 9 + doAssert a.len == 1 + doAssert a["a"] == 9 + + doAssert b.hasKeyOrPut("b", 5) == false + doAssert b.hasKey("b") + doAssert b.hasKeyOrPut("b", 8) + doAssert b["b"] == 5 + + doAssert c.getOrDefault("a") == 0 + doAssert c.getOrDefault("a", 3) == 3 + c["a"] = 6 + doAssert c.getOrDefault("a", 3) == 6 + + doAssert d.mgetOrPut("a", 3) == 3 + doAssert d.mgetOrPut("a", 6) == 3 + + var x = 99 + doAssert e.pop("a", x) == false + doAssert x == 99 + e["a"] = 77 + doAssert e.pop("a", x) + doAssert x == 77 + +block orderedTableWithoutInit: + var + a: OrderedTable[string, int] + b: OrderedTable[string, int] + c: OrderedTable[string, int] + d: OrderedTable[string, int] + + a["a"] = 7 + doAssert a.hasKey("a") + doAssert a.len == 1 + doAssert a["a"] == 7 + a["a"] = 9 + doAssert a.len == 1 + doAssert a["a"] == 9 + + doAssert b.hasKeyOrPut("b", 5) == false + doAssert b.hasKey("b") + doAssert b.hasKeyOrPut("b", 8) + doAssert b["b"] == 5 + + doAssert c.getOrDefault("a") == 0 + doAssert c.getOrDefault("a", 3) == 3 + c["a"] = 6 + doAssert c.getOrDefault("a", 3) == 6 + + doAssert d.mgetOrPut("a", 3) == 3 + doAssert d.mgetOrPut("a", 6) == 3 + +block countTableWithoutInit: + var + a: CountTable[string] + b: CountTable[string] + c: CountTable[string] + d: CountTable[string] + e: CountTable[string] + + a["a"] = 7 + doAssert a.hasKey("a") + doAssert a.len == 1 + doAssert a["a"] == 7 + a["a"] = 9 + doAssert a.len == 1 + doAssert a["a"] == 9 + + doAssert b["b"] == 0 + b.inc("b") + doAssert b["b"] == 1 + + doAssert c.getOrDefault("a") == 0 + doAssert c.getOrDefault("a", 3) == 3 + c["a"] = 6 + doAssert c.getOrDefault("a", 3) == 6 + + e["f"] = 3 + merge(d, e) + doAssert d.hasKey("f") + d.inc("f") + merge(d, e) + doAssert d["f"] == 7 diff --git a/tests/stdlib/ttasks.nim b/tests/stdlib/ttasks.nim new file mode 100644 index 000000000..ba65590d9 --- /dev/null +++ b/tests/stdlib/ttasks.nim @@ -0,0 +1,561 @@ +discard """ + targets: "c cpp" + matrix: "--gc:orc --threads:off" +""" + +import std/[tasks, strformat] +import std/assertions + +block: + var s = "" + proc `+`(x: int, y: string) = + s.add $x & y + + let literal = "Nim" + let t = toTask(521 + literal) + t.invoke() + + doAssert s == "521Nim" + +block: + var s = "" + proc `!`(x: int) = + s.add $x + + let t = toTask !12 + t.invoke() + + doAssert s == "12" + + +block: + block: + var called = 0 + proc hello(x: static range[1 .. 5]) = + called += x + + let b = toTask hello(3) + b.invoke() + doAssert called == 3 + b.invoke() + doAssert called == 6 + + block: + var called = 0 + proc hello(x: range[1 .. 5]) = + called += x + + let b = toTask hello(3) + b.invoke() + doAssert called == 3 + b.invoke() + doAssert called == 6 + + block: + var called = 0 + proc hello(x: 1 .. 5) = + called += x + + let b = toTask hello(3) + b.invoke() + doAssert called == 3 + b.invoke() + doAssert called == 6 + + block: + var temp = "" + proc hello(a: int or seq[string]) = + when a is seq[string]: + for s in a: + temp.add s + else: + temp.addInt a + + let x = @["1", "2", "3", "4"] + let b = toTask hello(x) + b.invoke() + doAssert temp == "1234" + b.invoke() + doAssert temp == "12341234" + + + block: + var temp = "" + + proc hello(a: int or string) = + when a is string: + temp.add a + + let x = "!2" + + let b = toTask hello(x) + b.invoke() + doAssert temp == x + + block: + var temp = "" + proc hello(a: int or string) = + when a is string: + temp.add a + + let x = "!2" + let b = toTask hello(x) + b.invoke() + doAssert temp == x + + block: + var x = 0 + proc hello(typ: typedesc) = + x += typ(12) + + let b = toTask hello(int) + b.invoke() + doAssert x == 12 + + block: + var temp = "" + proc hello(a: int or seq[string]) = + when a is seq[string]: + for s in a: + temp.add s + + let x = @["1", "2", "3", "4"] + let b = toTask hello(x) + b.invoke() + doAssert temp == "1234" + + block: + var temp = "" + proc hello(a: int | string) = + when a is string: + temp.add a + + let x = "!2" + let b = toTask hello(x) + b.invoke() + doAssert temp == x + + block: + var x = 0 + proc hello(a: int | string) = + when a is int: + x = a + + let b = toTask hello(12) + b.invoke() + doAssert x == 12 + + block: + var a1: seq[int] + var a2 = 0 + proc hello(c: seq[int], a: int) = + a1 = c + a2 = a + + let x = 12 + var y = @[1, 3, 1, 4, 5, x, 1] + let b = toTask hello(y, 12) + b.invoke() + + doAssert a1 == y + doAssert a2 == x + + block: + var a1: seq[int] + var a2 = 0 + proc hello(c: seq[int], a: int) = + a1 = c + a2 = a + var x = 2 + let b = toTask hello(@[1, 3, 1, 4, 5, x, 1], 12) + b.invoke() + + doAssert a1 == @[1, 3, 1, 4, 5, x, 1] + doAssert a2 == 12 + + block: + var a1: array[7, int] + var a2 = 0 + proc hello(c: array[7, int], a: int) = + a1 = c + a2 = a + + let b = toTask hello([1, 3, 1, 4, 5, 2, 1], 12) + b.invoke() + + doAssert a1 == [1, 3, 1, 4, 5, 2, 1] + doAssert a2 == 12 + + block: + var a1: seq[int] + var a2 = 0 + proc hello(c: seq[int], a: int) = + a1 = c + a2 = a + + let b = toTask hello(@[1, 3, 1, 4, 5, 2, 1], 12) + b.invoke() + + doAssert a1 == @[1, 3, 1, 4, 5, 2, 1] + doAssert a2 == 12 + + block: + var a1: seq[int] + var a2 = 0 + proc hello(a: int, c: seq[int]) = + a1 = c + a2 = a + + let b = toTask hello(8, @[1, 3, 1, 4, 5, 2, 1]) + b.invoke() + + doAssert a1 == @[1, 3, 1, 4, 5, 2, 1] + doAssert a2 == 8 + + let c = toTask 8.hello(@[1, 3, 1, 4, 5, 2, 1]) + c.invoke() + + doAssert a1 == @[1, 3, 1, 4, 5, 2, 1] + doAssert a2 == 8 + + block: + var a1: seq[seq[int]] + var a2: int + proc hello(a: int, c: openArray[seq[int]]) = + a1 = @c + a2 = a + + let b = toTask hello(8, @[@[3], @[4], @[5], @[6], @[12], @[7]]) + b.invoke() + + doAssert a1 == @[@[3], @[4], @[5], @[6], @[12], @[7]] + doAssert a2 == 8 + + block: + var a1: seq[int] + var a2: int + proc hello(a: int, c: openArray[int]) = + a1 = @c + a2 = a + + let b = toTask hello(8, @[3, 4, 5, 6, 12, 7]) + b.invoke() + + doAssert a1 == @[3, 4, 5, 6, 12, 7] + doAssert a2 == 8 + + block: + var a1: seq[int] + var a2: int + proc hello(a: int, c: static varargs[int]) = + a1 = @c + a2 = a + + let b = toTask hello(8, @[3, 4, 5, 6, 12, 7]) + b.invoke() + + doAssert a1 == @[3, 4, 5, 6, 12, 7] + doAssert a2 == 8 + + block: + var a1: seq[int] + var a2: int + proc hello(a: int, c: static varargs[int]) = + a1 = @c + a2 = a + + let b = toTask hello(8, [3, 4, 5, 6, 12, 7]) + b.invoke() + + doAssert a1 == @[3, 4, 5, 6, 12, 7] + doAssert a2 == 8 + + block: + var a1: seq[int] + var a2: int + proc hello(a: int, c: varargs[int]) = + a1 = @c + a2 = a + + let x = 12 + let b = toTask hello(8, 3, 4, 5, 6, x, 7) + b.invoke() + + doAssert a1 == @[3, 4, 5, 6, 12, 7] + doAssert a2 == 8 + + block: + var x = 12 + + proc hello(x: ptr int) = + x[] += 12 + + let b = toTask hello(addr x) + b.invoke() + + doAssert x == 24 + + let c = toTask x.addr.hello + invoke(c) + + doAssert x == 36 + block: + type + Test = ref object + id: int + + var x = 0 + proc hello(a: int, c: static Test) = + x += a + x += c.id + + let b = toTask hello(8, Test(id: 12)) + b.invoke() + + doAssert x == 20 + + block: + type + Test = object + id: int + + var x = 0 + proc hello(a: int, c: static Test) = + x += a + x += c.id + + let b = toTask hello(8, Test(id: 12)) + b.invoke() + doAssert x == 20 + + block: + var x = 0 + proc hello(a: int, c: static seq[int]) = + x += a + for i in c: + x += i + + let b = toTask hello(8, @[3, 4, 5, 6, 12, 7]) + b.invoke() + doAssert x == 45 + + block: + var x = 0 + proc hello(a: int, c: static array[5, int]) = + x += a + for i in c: + x += i + + let b = toTask hello(8, [3, 4, 5, 6, 12]) + b.invoke() + doAssert x == 38 + + block: + var aVal = 0 + var cVal = "" + + proc hello(a: int, c: static string) = + aVal += a + cVal.add c + + var x = 1314 + let b = toTask hello(x, "hello") + b.invoke() + + doAssert aVal == x + doAssert cVal == "hello" + + block: + var aVal = "" + + proc hello(a: static string) = + aVal.add a + let b = toTask hello("hello") + b.invoke() + + doAssert aVal == "hello" + + block: + var aVal = 0 + var cVal = "" + + proc hello(a: static int, c: static string) = + aVal += a + cVal.add c + let b = toTask hello(8, "hello") + b.invoke() + + doAssert aVal == 8 + doAssert cVal == "hello" + + block: + var aVal = 0 + var cVal = 0 + + proc hello(a: static int, c: int) = + aVal += a + cVal += c + + let b = toTask hello(c = 0, a = 8) + b.invoke() + + doAssert aVal == 8 + doAssert cVal == 0 + + block: + var aVal = 0 + var cVal = 0 + + proc hello(a: int, c: static int) = + aVal += a + cVal += c + + let b = toTask hello(c = 0, a = 8) + b.invoke() + + doAssert aVal == 8 + doAssert cVal == 0 + + block: + var aVal = 0 + var cVal = 0 + + proc hello(a: static int, c: static int) = + aVal += a + cVal += c + + let b = toTask hello(0, 8) + b.invoke() + + doAssert aVal == 0 + doAssert cVal == 8 + + block: + var temp = "" + proc hello(x: int, y: seq[string], d = 134) = + temp = fmt"{x=} {y=} {d=}" + + + proc main() = + var x = @["23456"] + let t = toTask hello(2233, x) + t.invoke() + + doAssert temp == """x=2233 y=@["23456"] d=134""" + + main() + + + block: + var temp = "" + proc hello(x: int, y: seq[string], d = 134) = + temp.add fmt"{x=} {y=} {d=}" + + proc ok() = + temp = "ok" + + proc main() = + var x = @["23456"] + let t = toTask hello(2233, x) + t.invoke() + t.invoke() + + doAssert temp == """x=2233 y=@["23456"] d=134x=2233 y=@["23456"] d=134""" + + main() + + var x = @["4"] + let m = toTask hello(2233, x, 7) + m.invoke() + + doAssert temp == """x=2233 y=@["23456"] d=134x=2233 y=@["23456"] d=134x=2233 y=@["4"] d=7""" + + let n = toTask ok() + n.invoke() + + doAssert temp == "ok" + + block: + var called = 0 + block: + proc hello() = + inc called + + let a = toTask hello() + invoke(a) + + doAssert called == 1 + + block: + proc hello(a: int) = + inc called, a + + let b = toTask hello(13) + let c = toTask hello(a = 14) + b.invoke() + c.invoke() + + doAssert called == 28 + + block: + proc hello(a: int, c: int) = + inc called, a + + let b = toTask hello(c = 0, a = 8) + b.invoke() + + doAssert called == 36 + + block: + proc returnsSomething(a, b: int): int = a + b + + proc noArgsButReturnsSomething(): string = "abcdef" + + proc testReturnValues() = + let t = toTask returnsSomething(2233, 11) + var res: int + t.invoke(addr res) + doAssert res == 2233+11 + + let tb = toTask noArgsButReturnsSomething() + var resB: string + tb.invoke(addr resB) + doAssert resB == "abcdef" + + testReturnValues() + + +block: # bug #23635 + block: + type + Store = object + run: proc (a: int) {.nimcall, gcsafe.} + + block: + var count = 0 + proc hello(a: int) = + inc count, a + + var store = Store() + store.run = hello + + let b = toTask store.run(13) + b.invoke() + doAssert count == 13 + + block: + type + Store = object + run: proc () {.nimcall, gcsafe.} + + block: + var count = 0 + proc hello() = + inc count, 1 + + var store = Store() + store.run = hello + + let b = toTask store.run() + b.invoke() + doAssert count == 1 diff --git a/tests/stdlib/ttempfiles.nim b/tests/stdlib/ttempfiles.nim new file mode 100644 index 000000000..352788c42 --- /dev/null +++ b/tests/stdlib/ttempfiles.nim @@ -0,0 +1,51 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + joinable: false # not strictly necessary +""" + +import std/tempfiles +import std/[os, nre] +import std/[assertions, syncio] + +const + prefix = "D20210502T100442" # safety precaution to only affect files/dirs with this prefix + suffix = ".tmp" + +block: + var t1 = createTempFile(prefix, suffix) + var t2 = createTempFile(prefix, suffix) + defer: + close(t1.cfile) + close(t2.cfile) + removeFile(t1.path) + removeFile(t2.path) + + doAssert t1.path != t2.path + + let s = "1234" + write(t1.cfile, s) + doAssert readAll(t2.cfile) == "" + doAssert readAll(t1.cfile) == "" + t1.cfile.setFilePos 0 + doAssert readAll(t1.cfile) == s + +block: # createTempDir + doAssertRaises(OSError): discard createTempDir(prefix, suffix, "nonexistent") + + block: + let dir1 = createTempDir(prefix, suffix) + let dir2 = createTempDir(prefix, suffix) + defer: + removeDir(dir1) + removeDir(dir2) + doAssert dir1 != dir2 + + doAssert dirExists(dir1) + doAssert dir1.lastPathPart.contains(re"^D20210502T100442(\w+).tmp$") + doAssert dir1.parentDir == getTempDir().normalizePathEnd() + + block: + let dir3 = createTempDir(prefix, "_mytmp", ".") + doAssert dir3.lastPathPart.contains(re"^D20210502T100442(\w+)_mytmp$") + doAssert dir3.parentDir == "." # not getCurrentDir(): we honor the absolute/relative state of input `dir` + removeDir(dir3) diff --git a/tests/stdlib/tterminal.nim b/tests/stdlib/tterminal.nim new file mode 100644 index 000000000..16365e71c --- /dev/null +++ b/tests/stdlib/tterminal.nim @@ -0,0 +1,7 @@ +discard """ + action: compile +""" +import terminal, colors + +styledEcho fgColor, colRed, "Test" +styledEcho bgColor, colBlue, "Test" diff --git a/tests/stdlib/tterminal_12759.nim b/tests/stdlib/tterminal_12759.nim new file mode 100644 index 000000000..e9ea3127c --- /dev/null +++ b/tests/stdlib/tterminal_12759.nim @@ -0,0 +1,11 @@ +discard """ + action: "compile" +""" + +import terminal +import std/syncio + +proc test() {.raises:[IOError, ValueError].} = + setBackgroundColor(stdout, bgRed) + +test() diff --git a/tests/stdlib/tterminal_15874.nim b/tests/stdlib/tterminal_15874.nim new file mode 100644 index 000000000..c3455c350 --- /dev/null +++ b/tests/stdlib/tterminal_15874.nim @@ -0,0 +1,8 @@ +discard """ + cmd: "nim c --app:console $file" + action: "compile" +""" + +import terminal + +writeStyled("hello", {styleBright}) diff --git a/tests/stdlib/ttestutils.nim b/tests/stdlib/ttestutils.nim new file mode 100644 index 000000000..0f8bf16cf --- /dev/null +++ b/tests/stdlib/ttestutils.nim @@ -0,0 +1,40 @@ +import stdtest/testutils +import std/assertions + +block: # assertAll + assertAll: + 1+1 == 2 + var a = 3 + a == 3 + + doAssertRaises(AssertionDefect): + assertAll: + 1+1 == 2 + var a = 3 + a == 4 + +block: # greedyOrderedSubsetLines + assertAll: + greedyOrderedSubsetLines("a1\na3", "a0\na1\na2\na3\na4") + not greedyOrderedSubsetLines("a3\na1", "a0\na1\na2\na3\na4") # out of order + not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs + + not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\nprefix:a5") + not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\na5:suffix") + not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\nprefix:a5") + not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\na5:suffix") + +block: # greedyOrderedSubsetLines with allowPrefixMatch = true + template fn(a, b): bool = + greedyOrderedSubsetLines(a, b, allowPrefixMatch = true) + assertAll: + fn("a1\na3", "a0\na1\na2\na3_suffix\na4") + not fn("a1\na3", "a0\na1\na2\nprefix_a3\na4") + # these are same as above, could be refactored + not fn("a3\na1", "a0\na1\na2\na3\na4") # out of order + not fn("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs + + not fn("a1\na5", "a0\na1\na2\na3\na4\nprefix:a5") + fn("a1\na5", "a0\na1\na2\na3\na4\na5:suffix") + not fn("a5", "a0\na1\na2\na3\na4\nprefix:a5") + fn("a5", "a0\na1\na2\na3\na4\na5:suffix") diff --git a/tests/stdlib/tthreadpool.nim b/tests/stdlib/tthreadpool.nim new file mode 100644 index 000000000..1947074be --- /dev/null +++ b/tests/stdlib/tthreadpool.nim @@ -0,0 +1,14 @@ +discard """ + matrix: "--mm:arc; --mm:refc" + disabled: "freebsd" + output: "42" +""" +import std/assertions +from std/threadpool import spawn, `^`, sync +block: # bug #12005 + proc doworkok(i: int) {.thread.} = echo i + spawn(doworkok(42)) + sync() # this works when returning void! + + proc doworkbad(i: int): int {.thread.} = i + doAssert ^spawn(doworkbad(42)) == 42 # bug was here diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index e6305b2d0..0f04168dc 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -1,32 +1,27 @@ discard """ - target: "c js" + matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:on; --backend:js --jsbigint64:off -d:nimStringHash2" """ import times, strutils, unittest +import std/assertions when not defined(js): import os -# Normally testament configures unittest with environment variables, -# but that doesn't work for the JS target. So instead we must set the correct -# settings here. -addOutputFormatter( - newConsoleOutputFormatter(PRINT_FAILURES, colorOutput = false)) - proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} = let offset = hours * 3600 + minutes * 60 + seconds - proc zonedTimeFromAdjTime(adjTime: Time): ZonedTime {.locks: 0.} = + proc zonedTimeFromAdjTime(adjTime: Time): ZonedTime = result.isDst = false result.utcOffset = offset result.time = adjTime + initDuration(seconds = offset) - proc zonedTimeFromTime(time: Time): ZonedTime {.locks: 0.}= + proc zonedTimeFromTime(time: Time): ZonedTime = result.isDst = false result.utcOffset = offset result.time = time - newTimezone("", zonedTimeFromTime, zonedTImeFromAdjTime) + newTimezone("", zonedTimeFromTime, zonedTimeFromAdjTime) template parseTest(s, f, sExpected: string, ydExpected: int) = let @@ -76,7 +71,7 @@ template runTimezoneTests() = "2006-01-12T22:04:05Z", 11) # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" parseTest("2006-01-12T15:04:05.999999999Z-07:00", - "yyyy-MM-dd'T'HH:mm:ss'.999999999Z'zzz", "2006-01-12T22:04:05Z", 11) + "yyyy-MM-dd'T'HH:mm:ss.'999999999Z'zzz", "2006-01-12T22:04:05Z", 11) for tzFormat in ["z", "zz", "zzz"]: # formatting timezone as 'Z' for UTC parseTest("2001-01-12T22:04:05Z", "yyyy-MM-dd'T'HH:mm:ss" & tzFormat, @@ -90,6 +85,10 @@ template runTimezoneTests() = "2001-01-12T08:04:05Z", 11) parseTest("2001-01-12T15:04:05 +07:30:59", "yyyy-MM-dd'T'HH:mm:ss zzzz", "2001-01-12T07:33:06Z", 11) + parseTest("2001-01-12T15:04:05 +0700", "yyyy-MM-dd'T'HH:mm:ss ZZZ", + "2001-01-12T08:04:05Z", 11) + parseTest("2001-01-12T15:04:05 +073059", "yyyy-MM-dd'T'HH:mm:ss ZZZZ", + "2001-01-12T07:33:06Z", 11) # Kitchen = "3:04PM" parseTestTimeOnly("3:04PM", "h:mmtt", "15:04:00") @@ -122,7 +121,7 @@ template usingTimezone(tz: string, body: untyped) = body putEnv("TZ", oldZone) -suite "ttimes": +block: # ttimes # Generate tests for multiple timezone files where available # Set the TZ env var for each test @@ -149,7 +148,7 @@ suite "ttimes": test "parseTest": runTimezoneTests() - test "dst handling": + block: # dst handling usingTimezone("Europe/Stockholm"): # In case of an impossible time, the time is moved to after the # impossible time period @@ -169,7 +168,7 @@ suite "ttimes": check initDateTime(21, mOct, 2017, 01, 00, 00).format(f) == "2017-10-21 01:00 +02:00" - test "issue #6520": + block: # issue #6520 usingTimezone("Europe/Stockholm"): var local = fromUnix(1469275200).local var utc = fromUnix(1469275200).utc @@ -178,19 +177,19 @@ suite "ttimes": local.utcOffset = 0 check claimedOffset == utc.toTime - local.toTime - test "issue #5704": + block: # issue #5704 usingTimezone("Asia/Seoul"): let diff = parse("19700101-000000", "yyyyMMdd-hhmmss").toTime - parse("19000101-000000", "yyyyMMdd-hhmmss").toTime check diff == initDuration(seconds = 2208986872) - test "issue #6465": + block: # issue #6465 usingTimezone("Europe/Stockholm"): let dt = parse("2017-03-25 12:00", "yyyy-MM-dd hh:mm") check $(dt + initTimeInterval(days = 1)) == "2017-03-26T12:00:00+02:00" check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00" - test "adding/subtracting time across dst": + block: # adding/subtracting time across dst usingTimezone("Europe/Stockholm"): let dt1 = initDateTime(26, mMar, 2017, 03, 00, 00) check $(dt1 - 1.seconds) == "2017-03-26T01:59:59+01:00" @@ -198,55 +197,55 @@ suite "ttimes": var dt2 = initDateTime(29, mOct, 2017, 02, 59, 59) check $(dt2 + 1.seconds) == "2017-10-29T02:00:00+01:00" - test "datetime before epoch": + block: # datetime before epoch check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52Z" - test "incorrect inputs: empty string": + block: # incorrect inputs: empty string parseTestExcp("", "yyyy-MM-dd") - test "incorrect inputs: year": + block: # incorrect inputs: year parseTestExcp("20-02-19", "yyyy-MM-dd") - test "incorrect inputs: month number": + block: # incorrect inputs: month number parseTestExcp("2018-2-19", "yyyy-MM-dd") - test "incorrect inputs: month name": + block: # incorrect inputs: month name parseTestExcp("2018-Fe", "yyyy-MMM-dd") - test "incorrect inputs: day": + block: # incorrect inputs: day parseTestExcp("2018-02-1", "yyyy-MM-dd") - test "incorrect inputs: day of week": + block: # incorrect inputs: day of week parseTestExcp("2018-Feb-Mo", "yyyy-MMM-ddd") - test "incorrect inputs: hour": + block: # incorrect inputs: hour parseTestExcp("2018-02-19 1:30", "yyyy-MM-dd hh:mm") - test "incorrect inputs: minute": + block: # incorrect inputs: minute parseTestExcp("2018-02-19 16:3", "yyyy-MM-dd hh:mm") - test "incorrect inputs: second": + block: # incorrect inputs: second parseTestExcp("2018-02-19 16:30:0", "yyyy-MM-dd hh:mm:ss") - test "incorrect inputs: timezone (z)": + block: # incorrect inputs: timezone (z) parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss z") - test "incorrect inputs: timezone (zz) 1": + block: # incorrect inputs: timezone (zz) 1 parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss zz") - test "incorrect inputs: timezone (zz) 2": + block: # incorrect inputs: timezone (zz) 2 parseTestExcp("2018-02-19 16:30:00 +1", "yyyy-MM-dd hh:mm:ss zz") - test "incorrect inputs: timezone (zzz) 1": + block: # incorrect inputs: timezone (zzz) 1 parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss zzz") - test "incorrect inputs: timezone (zzz) 2": + block: # incorrect inputs: timezone (zzz) 2 parseTestExcp("2018-02-19 16:30:00 +01:", "yyyy-MM-dd hh:mm:ss zzz") - test "incorrect inputs: timezone (zzz) 3": + block: # incorrect inputs: timezone (zzz) 3 parseTestExcp("2018-02-19 16:30:00 +01:0", "yyyy-MM-dd hh:mm:ss zzz") - test "incorrect inputs: year (yyyy/uuuu)": + block: # incorrect inputs: year (yyyy/uuuu) parseTestExcp("-0001", "yyyy") parseTestExcp("-0001", "YYYY") parseTestExcp("1", "yyyy") @@ -255,7 +254,7 @@ suite "ttimes": parseTestExcp("12345", "uuuu") parseTestExcp("-1 BC", "UUUU g") - test "incorrect inputs: invalid sign": + block: # incorrect inputs: invalid sign parseTestExcp("+1", "YYYY") parseTestExcp("+1", "dd") parseTestExcp("+1", "MM") @@ -263,10 +262,10 @@ suite "ttimes": parseTestExcp("+1", "mm") parseTestExcp("+1", "ss") - test "_ as a separator": + block: # _ as a separator discard parse("2000_01_01", "YYYY'_'MM'_'dd") - test "dynamic timezone": + block: # dynamic timezone let tz = staticTz(seconds = -9000) let dt = initDateTime(1, mJan, 2000, 12, 00, 00, tz) check dt.utcOffset == -9000 @@ -275,13 +274,13 @@ suite "ttimes": check $dt.utc == "2000-01-01T09:30:00Z" check $dt.utc.inZone(tz) == $dt - test "isLeapYear": + block: # isLeapYear check isLeapYear(2016) check (not isLeapYear(2015)) check isLeapYear(2000) check (not isLeapYear(1900)) - test "TimeInterval": + block: # TimeInterval let t = fromUnix(876124714).utc # Mon 6 Oct 08:58:34 BST 1997 # Interval tests let t2 = t - 2.years @@ -293,7 +292,7 @@ suite "ttimes": check (t + 1.hours).toTime.toUnix == t.toTime.toUnix + 60 * 60 check (t - 1.hours).toTime.toUnix == t.toTime.toUnix - 60 * 60 - test "TimeInterval - months": + block: # TimeInterval - months var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc()) check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00Z" dt = initDateTime(15, mMar, 2017, 00, 00, 00, utc()) @@ -302,7 +301,7 @@ suite "ttimes": # This happens due to monthday overflow. It's consistent with Phobos. check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00Z" - test "duration": + block: # duration let d = initDuration check d(hours = 48) + d(days = 5) == d(weeks = 1) let dt = initDateTime(01, mFeb, 2000, 00, 00, 00, 0, utc()) + d(milliseconds = 1) @@ -322,7 +321,7 @@ suite "ttimes": check (initDuration(seconds = 1, nanoseconds = 3) <= initDuration(seconds = 1, nanoseconds = 1)).not - test "large/small dates": + block: # large/small dates discard initDateTime(1, mJan, -35_000, 12, 00, 00, utc()) # with local tz discard initDateTime(1, mJan, -35_000, 12, 00, 00) @@ -334,7 +333,7 @@ suite "ttimes": let dt2 = dt + 35_001.years check $dt2 == "0001-01-01T12:00:01Z" - test "compare datetimes": + block: # compare datetimes var dt1 = now() var dt2 = dt1 check dt1 == dt2 @@ -342,7 +341,7 @@ suite "ttimes": dt2 = dt2 + 1.seconds check dt1 < dt2 - test "adding/subtracting TimeInterval": + block: # adding/subtracting TimeInterval # add/subtract TimeIntervals and Time/TimeInfo let now = getTime().utc let isSpecial = now.isLeapDay @@ -380,14 +379,14 @@ suite "ttimes": check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100 check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100 - test "issue 7620": + block: # issue 7620 let layout = "M/d/yyyy' 'h:mm:ss' 'tt' 'z" let t7620_am = parse("4/15/2017 12:01:02 AM +0", layout, utc()) check t7620_am.format(layout) == "4/15/2017 12:01:02 AM Z" let t7620_pm = parse("4/15/2017 12:01:02 PM +0", layout, utc()) check t7620_pm.format(layout) == "4/15/2017 12:01:02 PM Z" - test "format": + block: # format var dt = initDateTime(1, mJan, -0001, 17, 01, 02, 123_456_789, staticTz(hours = 1, minutes = 2, seconds = 3)) @@ -456,7 +455,7 @@ suite "ttimes": doAssert dt.format("zz") == tz[2] doAssert dt.format("zzz") == tz[3] - test "format locale": + block: # format locale let loc = DateTimeLocale( MMM: ["Fir","Sec","Thi","Fou","Fif","Six","Sev","Eig","Nin","Ten","Ele","Twe"], MMMM: ["Firsty", "Secondy", "Thirdy", "Fourthy", "Fifthy", "Sixthy", "Seventhy", "Eighthy", "Ninthy", "Tenthy", "Eleventhy", "Twelfthy"], @@ -473,7 +472,7 @@ suite "ttimes": check dt.format("MMM", loc) == "Fir" check dt.format("MMMM", loc) == "Firsty" - test "parse": + block: # parse check $parse("20180101", "yyyyMMdd", utc()) == "2018-01-01T00:00:00Z" parseTestExcp("+120180101", "yyyyMMdd") @@ -494,7 +493,7 @@ suite "ttimes": parseTestExcp("2000 A", "yyyy g") - test "parse locale": + block: # parse locale let loc = DateTimeLocale( MMM: ["Fir","Sec","Thi","Fou","Fif","Six","Sev","Eig","Nin","Ten","Ele","Twe"], MMMM: ["Firsty", "Secondy", "Thirdy", "Fourthy", "Fifthy", "Sixthy", "Seventhy", "Eighthy", "Ninthy", "Tenthy", "Eleventhy", "Twelfthy"], @@ -504,7 +503,7 @@ suite "ttimes": check $parse("02 Fir 2019", "dd MMM yyyy", utc(), loc) == "2019-01-02T00:00:00Z" check $parse("Fourthy 6, 2017", "MMMM d, yyyy", utc(), loc) == "2017-04-06T00:00:00Z" - test "timezoneConversion": + block: # timezoneConversion var l = now() let u = l.utc l = u.local @@ -512,7 +511,7 @@ suite "ttimes": check l.timezone == local() check u.timezone == utc() - test "getDayOfWeek": + block: # getDayOfWeek check getDayOfWeek(01, mJan, 0000) == dSat check getDayOfWeek(01, mJan, -0023) == dSat check getDayOfWeek(21, mSep, 1900) == dFri @@ -521,29 +520,29 @@ suite "ttimes": check getDayOfWeek(01, mJan, 2000) == dSat check getDayOfWeek(01, mJan, 2021) == dFri - test "between - simple": + block: # between - simple let x = initDateTime(10, mJan, 2018, 13, 00, 00) let y = initDateTime(11, mJan, 2018, 12, 00, 00) doAssert x + between(x, y) == y - test "between - dst start": + block: # between - dst start usingTimezone("Europe/Stockholm"): let x = initDateTime(25, mMar, 2018, 00, 00, 00) let y = initDateTime(25, mMar, 2018, 04, 00, 00) doAssert x + between(x, y) == y - test "between - empty interval": + block: # between - empty interval let x = now() let y = x doAssert x + between(x, y) == y - test "between - dst end": + block: # between - dst end usingTimezone("Europe/Stockholm"): let x = initDateTime(27, mOct, 2018, 02, 00, 00) let y = initDateTime(28, mOct, 2018, 01, 00, 00) doAssert x + between(x, y) == y - test "between - long day": + block: # between - long day usingTimezone("Europe/Stockholm"): # This day is 25 hours long in Europe/Stockholm let x = initDateTime(28, mOct, 2018, 00, 30, 00) @@ -551,7 +550,7 @@ suite "ttimes": doAssert between(x, y) == 24.hours + 30.minutes doAssert x + between(x, y) == y - test "between - offset change edge case": + block: # between - offset change edge case # This test case is important because in this case # `x + between(x.utc, y.utc) == y` is not true, which is very rare. usingTimezone("America/Belem"): @@ -560,19 +559,19 @@ suite "ttimes": doAssert x + between(x, y) == y doAssert y + between(y, x) == x - test "between - all units": + block: # between - all units let x = initDateTime(1, mJan, 2000, 00, 00, 00, utc()) let ti = initTimeInterval(1, 1, 1, 1, 1, 1, 1, 1, 1, 1) let y = x + ti doAssert between(x, y) == ti doAssert between(y, x) == -ti - test "between - monthday overflow": + block: # between - monthday overflow let x = initDateTime(31, mJan, 2001, 00, 00, 00, utc()) let y = initDateTime(1, mMar, 2001, 00, 00, 00, utc()) doAssert x + between(x, y) == y - test "between - misc": + block: # between - misc block: let x = initDateTime(31, mDec, 2000, 12, 00, 00, utc()) let y = initDateTime(01, mJan, 2001, 00, 00, 00, utc()) @@ -614,7 +613,7 @@ suite "ttimes": doAssert x + between(x, y) == y doAssert between(x, y) == 1.months + 1.weeks - test "default DateTime": # https://github.com/nim-lang/RFCs/issues/211 + block: # default DateTime https://github.com/nim-lang/RFCs/issues/211 var num = 0 for ai in Month: num.inc check num == 12 @@ -640,7 +639,7 @@ suite "ttimes": expect(AssertionDefect): discard a.format initTimeFormat("yyyy") expect(AssertionDefect): discard between(a, a) - test "inX procs": + block: # inX procs doAssert initDuration(seconds = 1).inSeconds == 1 doAssert initDuration(seconds = -1).inSeconds == -1 doAssert initDuration(seconds = -1, nanoseconds = 1).inSeconds == 0 @@ -648,3 +647,138 @@ suite "ttimes": doAssert initDuration(milliseconds = 500).inMilliseconds == 500 doAssert initDuration(milliseconds = -500).inMilliseconds == -500 doAssert initDuration(nanoseconds = -999999999).inMilliseconds == -999 + + block: # getIsoWeekAndYear + doAssert getIsoWeekAndYear(initDateTime(04, mNov, 2019, 00, 00, 00)) == (isoweek: 45.IsoWeekRange, isoyear: 2019.IsoYear) + doAssert initDateTime(dMon, 45, 2019.IsoYear, 00, 00, 00) == initDateTime(04, mNov, 2019, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(28, mDec, 2019, 00, 00, 00)) == (isoweek: 52.IsoWeekRange, isoyear: 2019.IsoYear) + doAssert initDateTime(dSat, 52, 2019.IsoYear, 00, 00, 00) == initDateTime(28, mDec, 2019, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(29, mDec, 2019, 00, 00, 00)) == (isoweek: 52.IsoWeekRange, isoyear: 2019.IsoYear) + doAssert initDateTime(dSun, 52, 2019.IsoYear, 00, 00, 00) == initDateTime(29, mDec, 2019, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(30, mDec, 2019, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dMon, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(30, mDec, 2019, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(31, mDec, 2019, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dTue, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(31, mDec, 2019, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mJan, 2020, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dWed, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(01, mJan, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(02, mJan, 2020, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dThu, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(02, mJan, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(05, mApr, 2020, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dSun, 14, 2020.IsoYear, 00, 00, 00) == initDateTime(05, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(06, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dMon, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(06, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(10, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dFri, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(10, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(12, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dSun, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(12, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(13, mApr, 2020, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dMon, 16, 2020.IsoYear, 00, 00, 00) == initDateTime(13, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(15, mApr, 2020, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dThu, 16, 2020.IsoYear, 00, 00, 00) == initDateTime(16, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(17, mJul, 2020, 00, 00, 00)) == (isoweek: 29.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dFri, 29, 2020.IsoYear, 00, 00, 00) == initDateTime(17, mJul, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(19, mJul, 2020, 00, 00, 00)) == (isoweek: 29.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dSun, 29, 2020.IsoYear, 00, 00, 00) == initDateTime(19, mJul, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(20, mJul, 2020, 00, 00, 00)) == (isoweek: 30.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dMon, 30, 2020.IsoYear, 00, 00, 00) == initDateTime(20, mJul, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(23, mJul, 2020, 00, 00, 00)) == (isoweek: 30.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dThu, 30, 2020.IsoYear, 00, 00, 00) == initDateTime(23, mJul, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(31, mDec, 2020, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dThu, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(31, mDec, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dFri, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(01, mJan, 2021, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(02, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dSat, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(02, mJan, 2021, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(03, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dSun, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(03, mJan, 2021, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(04, mJan, 2021, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2021.IsoYear) + doAssert initDateTime(dMon, 01, 2021.IsoYear, 00, 00, 00) == initDateTime(04, mJan, 2021, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mFeb, 2021, 00, 00, 00)) == (isoweek: 05.IsoWeekRange, isoyear: 2021.IsoYear) + doAssert initDateTime(dMon, 05, 2021.IsoYear, 00, 00, 00) == initDateTime(01, mFeb, 2021, 00, 00, 00) + + doAssert getIsoWeekAndYear(initDateTime(01, mFeb, 2021, 01, 02, 03, 400_000_000, staticTz(hours=1))) == (isoweek: 05.IsoWeekRange, isoyear: 2021.IsoYear) + doAssert initDateTime(dMon, 05, 2021.IsoYear, 01, 02, 03, 400_000_000, staticTz(hours=1)) == initDateTime(01, mFeb, 2021, 01, 02, 03, 400_000_000, staticTz(hours=1)) + + doAssert getIsoWeekAndYear(initDateTime(01, mApr, +0001, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: 0001.IsoYear) + doAssert initDateTime(dSun, 13, 0001.IsoYear, 00, 00, 00) == initDateTime(01, mApr, 0001, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mApr, +0000, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: 0000.IsoYear) + doAssert initDateTime(dSat, 13, 0000.IsoYear, 00, 00, 00) == initDateTime(01, mApr, 0000, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0001, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: (-0001).IsoYear) + doAssert initDateTime(dThu, 13, (-0001).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0001, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0002, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: (-0002).IsoYear) + doAssert initDateTime(dWed, 14, (-0002).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0002, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0753, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: (-0753).IsoYear) + doAssert initDateTime(dMon, 14, (-0753).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0753, 00, 00, 00) + + block: # getWeeksInIsoYear + doAssert getWeeksInIsoYear((-0014).IsoYear) == 52 + doAssert getWeeksInIsoYear((-0013).IsoYear) == 53 + doAssert getWeeksInIsoYear((-0012).IsoYear) == 52 + + doAssert getWeeksInIsoYear((-0009).IsoYear) == 52 + doAssert getWeeksInIsoYear((-0008).IsoYear) == 53 + doAssert getWeeksInIsoYear((-0007).IsoYear) == 52 + + doAssert getWeeksInIsoYear((-0003).IsoYear) == 52 + doAssert getWeeksInIsoYear((-0002).IsoYear) == 53 + doAssert getWeeksInIsoYear((-0001).IsoYear) == 52 + + doAssert getWeeksInIsoYear(0003.IsoYear) == 52 + doAssert getWeeksInIsoYear(0004.IsoYear) == 53 + doAssert getWeeksInIsoYear(0005.IsoYear) == 52 + + doAssert getWeeksInIsoYear(1653.IsoYear) == 52 + doAssert getWeeksInIsoYear(1654.IsoYear) == 53 + doAssert getWeeksInIsoYear(1655.IsoYear) == 52 + + doAssert getWeeksInIsoYear(1997.IsoYear) == 52 + doAssert getWeeksInIsoYear(1998.IsoYear) == 53 + doAssert getWeeksInIsoYear(1999.IsoYear) == 52 + + doAssert getWeeksInIsoYear(2008.IsoYear) == 52 + doAssert getWeeksInIsoYear(2009.IsoYear) == 53 + doAssert getWeeksInIsoYear(2010.IsoYear) == 52 + + doAssert getWeeksInIsoYear(2014.IsoYear) == 52 + doAssert getWeeksInIsoYear(2015.IsoYear) == 53 + doAssert getWeeksInIsoYear(2016.IsoYear) == 52 + + block: # parse and generate iso years + # short calendar week with text + parseTest("KW 23 2023", "'KW' VV GGGG", + "2023-06-05T00:00:00Z", 155) + parseTest("KW 5 2023", "'KW' V GGGG", + "2023-01-30T00:00:00Z", 29) + parseTest("KW 05 23 Saturday", "'KW' V GG dddd", + "2023-02-04T00:00:00Z", 34) + parseTest("KW 53 20 Fri", "'KW' VV GG ddd", + "2021-01-01T00:00:00Z", 0) + + parseTestExcp("KW 23", "'KW' VV") # no year + parseTestExcp("KW 23", "'KW' V") # no year + parseTestExcp("KW 23", "'KW' GG") # no week + parseTestExcp("KW 2023", "'KW' GGGG") # no week + + var dt = initDateTime(5, mJan, 2023, 0, 0, 0, utc()) + check dt.format("V") == "1" + check dt.format("VV") == "01" + check dt.format("GG") == "23" + check dt.format("GGGG") == "2023" + check dt.format("dddd 'KW'V GGGG") == "Thursday KW1 2023" + + block: # Can be used inside gcsafe proc + proc test(): DateTime {.gcsafe.} = + result = "1970".parse("yyyy") + doAssert test().year == 1970 + + block: # test FormatLiterals + # since #23861 + block: + let dt = dateTime(2024, mJul, 21, 17, 01, 02, 123_321_123, utc()) + check dt.format("ss.fff") == "02.123" + check dt.format("fff.ffffff") == "123.123321" + block: + let dt = parse("2024.07.21", "yyyy.MM.dd") + check dt.year == 2024 + check dt.month == mJul + check dt.monthday == 21 diff --git a/tests/stdlib/ttypeinfo.nim b/tests/stdlib/ttypeinfo.nim new file mode 100644 index 000000000..9bbc2e92c --- /dev/null +++ b/tests/stdlib/ttypeinfo.nim @@ -0,0 +1,93 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/typeinfo +import std/assertions + +type + TE = enum + blah, blah2 + + TestObj = object + test, asd: int + case test2: TE + of blah: + help: string + else: + nil + + +var test = @[0,1,2,3,4] +var x = toAny(test) +var y = 78 +x[4] = toAny(y) +doAssert x[2].getInt == 2 + +var test2: tuple[name: string, s: int] = ("test", 56) +var x2 = toAny(test2) +var i = 0 +for n, a in fields(x2): + case i + of 0: doAssert n == "Field0" and $a.kind == "akString" + of 1: doAssert n == "Field1" and $a.kind == "akInt" + else: doAssert false + inc i + +var test3: TestObj +test3.test = 42 +test3 = TestObj(test2: blah2) +var x3 = toAny(test3) +i = 0 +for n, a in fields(x3): + case i + of 0: doAssert n == "test" and $a.kind == "akInt" + of 1: doAssert n == "asd" and $a.kind == "akInt" + of 2: doAssert n == "test2" and $a.kind == "akEnum" + else: doAssert false + inc i + +var test4: ref string +new(test4) +test4[] = "test" +var x4 = toAny(test4) +doAssert($x4[].kind() == "akString") + +block: + # gimme a new scope dammit + var myArr: array[0..4, array[0..4, string]] = [ + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"]] + var m = toAny(myArr) + for i in 0 .. m.len-1: + for j in 0 .. m[i].len-1: + doAssert getString(m[i][j]) == myArr[i][j] + +block: + type + Test = enum + Hello, he_llo + + var x = hello + var y = x.toAny + + doAssert getEnumOrdinal(y, "Hello") == 0 + doAssert getEnumOrdinal(y, "hello") == 1 + +block: # bug #23556 + proc test = + var + t: seq[int] + aseq = toAny(t) + + invokeNewSeq(aseq, 0) + + # Got random value only when loop 8 times. + for i in 1 .. 8: + extendSeq(aseq) + + doAssert t == @[0, 0, 0, 0, 0, 0, 0, 0] + + for i in 1 .. 7: + test() diff --git a/tests/stdlib/ttypeinfo.nims b/tests/stdlib/ttypeinfo.nims new file mode 100644 index 000000000..d31d0b26f --- /dev/null +++ b/tests/stdlib/ttypeinfo.nims @@ -0,0 +1 @@ +--styleCheck:off \ No newline at end of file diff --git a/tests/stdlib/ttypetraits.nim b/tests/stdlib/ttypetraits.nim new file mode 100644 index 000000000..6851b9220 --- /dev/null +++ b/tests/stdlib/ttypetraits.nim @@ -0,0 +1,94 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" +""" + +# xxx merge with tests/metatype/ttypetraits.nim + +import std/typetraits +import std/assertions + +macro testClosure(fn: typed, flag: static bool) = + if flag: + doAssert hasClosure(fn) + else: + doAssert not hasClosure(fn) + +block: + proc h1() = + echo 1 + + testClosure(h1, false) + + proc h2() {.nimcall.} = + echo 2 + + testClosure(h2, false) + + +block: + proc fn(): auto = + proc hello() {.nimcall.} = + echo 3 + hello + + let name = fn() + testClosure(name, false) + +block: + proc fn(): auto = + proc hello() = + echo 3 + hello + + let name = fn() + testClosure(name, false) + +block: + proc fn(): auto = + var x = 0 + proc hello() = + echo 3 + inc x + hello + + let name = fn() + testClosure(name, true) + +block: + proc fn(): auto = + var x = 0 + proc hello() {.closure.} = + echo 3 + inc x + hello + + let name = fn() + testClosure(name, true) + +block: + proc fn(): auto = + var x = 0 + proc hello() {.closure.} = + echo 3 + inc x + hello + + let name = fn() + testClosure(name, true) + + let name2 = name + testClosure(name2, true) + +block: + iterator hello(): int = + yield 1 + + testClosure(hello, false) + +when not defined(js): + block: + iterator hello(): int {.closure.}= + yield 1 + + testClosure(hello, true) diff --git a/tests/stdlib/tunicode.nim b/tests/stdlib/tunicode.nim new file mode 100644 index 000000000..b9e68b15b --- /dev/null +++ b/tests/stdlib/tunicode.nim @@ -0,0 +1,228 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/unicode +import std/assertions + +proc asRune(s: static[string]): Rune = + ## Compile-time conversion proc for converting string literals to a Rune + ## value. Returns the first Rune of the specified string. + ## + ## Shortcuts code like ``"å".runeAt(0)`` to ``"å".asRune`` and returns a + ## compile-time constant. + if s.len == 0: Rune(0) + else: s.runeAt(0) + +let + someString = "öÑ" + someRunes = toRunes(someString) + compared = (someString == $someRunes) +doAssert compared == true + +proc testReplacements(word: string): string = + case word + of "two": + return "2" + of "foo": + return "BAR" + of "βeta": + return "beta" + of "alpha": + return "αlpha" + else: + return "12345" + +doAssert translate("two not alpha foo βeta", testReplacements) == "2 12345 αlpha BAR beta" +doAssert translate(" two not foo βeta ", testReplacements) == " 2 12345 BAR beta " + +doAssert title("foo bar") == "Foo Bar" +doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma" +doAssert title("") == "" + +doAssert capitalize("βeta") == "Βeta" +doAssert capitalize("foo") == "Foo" +doAssert capitalize("") == "" + +doAssert swapCase("FooBar") == "fOObAR" +doAssert swapCase(" ") == " " +doAssert swapCase("Αlpha Βeta Γamma") == "αLPHA βETA γAMMA" +doAssert swapCase("a✓B") == "A✓b" +doAssert swapCase("Јамогујестистаклоитоминештети") == "јАМОГУЈЕСТИСТАКЛОИТОМИНЕШТЕТИ" +doAssert swapCase("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") == "ὝΑΛΟΝΦΑΓΕῖΝΔΎΝΑΜΑΙΤΟῦΤΟΟὔΜΕΒΛΆΠΤΕΙ" +doAssert swapCase("Կրնամապակիուտեևինծիանհանգիստչըներ") == "կՐՆԱՄԱՊԱԿԻՈՒՏԵևԻՆԾԻԱՆՀԱՆԳԻՍՏՉԸՆԵՐ" +doAssert swapCase("") == "" + +doAssert isAlpha("r") +doAssert isAlpha("α") +doAssert isAlpha("ϙ") +doAssert isAlpha("ஶ") +doAssert isAlpha("网") +doAssert(not isAlpha("$")) +doAssert(not isAlpha("")) + +doAssert isAlpha("Βeta") +doAssert isAlpha("Args") +doAssert isAlpha("𐌼𐌰𐌲𐌲𐌻𐌴𐍃𐍄𐌰𐌽") +doAssert isAlpha("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") +doAssert isAlpha("Јамогујестистаклоитоминештети") +doAssert isAlpha("Կրնամապակիուտեևինծիանհանգիստչըներ") +doAssert isAlpha("编程语言") +doAssert(not isAlpha("$Foo✓")) +doAssert(not isAlpha("⠙⠕⠑⠎⠝⠞")) + +doAssert isSpace("\t") +doAssert isSpace("\l") +doAssert(not isSpace("Β")) +doAssert(not isSpace("Βeta")) + +doAssert isSpace("\t\l \v\r\f") +doAssert isSpace(" ") +doAssert(not isSpace("")) +doAssert(not isSpace("ΑΓc \td")) + +doAssert(not isLower(' '.Rune)) + +doAssert(not isUpper(' '.Rune)) + +doAssert toUpper("Γ") == "Γ" +doAssert toUpper("b") == "B" +doAssert toUpper("α") == "Α" +doAssert toUpper("✓") == "✓" +doAssert toUpper("ϙ") == "Ϙ" +doAssert toUpper("") == "" + +doAssert toUpper("ΑΒΓ") == "ΑΒΓ" +doAssert toUpper("AAccβ") == "AACCΒ" +doAssert toUpper("A✓$β") == "A✓$Β" + +doAssert toLower("a") == "a" +doAssert toLower("γ") == "γ" +doAssert toLower("Γ") == "γ" +doAssert toLower("4") == "4" +doAssert toLower("Ϙ") == "ϙ" +doAssert toLower("") == "" + +doAssert toLower("abcdγ") == "abcdγ" +doAssert toLower("abCDΓ") == "abcdγ" +doAssert toLower("33aaΓ") == "33aaγ" + +doAssert reversed("Reverse this!") == "!siht esreveR" +doAssert reversed("先秦兩漢") == "漢兩秦先" +doAssert reversed("as⃝df̅") == "f̅ds⃝a" +doAssert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞" +doAssert reversed("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") == "ιετπάλβεμὔοοτῦοτιαμανύδνῖεγαϕνολαὕ" +doAssert reversed("Јамогујестистаклоитоминештети") == "итетшенимотиолкатситсејугомаЈ" +doAssert reversed("Կրնամապակիուտեևինծիանհանգիստչըներ") == "րենըչտսիգնահնաիծնիևետւոիկապամանրԿ" +doAssert len(toRunes("as⃝df̅")) == runeLen("as⃝df̅") +const test = "as⃝" +doAssert lastRune(test, test.len-1)[1] == 3 +doAssert graphemeLen("è", 0) == 2 + +# test for rune positioning and runeSubStr() +let s = "Hänsel ««: 10,00€" + +var t = "" +for c in s.utf8: + t.add c + +doAssert(s == t) + +doAssert(runeReverseOffset(s, 1) == (20, 18)) +doAssert(runeReverseOffset(s, 19) == (-1, 18)) + +doAssert(runeStrAtPos(s, 0) == "H") +doAssert(runeSubStr(s, 0, 1) == "H") +doAssert(runeStrAtPos(s, 10) == ":") +doAssert(runeSubStr(s, 10, 1) == ":") +doAssert(runeStrAtPos(s, 9) == "«") +doAssert(runeSubStr(s, 9, 1) == "«") +doAssert(runeStrAtPos(s, 17) == "€") +doAssert(runeSubStr(s, 17, 1) == "€") +# echo runeStrAtPos(s, 18) # index error + +doAssert(runeSubStr(s, 0) == "Hänsel ««: 10,00€") +doAssert(runeSubStr(s, -18) == "Hänsel ««: 10,00€") +doAssert(runeSubStr(s, 10) == ": 10,00€") +doAssert(runeSubStr(s, 18) == "") +doAssert(runeSubStr(s, 0, 10) == "Hänsel ««") + +doAssert(runeSubStr(s, 12) == "10,00€") +doAssert(runeSubStr(s, -6) == "10,00€") + +doAssert(runeSubStr(s, 12, 5) == "10,00") +doAssert(runeSubStr(s, 12, -1) == "10,00") +doAssert(runeSubStr(s, -6, 5) == "10,00") +doAssert(runeSubStr(s, -6, -1) == "10,00") + +doAssert(runeSubStr(s, 0, 100) == "Hänsel ««: 10,00€") +doAssert(runeSubStr(s, -100, 100) == "Hänsel ««: 10,00€") +doAssert(runeSubStr(s, 0, -100) == "") +doAssert(runeSubStr(s, 100, -100) == "") + +block splitTests: + let s = " this is an example " + let s2 = ":this;is;an:example;;" + let s3 = ":this×is×an:example××" + doAssert s.split() == @["", "this", "is", "an", "example", "", ""] + doAssert s2.split(seps = [':'.Rune, ';'.Rune]) == @["", "this", "is", "an", + "example", "", ""] + doAssert s3.split(seps = [':'.Rune, "×".asRune]) == @["", "this", "is", + "an", "example", "", ""] + doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example "] + doAssert s.split(' '.Rune, maxsplit = 1) == @["", "this is an example "] + doAssert s3.split("×".runeAt(0)) == @[":this", "is", "an:example", "", ""] + +block stripTests: + doAssert(strip("") == "") + doAssert(strip(" ") == "") + doAssert(strip("y") == "y") + doAssert(strip(" foofoofoo ") == "foofoofoo") + doAssert(strip("sfoofoofoos", runes = ['s'.Rune]) == "foofoofoo") + + block: + let stripTestRunes = ['b'.Rune, 'a'.Rune, 'r'.Rune] + doAssert(strip("barfoofoofoobar", runes = stripTestRunes) == "foofoofoo") + doAssert(strip("sfoofoofoos", leading = false, runes = ['s'.Rune]) == "sfoofoofoo") + doAssert(strip("sfoofoofoos", trailing = false, runes = ['s'.Rune]) == "foofoofoos") + + block: + let stripTestRunes = ["«".asRune, "»".asRune] + doAssert(strip("«TEXT»", runes = stripTestRunes) == "TEXT") + doAssert(strip("copyright©", leading = false, runes = ["©".asRune]) == "copyright") + doAssert(strip("¿Question?", trailing = false, runes = ["¿".asRune]) == "Question?") + doAssert(strip("×text×", leading = false, runes = ["×".asRune]) == "×text") + doAssert(strip("×text×", trailing = false, runes = ["×".asRune]) == "text×") + +block repeatTests: + doAssert repeat('c'.Rune, 5) == "ccccc" + doAssert repeat("×".asRune, 5) == "×××××" + +block alignTests: + doAssert align("abc", 4) == " abc" + doAssert align("a", 0) == "a" + doAssert align("1232", 6) == " 1232" + doAssert align("1232", 6, '#'.Rune) == "##1232" + doAssert align("1232", 6, "×".asRune) == "××1232" + doAssert alignLeft("abc", 4) == "abc " + doAssert alignLeft("a", 0) == "a" + doAssert alignLeft("1232", 6) == "1232 " + doAssert alignLeft("1232", 6, '#'.Rune) == "1232##" + doAssert alignLeft("1232", 6, "×".asRune) == "1232××" + +block differentSizes: + # upper and lower variants have different number of bytes + doAssert toLower("AẞC") == "aßc" + doAssert toLower("ȺẞCD") == "ⱥßcd" + doAssert toUpper("ⱥbc") == "ȺBC" + doAssert toUpper("rsⱦuv") == "RSȾUV" + doAssert swapCase("ⱥbCd") == "ȺBcD" + doAssert swapCase("XyꟆaB") == "xYᶎAb" + doAssert swapCase("aᵹcᲈd") == "AꝽCꙊD" + +block: # bug #17768 + let s1 = "abcdef" + let s2 = "abcdéf" + + doAssert s1.runeSubStr(0, -1) == "abcde" + doAssert s2.runeSubStr(0, -1) == "abcdé" diff --git a/tests/stdlib/tunidecode.nim b/tests/stdlib/tunidecode.nim index 576bfa2cb..653016ea9 100644 --- a/tests/stdlib/tunidecode.nim +++ b/tests/stdlib/tunidecode.nim @@ -1,14 +1,13 @@ discard """ cmd: "nim $target --hints:on -d:embedUnidecodeTable $options $file" - output: "Ausserst" """ import unidecode import std/unidecode # #14112 +import std/assertions loadUnidecodeTable("lib/pure/unidecode/unidecode.dat") -#assert unidecode("\x53\x17\x4E\xB0") == "Bei Jing" -echo unidecode("Äußerst") - +doAssert unidecode("北京") == "Bei Jing " +doAssert unidecode("Äußerst") == "Ausserst" diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index 9ef689e32..0442c7863 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -1,5 +1,7 @@ discard """ - output: '''[Suite] suite with only teardown + output: ''' + +[Suite] suite with only teardown [Suite] suite with only setup @@ -16,11 +18,13 @@ discard """ [Suite] test suite [Suite] test name filtering - ''' +matrix: "--mm:refc; --mm:orc" +targets: "c js" """ -import unittest, sequtils +import std/[unittest, sequtils, assertions] +from std/unittest {.all.} import matchFilter proc doThings(spuds: var int): int = spuds = 24 @@ -31,12 +35,12 @@ test "#964": check spuds == 24 -from strutils import toUpperAscii +from std/strutils import toUpperAscii test "#1384": check(@["hello", "world"].map(toUpperAscii) == @["HELLO", "WORLD"]) -import options +import std/options test "unittest typedescs": check(none(int) == none(int)) check(none(int) != some(1)) @@ -47,14 +51,14 @@ test "unittest multiple requires": require(true) -import random -from strutils import parseInt +import std/random +from std/strutils import parseInt proc defectiveRobot() = case rand(1..4) of 1: raise newException(OSError, "CANNOT COMPUTE!") of 2: discard parseInt("Hello World!") of 3: raise newException(IOError, "I can't do that Dave.") - else: assert 2 + 2 == 5 + else: doAssert 2 + 2 == 5 test "unittest expect": expect IOError, OSError, ValueError, AssertionDefect: defectiveRobot() @@ -141,38 +145,50 @@ suite "test suite": check(a == b) -when defined(testing): - suite "test name filtering": - test "test name": - check matchFilter("suite1", "foo", "") - check matchFilter("suite1", "foo", "foo") - check matchFilter("suite1", "foo", "::") - check matchFilter("suite1", "foo", "*") - check matchFilter("suite1", "foo", "::foo") - check matchFilter("suite1", "::foo", "::foo") - - test "test name - glob": - check matchFilter("suite1", "foo", "f*") - check matchFilter("suite1", "foo", "*oo") - check matchFilter("suite1", "12345", "12*345") - check matchFilter("suite1", "q*wefoo", "q*wefoo") - check false == matchFilter("suite1", "foo", "::x") - check false == matchFilter("suite1", "foo", "::x*") - check false == matchFilter("suite1", "foo", "::*x") - # overlap - check false == matchFilter("suite1", "12345", "123*345") - check matchFilter("suite1", "ab*c::d*e::f", "ab*c::d*e::f") - - test "suite name": - check matchFilter("suite1", "foo", "suite1::") - check false == matchFilter("suite1", "foo", "suite2::") - check matchFilter("suite1", "qwe::foo", "qwe::foo") - check matchFilter("suite1", "qwe::foo", "suite1::qwe::foo") - - test "suite name - glob": - check matchFilter("suite1", "foo", "::*") - check matchFilter("suite1", "foo", "*::*") - check matchFilter("suite1", "foo", "*::foo") - check false == matchFilter("suite1", "foo", "*ite2::") - check matchFilter("suite1", "q**we::foo", "q**we::foo") - check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e") +suite "test name filtering": + test "test name": + check matchFilter("suite1", "foo", "") + check matchFilter("suite1", "foo", "foo") + check matchFilter("suite1", "foo", "::") + check matchFilter("suite1", "foo", "*") + check matchFilter("suite1", "foo", "::foo") + check matchFilter("suite1", "::foo", "::foo") + + test "test name - glob": + check matchFilter("suite1", "foo", "f*") + check matchFilter("suite1", "foo", "*oo") + check matchFilter("suite1", "12345", "12*345") + check matchFilter("suite1", "q*wefoo", "q*wefoo") + check false == matchFilter("suite1", "foo", "::x") + check false == matchFilter("suite1", "foo", "::x*") + check false == matchFilter("suite1", "foo", "::*x") + # overlap + check false == matchFilter("suite1", "12345", "123*345") + check matchFilter("suite1", "ab*c::d*e::f", "ab*c::d*e::f") + + test "suite name": + check matchFilter("suite1", "foo", "suite1::") + check false == matchFilter("suite1", "foo", "suite2::") + check matchFilter("suite1", "qwe::foo", "qwe::foo") + check matchFilter("suite1", "qwe::foo", "suite1::qwe::foo") + + test "suite name - glob": + check matchFilter("suite1", "foo", "::*") + check matchFilter("suite1", "foo", "*::*") + check matchFilter("suite1", "foo", "*::foo") + check false == matchFilter("suite1", "foo", "*ite2::") + check matchFilter("suite1", "q**we::foo", "q**we::foo") + check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e") + + +block: + type MyFoo = object + var obj = MyFoo() + let check = 1 + check(obj == obj) + +block: + let check = 123 + var a = 1 + var b = 1 + check(a == b) diff --git a/tests/stdlib/tunittest_error.nim b/tests/stdlib/tunittest_error.nim new file mode 100644 index 000000000..7f05ec2a9 --- /dev/null +++ b/tests/stdlib/tunittest_error.nim @@ -0,0 +1,22 @@ +discard """ + exitcode: 1 + outputsub: "failed: 1 == 3" + matrix: "-d:case1; -d:case2" + targets: "c js" + joinable: false +""" + +when defined case1: + import unittest + suite "Test": + test "test require": + check 1==2 + check 1==3 + +when defined case2: + import unittest + suite "Test": + test "test require": + require 1 == 3 + if true: + quit 0 # intentional diff --git a/tests/stdlib/tunittestpass.nim b/tests/stdlib/tunittestpass.nim new file mode 100644 index 000000000..d8de277b7 --- /dev/null +++ b/tests/stdlib/tunittestpass.nim @@ -0,0 +1,20 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + + +import unittest + +block: + check (type(1.0)) is float + check type(1.0) is float + check (typeof(1)) isnot float + check typeof(1) isnot float + + check 1.0 is float + check 1 isnot float + + type T = type(0.1) + check T is float + check T isnot int diff --git a/tests/stdlib/tunittesttemplate.nim b/tests/stdlib/tunittesttemplate.nim index 2ca50a18b..c29e0de01 100644 --- a/tests/stdlib/tunittesttemplate.nim +++ b/tests/stdlib/tunittesttemplate.nim @@ -8,9 +8,9 @@ discard """ """ -# bug #6736 -import unittest +# bug #6736 +import std/unittest type A = object diff --git a/tests/stdlib/tunixsocket.nim b/tests/stdlib/tunixsocket.nim new file mode 100644 index 000000000..636fd08c6 --- /dev/null +++ b/tests/stdlib/tunixsocket.nim @@ -0,0 +1,35 @@ +import std/[assertions, net, os, osproc] + +# XXX: Make this test run on Windows too when we add support for Unix sockets on Windows +when defined(posix) and not defined(nimNetLite): + const nim = getCurrentCompilerExe() + let + dir = currentSourcePath().parentDir() + serverPath = dir / "unixsockettest" + + let (_, err) = execCmdEx(nim & " c " & quoteShell(dir / "unixsockettest.nim")) + doAssert err == 0 + + let svproc = startProcess(serverPath, workingDir = dir) + doAssert svproc.running() + # Wait for the server to open the socket and listen from it + sleep(400) + + block unixSocketSendRecv: + let + unixSocketPath = dir / "usox" + socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_NONE) + + socket.connectUnix(unixSocketPath) + # for a blocking Unix socket this should never fail + socket.send("data sent through the socket\c\l", maxRetries = 0) + var resp: string + socket.readLine(resp) + doAssert resp == "Hello from server" + + socket.send("bye\c\l") + socket.readLine(resp) + doAssert resp == "bye" + socket.close() + + svproc.close() diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim new file mode 100644 index 000000000..9c717c5b1 --- /dev/null +++ b/tests/stdlib/turi.nim @@ -0,0 +1,323 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" + +import std/uri +from std/uri {.all.} as uri2 import removeDotSegments +from std/sequtils import toSeq +import std/assertions + +template main() = + block: # encodeUrl, decodeUrl + const test1 = "abc\L+def xyz" + doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz" + doAssert decodeUrl(encodeUrl(test1)) == test1 + doAssert encodeUrl(test1, false) == "abc%0A%2Bdef%20xyz" + doAssert decodeUrl(encodeUrl(test1, false), false) == test1 + doAssert decodeUrl(encodeUrl(test1)) == test1 + + block: # removeDotSegments + doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz" + doAssert removeDotSegments("") == "" # empty test + doAssert removeDotSegments(".") == "." # trailing period + doAssert removeDotSegments("a1/a2/../a3/a4/a5/./a6/a7/././") == "a1/a3/a4/a5/a6/a7/" + doAssert removeDotSegments("https://a1/a2/../a3/a4/a5/./a6/a7/././") == "https://a1/a3/a4/a5/a6/a7/" + doAssert removeDotSegments("http://a1/a2") == "http://a1/a2" + doAssert removeDotSegments("http://www.ai.") == "http://www.ai." + when false: # xxx these cases are buggy + # this should work, refs https://webmasters.stackexchange.com/questions/73934/how-can-urls-have-a-dot-at-the-end-e-g-www-bla-de + doAssert removeDotSegments("http://www.ai./") == "http://www.ai./" # fails + echo removeDotSegments("http://www.ai./") # http://www.ai/ + echo removeDotSegments("a/b.../c") # b.c + echo removeDotSegments("a/b../c") # bc + echo removeDotSegments("a/.../c") # .c + echo removeDotSegments("a//../b") # a/b + echo removeDotSegments("a/b/c//") # a/b/c// + + block: # parseUri + block: + let org = "udp://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080" + let url = parseUri(org) + doAssert url.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true + let newUrl = parseUri($url) + doAssert newUrl.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true + + block: + let str = "http://localhost" + let test = parseUri(str) + doAssert test.path == "" + + block: + let str = "http://localhost/" + let test = parseUri(str) + doAssert test.path == "/" + + block: + let str = "http://localhost:8080/test" + let test = parseUri(str) + doAssert test.scheme == "http" + doAssert test.port == "8080" + doAssert test.path == "/test" + doAssert test.hostname == "localhost" + doAssert($test == str) + + block: + let str = "foo://username:password@example.com:8042/over/there" & + "/index.dtb?type=animal&name=narwhal#nose" + let test = parseUri(str) + doAssert test.scheme == "foo" + doAssert test.username == "username" + doAssert test.password == "password" + doAssert test.hostname == "example.com" + doAssert test.port == "8042" + doAssert test.path == "/over/there/index.dtb" + doAssert test.query == "type=animal&name=narwhal" + doAssert test.anchor == "nose" + doAssert($test == str) + + block: + # IPv6 address + let str = "foo://[::1]:1234/bar?baz=true&qux#quux" + let uri = parseUri(str) + doAssert uri.scheme == "foo" + doAssert uri.hostname == "::1" + doAssert uri.port == "1234" + doAssert uri.path == "/bar" + doAssert uri.query == "baz=true&qux" + doAssert uri.anchor == "quux" + + block: + let str = "urn:example:animal:ferret:nose" + let test = parseUri(str) + doAssert test.scheme == "urn" + doAssert test.path == "example:animal:ferret:nose" + doAssert($test == str) + + block: + let str = "mailto:username@example.com?subject=Topic" + let test = parseUri(str) + doAssert test.scheme == "mailto" + doAssert test.username == "username" + doAssert test.hostname == "example.com" + doAssert test.query == "subject=Topic" + doAssert($test == str) + + block: + let str = "magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" + let test = parseUri(str) + doAssert test.scheme == "magnet" + doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" + doAssert($test == str) + + block: + let str = "/test/foo/bar?q=2#asdf" + let test = parseUri(str) + doAssert test.scheme == "" + doAssert test.path == "/test/foo/bar" + doAssert test.query == "q=2" + doAssert test.anchor == "asdf" + doAssert($test == str) + + block: + let str = "test/no/slash" + let test = parseUri(str) + doAssert test.path == "test/no/slash" + doAssert($test == str) + + block: + let str = "//git@github.com:dom96/packages" + let test = parseUri(str) + doAssert test.scheme == "" + doAssert test.username == "git" + doAssert test.hostname == "github.com" + doAssert test.port == "dom96" + doAssert test.path == "/packages" + + block: + let str = "file:///foo/bar/baz.txt" + let test = parseUri(str) + doAssert test.scheme == "file" + doAssert test.username == "" + doAssert test.hostname == "" + doAssert test.port == "" + doAssert test.path == "/foo/bar/baz.txt" + + block: # combine + block: + let concat = combine(parseUri("http://google.com/foo/bar/"), parseUri("baz")) + doAssert concat.path == "/foo/bar/baz" + doAssert concat.hostname == "google.com" + doAssert concat.scheme == "http" + + block: + let concat = combine(parseUri("http://google.com/foo"), parseUri("/baz")) + doAssert concat.path == "/baz" + doAssert concat.hostname == "google.com" + doAssert concat.scheme == "http" + + block: + let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar")) + doAssert concat.path == "/foo/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test"), parseUri("/bar")) + doAssert concat.path == "/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar")) + doAssert concat.path == "/foo/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar")) + doAssert concat.path == "/foo/test/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/")) + doAssert concat.path == "/foo/test/bar/" + + block: + let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"), + parseUri("baz")) + doAssert concat.path == "/foo/test/bar/baz" + + block: # `/` + block: + let test = parseUri("http://example.com/foo") / "bar/asd" + doAssert test.path == "/foo/bar/asd" + + block: + let test = parseUri("http://example.com/foo/") / "/bar/asd" + doAssert test.path == "/foo/bar/asd" + + block: # bug #3207 + doAssert parseUri("http://qq/1").combine(parseUri("https://qqq")).`$` == "https://qqq" + + block: # bug #4959 + let foo = parseUri("http://example.com") / "/baz" + doAssert foo.path == "/baz" + + block: # bug found on stream 13/10/17 + let foo = parseUri("http://localhost:9515") / "status" + doAssert $foo == "http://localhost:9515/status" + + block: # bug #6649 #6652 + var foo = parseUri("http://example.com") + foo.hostname = "example.com" + foo.path = "baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com/" + foo.path = "baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com" + foo.path = "/baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com/" + foo.path = "/baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com/" + foo.port = "8000" + foo.path = "baz" + doAssert $foo == "http://example.com:8000/baz" + + foo = parseUri("file:/dir/file") + foo.path = "relative" + doAssert $foo == "file:relative" + + block: # isAbsolute tests + doAssert "www.google.com".parseUri().isAbsolute() == false + doAssert "http://www.google.com".parseUri().isAbsolute() == true + doAssert "file:/dir/file".parseUri().isAbsolute() == true + doAssert "file://localhost/dir/file".parseUri().isAbsolute() == true + doAssert "urn:ISSN:1535-3613".parseUri().isAbsolute() == true + + # path-relative URL *relative + doAssert "about".parseUri().isAbsolute == false + doAssert "about/staff.html".parseUri().isAbsolute == false + doAssert "about/staff.html?".parseUri().isAbsolute == false + doAssert "about/staff.html?parameters".parseUri().isAbsolute == false + + # absolute-path-relative URL *relative + doAssert "/".parseUri().isAbsolute == false + doAssert "/about".parseUri().isAbsolute == false + doAssert "/about/staff.html".parseUri().isAbsolute == false + doAssert "/about/staff.html?".parseUri().isAbsolute == false + doAssert "/about/staff.html?parameters".parseUri().isAbsolute == false + + # scheme-relative URL *relative + doAssert "//username:password@example.com:8888".parseUri().isAbsolute == false + doAssert "//username@example.com".parseUri().isAbsolute == false + doAssert "//example.com".parseUri().isAbsolute == false + doAssert "//example.com/".parseUri().isAbsolute == false + doAssert "//example.com/about".parseUri().isAbsolute == false + doAssert "//example.com/about/staff.html".parseUri().isAbsolute == false + doAssert "//example.com/about/staff.html?".parseUri().isAbsolute == false + doAssert "//example.com/about/staff.html?parameters".parseUri().isAbsolute == false + + # absolute URL *absolute + doAssert "https://username:password@example.com:8888".parseUri().isAbsolute == true + doAssert "https://username@example.com".parseUri().isAbsolute == true + doAssert "https://example.com".parseUri().isAbsolute == true + doAssert "https://example.com/".parseUri().isAbsolute == true + doAssert "https://example.com/about".parseUri().isAbsolute == true + doAssert "https://example.com/about/staff.html".parseUri().isAbsolute == true + doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true + doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true + + block: # encodeQuery tests + doAssert encodeQuery({:}) == "" + doAssert encodeQuery({"foo": "bar"}) == "foo=bar" + doAssert encodeQuery({"foo": "bar & baz"}) == "foo=bar+%26+baz" + doAssert encodeQuery({"foo": "bar & baz"}, usePlus = false) == "foo=bar%20%26%20baz" + doAssert encodeQuery({"foo": ""}) == "foo" + doAssert encodeQuery({"foo": ""}, omitEq = false) == "foo=" + doAssert encodeQuery({"a": "1", "b": "", "c": "3"}) == "a=1&b&c=3" + doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, sep = ';') == "a=1;b;c=3" + doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false) == "a=1&b=&c=3" + doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false, sep = ';') == "a=1;b=;c=3" + + block: # `?` + block: + var foo = parseUri("http://example.com") / "foo" ? {"bar": "1", "baz": "qux"} + var foo1 = parseUri("http://example.com/foo?bar=1&baz=qux") + doAssert foo == foo1 + block: + var foo = parseUri("http://example.com") / "foo" ? {"do": "do", "bar": ""} + var foo1 = parseUri("http://example.com/foo?do=do&bar") + doAssert foo == foo1 + + block: # getDataUri, dataUriBase64 + doAssert getDataUri("", "text/plain") == "data:text/plain;charset=utf-8;base64," + doAssert getDataUri(" ", "text/plain") == "data:text/plain;charset=utf-8;base64,IA==" + doAssert getDataUri("c\xf7>", "text/plain") == "data:text/plain;charset=utf-8;base64,Y/c+" + doAssert getDataUri("Hello World", "text/plain") == "data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQ=" + doAssert getDataUri("leasure.", "text/plain") == "data:text/plain;charset=utf-8;base64,bGVhc3VyZS4=" + doAssert getDataUri("""!@#$%^&*()_+""", "text/plain") == "data:text/plain;charset=utf-8;base64,IUAjJCVeJiooKV8r" + doAssert(getDataUri("the quick brown dog jumps over the lazy fox", "text/plain") == + "data:text/plain;charset=utf-8;base64,dGhlIHF1aWNrIGJyb3duIGRvZyBqdW1wcyBvdmVyIHRoZSBsYXp5IGZveA==") + doAssert(getDataUri("The present is theirs\n The future, for which I really worked, is mine.", "text/plain") == + "data:text/plain;charset=utf-8;base64,VGhlIHByZXNlbnQgaXMgdGhlaXJzCiAgICAgIFRoZSBmdXR1cmUsIGZvciB3aGljaCBJIHJlYWxseSB3b3JrZWQsIGlzIG1pbmUu") + + block: # decodeQuery + doAssert toSeq(decodeQuery("a=1&b=0")) == @[("a", "1"), ("b", "0")] + doAssert toSeq(decodeQuery("a=1;b=0", sep = ';')) == @[("a", "1"), ("b", "0")] + doAssert toSeq(decodeQuery("a=1&b=2c=6")) == @[("a", "1"), ("b", "2c=6")] + doAssert toSeq(decodeQuery("a=1;b=2c=6", sep = ';')) == @[("a", "1"), ("b", "2c=6")] + + block: # bug #17481 + let u1 = parseUri("./") + let u2 = parseUri("./path") + let u3 = parseUri("a/path") + doAssert u1.scheme.len == 0 + doAssert u1.path == "./" + doAssert u2.scheme.len == 0 + doAssert u2.path == "./path" + doAssert u3.scheme.len == 0 + doAssert u3.path == "a/path" + +static: main() +main() diff --git a/tests/stdlib/tuserlocks.nim b/tests/stdlib/tuserlocks.nim new file mode 100644 index 000000000..927077120 --- /dev/null +++ b/tests/stdlib/tuserlocks.nim @@ -0,0 +1,21 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/rlocks +import std/assertions + +var r: RLock +r.initRLock() +doAssert r.tryAcquire() +doAssert r.tryAcquire() +r.release() +r.release() + +block: + var x = 12 + withRLock r: + inc x + doAssert x == 13 + +r.deinitRLock() diff --git a/tests/stdlib/tvarargs.nim b/tests/stdlib/tvarargs.nim new file mode 100644 index 000000000..2edc26264 --- /dev/null +++ b/tests/stdlib/tvarargs.nim @@ -0,0 +1,18 @@ +discard """ + targets: "c js" + matrix: "--mm:refc; --mm:orc" +""" +import std/assertions + +template main = + proc hello(x: varargs[string]): seq[string] = + var s: seq[string] + s.add x + s + + doAssert hello() == @[] + doAssert hello("a1") == @["a1"] + doAssert hello("a1", "a2") == @["a1", "a2"] + +static: main() +main() diff --git a/tests/stdlib/tvarints.nim b/tests/stdlib/tvarints.nim index dcdb756ce..f9624ee5b 100644 --- a/tests/stdlib/tvarints.nim +++ b/tests/stdlib/tvarints.nim @@ -1,15 +1,11 @@ discard """ - cmd: "nim c -r --styleCheck:hint --panics:on $options $file" - matrix: "-d:danger; -d:release" - targets: "c cpp" - nimout: "" - action: "run" - exitcode: 0 - timeout: 60.0 + matrix: "--mm:refc; --mm:orc" """ import std/varints +import std/assertions +# xxx doesn't work with js: tvarints.nim(18, 14) `wrLen == rdLen` [AssertionDefect] block: var dest: array[50, byte] @@ -37,48 +33,41 @@ block: doAssert cast[float64](got) == test block: - var hugeIntArray: array[50, byte] + var hugeIntArray: array[9, byte] var readedInt: uint64 - doAssert writeVu64(hugeIntArray, 0.uint64) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == 0.uint64 - doAssert writeVu64(hugeIntArray, uint64.high) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == uint64.high - doAssert writeVu64(hugeIntArray, uint64(int64.high)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == uint64(int64.high) - doAssert writeVu64(hugeIntArray, uint64(int32.high)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == uint64(int32.high) - doAssert writeVu64(hugeIntArray, uint64(int16.high)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == uint64(int16.high) - doAssert writeVu64(hugeIntArray, uint64(int8.high)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == uint64(int8.high) - doAssert writeVu64(hugeIntArray, cast[uint64](0.0)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](0.0) - doAssert writeVu64(hugeIntArray, cast[uint64](-0.0)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](-0.0) - doAssert writeVu64(hugeIntArray, cast[uint64](0.1)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](0.1) - doAssert writeVu64(hugeIntArray, cast[uint64](0.9555555555555555555555501)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](0.9555555555555555555555501) - doAssert writeVu64(hugeIntArray, cast[uint64](+Inf)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](+Inf) - doAssert writeVu64(hugeIntArray, cast[uint64](NegInf)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](NegInf) - doAssert writeVu64(hugeIntArray, cast[uint64](Nan)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](Nan) - doAssert writeVu64(hugeIntArray, cast[uint64](3.1415926535897932384626433)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](3.1415926535897932384626433) - doAssert writeVu64(hugeIntArray, cast[uint64](2.71828182845904523536028747)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](2.71828182845904523536028747) + + template chk(a) = + let b = cast[uint64](a) + doAssert writeVu64(hugeIntArray, b) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == b + + chk 0 + chk uint64.high + chk int64.high + chk int32.high + chk int16.high + chk int16.high + chk int8.high + chk 0.0 + chk -0.0 + chk 0.1 + chk Inf + chk NegInf + chk NaN + chk 3.1415926535897932384626433 block: - doAssert encodeZigzag(decodeZigzag(0.uint64)) == 0.uint64 - doAssert encodeZigzag(decodeZigzag(uint64(uint32.high))) == uint64(uint32.high) - doAssert encodeZigzag(decodeZigzag(uint64(int32.high))) == uint64(int32.high) - doAssert encodeZigzag(decodeZigzag(uint64(int16.high))) == uint64(int16.high) - doAssert encodeZigzag(decodeZigzag(uint64(int8.high))) == uint64(int8.high) - doAssert encodeZigzag(decodeZigzag(cast[uint64](0.0))) == cast[uint64](0.0) - doAssert encodeZigzag(decodeZigzag(cast[uint64](0.1))) == cast[uint64](0.1) - doAssert encodeZigzag(decodeZigzag(cast[uint64](0.9555555555555555555555501))) == cast[uint64](0.9555555555555555555555501) - doAssert encodeZigzag(decodeZigzag(cast[uint64](+Inf))) == cast[uint64](+Inf) - doAssert encodeZigzag(decodeZigzag(cast[uint64](3.1415926535897932384626433))) == cast[uint64](3.1415926535897932384626433) - doAssert encodeZigzag(decodeZigzag(cast[uint64](2.71828182845904523536028747))) == cast[uint64](2.71828182845904523536028747) + template chk(a) = + let b = cast[uint64](a) + doAssert encodeZigzag(decodeZigzag(b)) == b + chk 0 + chk uint32.high + chk int32.high + chk int16.high + chk int8.high + chk 0.0 + chk 0.1 + chk 0.9555555555555555555555501 + chk Inf + chk 3.1415926535897932384626433 + chk 2.71828182845904523536028747 diff --git a/tests/stdlib/tvmutils.nim b/tests/stdlib/tvmutils.nim new file mode 100644 index 000000000..63804c136 --- /dev/null +++ b/tests/stdlib/tvmutils.nim @@ -0,0 +1,31 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + joinable: false + nimout: ''' +0 +1 +2 +tvmutils.nim(28, 13) [opcLdImmInt] if i == 4: +tvmutils.nim(28, 10) [opcEqInt] if i == 4: +tvmutils.nim(28, 10) [opcFJmp] if i == 4: +tvmutils.nim(28, 13) [opcLdImmInt] if i == 4: +tvmutils.nim(28, 10) [opcEqInt] if i == 4: +tvmutils.nim(28, 10) [opcFJmp] if i == 4: +tvmutils.nim(29, 7) [opcLdConst] vmTrace(false) +tvmutils.nim(29, 15) [opcLdImmInt] vmTrace(false) +tvmutils.nim(29, 14) [opcIndCall] vmTrace(false) +5 +6 +''' +""" +# line 20 (only showing a subset of nimout to avoid making the test rigid) +import std/vmutils +proc main() = + for i in 0..<7: + echo i + if i == 2: + vmTrace(true) + if i == 4: + vmTrace(false) + +static: main() diff --git a/tests/stdlib/tvolatile.nim b/tests/stdlib/tvolatile.nim new file mode 100644 index 000000000..c097f9723 --- /dev/null +++ b/tests/stdlib/tvolatile.nim @@ -0,0 +1,15 @@ +import std/[volatile, assertions] + +var st: int +var foo: ptr int = addr st +volatileStore(foo, 12) +doAssert volatileLoad(foo) == 12 + +# bug #14623 +proc bar = + var st: int + var foo: ptr int = addr st + volatileStore(foo, 12) + doAssert volatileLoad(foo) == 12 + +bar() diff --git a/tests/stdlib/twchartoutf8.nim b/tests/stdlib/twchartoutf8.nim index a6602e3e3..e437177ac 100644 --- a/tests/stdlib/twchartoutf8.nim +++ b/tests/stdlib/twchartoutf8.nim @@ -1,13 +1,17 @@ discard """ + matrix: "--mm:refc; --mm:orc" output: '''OK''' """ +import std/[syncio, assertions] + #assume WideCharToMultiByte always produce correct result #windows only when not defined(windows): echo "OK" else: + import std/widestrs {.push gcsafe.} const CP_UTF8 = 65001'i32 @@ -66,8 +70,7 @@ else: #RFC-2781 "UTF-16, an encoding of ISO 10646" - var wc: WideCString - unsafeNew(wc, 1024 * 4 + 2) + var wc: WideCString = newWideCString(1024 * 2) #U+0000 to U+D7FF #skip the U+0000 diff --git a/tests/stdlib/twith.nim b/tests/stdlib/twith.nim new file mode 100644 index 000000000..ceadbe7bf --- /dev/null +++ b/tests/stdlib/twith.nim @@ -0,0 +1,44 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/with +import std/[assertions, formatfloat] + +type + Foo = object + col, pos: string + name: string + +proc setColor(f: var Foo; r, g, b: int) = f.col = $(r, g, b) +proc setPosition(f: var Foo; x, y: float) = f.pos = $(x, y) + +var f: Foo +with(f, setColor(2, 3, 4), setPosition(0.0, 1.0)) +doAssert f.col == "(2, 3, 4)" +doAssert f.pos == "(0.0, 1.0)" + +f = Foo() +with f: + col = $(2, 3, 4) + pos = $(0.0, 1.0) + _.name = "bar" +doAssert f.col == "(2, 3, 4)" +doAssert f.pos == "(0.0, 1.0)" +doAssert f.name == "bar" + +type + Baz* = object + a*, b*: int + Bar* = object + x*: int + baz*: Baz + +var bar: Bar +with bar: + x = 1 + with baz: + a = 2 + +doAssert bar.x == 1 +doAssert bar.baz.a == 2 diff --git a/tests/stdlib/twordwrap.nim b/tests/stdlib/twordwrap.nim new file mode 100644 index 000000000..5d49477d3 --- /dev/null +++ b/tests/stdlib/twordwrap.nim @@ -0,0 +1,48 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/wordwrap +import std/assertions + +when true: + let + inp = """ this is a long text -- muchlongerthan10chars and here + it goes""" + outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes" + doAssert wrapWords(inp, 10, false) == outp + + let + longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow""" + longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow" + doAssert wrapWords(longInp, 8, true) == longOutp + +# test we don't break Umlauts into invalid bytes: +let fies = "äöüöäöüöäöüöäöüööäöüöäößßßßüöäößßßßßß" +let fiesRes = "ä\nö\nü\nö\nä\nö\nü\nö\nä\nö\nü\nö\nä\nö\nü\nö\nö\nä\nö\nü\nö\nä\nö\nß\nß\nß\nß\nü\nö\nä\nö\nß\nß\nß\nß\nß\nß" +doAssert wrapWords(fies, 1, true) == fiesRes + +let longlongword = """abc uitdaeröägfßhydüäpydqfü,träpydqgpmüdträpydföägpydörztdüöäfguiaeowäzjdtrüöäp psnrtuiydrözenrüöäpyfdqazpesnrtulocjtüö +äzydgyqgfqfgprtnwjlcydkqgfüöezmäzydydqüüöäpdtrnvwfhgckdumböäpydfgtdgfhtdrntdrntydfogiayqfguiatrnydrntüöärtniaoeydfgaoeiqfglwcßqfgxvlcwgtfhiaoen +rsüöäapmböäptdrniaoydfglckqfhouenrtsüöäptrniaoeyqfgulocfqclgwxßqflgcwßqfxglcwrniatrnmüböäpmöäbpümöäbpüöämpbaoestnriaesnrtdiaesrtdniaesdrtnaetdr +iaoenvlcyfglwckßqfgvwkßqgfvlwkßqfgvlwckßqvlwkgfUIαοιαοιαχολωχσωχνωκψρχκψρτιεαοσηζϵηζιοεννκεωνιαλωσωκνκψρκγτφγτχκγτεκργτιχνκιωχσιλωσλωχξλξλξωχωχ +ξχλωωχαοεοιαεοαεοιαεοαεοιαοεσναοεκνρκψγκψφϵιηαααοε""" +let longlongwordRes = """ +abc uitdaeröägfßhydüäpydqfü,träpydqgpmüdträpydföägpydörztdüöäfguiaeowäzjdtrüöäp +psnrtuiydrözenrüöäpyfdqazpesnrtulocjtüöäzydgyqgfqfgprtnwjlcydkqgfüöezmäzydydqüü +öäpdtrnvwfhgckdumböäpydfgtdgfhtdrntdrntydfogiayqfguiatrnydrntüöärtniaoeydfgaoeiq +fglwcßqfgxvlcwgtfhiaoenrsüöäapmböäptdrniaoydfglckqfhouenrtsüöäptrniaoeyqfgulocf +qclgwxßqflgcwßqfxglcwrniatrnmüböäpmöäbpümöäbpüöämpbaoestnriaesnrtdiaesrtdniaesdr +tnaetdriaoenvlcyfglwckßqfgvwkßqgfvlwkßqfgvlwckßqvlwkgfUIαοιαοιαχολωχσωχνωκψρχκψ +ρτιεαοσηζϵηζιοεννκεωνιαλωσωκνκψρκγτφγτχκγτεκργτιχνκιωχσιλωσλωχξλξλξωχωχ +ξχλωωχαοεοιαεοαεοιαεοαεοιαοεσναοεκνρκψγκψφϵιηαααοε""" +doAssert wrapWords(longlongword) == longlongwordRes + +# bug #14579 +const input60 = """ +This string is wrapped to 60 characters. If we call +wrapwords on it it will be re-wrapped to 80 characters. +""" +const input60Res = """This string is wrapped to 60 characters. If we call wrapwords on it it will be +re-wrapped to 80 characters.""" +doAssert wrapWords(input60) == input60Res \ No newline at end of file diff --git a/tests/stdlib/twrapnils.nim b/tests/stdlib/twrapnils.nim index c56a65d11..3da230b5e 100644 --- a/tests/stdlib/twrapnils.nim +++ b/tests/stdlib/twrapnils.nim @@ -1,82 +1,224 @@ -import std/wrapnils +discard """ + matrix: "--mm:refc; --mm:orc" +""" -const wrapnilExtendedExports = declared(wrapnil) - # for now, wrapnil, isValid, unwrap are not exported +import std/wrapnils +from std/options import get, isSome +import std/assertions proc checkNotZero(x: float): float = doAssert x != 0 x -var witness = 0 - proc main() = - type Bar = object - b1: int - b2: ptr string - - type Foo = ref object - x1: float - x2: Foo - x3: string - x4: Bar - x5: seq[int] - x6: ptr Bar - x7: array[2, string] - x8: seq[int] - x9: ref Bar - - type Gook = ref object - foo: Foo - - proc fun(a: Bar): auto = a.b2 - - var a: Foo - var x6 = create(Bar) - x6.b1 = 42 - var a2 = Foo(x1: 1.0, x5: @[10, 11], x6: x6) - var a3 = Foo(x1: 1.2, x3: "abc") - a3.x2 = a3 - - var gook = Gook(foo: a) - - proc initFoo(x1: float): auto = - witness.inc - result = Foo(x1: x1) - - doAssert ?.a.x2.x2.x1 == 0.0 - doAssert ?.a3.x2.x2.x1 == 1.2 - doAssert ?.a3.x2.x2.x3[1] == 'b' - - doAssert ?.a3.x2.x2.x5.len == 0 - doAssert a3.x2.x2.x3.len == 3 - - when wrapnilExtendedExports: - # example calling wrapnil directly, with and without unwrap - doAssert a3.wrapnil.x2.x2.x3.len == wrapnil(3) - doAssert a3.wrapnil.x2.x2.x3.len.unwrap == 3 - doAssert a2.wrapnil.x4.isValid - doAssert not a.wrapnil.x4.isValid - - doAssert ?.a.x2.x2.x3[1] == default(char) - # here we only apply wrapnil around gook.foo, not gook (and assume gook is not nil) - doAssert ?.(gook.foo).x2.x2.x1 == 0.0 - - doAssert ?.a2.x6[] == Bar(b1: 42) # deref for ptr Bar - - doAssert ?.a2.x1.checkNotZero == 1.0 - doAssert a == nil - # shows that checkNotZero won't be called if a nil is found earlier in chain - doAssert ?.a.x1.checkNotZero == 0.0 - - # checks that a chain without nil but with an empty seq still throws IndexDefect - doAssertRaises(IndexDefect): discard ?.a2.x8[3] - - # make sure no double evaluation bug - doAssert witness == 0 - doAssert ?.initFoo(1.3).x1 == 1.3 - doAssert witness == 1 - - # here, it's used twice, to deref `ref Bar` and then `ptr string` - doAssert ?.a.x9[].fun[] == "" + var witness = 0 + block: + type Bar = object + b1: int + b2: ptr string + + type Foo = ref object + x1: float + x2: Foo + x3: string + x4: Bar + x5: seq[int] + x6: ptr Bar + x7: array[2, string] + x8: seq[int] + x9: ref Bar + + type Goo = ref object + foo: Foo + + proc fun(a: Bar): auto = a.b2 + + var a: Foo + + var x6: ptr Bar + when nimvm: discard # pending https://github.com/timotheecour/Nim/issues/568 + else: + x6 = create(Bar) + x6.b1 = 42 + var a2 = Foo(x1: 1.0, x5: @[10, 11], x6: x6) + var a3 = Foo(x1: 1.2, x3: "abc") + a3.x2 = a3 + + var goo = Goo(foo: a) + + proc initFoo(x1: float): auto = + witness.inc + result = Foo(x1: x1) + + doAssert ?.a.x2.x2.x1 == 0.0 + doAssert ?.a3.x2.x2.x1 == 1.2 + doAssert ?.a3.x2.x2.x3[1] == 'b' + + doAssert ?.a3.x2.x2.x5.len == 0 + doAssert a3.x2.x2.x3.len == 3 + + doAssert ?.a.x2.x2.x3[1] == default(char) + # here we only apply wrapnil around goo.foo, not goo (and assume goo is not nil) + doAssert ?.(goo.foo).x2.x2.x1 == 0.0 + + when nimvm: discard + else: + doAssert ?.a2.x6[] == Bar(b1: 42) # deref for ptr Bar + + doAssert ?.a2.x1.checkNotZero == 1.0 + doAssert a == nil + # shows that checkNotZero won't be called if a nil is found earlier in chain + doAssert ?.a.x1.checkNotZero == 0.0 + + when nimvm: discard + else: + # checks that a chain without nil but with an empty seq still raises + doAssertRaises(IndexDefect): discard ?.a2.x8[3] + + # make sure no double evaluation bug + doAssert witness == 0 + doAssert ?.initFoo(1.3).x1 == 1.3 + doAssert witness == 1 + + # here, it's used twice, to deref `ref Bar` and then `ptr string` + doAssert ?.a.x9[].fun[] == "" + + block: # `??.` + doAssert (??.a3.x2.x2.x3.len).get == 3 + doAssert (??.a2.x4).isSome + doAssert not (??.a.x4).isSome + + block: + type + A = object + b: B + B = object + c: C + C = object + d: D + D = ref object + e: E + e2: array[2, E] + e3: seq[E] + d3: D + i4: int + E = object + f: int + d2: D + proc identity[T](a: T): T = a + proc identity2[T](a: T, ignore: int): T = a + var a: A + doAssert ?.a.b.c.d.e.f == 0 + doAssert ?.a.b.c.d.e.d2.d3[].d3.e.d2.e.f == 0 + doAssert ?.a.b.c.d.d3[].e.f == 0 + doAssert ?.a.b.c.d.e2[0].d2.e3[0].f == 0 + doAssert ?.a == A.default + doAssert ?.a.b.c.d.e == E.default + doAssert ?.a.b.c.d.e.d2 == nil + + doAssert ?.a.identity.b.c.identity2(12).d.d3.e.f == 0 + doAssert ?.a.b.c.d.d3.e2[0].f == 0 + a.b.c.d = D() + a.b.c.d.d3 = a.b.c.d + a.b.c.d.e2[0].f = 5 + doAssert ?.a.b.c.d.d3.e2[0].f == 5 + + var d: D = nil + doAssert ?.d.identity.i4 == 0 + doAssert ?.d.i4.identity == 0 + + block: # case objects + type + Kind = enum k0, k1, k2 + V = object + case kind: Kind + of k0: + x0: int + of k1: + x1: int + of k2: + x2: int + A = object + v0: V + + block: + var a = V(kind: k0, x0: 3) + doAssert ?.a.x0 == 3 + doAssert ?.a.x1 == 0 + a = V(kind: k1, x1: 5) + doAssert ?.a.x0 == 0 + doAssert ?.a.x1 == 5 + + block: + var a = A(v0: V(kind: k0, x0: 10)) + doAssert ?.a.v0.x0 == 10 + doAssert ?.a.v0.x1 == 0 + a.v0 = V(kind: k2, x2: 8) + doAssert ?.a.v0.x0 == 0 + doAssert ?.a.v0.x1 == 0 + doAssert ?.a.v0.x2 == 8 + + block: # `nnkCall` + type + A = object + a0: int + d: D + D = ref object + i4: int + + proc identity[T](a: T): T = a + var d: D = nil + doAssert ?.d.i4.identity == 0 + doAssert ?.identity(?.d.i4) == 0 + doAssert ?.identity(d.i4) == 0 + doAssert ?.identity(d) == nil + doAssert ?.identity(d[]) == default(typeof(d[])) + doAssert ?.identity(d[]).i4 == 0 + var a: A + doAssert ?.identity(a) == default(A) + doAssert ?.identity(a.a0) == 0 + doAssert ?.identity(a.d) == nil + doAssert ?.identity(a.d.i4) == 0 + + block: # lvalue semantic propagation + type + A = ref object + a0: A + a1: seq[A] + a2: int + + B = object + b0: int + case cond: bool + of false: discard + of true: + b1: float + + block: + var a: A + doAssert ?.a.a0.a1[0].a2.addr == nil + a = A(a2: 3) + doAssert ?.a.a0.a1[0].a2.addr == nil + a.a0 = a + a.a1 = @[a] + let p = ?.a.a0.a1[0].a2.addr + doAssert p != nil + p[] = 5 + doAssert a.a2 == 5 + + block: + var b = B(cond: false, b0: 3) + let p = ?.b.b1.addr + doAssert p == nil + b = B(cond: true, b1: 4.5) + let p2 = ?.b.b1.addr + doAssert p2 != nil + p2[] = 4.6 + doAssert b.b1 == 4.6 + # useful pattern, impossible with Options + if (let p3 = ?.b.b1.addr; p3 != nil): + p3[] = 4.7 + doAssert b.b1 == 4.7 main() +static: main() diff --git a/tests/stdlib/twrongstattype.nim b/tests/stdlib/twrongstattype.nim new file mode 100644 index 000000000..4a1fc30c6 --- /dev/null +++ b/tests/stdlib/twrongstattype.nim @@ -0,0 +1,14 @@ +# issue #24076 + +when defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd): + import std/posix + proc uid(x: uint32): Uid = Uid(x) + var y: uint32 + let myUid = geteuid() + discard myUid == uid(y) + proc dev(x: uint32): Dev = Dev(x) + let myDev = 1.Dev + discard myDev == dev(y) + proc nlink(x: uint32): Nlink = Nlink(x) + let myNlink = 1.Nlink + discard myNlink == nlink(y) diff --git a/tests/stdlib/txmltree.nim b/tests/stdlib/txmltree.nim new file mode 100644 index 000000000..add12a3fc --- /dev/null +++ b/tests/stdlib/txmltree.nim @@ -0,0 +1,120 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/[xmltree, assertions, xmlparser] + + +block: + var + x: XmlNode + + x = <>a(href = "http://nim-lang.org", newText("Nim rules.")) + doAssert $x == """<a href="http://nim-lang.org">Nim rules.</a>""" + + x = <>outer(<>inner()) + doAssert $x == """<outer> + <inner /> +</outer>""" + + x = <>outer(<>middle(<>inner1(), <>inner2(), <>inner3(), <>inner4())) + doAssert $x == """<outer> + <middle> + <inner1 /> + <inner2 /> + <inner3 /> + <inner4 /> + </middle> +</outer>""" + + x = <>l0(<>l1(<>l2(<>l3(<>l4())))) + doAssert $x == """<l0> + <l1> + <l2> + <l3> + <l4 /> + </l3> + </l2> + </l1> +</l0>""" + + x = <>l0(<>l1p1(), <>l1p2(), <>l1p3()) + doAssert $x == """<l0> + <l1p1 /> + <l1p2 /> + <l1p3 /> +</l0>""" + + x = <>l0(<>l1(<>l2p1(), <>l2p2())) + doAssert $x == """<l0> + <l1> + <l2p1 /> + <l2p2 /> + </l1> +</l0>""" + + x = <>l0(<>l1(<>l2_1(), <>l2_2(<>l3_1(), <>l3_2(), <>l3_3(<>l4_1(), <>l4_2(), <>l4_3())), <>l2_3(), <>l2_4())) + doAssert $x == """<l0> + <l1> + <l2_1 /> + <l2_2> + <l3_1 /> + <l3_2 /> + <l3_3> + <l4_1 /> + <l4_2 /> + <l4_3 /> + </l3_3> + </l2_2> + <l2_3 /> + <l2_4 /> + </l1> +</l0>""" + + let + innermost = newElement("innermost") + middle = newXmlTree("middle", [innermost]) + innermost.add newText("innermost text") + x = newXmlTree("outer", [middle]) + doAssert $x == """<outer> + <middle> + <innermost>innermost text</innermost> + </middle> +</outer>""" + + x = newElement("myTag") + x.add newText("my text") + x.add newElement("sonTag") + x.add newEntity("my entity") + doAssert $x == "<myTag>my text<sonTag />&my entity;</myTag>" + +block: # bug #21290 + let x = newXmlTree("foo",[ + newXmlTree("bar",[ + newText("Hola"), + newXmlTree("qux",[ + newXmlTree("plugh",[]) + ]) + ]) + ]) + + let s = $x + doAssert $parseXml(s) == s + doAssert s == """<foo> + <bar>Hola<qux> <plugh /> </qux></bar> +</foo>""" + +block: #21541 + let root = <>root() + root.add <>child(newText("hello")) + root.add <>more(newVerbatimText("hola")) + let s = $root + doAssert s == """<root> + <child>hello</child> + <more>hola</more> +</root>""" + + let temp = newVerbatimText("Hello!") + doAssert temp.text == "Hello!" + temp.text = "Hola!" + doAssert temp.text == "Hola!" diff --git a/tests/stdlib/tyield.nim b/tests/stdlib/tyield.nim new file mode 100644 index 000000000..f385ddd05 --- /dev/null +++ b/tests/stdlib/tyield.nim @@ -0,0 +1,258 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" +""" + +import std/[sugar, algorithm] +import std/assertions + +block: + var x = @[(6.0, 6, '6'), + (5.0, 5, '5'), + (4.0, 4, '4'), + (3.0, 3, '3'), + (2.0, 2, '2'), + (1.0, 1, '1')] + + let y = x.reversed + + block: + let res = collect: + for (f, i, c) in x: + (f, i, c) + + doAssert res == x + + iterator popAscending[T](q: var seq[T]): T = + while q.len > 0: yield q.pop + + block: + var res = collect: + for f, i, c in popAscending(x): + (f, i, c) + + doAssert res == y + + let z = reversed(res) + let res2 = collect: + for (f, i, c) in popAscending(res): + (f, i, c) + + doAssert res2 == z + + +block: + var visits = 0 + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + iterator foo(): (int, int) = + yield bar() + + for a, b in foo(): + doAssert a == b + + doAssert visits == 1 + + block: + proc iterAux(a: seq[int], i: var int): (int, string) = + result = (a[i], $a[i]) + inc i + + iterator pairs(a: seq[int]): (int, string) = + var i = 0 + while i < a.len: + yield iterAux(a, i) + + var x = newSeq[int](10) + for i in 0 ..< x.len: + x[i] = i + + let res = collect: + for k, v in x: + (k, v) + + let expected = collect: + for i in 0 ..< x.len: + (i, $i) + + doAssert res == expected + + block: + proc bar(): (int, int, int) = + inc visits + (visits, visits, visits) + + iterator foo(): (int, int, int) = + yield bar() + + for a, b, c in foo(): + doAssert a == b + + doAssert visits == 2 + + + block: + + proc bar(): int = + inc visits + visits + + proc car(): int = + inc visits + visits + + iterator foo(): (int, int) = + yield (bar(), car()) + yield (bar(), car()) + + for a, b in foo(): + doAssert b == a + 1 + + doAssert visits == 6 + + + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + proc t2(): int = 99 + + iterator foo(): (int, int) = + yield (12, t2()) + yield bar() + + let res = collect: + for (a, b) in foo(): + (a, b) + + doAssert res == @[(12, 99), (7, 7)] + doAssert visits == 7 + + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + proc t2(): int = 99 + + iterator foo(): (int, int) = + yield ((12, t2())) + yield (bar()) + + let res = collect: + for (a, b) in foo(): + (a, b) + + doAssert res == @[(12, 99), (8, 8)] + doAssert visits == 8 + + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + proc t1(): int = 99 + proc t2(): int = 99 + + iterator foo(): (int, int) = + yield (t1(), t2()) + yield bar() + + let res = collect: + for a, b in foo(): + (a, b) + + doAssert res == @[(99, 99), (9, 9)] + doAssert visits == 9 + + + block: + proc bar(): ((int, int), string) = + inc visits + ((visits, visits), $visits) + + proc t2(): int = 99 + + iterator foo(): ((int, int), string) = + yield ((1, 2), $t2()) + yield bar() + + let res = collect: + for a, b in foo(): + (a, b) + + doAssert res == @[((1, 2), "99"), ((10, 10), "10")] + doAssert visits == 10 + + + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + iterator foo(): (int, int) = + yield (for i in 0 ..< 10: discard bar(); bar()) + yield (bar()) + + let res = collect: + for (a, b) in foo(): + (a, b) + + doAssert res == @[(21, 21), (22, 22)] + + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + proc t2(): int = 99 + + iterator foo(): (int, int) = + yield if true: bar() else: (t2(), t2()) + yield (bar()) + + let res = collect: + for a, b in foo(): + (a, b) + + doAssert res == @[(23, 23), (24, 24)] + + +block: + iterator foo(): (int, int, int) = + var time = 777 + yield (1, time, 3) + + let res = collect: + for a, b, c in foo(): + (a, b, c) + + doAssert res == @[(1, 777, 3)] + +block: + iterator foo(): (int, int, int) = + var time = 777 + yield (1, time, 3) + + let res = collect: + for t in foo(): + (t[0], t[1], t[2]) + + doAssert res == @[(1, 777, 3)] + + +block: + proc bar(): (int, int, int) = + (1, 2, 3) + iterator foo(): (int, int, int) = + yield bar() + + let res = collect: + for a, b, c in foo(): + (a, b, c) + + doAssert res == @[(1, 2, 3)] diff --git a/tests/stdlib/unixsockettest.nim b/tests/stdlib/unixsockettest.nim new file mode 100644 index 000000000..8f95d0808 --- /dev/null +++ b/tests/stdlib/unixsockettest.nim @@ -0,0 +1,26 @@ +import std/[assertions, net, os] + +let unixSocketPath = getCurrentDir() / "usox" + +removeFile(unixSocketPath) + +let socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_NONE) +socket.bindUnix(unixSocketPath) +socket.listen() + +var + clientSocket: Socket + data: string + +socket.accept(clientSocket) +clientSocket.readLine(data) +doAssert data == "data sent through the socket" +clientSocket.send("Hello from server\c\l") + +clientSocket.readLine(data) +doAssert data == "bye" +clientSocket.send("bye\c\l") + +clientSocket.close() +socket.close() +removeFile(unixSocketPath) diff --git a/tests/stdlib/uselocks.nim b/tests/stdlib/uselocks.nim index cde9641b2..f87623b5e 100644 --- a/tests/stdlib/uselocks.nim +++ b/tests/stdlib/uselocks.nim @@ -1,4 +1,5 @@ import locks +import std/assertions type MyType* [T] = object lock: Lock @@ -9,3 +10,7 @@ proc createMyType*[T]: MyType[T] = proc use* (m: var MyType): int = withLock m.lock: result = 3 + +block: + var l: Lock + doAssert $l == "()" |