diff options
Diffstat (limited to 'tests/stdlib')
253 files changed, 23849 insertions, 2482 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/genericstrformat.nim b/tests/stdlib/genericstrformat.nim new file mode 100644 index 000000000..0446f3269 --- /dev/null +++ b/tests/stdlib/genericstrformat.nim @@ -0,0 +1,16 @@ +# from issue #7632 +# imported and used in tstrformat + +import strformat + +proc fails*(a: static[int]): string = + &"formatted {a:2}" + +proc fails2*[N: static[int]](a: int): string = + &"formatted {a:2}" + +proc works*(a: int): string = + &"formatted {a:2}" + +proc fails0*(a: int or uint): string = + &"formatted {a:2}" 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/mstackframes.nim b/tests/stdlib/mstackframes.nim new file mode 100644 index 000000000..f3daa1437 --- /dev/null +++ b/tests/stdlib/mstackframes.nim @@ -0,0 +1,38 @@ +import std/stackframes + + + +# line 5 +var count = 0 + +proc main1(n: int) = + setFrameMsg $("main1", n) + if n > 0: + main1(n-1) + +proc main2(n: int) = + count.inc + setFrameMsg $("main2", n, count) + proc bar() = + setFrameMsg $("bar ",) + if n < 3: raise newException(CatchableError, "on purpose") + bar() + main2(n-1) + +proc main() = + var z = 0 + setFrameMsg "\n z: " & $z, prefix = "" + # multiple calls inside a frame are possible + z.inc + setFrameMsg "\n z: " & $z, prefix = "" + try: + main2(5) + except CatchableError: + main1(10) # goes deep and then unwinds; sanity check to ensure `setFrameMsg` from inside + # `main1` won't invalidate the stacktrace; if StackTraceEntry.frameMsg + # were a reference instead of a copy, this would fail. + let e = getCurrentException() + let trace = e.getStackTrace + echo trace + +main() diff --git a/tests/stdlib/nre/captures.nim b/tests/stdlib/nre/captures.nim index 19c344a8d..acc141baf 100644 --- a/tests/stdlib/nre/captures.nim +++ b/tests/stdlib/nre/captures.nim @@ -1,59 +1,64 @@ 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].get == 0 .. 0) + check("1 23".find(ex1).captureBounds[0] == 0 .. 0) check("1 23".find(ex1, 1).matchBounds == 2 .. 2) check("1 23".find(ex1, 3).matchBounds == 3 .. 3) let ex2 = re("()()()()()()()()()()([0-9])") - check("824".find(ex2).captureBounds[0].get == 0 .. -1) - check("824".find(ex2).captureBounds[10].get == 0 .. 0) + check("824".find(ex2).captureBounds[0] == 0 .. -1) + check("824".find(ex2).captureBounds[10] == 0 .. 0) let ex3 = re("([0-9]+)") - check("824".find(ex3).captureBounds[0].get == 0 .. 2) + 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") let ex2 = "foo".find(re("(?<foo>foo)(?<bar>bar)?")) + check("foo" in ex2.captureBounds) check(ex2.captures["foo"] == "foo") - check(ex2.captures["bar"] == nil) + check(not ("bar" in ex2.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(ex1.captureBounds["foo"] == some(0..2)) - check(ex1.captureBounds["bar"] == none(Slice[int])) + check("foo" in ex1.captureBounds) + check(ex1.captureBounds["foo"] == 0..2) + check(not ("bar" in ex1.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", "bar" : nil}.toTable()) - check(ex1.captureBounds.toTable == {"foo" : some(0..2), "bar" : none(Slice[int])}.toTable()) - check(ex1.captures.toTable("") == {"foo" : "foo", "bar" : ""}.toTable()) + check(ex1.captures.toTable == {"foo" : "foo"}.toTable()) + check(ex1.captureBounds.toTable == {"foo" : 0..2}.toTable()) 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 == @["foo", nil]) + check(ex1.captures.toSeq == @[some("foo"), none(string)]) check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int])]) - check(ex1.captures.toSeq("") == @["foo", ""]) + check(ex1.captures.toSeq(some("")) == @[some("foo"), some("")]) let ex2 = "foobar".find(re("(?<foo>foo)(?<bar>bar)?")) - check(ex2.captures.toSeq == @["foo", "bar"]) + check(ex2.captures.toSeq == @[some("foo"), some("bar")]) 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 38ee5214b..7e09a4b2f 100644 --- a/tests/stdlib/nre/match.nim +++ b/tests/stdlib/nre/match.nim @@ -1,18 +1,18 @@ 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") - check("abc".match(re"(\w)").captureBounds[0].get == 0 .. 0) - check("abc".match(re"").captureBounds[-1].get == 0 .. -1) - check("abc".match(re"abc").captureBounds[-1].get == 0 .. 2) + check("abc".match(re"(\w)").captureBounds[0] == 0 .. 0) + 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 516fd4328..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,6 +15,8 @@ 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": - expect ValueError: discard "ab".replace(re"(a)|(b)", "$1$2") - expect ValueError: discard "b".replace(re"(a)?(b)", "$1$2") + 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}") + expect KeyError: discard "b".replace(re"(?<foo>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/osproctest.nim b/tests/stdlib/osproctest.nim new file mode 100644 index 000000000..8c4fba9ba --- /dev/null +++ b/tests/stdlib/osproctest.nim @@ -0,0 +1,8 @@ +# This is test program for the osproc module. + +import os + +echo getCurrentDir() + +for i in 1..paramCount(): + echo paramStr(i) diff --git a/tests/stdlib/somesql.sql b/tests/stdlib/somesql.sql index 285f93cec..74afcbab0 100644 --- a/tests/stdlib/somesql.sql +++ b/tests/stdlib/somesql.sql @@ -295,4 +295,4 @@ create table anon207( anon209 varchar(30) not null, anon204 varchar(30) default null, anon70 int not null references anon40(anon41)); - +select * from anon207 where anon41 in (1, 2, 3); diff --git a/tests/stdlib/t10231.nim b/tests/stdlib/t10231.nim new file mode 100644 index 000000000..3d09721aa --- /dev/null +++ b/tests/stdlib/t10231.nim @@ -0,0 +1,16 @@ +discard """ + targets: "cpp" + action: run + exitcode: 0 +""" + +import os +import std/assertions + +# consider moving this inside tosproc (taking care that it's for cpp mode) + +if paramCount() == 0: + # main process + doAssert execShellCmd(getAppFilename().quoteShell & " test") == 1 +else: + quit 1 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 new file mode 100644 index 000000000..c55e93e73 --- /dev/null +++ b/tests/stdlib/t8925.nim @@ -0,0 +1,16 @@ +discard """ + errormsg: "type mismatch between pattern '$$i' (position: 1) and HourRange var 'hour'" + file: "strscans.nim" +""" + +import strscans + +type + HourRange = range[0..23] + +var + hour: HourRange + timeStr: string + +if scanf(timeStr, "$i", hour): + discard 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 new file mode 100644 index 000000000..5a7e2da40 --- /dev/null +++ b/tests/stdlib/tasynchttpserver.nim @@ -0,0 +1,121 @@ +discard """ + cmd: "nim c --threads:on $file" + exitcode: 0 + output: "OK" + disabled: false +""" + +import strutils +from net import TimeoutError +import std/assertions + +import httpclient, asynchttpserver, asyncdispatch, asyncfutures + +template runTest( + handler: proc (request: Request): Future[void] {.gcsafe.}, + request: proc (server: AsyncHttpServer): Future[AsyncResponse], + test: proc (response: AsyncResponse, body: string): Future[void]) = + + let server = newAsyncHttpServer() + + discard server.serve(Port(64123), handler) + + let + response = waitFor(request(server)) + body = waitFor(response.body) + + discard test(response, body) + +proc test200() {.async.} = + proc handler(request: Request) {.async.} = + await request.respond(Http200, "Hello World, 200") + + proc request(server: AsyncHttpServer): Future[AsyncResponse] {.async.} = + let + client = newAsyncHttpClient() + clientResponse = await client.request("http://localhost:64123/") + + server.close() + + return clientResponse + + proc test(response: AsyncResponse, body: string) {.async.} = + doAssert(response.status == $Http200) + doAssert(body == "Hello World, 200") + doAssert(response.headers.hasKey("Content-Length")) + doAssert(response.headers["Content-Length"] == "16") + + runTest(handler, request, test) + +proc test404() {.async.} = + proc handler(request: Request) {.async.} = + await request.respond(Http404, "Hello World, 404") + + proc request(server: AsyncHttpServer): Future[AsyncResponse] {.async.} = + let + client = newAsyncHttpClient() + clientResponse = await client.request("http://localhost:64123/") + + server.close() + + return clientResponse + + proc test(response: AsyncResponse, body: string) {.async.} = + doAssert(response.status == $Http404) + doAssert(body == "Hello World, 404") + doAssert(response.headers.hasKey("Content-Length")) + doAssert(response.headers["Content-Length"] == "16") + + runTest(handler, request, test) + +proc testCustomEmptyHeaders() {.async.} = + proc handler(request: Request) {.async.} = + await request.respond(Http200, "Hello World, 200", newHttpHeaders()) + + proc request(server: AsyncHttpServer): Future[AsyncResponse] {.async.} = + let + client = newAsyncHttpClient() + clientResponse = await client.request("http://localhost:64123/") + + server.close() + + return clientResponse + + proc test(response: AsyncResponse, body: string) {.async.} = + doAssert(response.status == $Http200) + doAssert(body == "Hello World, 200") + doAssert(response.headers.hasKey("Content-Length")) + doAssert(response.headers["Content-Length"] == "16") + + runTest(handler, request, test) + +proc testCustomContentLength() {.async.} = + proc handler(request: Request) {.async.} = + let headers = newHttpHeaders() + headers["Content-Length"] = "0" + await request.respond(Http200, "Hello World, 200", headers) + + proc request(server: AsyncHttpServer): Future[AsyncResponse] {.async.} = + let + client = newAsyncHttpClient() + clientResponse = await client.request("http://localhost:64123/") + + server.close() + + return clientResponse + + proc test(response: AsyncResponse, body: string) {.async.} = + 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()) + +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 new file mode 100644 index 000000000..c3bfb818e --- /dev/null +++ b/tests/stdlib/tbase64.nim @@ -0,0 +1,63 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" +""" +import std/assertions +import std/base64 + +template main() = + doAssert encode("a") == "YQ==" + doAssert encode("Hello World") == "SGVsbG8gV29ybGQ=" + doAssert encode("leasure.") == "bGVhc3VyZS4=" + doAssert encode("easure.") == "ZWFzdXJlLg==" + doAssert encode("asure.") == "YXN1cmUu" + doAssert encode("sure.") == "c3VyZS4=" + doAssert encode([1,2,3]) == "AQID" + doAssert encode(['h','e','y']) == "aGV5" + + doAssert encode("") == "" + doAssert decode("") == "" + + doAssert decode(" ") == "" + + const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + const testInputExpands = "++++++++++++++++++++++++++++++" + const longText = """Man is distinguished, not only by his reason, but by this + singular passion from other animals, which is a lust of the mind, + that by a perseverance of delight in the continued and indefatigable + generation of knowledge, exceeds the short vehemence of any carnal + pleasure.""" + const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.", + "asure.", longText, testInputExpandsTo76, testInputExpands] + + 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 + + doAssertRaises(ValueError): discard decode("SGVsbG\x008gV29ybGQ=") + + block base64urlSafe: + doAssert encode("c\xf7>", safe = true) == "Y_c-" + doAssert encode("c\xf7>", safe = false) == "Y/c+" # Not a nice URL :( + doAssert decode("Y/c+") == decode("Y_c-") + # Output must not change with safe=true + doAssert encode("Hello World", safe = true) == "SGVsbG8gV29ybGQ=" + doAssert encode("leasure.", safe = true) == "bGVhc3VyZS4=" + doAssert encode("easure.", safe = true) == "ZWFzdXJlLg==" + doAssert encode("asure.", safe = true) == "YXN1cmUu" + doAssert encode("sure.", safe = true) == "c3VyZS4=" + doAssert encode([1,2,3], safe = true) == "AQID" + doAssert encode(['h','e','y'], safe = true) == "aGV5" + 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 8301256c4..3ecab2c64 100644 --- a/tests/stdlib/tbitops.nim +++ b/tests/stdlib/tbitops.nim @@ -1,9 +1,12 @@ discard """ - file: "tbitops.nim" - output: "OK" + nimout: "OK" + matrix: "--mm:refc; --mm:orc" + output: ''' +OK +''' """ import bitops - +import std/assertions proc main() = const U8 = 0b0011_0010'u8 @@ -16,98 +19,123 @@ proc main() = 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 + 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( 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) + 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) @@ -124,45 +152,200 @@ proc main() = 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) - - 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) + 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 + v.flipMask(0b0101_0101) + doAssert v == 0b1001_1001 + v.clearMask(0b1000_1000) + doAssert v == 0b0001_0001 + v.clearMask(0b0001_0001) + doAssert v == 0b0000_0000 + v.setMask(0b0001_1110) + doAssert v == 0b0001_1110 + v.mask(0b0101_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 + let t = 0b1100_0110'u8 + 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 + + # same thing, but with exclusive ranges. + 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 + + # loop test: + let c = 0b1111_1111'u8 + for i in 0 .. 7: + 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 + a.setMask(4 .. 7) + doAssert a == 0b1111_1100 + a.flipMask(1 .. 3) + doAssert a == 0b1111_0010 + a.flipMask(2 .. 4) + doAssert a == 0b1110_1110 + a.clearMask(2 .. 4) + doAssert a == 0b1110_0010 + a.mask(0 .. 3) + 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 + 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 + block: + # single bit operations + var v: uint8 + v.setBit(0) + doAssert v == 0x0000_0001 + v.setBit(1) + doAssert v == 0b0000_0011 + v.flipBit(7) + doAssert v == 0b1000_0011 + v.clearBit(0) + doAssert v == 0b1000_0010 + v.flipBit(1) + doAssert v == 0b1000_0000 + doAssert v.testBit(7) + doAssert not v.testBit(6) + block: + # multi bit operations + var v: uint8 + v.setBits(0, 1, 7) + doAssert v == 0b1000_0011 + v.flipBits(2, 3) + doAssert v == 0b1000_1111 + v.clearBits(7, 0, 1) + doAssert v == 0b0000_1100 + block: + # signed + var v: int8 + v.setBit(7) + doAssert v == -128 + block: + var v: uint64 + v.setBit(63) + doAssert v == 0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000'u64 + + block: + proc testReverseBitsInvo(x: SomeUnsignedInt) = + doAssert reverseBits(reverseBits(x)) == x + + proc testReverseBitsPerType(x, reversed: uint64) = + doAssert reverseBits(x) == reversed + doAssert reverseBits(cast[uint32](x)) == cast[uint32](reversed shr 32) + doAssert reverseBits(cast[uint32](x shr 16)) == cast[uint32](reversed shr 16) + doAssert reverseBits(cast[uint16](x)) == cast[uint16](reversed shr 48) + doAssert reverseBits(cast[uint8](x)) == cast[uint8](reversed shr 56) + + testReverseBitsInvo(x) + testReverseBitsInvo(cast[uint32](x)) + testReverseBitsInvo(cast[uint16](x)) + testReverseBitsInvo(cast[uint8](x)) + + proc testReverseBitsRefl(x, reversed: uint64) = + testReverseBitsPerType(x, reversed) + testReverseBitsPerType(reversed, x) + + proc testReverseBitsShift(d, b: uint64) = + var + x = d + y = b + + for i in 1..64: + testReverseBitsRefl(x, y) + x = x shl 1 + y = y shr 1 + + proc testReverseBits(d, b: uint64) = + testReverseBitsShift(d, b) + + testReverseBits(0x0u64, 0x0u64) + testReverseBits(0xffffffffffffffffu64, 0xffffffffffffffffu64) + testReverseBits(0x0123456789abcdefu64, 0xf7b3d591e6a2c480u64) + testReverseBits(0x5555555555555555u64, 0xaaaaaaaaaaaaaaaau64) + testReverseBits(0x5555555500000001u64, 0x80000000aaaaaaaau64) + testReverseBits(0x55555555aaaaaaaau64, 0x55555555aaaaaaaau64) + testReverseBits(0xf0f0f0f00f0f0f0fu64, 0xf0f0f0f00f0f0f0fu64) + testReverseBits(0x181881810ff00916u64, 0x68900ff081811818u64) echo "OK" + # bug #7587 + doAssert popcount(0b11111111'i8) == 8 + +block: # not ready for vm because exception is compile error + try: + var v: uint32 + var i = 32 + v.setBit(i) + doAssert false + except RangeDefect: + discard + except: + doAssert false + + main() +static: + # test everything on vm as well + 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/tbitops2.nim b/tests/stdlib/tbitops2.nim deleted file mode 100644 index 31952316c..000000000 --- a/tests/stdlib/tbitops2.nim +++ /dev/null @@ -1,168 +0,0 @@ -discard """ - file: "tbitops.nim" - output: "OK" -""" -import bitops - - -proc main() = - 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" - -main() diff --git a/tests/stdlib/tbitops2.nim.cfg b/tests/stdlib/tbitops2.nim.cfg deleted file mode 100644 index e1cb77e82..000000000 --- a/tests/stdlib/tbitops2.nim.cfg +++ /dev/null @@ -1,2 +0,0 @@ --d:noIntrinsicsBitOpts --d:noUndefinedBitOps 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/tbug5382.nim b/tests/stdlib/tbug5382.nim deleted file mode 100644 index c86656d32..000000000 --- a/tests/stdlib/tbug5382.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - output: ''' -02 -''' -""" -import re - -let regexp = re"^\/([0-9]{2})\.html$" -var matches: array[1, string] -discard "/02.html".find(regexp, matches) -echo matches[0] 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 new file mode 100644 index 000000000..ef39450da --- /dev/null +++ b/tests/stdlib/tcgi.nim @@ -0,0 +1,31 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/unittest +import std/[cgi, strtabs, sugar] +import std/assertions + +block: # Test cgi module + const queryString = "foo=bar&фу=бар&checked=✓&list=1,2,3&with_space=text%20with%20space" + + block: # test query parsing with readData + let parsedQuery = readData(queryString) + + check parsedQuery["foo"] == "bar" + check parsedQuery["фу"] == "бар" + check parsedQuery["checked"] == "✓" + check parsedQuery["list"] == "1,2,3" + check parsedQuery["with_space"] == "text with space" + + 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/tcount.nim b/tests/stdlib/tcount.nim deleted file mode 100644 index ce1d14b6c..000000000 --- a/tests/stdlib/tcount.nim +++ /dev/null @@ -1,29 +0,0 @@ -discard """ - output: '''1 -2 -3 -4 -5 -done''' -""" - -# bug #1845, #2224 - -var arr = [3,2,1,5,4] - -# bubble sort -for i in low(arr)..high(arr): - for j in i+1..high(arr): # Error: unhandled exception: value out of range: 5 [RangeError] - if arr[i] > arr[j]: - let tmp = arr[i] - arr[i] = arr[j] - arr[j] = tmp - -for i in low(arr)..high(arr): - echo arr[i] - -# check this terminates: -for x in countdown('\255', '\0'): - discard - -echo "done" diff --git a/tests/stdlib/tcputime.nim b/tests/stdlib/tcputime.nim deleted file mode 100644 index 2fc46ee64..000000000 --- a/tests/stdlib/tcputime.nim +++ /dev/null @@ -1,13 +0,0 @@ - -import times, os - -var e = epochTime() -var c = cpuTime() - -os.sleep(1500) - -e = epochTime() - e -c = cpuTime() - c - -echo "epochTime: ", e, " cpuTime: ", c - diff --git a/tests/stdlib/tcritbits.nim b/tests/stdlib/tcritbits.nim index 8280ec881..e6282f045 100644 --- a/tests/stdlib/tcritbits.nim +++ b/tests/stdlib/tcritbits.nim @@ -1,28 +1,89 @@ discard """ - output: '''abc -def -definition -prefix -xyz -def -definition''' + matrix: "--mm:refc; --mm:orc" + targets: "c js" """ -import critbits +import std/[sequtils,critbits] +import std/assertions -when isMainModule: - var r: TCritBitTree[void] +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.del "def" - for w in r.items: - echo w + 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 - for w in r.itemsWithPrefix("de"): - echo w + 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 new file mode 100644 index 000000000..42dc646f2 --- /dev/null +++ b/tests/stdlib/tdecls.nim @@ -0,0 +1,50 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" +""" +import std/assertions +import std/decls + +template fun() = + var s = @[10,11,12] + var a {.byaddr.} = s[0] + a+=100 + doAssert s == @[110,11,12] + doAssert a is int + 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 + var foo {.byaddr.} = s[0]) + + doAssert not compiles(block: + # ditto + var foo {.byaddr.} = s[0] + var foo {.byaddr.} = s[0]) + {.pop.} + + block: + var b {.byaddr.} = s[1] # redeclaration ok in sub scope + b = 123 + + doAssert s == @[110,123,12] + + b = b * 10 + doAssert s == @[1100,123,12] + + doAssert not compiles(block: + var b2 {.byaddr.}: float = s[2]) + + doAssert compiles(block: + var b2 {.byaddr.}: int = s[2]) + +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/techo.nim b/tests/stdlib/techo.nim deleted file mode 100644 index 9cef9205f..000000000 --- a/tests/stdlib/techo.nim +++ /dev/null @@ -1,3 +0,0 @@ -# Simplest Nim program - -echo "Hello, World!" 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/testequivalence.nim b/tests/stdlib/testequivalence.nim deleted file mode 100644 index 7acaad340..000000000 --- a/tests/stdlib/testequivalence.nim +++ /dev/null @@ -1,14 +0,0 @@ -discard """ - output: '''''' -""" -import sets - -doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3,4]), "equivalent or subset") -doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3]), "equivalent or subset") -doAssert((not(toSet(@[1,2,3]) <= toSet(@[1,2]))), "equivalent or subset") -doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3,4]), "strict subset") -doAssert((not(toSet(@[1,2,3]) < toSet(@[1,2,3]))), "strict subset") -doAssert((not(toSet(@[1,2,3]) < toSet(@[1,2]))), "strict subset") -doAssert((not(toSet(@[1,2,3]) == toSet(@[1,2,3,4]))), "==") -doAssert(toSet(@[1,2,3]) == toSet(@[1,2,3]), "==") -doAssert((not(toSet(@[1,2,3]) == toSet(@[1,2]))), "==") diff --git a/tests/stdlib/texitprocs.nim b/tests/stdlib/texitprocs.nim new file mode 100644 index 000000000..ea29d8f58 --- /dev/null +++ b/tests/stdlib/texitprocs.nim @@ -0,0 +1,22 @@ +discard """ +matrix: "--mm:refc; --mm:orc" +targets: "c cpp js" +output: ''' +ok4 +ok3 +ok2 +ok1 +''' +""" + +import std/exitprocs + +proc fun1() {.noconv.} = echo "ok1" +proc fun2() = echo "ok2" +proc fun3() {.noconv.} = echo "ok3" +proc fun4() = echo "ok4" + +addExitProc(fun1) +addExitProc(fun2) +addExitProc(fun3) +addExitProc(fun4) diff --git a/tests/stdlib/tfdleak.nim b/tests/stdlib/tfdleak.nim new file mode 100644 index 000000000..272a7507c --- /dev/null +++ b/tests/stdlib/tfdleak.nim @@ -0,0 +1,152 @@ +discard """ + exitcode: 0 + output: "" + 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 + + # Note: Windows 10-only API + proc compareObjectHandles(first, second: Handle): WINBOOL + {.stdcall, dynlib: "kernelbase", + importc: "CompareObjectHandles".} +else: + import posix + +proc leakCheck(f: AsyncFD | int | FileHandle | SocketHandle, msg: string, + expectLeak = defined(nimInheritHandles)) = + var args = @[$f.int, msg, $expectLeak] + + when defined(windows): + var refFd: Handle + # NOTE: This function shouldn't be used to duplicate sockets, + # as this function may mess with the socket internal refcounting. + # but due to the lack of type segmentation in the stdlib for + # Windows (AsyncFD can be a file or a socket), we will have to + # settle with this. + # + # Now, as a poor solution for the refcounting problem, we just + # simply let the duplicated handle leak. This should not interfere + # with the test since new handles can't occupy the slot held by + # the leaked ones. + if duplicateHandle(getCurrentProcess(), f.Handle, + getCurrentProcess(), addr refFd, + 0, 1, DUPLICATE_SAME_ACCESS) == 0: + raiseOSError osLastError(), "Couldn't create the reference handle" + args.add $refFd + + discard startProcess( + getAppFilename(), + args = args, + options = {poParentStreams} + ).waitForExit + +proc isValidHandle(f: int): bool = + ## Check if a handle is valid. Requires OS-native handles. + when defined(windows): + var flags: DWORD + result = getHandleInformation(f.Handle, addr flags) != 0 + else: + result = fcntl(f.cint, F_GETFD) != -1 + +proc main() = + if paramCount() == 0: + # Parent process + let f = syncio.open("__test_fdleak", fmReadWrite) + defer: close f + + leakCheck(f.getOsFileHandle, "system.open()") + + doAssert f.reopen("__test_fdleak2", fmReadWrite), "reopen failed" + + leakCheck(f.getOsFileHandle, "reopen") + + let sock = createNativeSocket() + defer: close sock + leakCheck(sock, "createNativeSocket()") + if sock.setInheritable(not defined(nimInheritHandles)): + leakCheck(sock, "createNativeSocket()", not defined(nimInheritHandles)) + else: + raiseOSError osLastError() + + let server = newSocket() + defer: close server + server.bindAddr(address = "127.0.0.1") + server.listen() + let (_, port) = server.getLocalAddr + + leakCheck(server.getFd, "newSocket()") + + let client = newSocket() + defer: close client + client.connect("127.0.0.1", port) + + var input: Socket + server.accept(input) + + leakCheck(input.getFd, "accept()") + + # ioselectors_select doesn't support returning a handle. + when not defined(windows): + let selector = newSelector[int]() + leakCheck(selector.getFd, "selector()", false) + + var mf = memfiles.open("__test_fdleak3", fmReadWrite, newFileSize = 1) + defer: close mf + when defined(windows): + leakCheck(mf.mapHandle, "memfiles.open().mapHandle", false) + else: + leakCheck(mf.handle, "memfiles.open().handle", false) + + let sockAsync = createAsyncNativeSocket() + defer: closeSocket sockAsync + leakCheck(sockAsync, "createAsyncNativeSocket()") + if sockAsync.setInheritable(not defined(nimInheritHandles)): + leakCheck(sockAsync, "createAsyncNativeSocket()", not defined(nimInheritHandles)) + else: + raiseOSError osLastError() + + let serverAsync = newAsyncSocket() + defer: close serverAsync + serverAsync.bindAddr(address = "127.0.0.1") + serverAsync.listen() + let (_, portAsync) = serverAsync.getLocalAddr + + leakCheck(serverAsync.getFd, "newAsyncSocket()") + + let clientAsync = newAsyncSocket() + defer: close clientAsync + waitFor clientAsync.connect("127.0.0.1", portAsync) + + let inputAsync = waitFor serverAsync.accept() + + leakCheck(inputAsync.getFd, "accept() async") + else: + let + fd = parseInt(paramStr 1) + expectLeak = parseBool(paramStr 3) + msg = (if expectLeak: "not " else: "") & "leaked " & paramStr 2 + let validHandle = + when defined(windows): + # On Windows, due to the use of winlean, causes the program to open + # a handle to the various dlls that's loaded. This handle might + # collide with the handle sent for testing. + # + # As a walkaround, we pass an another handle that's purposefully leaked + # as a reference so that we can verify whether the "leaked" handle + # is the right one. + let refFd = parseInt(paramStr 4) + fd.isValidHandle and compareObjectHandles(fd, refFd) != 0 + else: + fd.isValidHandle + if expectLeak xor validHandle: + echo msg + +when isMainModule: main() diff --git a/tests/stdlib/tfdleak_multiple.nim b/tests/stdlib/tfdleak_multiple.nim new file mode 100644 index 000000000..c26681217 --- /dev/null +++ b/tests/stdlib/tfdleak_multiple.nim @@ -0,0 +1,31 @@ +discard """ +joinable: false +""" + +import os, osproc, strutils +import std/assertions + +const Iterations = 200 + +proc testFdLeak() = + var count = 0 + let + test = getAppDir() / "tfdleak" + exe = test.addFileExt(ExeExt).quoteShell + options = ["", "-d:nimInheritHandles"] + for opt in options: + let + run = "nim c $1 $2" % [opt, quoteShell test] + (output, status) = execCmdEx run + doAssert status == 0, "Test complination failed:\n$1\n$2" % [run, output] + for i in 1..Iterations: + let (output, status) = execCmdEx exe + doAssert status == 0, "Execution of " & exe & " failed" + if "leaked" in output: + count.inc + doAssert count == 0, "Leaked " & $count & " times" + +when defined(windows): + # tfdleak was only flaky for windows (and for netbsd, there is still a bug) + # note that this test is quite slow, 87 sec on windows. + testFdLeak() 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/tformat.nim b/tests/stdlib/tformat.nim deleted file mode 100644 index 160ab0fd5..000000000 --- a/tests/stdlib/tformat.nim +++ /dev/null @@ -1,12 +0,0 @@ -discard """ - file: "tformat.nim" - output: "Hi Andreas! How do you feel, Rumpf?" -""" -# Tests the new format proc (including the & and &= operators) - -import strutils - -echo("Hi $1! How do you feel, $2?\n" % ["Andreas", "Rumpf"]) -#OUT Hi Andreas! How do you feel, Rumpf? - - diff --git a/tests/stdlib/tfrexp1.nim b/tests/stdlib/tfrexp1.nim index c6bb2b38c..aa734ddac 100644 --- a/tests/stdlib/tfrexp1.nim +++ b/tests/stdlib/tfrexp1.nim @@ -1,13 +1,16 @@ discard """ - targets: "js c c++" - output: '''ok''' + matrix: "--mm:refc; --mm:orc" + targets: "js c cpp" """ -import math -import strformat +import std/math +import std/assertions const manualTest = false +when manualTest: + import strformat + proc frexp_test(lo, hi, step: float64) = var exp: int var frac: float64 @@ -39,6 +42,14 @@ when manualTest: frexp_test(-1000.0, 1000.0, 0.0125) else: - frexp_test(-1000000.0, 1000000.0, 0.125) + frexp_test(-200000.0, 200000.0, 0.125) + + +doAssert frexp(8.0) == (0.5, 4) +doAssert frexp(-8.0) == (-0.5, 4) +doAssert frexp(0.0) == (0.0, 0) -echo "ok" +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 new file mode 100644 index 000000000..3a90034c8 --- /dev/null +++ b/tests/stdlib/tgetaddrinfo.nim @@ -0,0 +1,38 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + exitcode: 0 + output: "" +""" + +# 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) + doAssert aiList != nil + doAssert aiList.ai_addr != nil + doAssert aiList.ai_addrlen.SockLen == sizeof(Sockaddr_in).SockLen + doAssert aiList.ai_next == nil + freeAddrInfo aiList + +when defined(posix) and not defined(haiku) and not defined(freebsd) and not defined(openbsd) and not defined(netbsd): + + block RAW_ICMP: + # the port will be ignored + let aiList = getAddrInfo("127.0.0.1", 999.Port, AF_INET, SOCK_RAW, IPPROTO_ICMP) + doAssert aiList != nil + doAssert aiList.ai_addr != nil + doAssert aiList.ai_addrlen.SockLen == sizeof(Sockaddr_in).SockLen + doAssert aiList.ai_next == nil + freeAddrInfo aiList + + block RAW_ICMPV6: + # the port will be ignored + let aiList = getAddrInfo("::1", 999.Port, AF_INET6, SOCK_RAW, IPPROTO_ICMPV6) + doAssert aiList != nil + doAssert aiList.ai_addr != nil + doAssert aiList.ai_addrlen.SockLen == sizeof(Sockaddr_in6).SockLen + doAssert aiList.ai_next == nil + freeAddrInfo aiList diff --git a/tests/stdlib/tgetfileinfo.nim b/tests/stdlib/tgetfileinfo.nim index 019c2eb7f..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 @@ -14,13 +17,13 @@ import os, strutils # 8 - Handle : Invalid Handle proc genBadFileName(limit = 100): string = - ## Generates a filename of a nonexistant file. + ## Generates a filename of a nonexistent file. ## Returns "" if generation fails. result = "a" var hitLimit = true for i in 0..100: - if existsFile(result): + if fileExists(result): result.add("a") else: hitLimit = false @@ -77,8 +80,8 @@ proc testGetFileInfo = # Case 6 and 8 block: let - testFile: TFile = nil - testHandle = TFileHandle(-1) + testFile: File = nil + testHandle = FileHandle(-1) try: discard getFileInfo(testFile) echo("Handle : Invalid File : Failure") @@ -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 c442b43fb..4555fbcb3 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,8 +1,242 @@ -import unittest -import hashes - -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) +discard """ + matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:on; --backend:c -d:nimStringHash2; --backend:cpp -d:nimStringHash2; --backend:js -d:nimStringHash2" +""" + +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)) + + 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)) + + 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 + + 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 + + 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) + + 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() + +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 new file mode 100644 index 000000000..853a1c0cc --- /dev/null +++ b/tests/stdlib/thtmlparser.nim @@ -0,0 +1,159 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c js" + output: ''' +true +https://example.com/test?format=jpg&name=orig## +https://example.com/test?format=jpg&name=orig##text +https://example.com/test?format=jpg##text +''' +""" +import htmlparser +import xmltree +import strutils +from streams import newStringStream +import std/assertions + +block t2813: + const + html = """ + <html> + <head> + <title>Test</title> + </head> + <body> + <table> + <thead> + <tr><td>A</td></tr> + <tr><td>B</td></tr> + </thead> + <tbody> + <tr><td></td>A<td></td></tr> + <tr><td></td>B<td></td></tr> + <tr><td></td>C<td></td></tr> + </tbody> + <tfoot> + <tr><td>A</td></tr> + </tfoot> + </table> + </body> + </html> + """ + var errors: seq[string] = @[] + let tree = parseHtml(newStringStream(html), "test.html", errors) + doAssert errors.len == 0 # Errors: </thead> expected,... + + var len = tree.findAll("tr").len # len = 6 + var rows: seq[XmlNode] = @[] + for n in tree.findAll("table"): + n.findAll("tr", rows) # len = 2 + break + doAssert tree.findAll("tr").len == rows.len + + +block t2814: + ## builds the two cases below and test that + ## ``//[dd,li]`` has "<p>that</p>" as children + ## + ## <dl> + ## <dt>this</dt> + ## <dd> + ## <p>that</p> + ## </dd> + ## </dl> + + ## + ## <ul> + ## <li> + ## <p>that</p> + ## </li> + ## </ul> + for ltype in [["dl","dd"], ["ul","li"]]: + let desc_item = if ltype[0]=="dl": "<dt>this</dt>" else: "" + let item = "$1<$2><p>that</p></$2>" % [desc_item, ltype[1]] + let list = """ <$1> + $2 + </$1> """ % [ltype[0], item] + + var errors : seq[string] = @[] + let parseH = parseHtml(newStringStream(list),"statichtml", errors =errors) + + if $parseH.findAll(ltype[1])[0].child("p") != "<p>that</p>": + echo "case " & ltype[0] & " failed !" + quit(2) + echo "true" + +block t6154: + let foo = """ + <!DOCTYPE html> + <html> + <head> + <title> foobar </title> + </head> + <body> + <p class=foo id=bar></p> + <p something=	foo	bar²></p> + <p something= 	foo	bar² foo =bloo></p> + <p class="foo2" id="bar2"></p> + <p wrong= ></p> + <p data-foo data-bar="correct!" enabled ></p> + <p quux whatever></p> + </body> + </html> + """ + + var errors: seq[string] = @[] + let html = parseHtml(newStringStream(foo), "statichtml", errors=errors) + doAssert "statichtml(11, 18) Error: attribute value expected" in errors + let ps = html.findAll("p") + doAssert ps.len == 7 + + doAssert ps[0].attrsLen == 2 + doAssert ps[0].attr("class") == "foo" + doAssert ps[0].attr("id") == "bar" + doAssert ps[0].len == 0 + + doAssert ps[1].attrsLen == 1 + doAssert ps[1].attr("something") == "\tfoo\tbar²" + doAssert ps[1].len == 0 + + doAssert ps[2].attrsLen == 2 + doAssert ps[2].attr("something") == "\tfoo\tbar²" + doAssert ps[2].attr("foo") == "bloo" + doAssert ps[2].len == 0 + + doAssert ps[3].attrsLen == 2 + doAssert ps[3].attr("class") == "foo2" + doAssert ps[3].attr("id") == "bar2" + doAssert ps[3].len == 0 + + doAssert ps[4].attrsLen == 1 + doAssert ps[4].attr("wrong") == "" + + doAssert ps[5].attrsLen == 3 + doAssert ps[5].attr("data-foo") == "" + doAssert ps[5].attr("data-bar") == "correct!" + doAssert ps[5].attr("enabled") == "" + doAssert ps[5].len == 0 + + doAssert ps[6].attrsLen == 2 + doAssert ps[6].attr("quux") == "" + doAssert ps[6].attr("whatever") == "" + doAssert ps[6].len == 0 + +# bug #11713, #1034 +var content = """ +# with & +<img src="https://example.com/test?format=jpg&name=orig" alt=""> +<img src="https://example.com/test?format=jpg&name=orig" alt="text"> + +# without & +<img src="https://example.com/test?format=jpg" alt="text"> +""" + +var + stream = newStringStream(content) + body = parseHtml(stream) + +for y in body.findAll("img"): + echo y.attr("src"), "##", y.attr("alt") diff --git a/tests/stdlib/thtmlparser2813.nim b/tests/stdlib/thtmlparser2813.nim deleted file mode 100644 index 4b04bc3f0..000000000 --- a/tests/stdlib/thtmlparser2813.nim +++ /dev/null @@ -1,45 +0,0 @@ -discard """ - output: "@[]" -""" -import htmlparser -import xmltree -from streams import newStringStream - -const - html = """ - <html> - <head> - <title>Test</title> - </head> - <body> - <table> - <thead> - <tr><td>A</td></tr> - <tr><td>B</td></tr> - </thead> - <tbody> - <tr><td></td>A<td></td></tr> - <tr><td></td>B<td></td></tr> - <tr><td></td>C<td></td></tr> - </tbody> - <tfoot> - <tr><td>A</td></tr> - </tfoot> - </table> - </body> - </html> - """ -var errors: seq[string] = @[] - -let tree = parseHtml(newStringStream(html), "test.html", errors) - -echo errors # Errors: </thead> expected,... - -var len = tree.findAll("tr").len # len = 6 - -var rows: seq[XmlNode] = @[] -for n in tree.findAll("table"): - n.findAll("tr", rows) # len = 2 - break - -assert tree.findAll("tr").len == rows.len diff --git a/tests/stdlib/thtmlparser2814.nim b/tests/stdlib/thtmlparser2814.nim deleted file mode 100644 index 968d390f1..000000000 --- a/tests/stdlib/thtmlparser2814.nim +++ /dev/null @@ -1,44 +0,0 @@ -discard """ - output: true -""" -import htmlparser -import xmltree -import strutils -from streams import newStringStream - - -## builds the two cases below and test that -## ``//[dd,li]`` has "<p>that</p>" as children -## -## <dl> -## <dt>this</dt> -## <dd> -## <p>that</p> -## </dd> -## </dl> - -## -## <ul> -## <li> -## <p>that</p> -## </li> -## </ul> - - -for ltype in [["dl","dd"], ["ul","li"]]: - let desc_item = if ltype[0]=="dl": "<dt>this</dt>" else: "" - let item = "$1<$2><p>that</p></$2>" % [desc_item, ltype[1]] - let list = """ <$1> - $2 -</$1> """ % [ltype[0], item] - - var errors : seq[string] = @[] - - let parseH = parseHtml(newStringStream(list),"statichtml", errors =errors) - - if $parseH.findAll(ltype[1])[0].child("p") != "<p>that</p>": - echo "case " & ltype[0] & " failed !" - quit(2) - - -echo "true" diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index fff02722a..0bd479670 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -1,33 +1,65 @@ discard """ cmd: "nim c --threads:on -d:ssl $file" - exitcode: 0 - output: "OK" - disabled: "travis" - disabled: "appveyor" + 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, + message: string): 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: + freeAddrInfo(aiList) + raiseOSError(osLastError()) + freeAddrInfo(aiList) + if listen(fd) != 0: + raiseOSError(osLastError()) + setBlocking(fd, false) + + var serverFd = fd.AsyncFD + register(serverFd) + result = serverFd + + proc onAccept(fut: Future[AsyncFD]) {.gcsafe.} = + if not fut.failed: + let clientFd = fut.read() + clientFd.send(message).callback = proc() = + clientFd.closeSocket() + serverFd.accept().callback = onAccept + serverFd.accept().callback = onAccept + 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: @@ -46,7 +78,7 @@ proc asyncTest() {.async.} = data["output"] = "soap12" data["uploaded_file"] = ("test.html", "text/html", "<html><head></head><body><p>test</p></body></html>") - resp = await client.post("http://validator.w3.org/check", multipart=data) + resp = await client.post("http://validator.w3.org/check", multipart = data) doAssert(resp.code.is2xx) # onProgressChanged @@ -58,6 +90,16 @@ proc asyncTest() {.async.} = await client.downloadFile("http://speedtest-ams2.digitalocean.com/100mb.test", "100mb.test") + # HTTP/1.1 without Content-Length - issue #10726 + var serverFd = makeIPv6HttpServer("::1", Port(18473), + "HTTP/1.1 200 \c\L" & + "\c\L" & + "Here comes reply") + resp = await client.request("http://[::1]:18473/") + body = await resp.body + doAssert(body == "Here comes reply") + serverFd.closeSocket() + client.close() # Proxy test @@ -68,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: @@ -96,7 +139,7 @@ proc syncTest() = data["output"] = "soap12" data["uploaded_file"] = ("test.html", "text/html", "<html><head></head><body><p>test</p></body></html>") - resp = client.post("http://validator.w3.org/check", multipart=data) + resp = client.post("http://validator.w3.org/check", multipart = data) doAssert(resp.code.is2xx) # onProgressChanged @@ -110,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. @@ -122,40 +171,17 @@ proc syncTest() = except: doAssert false, "TimeoutError should have been raised." -proc makeIPv6HttpServer(hostname: string, port: Port): AsyncFD = - let fd = newNativeSocket(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: - freeAddrInfo(aiList) - raiseOSError(osLastError()) - freeAddrInfo(aiList) - if listen(fd) != 0: - raiseOSError(osLastError()) - setBlocking(fd, false) - - var serverFd = fd.AsyncFD - register(serverFd) - result = serverFd - - proc onAccept(fut: Future[AsyncFD]) {.gcsafe.} = - if not fut.failed: - let clientFd = fut.read() - clientFd.send("HTTP/1.1 200 OK\r\LContent-Length: 0\r\LConnection: Closed\r\L\r\L").callback = proc() = - clientFd.closeSocket() - serverFd.accept().callback = onAccept - serverFd.accept().callback = onAccept - proc ipv6Test() = var client = newAsyncHttpClient() - let serverFd = makeIPv6HttpServer("::1", Port(18473)) + let serverFd = makeIPv6HttpServer("::1", Port(18473), + "HTTP/1.1 200 OK\r\LContent-Length: 0\r\LConnection: Closed\r\L\r\L") var resp = waitFor client.request("http://[::1]:18473/") doAssert(resp.status == "200 OK") serverFd.closeSocket() client.close() -syncTest() -waitFor(asyncTest()) ipv6Test() -echo "OK" +when enableRemoteNetworking: + syncTest() + waitFor(asyncTest()) diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim new file mode 100644 index 000000000..6b963f029 --- /dev/null +++ b/tests/stdlib/thttpclient_ssl.nim @@ -0,0 +1,150 @@ +discard """ + cmd: "nim $target --mm:refc -d:ssl $options $file" + disabled: "openbsd" +""" + +# Nim - Basic SSL integration tests +# (c) Copyright 2018 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# +## Warning: this test performs local networking. +## Test with: +## ./bin/nim c -d:ssl -p:. --threads:on -r tests/stdlib/thttpclient_ssl.nim + + +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, + openssl, + os, + strutils, + threadpool, + times, + unittest + + # bogus self-signed certificate + const + certFile = "tests/stdlib/thttpclient_ssl_cert.pem" + keyFile = "tests/stdlib/thttpclient_ssl_key.pem" + + proc log(msg: string) = + when defined(ssldebug): + echo " [" & $epochTime() & "] " & msg + # FIXME + echo " [" & $epochTime() & "] " & msg + discard + + proc runServer(port: Port): bool {.thread.} = + ## Run a trivial HTTPS server in a {.thread.} + ## Exit after serving one request + + var socket = newSocket() + socket.setSockOpt(OptReusePort, true) + socket.bindAddr(port) + + var ctx = newContext(certFile=certFile, keyFile=keyFile) + + ## Handle one connection + socket.listen() + + var client: Socket + var address = "" + + log "server: ready" + socket.acceptAddr(client, address) + log "server: incoming connection" + + var ssl: SslPtr = SSL_new(ctx.context) + discard SSL_set_fd(ssl, client.getFd()) + log "server: accepting connection" + ErrClearError() + if SSL_accept(ssl) <= 0: + ERR_print_errors_fp(stderr) + else: + const reply = "HTTP/1.0 200 OK\r\nServer: test\r\nContent-type: text/html\r\nContent-Length: 0\r\n\r\n" + log "server: sending reply" + discard SSL_write(ssl, reply.cstring, reply.len) + + log "server: receiving a line" + let line = client.recvLine() + log "server: received $# bytes" % $line.len + log "closing" + SSL_free(ssl) + close(client) + close(socket) + log "server: exited" + + + suite "SSL self signed certificate check": + + test "TCP socket": + const port = 12347.Port + let t = spawn runServer(port) + sleep(100) + var sock = newSocket() + var ctx = newContext() + ctx.wrapSocket(sock) + try: + log "client: connect" + sock.connect("127.0.0.1", port) + fail() + except: + let msg = getCurrentExceptionMsg() + check(msg.contains("certificate verify failed")) + + test "HttpClient default: no check": + const port = 12345.Port + let t = spawn runServer(port) + sleep(100) + + var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyNone)) + try: + log "client: connect" + discard client.getContent("https://127.0.0.1:12345") + except: + let msg = getCurrentExceptionMsg() + log "client: unexpected exception: " & msg + fail() + + test "HttpClient with CVerifyPeer": + const port = 12346.Port + let t = spawn runServer(port) + sleep(100) + + var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer)) + try: + log "client: connect" + discard client.getContent("https://127.0.0.1:12346") + log "getContent should have raised an exception" + fail() + except: + let msg = getCurrentExceptionMsg() + log "client: exception: " & msg + # SSL_shutdown:shutdown while in init + if not (msg.contains("alert number 48") or + 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_ssl_cert.pem b/tests/stdlib/thttpclient_ssl_cert.pem new file mode 100644 index 000000000..f15c15c52 --- /dev/null +++ b/tests/stdlib/thttpclient_ssl_cert.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIFCTCCAvGgAwIBAgIURYQOmGzeh3Vy7Gk6Go4uAPwcNwAwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTE5MDEyMzAwMTgzNFoXDTQ2MDYw +OTAwMTgzNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEAzoEVEl7yqY+RqIagXDD4JB7LyONDvh8aJvBMnJVBgjaL +JdkfQjvPGUzUkEbU5nc6u7lqFxzEv7hXrssQCB7TwJwfS2PT1Rj14IFlYPyw4DEe +P1RVS/awurtv3jwumarVl7LR+IQfo59kJ/P8jZt8H3HscDbyhXcHeOWI6q+XlfdV +mTUJVvABdUuOiIFjgfFVTpo+CKxy7c5caRDK7g1s9xB1/M9PUfJvHY1WrBWFOZf0 +Bl8iwn+ahuxfIVqsFL9leqLykgi1f4L20p7RaAK95TXCo3CszZm4Fsw9zhzkjoU7 +2h0nuYl197LZvRs3u/JJjzZERmsfVPIs5BtO8/MN1MvRn6hIGU5Q3kOVWqWxSkSl +njrf+uwUdn/24uSCnygNeDuJzwW/2q4N9YI3oovqNIGpkT3FbAm7UKwI4lwhwmqw +7WH+92ELj0BinmsMMRPD2OqvK+vzLVqwUIQkYug+Hjys6QGXMlrL0krrj7XOKSc3 +SvZa4j0S/Y5CKkw5xuZXxITsdaV6hGi3d/kuT+1ttOSfIIXJXDEiu4pYRfziKU1a +8EhHMEajEi6ueLw7QmEPVx398erRwiUuP2y43yZ4mwVwvN3i5jlVztl4XsglDmQ+ +hahstVdMMA34K2rK0U8q8YjdYm+z99NmGEPYrS6Qnpr1xrICN83FOWFI0k7ttyMC +AwEAAaNTMFEwHQYDVR0OBBYEFLqMY6eP3h3gu+ANs77xDBRnElxyMB8GA1UdIwQY +MBaAFLqMY6eP3h3gu+ANs77xDBRnElxyMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggIBAJS+wyy0r+tVAlCa6V/xxlCDtW9n6L2nsqJXEjME0VvwGs3m +ima68LyTQJqCSjjxSotaNOYKzUu4vRA3JssV+fUDR+NpmhpRuM74XmO05HUQkp9U +dBEHyXp2aRQ9LSdvHo5D+RW+J4sHFb3PbU8NPx/t5Dg7il92S2QJQz1jNl+Nezc6 +2O8Vt1YbvWXfqM47URTpnQbWoo38pI44AgAuW3QagucKWsyounmhx65XcdtLn99g +oZt496pU+hBpYu/IpXuBKNC4FvOrXTWAPkAbbYP39UFyiKwIyTosK+qdbhBlt1xi +bBPn6N1W9L2BvUwM8fEB/qBuR9UfcMsIYJsWbbXMfyeF6lbaP7xD01rm+yU5PMMI +Co40abixMntz4J3T2ixdCptf0He1U/UegOHwG1ZGgQzvOG6qI/xkNktDaSA75KR7 +BvPV1CmZC4ovVo1L4STrwnoRz5J49PNOHi9Okj9zJ99H7nsmsK16oxpIYkYHJWn+ +45jpG8SlDp7oev1OGGk/z+ZOTz+LcNxyvsRQVN8w5zNmjCSWiGqz+UUgppCZg8qd +ECWokNQ5Lr20t1whynrX5bH0l887WPCQmm5VduRoyKFGhCRBSzcCtowSpiwZglUk +CV0jgFKoteItdzZgsND5I1GaNOxZlnK3wN4H0pgZv7HlW6SP1OYd2Y67waJ7 +-----END CERTIFICATE----- diff --git a/tests/stdlib/thttpclient_ssl_key.pem b/tests/stdlib/thttpclient_ssl_key.pem new file mode 100644 index 000000000..6ab04122c --- /dev/null +++ b/tests/stdlib/thttpclient_ssl_key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDOgRUSXvKpj5Go +hqBcMPgkHsvI40O+Hxom8EyclUGCNosl2R9CO88ZTNSQRtTmdzq7uWoXHMS/uFeu +yxAIHtPAnB9LY9PVGPXggWVg/LDgMR4/VFVL9rC6u2/ePC6ZqtWXstH4hB+jn2Qn +8/yNm3wfcexwNvKFdwd45Yjqr5eV91WZNQlW8AF1S46IgWOB8VVOmj4IrHLtzlxp +EMruDWz3EHX8z09R8m8djVasFYU5l/QGXyLCf5qG7F8hWqwUv2V6ovKSCLV/gvbS +ntFoAr3lNcKjcKzNmbgWzD3OHOSOhTvaHSe5iXX3stm9Gze78kmPNkRGax9U8izk +G07z8w3Uy9GfqEgZTlDeQ5VapbFKRKWeOt/67BR2f/bi5IKfKA14O4nPBb/arg31 +gjeii+o0gamRPcVsCbtQrAjiXCHCarDtYf73YQuPQGKeawwxE8PY6q8r6/MtWrBQ +hCRi6D4ePKzpAZcyWsvSSuuPtc4pJzdK9lriPRL9jkIqTDnG5lfEhOx1pXqEaLd3 ++S5P7W205J8ghclcMSK7ilhF/OIpTVrwSEcwRqMSLq54vDtCYQ9XHf3x6tHCJS4/ +bLjfJnibBXC83eLmOVXO2XheyCUOZD6FqGy1V0wwDfgrasrRTyrxiN1ib7P302YY +Q9itLpCemvXGsgI3zcU5YUjSTu23IwIDAQABAoICAQCdR60/57cUs/dxjs/2R4nH +IPl/ELEYzeGCRMVlATz6qwZCFmN7c8ghceX32SrwOWEvd2G5Jr0ndIS76YdVV/1Z +ls8zAV5m0HL8wjDvtKYWqvJps5afm80w+++RKO8pNPcnahgIGsFqQszqrSbux7y6 +ym8VbJQ8WNMFHnWwoXpnyxCT9tQdNgE2UAzIJRwf7SpXCp0yx/1k6CZ0E0ksFGeo +qQ3kNhUoyegdbvfTazSkD/rZG36C+uM73i36Xm/wAXKN/CuaVC3AZ4QMGNBPUr9F +IzQSfY/vrCOMoZR1NoZRkmJqlogaBPsnZD34jRFfAYNLIz7PD2m2rhjIx4/Tt4wQ +5mUwga9ud0ly5wSzswudw07mTYtsLbWrUn6QdFxSwbQ0tXh9PJrqCSJDmYIptuu/ +6zjg8hQLg7y37xMDMCdKtviHx+ndVpW3StTwB/z7lDA6yuYY6nYN0dJTJS3qQheo +maPG4Xf4FBcD4Is73BjBCf3QR6WIv0ZOG3/GZ1OqLRrPg1u/3UJkpa4LE/6qNUxf +zdBZSPyQZExBvOqdklEI+1OcqofmWq2n7Amct45buDbFryehEhfJ1HHtkXkTEsut +azfQeaGem/jKxcTD+1bWs/Q5Nn+QFfKr0NFjXSLoITWQkgQD1qISw3DC72jYXlsm +S4CmCDW1dHZlmWZq+Mh34QKCAQEA+2JFRa1yYZ0tPt88sOjJYyw9yUxB9Nv9cKrs +kdkhKHKevF+0BUbRLfp9bod+Wlv66pgQi6ZGKkGD7lCam/3FIBlmmiG8AOoXdoGy +t17XCzlYy348mnHra2X+JBAN51ivPemdlGZShLbNMkGdL1khtjHL9vSr1KgFn3F/ +8nstVQ9nzHTCK0HWpBGn/EK3dd8lcYZDd7Fcgjz7E3xQDz/XZt0HMwwGaLnQ1L7T +glIyeNdqLBp4v0NT6L1AAk5rQJONo57AepblwacYhoW9mR5K0bm/BMo5+xwMtYz9 +69ZuMNW8qdaWrzeEsxM1PDbcOoVqChF10w0Ih/MkhKGpN/GxUQKCAQEA0kvWUkEK +1BBhwGyuKrMnUC3jnQ36KpsjlryMUArdjS2gVBztGW2p2CUWasEgZdxpwQmnqKyz +4hcZaU/JUleutTI5raxzju4Ve87c+koOiamhw/zaiLCpLn2j0Rh2qxp7QvPPRO0V +1MN347wjCTx/5/j8WffgWqWfqdrd8JheKal/OHlTZA3DG77FIVnUyov8Np+lTd1x +NpWr/AOreZlMBq/X/kmWCe+fP901fGdi3cdsKcJcdLPv9KFciSjBlAlaLMnBgLWo +RrIuNxdH3dRX4rzBSpdNq72n8NaH+A10eoXrlC4eWLo7vRSTe4WRgUAIbhVifnJk +z4B5FqC/aVgkMwKCAQEAtq983h0lcbDy76z2Ay65I/xDzqU/jX3OGfHtSDS+NxHN +L+JxBiCn5b0TKJ8JAQu1NoVaCNLGTPEdurQTF+f9OM2c1chMQ3HbqUCqKz6eEscT +M5dC3Y6KYptVbMnKAOVfPSQoY29U6qOaTbqHS6B/slNQAeFfeoS8yVmHfSVtFVLD +wT7c2OjY3pUCOn4Vq3CGWpETOMnJC9DbOhbua5aeqF9aWwuTIMpg7CrdtOidS1pp +CzIVrBF2yj22ZbatlNlmZpD5Gl3NDMWtOh25Yqwz/WP6YLXCGy4QQmP7KEfF/nFl +0RtkmGNFaYo89sx7kX/hRv3XXZAsMfhOAqElQ8W+cQKCAQAdL/lnIS/njv6CPpNN +yd/C+RuGSNJX54BhA3pWAawOVC7Ufc9KoDXakgsydeuRN65V5IkomA+/aYVVYIWI +sDLHY1kuCalgRRsmO+fftTefU7PoB8gtAJf6o+WAt+yAgwRonn4+Csnk5dxV917F +gWgfQieENSsmaaZnZME5C2zGS4gkxnIUiPRzfV7O6jDmi9dNnYrL69gyw0NDjx7V +mbk7lFxeJsh0SJXJv2IVCiRms68HfLpoWDENuvek8cssSMADR11cB9p7NW/Epa6L +01T/W0NYnvdgxsnwW1Yzz2pDNyMjReNgXTi9XYW6tyci0UhaPw2Ujzv+sM4dneHz +NRCRAoIBAHqXaeC1uTGSzfLvRz81ifgDRP8H9L1HLt7ZWL6XMp1ph+P6yYFXM4JK +WeP3cdKO/kQOD/fLuhYT92T2hHEadT8CQqpsBMQt29Zlm4oYWHB7ERiZqaGX3/T0 +U1TlL0WxthoHPY2HwA6pmDTmUzDk3tFlgk+XOmLsDacBdC6EsFwA+tyEPVxmkb0J +H+j7D4NxwysAyWCB9fWU1FV+JJJel+nz88i7Gb8uJ+kSktnFxjv/G9p+OkDYlaUt +j8lc6LOuNOA9M7XT1BIKpZytnSVtwZWkMmu23OLMM/d07tPJYtHIa92On7XKBPc2 +6THbQsJpR5AalTVvXs3X1RnCLnHiNYg= +-----END PRIVATE KEY----- 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 9f99df93a..93e7d85c6 100644 --- a/tests/stdlib/thttpcore.nim +++ b/tests/stdlib/thttpcore.nim @@ -1,28 +1,103 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" -import unittest +import httpcore, strutils +import std/assertions -import httpcore +block: + block HttpCode: + doAssert $Http418 == "418 I'm a teapot" + doAssert Http418.is4xx() == true + doAssert Http418.is2xx() == false -suite "httpcore": - - test "HttpCode": - assert $Http418 == "418 I'm a teapot" - assert Http418.is4xx() == true - assert 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"}) + + 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 new file mode 100644 index 000000000..191ef117e --- /dev/null +++ b/tests/stdlib/tintsets.nim @@ -0,0 +1,6 @@ +import ./mintsets + +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 index 28e1881e8..80a119763 100644 --- a/tests/stdlib/tio.nim +++ b/tests/stdlib/tio.nim @@ -1,40 +1,60 @@ discard """ - output: '''9 -b = false -123456789 -Second readLine raised an exception -123456789 -''' + matrix: "--mm:refc; --mm:orc" """ -# bug #5349 -import os -# test the file-IO - -const fn = "file9char.txt" - -writeFile(fn, "123456789") - -var f = open(fn) -echo getFileSize(f) - -var line = newString(10) -try: - let b = readLine(f, line) - echo "b = ", b -except: - echo "First readLine raised an exception" - -echo line - -try: - line = readLine(f) - let b = readLine(f, line) - echo "b = ", b -except: - echo "Second readLine raised an exception" - -echo line -f.close() - -removeFile(fn) +# 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 new file mode 100644 index 000000000..e425501f6 --- /dev/null +++ b/tests/stdlib/tjson.nim @@ -0,0 +1,382 @@ +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] +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 +doAssert(testJson{"doesnt_exist"}{"anything"}.isNil) +testJson{["e", "f"]} = %true +doAssert(testJson["e"]["f"].bval) + +# make sure UTF-16 decoding works. +doAssert(testJson["c"].str == "🎃") +doAssert(testJson["d"].str == "æ") + +# make sure no memory leek when parsing invalid string +let startMemory = getOccupiedMem() +for i in 0 .. 10000: + try: + discard parseJson"""{ invalid""" + except: + discard +# memory diff should less than 4M +doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) + + +# test `$` +let stringified = $testJson +let parsedAgain = parseJson(stringified) +doAssert(parsedAgain["b"].str == "asd") + +parsedAgain["abc"] = %5 +doAssert parsedAgain["abc"].num == 5 + +# Bounds checking +when compileOption("boundChecks"): + try: + let a = testJson["a"][9] + doAssert(false, "IndexDefect not thrown") + except IndexDefect: + discard + try: + let a = testJson["a"][-1] + doAssert(false, "IndexDefect not thrown") + except IndexDefect: + discard + try: + doAssert(testJson["a"][0].num == 1, "Index doesn't correspond to its value") + except: + doAssert(false, "IndexDefect thrown for valid index") + +doAssert(testJson{"b"}.getStr() == "asd", "Couldn't fetch a singly nested key with {}") +doAssert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil") +doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") +doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") +doAssert(testJson{"a"} == parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found") +doAssert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil") + +# Generator: +var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}] +doAssert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + +var j2 = %* + [ + { + "name": "John", + "age": 30 + }, + { + "name": "Susan", + "age": 31 + } + ] +doAssert j2 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + +var name = "John" +let herAge = 30 +const hisAge = 31 + +var j3 = %* + [ {"name": "John" + , "age": herAge + } + , {"name": "Susan" + , "age": hisAge + } + ] +doAssert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + +var j4 = %*{"test": nil} +doAssert j4 == %{"test": newJNull()} + +let seqOfNodes = @[%1, %2] +let jSeqOfNodes = %seqOfNodes +doAssert(jSeqOfNodes[1].num == 2) + +type MyObj = object + a, b: int + s: string + f32: float32 + f64: float64 + next: ref MyObj +var m: MyObj +m.s = "hi" +m.a = 5 +let jMyObj = %m +doAssert(jMyObj["a"].num == 5) +doAssert(jMyObj["s"].str == "hi") + +# Test loading of file. +when not defined(js): + var parsed = parseFile("tests/testdata/jsontest.json") + + try: + discard parsed["key2"][12123] + doAssert(false) + except IndexDefect: doAssert(true) + + var parsed2 = parseFile("tests/testdata/jsontest2.json") + doAssert(parsed2{"repository", "description"}.str == + "IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}") + +doAssert escapeJsonUnquoted("\10Foo🎃barÄ") == "\\nFoo🎃barÄ" +doAssert escapeJsonUnquoted("\0\7\20") == "\\u0000\\u0007\\u0014" # for #7887 +doAssert escapeJson("\10Foo🎃barÄ") == "\"\\nFoo🎃barÄ\"" +doAssert escapeJson("\0\7\20") == "\"\\u0000\\u0007\\u0014\"" # for #7887 + +# Test with extra data +when not defined(js): + try: + discard parseJson("123 456") + doAssert(false) + except JsonParsingError: + doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected]) + + try: + discard parseFile("tests/testdata/jsonwithextradata.json") + doAssert(false) + except JsonParsingError: + doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected]) + +# bug #6438 +doAssert($ %*[] == "[]") +doAssert($ %*{} == "{}") + +doAssert(not compiles(%{"error": "No messages"})) + +# bug #9111 +block: + type + Bar = string + Foo = object + a: int + b: Bar + + let + js = """{"a": 123, "b": "abc"}""".parseJson + foo = js.to Foo + + doAssert(foo.b == "abc") + +# Generate constructors for range[T] types +block: + type + Q1 = range[0'u8 .. 50'u8] + Q2 = range[0'u16 .. 50'u16] + Q3 = range[0'u32 .. 50'u32] + Q4 = range[0'i8 .. 50'i8] + Q5 = range[0'i16 .. 50'i16] + Q6 = range[0'i32 .. 50'i32] + Q7 = range[0'f32 .. 50'f32] + Q8 = range[0'f64 .. 50'f64] + Q9 = range[0 .. 50] + + X = object + m1: Q1 + m2: Q2 + m3: Q3 + m4: Q4 + m5: Q5 + m6: Q6 + m7: Q7 + m8: Q8 + m9: Q9 + + let obj = X( + m1: Q1(42), + m2: Q2(42), + m3: Q3(42), + m4: Q4(42), + m5: Q5(42), + m6: Q6(42), + m7: Q7(42), + m8: Q8(42), + m9: Q9(42) + ) + + doAssert(obj == to(%obj, type(obj))) + + when not defined(js): + const fragments = """[1,2,3] {"hi":3} 12 [] """ + var res = "" + for x in parseJsonFragments(newStringStream(fragments)): + res.add($x) + res.add " " + doAssert res == fragments + + +# test isRefSkipDistinct +type + MyRef = ref object + MyObject = object + MyDistinct = distinct MyRef + MyOtherDistinct = distinct MyRef + +var x0: ref int +var x1: MyRef +var x2: MyObject +var x3: MyDistinct +var x4: MyOtherDistinct + +doAssert isRefSkipDistinct(x0) +doAssert isRefSkipDistinct(x1) +doAssert not isRefSkipDistinct(x2) +doAssert isRefSkipDistinct(x3) +doAssert isRefSkipDistinct(x4) + + +doAssert isRefSkipDistinct(ref int) +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/tjsonexternproc.nim b/tests/stdlib/tjsonexternproc.nim index ec90e580d..1091d72cd 100644 --- a/tests/stdlib/tjsonexternproc.nim +++ b/tests/stdlib/tjsonexternproc.nim @@ -1,5 +1,11 @@ +discard """ +output: ''' +{"data":[1]} +''' +""" + # Test case for https://github.com/nim-lang/Nim/issues/6385 import mjsonexternproc # import json -foo(1) \ No newline at end of file +foo(1) diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index f13d2e5cb..5a1b4b294 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -1,28 +1,45 @@ discard """ - file: "tjsonmacro.nim" output: "" + matrix: "--mm:refc; --mm:orc" + targets: "c js" """ -import json, strutils, options, tables -when isMainModule: +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. +type + Pix = tuple[x, y: uint8, ch: uint16] +proc `%`(p: Pix): JsonNode = + result = %* { "x" : % p.x, + "y" : % p.y, + "ch" : % p.ch } + +proc testJson() = # Tests inspired by own use case (with some additional tests). # This should succeed. type Point[T] = object x, y: T - ReplayEventKind* = enum + ReplayEventKind = enum FoodAppeared, FoodEaten, DirectionChanged - ReplayEvent* = object + ReplayEvent = object time*: float case kind*: ReplayEventKind of FoodAppeared, FoodEaten: foodPos*: Point[float] + case subKind*: bool + of true: + it: int + of false: + ot: float of DirectionChanged: playerPos*: float - Replay* = ref object + Replay = ref object events*: seq[ReplayEvent] test: int test2: string @@ -34,13 +51,15 @@ when isMainModule: ReplayEvent( time: 1.2345, kind: FoodEaten, - foodPos: Point[float](x: 5.0, y: 1.0) + foodPos: Point[float](x: 5.0, y: 1.0), + subKind: true, + it: 7 ) ], test: 18827361, test2: "hello world", test3: true, - testNil: nil + testNil: "nil" ) let node = %x @@ -53,7 +72,7 @@ when isMainModule: doAssert y.test == 18827361 doAssert y.test2 == "hello world" doAssert y.test3 - doAssert y.testNil.isNil + doAssert y.testNil == "nil" # Test for custom object variants (without an enum) and with an else branch. block: @@ -91,7 +110,7 @@ when isMainModule: doAssert result.other == node["other"].getBiggestInt() # TODO: Test object variant with set in of branch. - # TODO: Should we support heterogenous arrays? + # TODO: Should we support heterogeneous arrays? # Tests that verify the error messages for invalid data. block: @@ -242,7 +261,7 @@ when isMainModule: 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) @@ -278,24 +297,26 @@ when isMainModule: doAssert parsed.color == Red block: - type - Car = object - engine: tuple[name: string, capacity: float] - model: string + when not defined(js): + # disable on js because of #12492 + type + Car = object + engine: tuple[name: string, capacity: float] + model: string - let j = """ - {"engine": {"name": "V8", "capacity": 5.5}, "model": "Skyline"} - """ + let j = """ + {"engine": {"name": "V8", "capacity": 5.5}, "model": "Skyline"} + """ - var i = 0 - proc mulTest: JsonNode = - i.inc() - return parseJson(j) + var i = 0 + proc mulTest(): JsonNode = + inc i + return parseJson(j) - let parsed = mulTest().to(Car) - doAssert parsed.engine.name == "V8" + let parsed = mulTest().to(Car) + doAssert parsed.engine.name == "V8" - doAssert i == 1 + doAssert i == 1 block: # Option[T] support! @@ -337,7 +358,7 @@ when isMainModule: n2: Option[int] n3: Option[string] n4: Option[bool] - + var j0 = parseJson("""{"n1": 1, "n2": null, "n3": null, "n4": null}""") let j0Deser = j0.to(Obj) doAssert j0Deser.n1 == 1 @@ -411,10 +432,219 @@ when isMainModule: doAssert dataDeser.a == 1 doAssert dataDeser.f == 6 doAssert dataDeser.i == 9.9'f32 - + # deserialize directly into a table block: let s = """{"a": 1, "b": 2}""" let t = parseJson(s).to(Table[string, int]) doAssert t["a"] == 1 - doAssert t["b"] == 2 \ No newline at end of file + doAssert t["b"] == 2 + + block: + # bug #8037 + type + Apple = distinct string + String = distinct Apple + Email = distinct string + MyList = distinct seq[int] + MyYear = distinct Option[int] + MyTable = distinct Table[string, int] + MyArr = distinct array[3, float] + MyRef = ref object + name: string + MyObj = object + color: int + MyDistRef = distinct MyRef + MyDistObj = distinct MyObj + Toot = object + name*: String + email*: Email + list: MyList + year: MyYear + dict: MyTable + arr: MyArr + person: MyDistRef + distfruit: MyDistObj + dog: MyRef + fruit: MyObj + emails: seq[String] + + var tJson = parseJson(""" + { + "name":"Bongo", + "email":"bongo@bingo.com", + "list": [11,7,15], + "year": 1975, + "dict": {"a": 1, "b": 2}, + "arr": [1.0, 2.0, 7.0], + "person": {"name": "boney"}, + "dog": {"name": "honey"}, + "fruit": {"color": 10}, + "distfruit": {"color": 11}, + "emails": ["abc", "123"] + } + """) + + var t = to(tJson, Toot) + doAssert string(t.name) == "Bongo" + doAssert string(t.email) == "bongo@bingo.com" + doAssert seq[int](t.list) == @[11,7,15] + doAssert Option[int](t.year).get() == 1975 + doAssert Table[string,int](t.dict)["a"] == 1 + doAssert Table[string,int](t.dict)["b"] == 2 + 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 t.dog.name == "honey" + doAssert t.fruit.color == 10 + doAssert seq[string](t.emails) == @["abc", "123"] + + block test_table: + var y = parseJson("""{"a": 1, "b": 2, "c": 3}""") + var u = y.to(MyTable) + var v = y.to(Table[string, int]) + doAssert Table[string, int](u)["a"] == 1 + doAssert Table[string, int](u)["b"] == 2 + doAssert Table[string, int](u)["c"] == 3 + doAssert v["a"] == 1 + + block primitive_string: + const kApple = "apple" + var u = newJString(kApple) + var v = u.to(Email) + var w = u.to(Apple) + var x = u.to(String) + doAssert string(v) == kApple + doAssert string(w) == kApple + doAssert string(x) == kApple + + block test_option: + var u = newJInt(1137) + var v = u.to(MyYear) + var w = u.to(Option[int]) + doAssert Option[int](v).get() == 1137 + doAssert w.get() == 1137 + + block test_object: + var u = parseJson("""{"color": 987}""") + var v = u.to(MyObj) + var w = u.to(MyDistObj) + doAssert v.color == 987 + doAssert MyObj(w).color == 987 + + block test_ref_object: + var u = parseJson("""{"name": "smith"}""") + var v = u.to(MyRef) + var w = u.to(MyDistRef) + doAssert v.name == "smith" + doAssert MyRef(w).name == "smith" + + block: + # bug #12015 + type + Cluster = object + works: tuple[x, y: uint8, ch: uint16] # working + fails: Pix # previously broken + + let data = (x: 123'u8, y: 53'u8, ch: 1231'u16) + let c = Cluster(works: data, fails: data) + let cFromJson = (% c).to(Cluster) + doAssert c == cFromJson + + block: + # bug related to #12015 + type + PixInt = tuple[x, y, ch: int] + SomePix = Pix | PixInt + Cluster[T: SomePix] = seq[T] + ClusterObject[T: SomePix] = object + data: Cluster[T] + RecoEvent[T: SomePix] = object + cluster: seq[ClusterObject[T]] + + let data = @[(x: 123'u8, y: 53'u8, ch: 1231'u16)] + var c = RecoEvent[Pix](cluster: @[ClusterObject[Pix](data: data)]) + let cFromJson = (% c).to(RecoEvent[Pix]) + doAssert c == cFromJson + + + block: + # ref objects with cycles. + type + Misdirection = object + cycle: Cycle + + Cycle = ref object + foo: string + cycle: Misdirection + + let data = """ + {"cycle": null} + """ + + let dataParsed = parseJson(data) + let dataDeser = to(dataParsed, Misdirection) + + block: + # ref object from #12316 + type + Foo = ref Bar + Bar = object + + discard "null".parseJson.to Foo + + block: + # named array #12289 + type Vec = array[2, int] + let arr = "[1,2]".parseJson.to Vec + doAssert arr == [1,2] + + block: + # test error message in exception + + type + MyType = object + otherMember: string + member: MySubType + + MySubType = object + somethingElse: string + list: seq[MyData] + + MyData = object + value: int + + let jsonNode = parseJson(""" + { + "otherMember": "otherValue", + "member": { + "somethingElse": "something", + "list": [{"value": 1}, {"value": 2}, {}] + } + } + """) + + try: + let tmp = jsonNode.to(MyType) + doAssert false, "this should be unreachable" + 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() +static: + testJson() diff --git a/tests/stdlib/tjsonmacro_reject.nim b/tests/stdlib/tjsonmacro_reject.nim index 00506449f..ada365d7d 100644 --- a/tests/stdlib/tjsonmacro_reject.nim +++ b/tests/stdlib/tjsonmacro_reject.nim @@ -1,7 +1,7 @@ discard """ + errormsg: "Use a named tuple instead of: (string, float)" file: "tjsonmacro_reject.nim" line: 11 - errormsg: "Use a named tuple instead of: (string, float)" """ import json @@ -15,4 +15,4 @@ let j = """ {"engine": {"name": "V8", "capacity": 5.5}, model: "Skyline"} """ let parsed = parseJson(j) -echo(to(parsed, Car)) \ No newline at end of file +echo(to(parsed, Car)) diff --git a/tests/stdlib/tjsonmacro_reject2.nim b/tests/stdlib/tjsonmacro_reject2.nim deleted file mode 100644 index b01153553..000000000 --- a/tests/stdlib/tjsonmacro_reject2.nim +++ /dev/null @@ -1,21 +0,0 @@ -discard """ - file: "tjsonmacro_reject2.nim" - line: 10 - errormsg: "The `to` macro does not support ref objects with cycles." -""" -import json - -type - Misdirection = object - cycle: Cycle - - Cycle = ref object - foo: string - cycle: Misdirection - -let data = """ - {"cycle": null} -""" - -let dataParsed = parseJson(data) -let dataDeser = to(dataParsed, Cycle) \ No newline at end of file diff --git a/tests/stdlib/tjsontestsuite.nim b/tests/stdlib/tjsontestsuite.nim index 06f783a73..db31963fd 100644 --- a/tests/stdlib/tjsontestsuite.nim +++ b/tests/stdlib/tjsontestsuite.nim @@ -1,3 +1,7 @@ +discard """ +disabled: true +""" + ## JSON tests based on https://github.com/nst/JSONTestSuite import unittest, diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim new file mode 100644 index 000000000..9acf4c9e5 --- /dev/null +++ b/tests/stdlib/tjsonutils.nim @@ -0,0 +1,476 @@ +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, "\n" & $j & "\n" & expected + doAssert j.jsonTo(T).toJson == j + var t2: T + t2.fromJson(j) + doAssert t2.toJson == 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. + 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 + +proc `==`(a, b: Foo): bool = + a.id == b.id + +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""") + when not defined(js): + testRoundtrip(cast[pointer](12345)): """12345""" + when nimvm: + discard + # bugs: + # Error: unhandled exception: 'intVal' is not accessible using discriminant 'kind' of type 'TNode' [ + # Error: VM does not support 'cast' from tyNil to tyPointer + else: + testRoundtrip(pointer(nil)): """0""" + testRoundtrip(cast[pointer](nil)): """0""" + + # refs bug #9423 + testRoundtrip(Foo(1.5)): """1.5""" + + block: # OrderedTable + testRoundtrip({"z": "Z", "y": "Y"}.toOrderedTable): """{"z":"Z","y":"Y"}""" + 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: # 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, 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,"cstring1":"foo","cstring2":"","cstring3":null}]""" + + block: + # 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: # 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 + case t1: bool + of true: z1: int8 + of false: z2: uint16 + x1: string + testRoundtrip(Foo(t1: true, z1: 5, x1: "bar")): """{"x0":0.0,"t1":true,"z1":5,"x1":"bar"}""" + testRoundtrip(Foo(x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}""" + type PFoo = ref Foo + testRoundtrip(PFoo(x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}""" + + block: # ref case object + type Foo = ref object + x0: float + case t1: bool + of true: z1: int8 + of false: z2: uint16 + x1: string + testRoundtrip(Foo(t1: true, z1: 5, x1: "bar")): """{"x0":0.0,"t1":true,"z1":5,"x1":"bar"}""" + testRoundtrip(Foo(x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}""" + + block: # generic case object + type Foo[T] = ref object + x0: float + case t1: bool + of true: z1: int8 + of false: z2: uint16 + x1: string + testRoundtrip(Foo[float](t1: true, z1: 5, x1: "bar")): """{"x0":0.0,"t1":true,"z1":5,"x1":"bar"}""" + testRoundtrip(Foo[int](x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}""" + # sanity check: nesting inside a tuple + testRoundtrip((Foo[int](x0: 1.5, t1: false, z2: 6), "foo")): """[{"x0":1.5,"t1":false,"z2":6,"x1":""},"foo"]""" + + block: # case object: 2 discriminants, `when` branch, range discriminant + type Foo[T] = object + case t1: bool + of true: + z1: int8 + of false: + z2: uint16 + when T is float: + case t2: range[0..3] + of 0: z3: int8 + of 2,3: z4: uint16 + else: discard + testRoundtrip(Foo[float](t1: true, z1: 5, t2: 3, z4: 12)): """{"t1":true,"z1":5,"t2":3,"z4":12}""" + 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 37e73c53f..5993278c7 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -1,66 +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: TSinglyLinkedList[int] - for d in items(data): L.prepend(d) - assert($L == "[6, 5, 4, 3, 2, 1]") - 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: TSinglyLinkedList[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: TDoublyLinkedList[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: TSinglyLinkedRing[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: TDoublyLinkedRing[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 38937590f..32991ccc9 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -1,23 +1,24 @@ discard """ - output: '''{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"} -true -true -alpha 100 -omega 200 -''' + matrix: "--mm:orc; --mm:refc" """ -import marshal +import std/marshal +import std/[assertions, objectdollar, streams] -template testit(x) = discard $$to[type(x)]($$x) +# TODO: add static tests -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) +proc testit[T](x: T): string = $$to[T]($$x) + +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 @@ -48,60 +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 - +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 581308a7e..22e5f7d88 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -1,82 +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] ^ +import std/math +import std/assertions -''' -""" -import math, random, os -import unittest -import sets - -suite "random int": - test "there might be some randomness": - var set = initSet[int](128) - randomize() - for i in 1..1000: - incl(set, random(high(int))) - check len(set) == 1000 - test "single number bounds work": - randomize() - var rand: int - for i in 1..1000: - rand = random(1000) - check rand < 1000 - check rand > -1 - test "slice bounds work": - randomize() - var rand: int - for i in 1..1000: - rand = random(100..1000) - check rand < 1000 - check rand >= 100 - test "randomize() again gives new numbers": - randomize() - var rand1 = random(1000000) - os.sleep(200) - randomize() - var rand2 = random(1000000) - check rand1 != rand2 - - -suite "random float": - test "there might be some randomness": - var set = initSet[float](128) - randomize() - for i in 1..100: - incl(set, random(1.0)) - check len(set) == 100 - test "single number bounds work": - randomize() - var rand: float - for i in 1..1000: - rand = random(1000.0) - check rand < 1000.0 - check rand > -1.0 - test "slice bounds work": - randomize() - var rand: float - for i in 1..1000: - rand = random(100.0..1000.0) - check rand < 1000.0 - check rand >= 100.0 - test "randomize() again gives new numbers": - randomize() - var rand1:float = random(1000000.0) - os.sleep(200) - randomize() - var rand2:float = random(1000000.0) - check rand1 != rand2 - -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) \ No newline at end of file +# Function for approximate comparison of floats +proc `==~`(x, y: float): bool = abs(x - y) < 1e-9 + + +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/tmath2.nim b/tests/stdlib/tmath2.nim deleted file mode 100644 index eb0506f5f..000000000 --- a/tests/stdlib/tmath2.nim +++ /dev/null @@ -1,85 +0,0 @@ -# tests for the interpreter - -proc loops(a: var int) = - discard - #var - # b: int - #b = glob - #while b != 0: - # b = b + 1 - #a = b - -proc mymax(a, b: int): int = - #loops(result) - result = a - if b > a: result = b - -proc test(a, b: int) = - var - x, y: int - x = 0 - y = 7 - if x == a + b * 3 - 7 or - x == 8 or - x == y and y > -56 and y < 699: - y = 0 - elif y == 78 and x == 0: - y = 1 - elif y == 0 and x == 0: - y = 2 - else: - y = 3 - -type - TTokType = enum - tkNil, tkType, tkConst, tkVar, tkSymbol, tkIf, - tkWhile, tkFor, tkLoop, tkCase, tkLabel, tkGoto - -proc testCase(t: TTokType): int = - case t - of tkNil, tkType, tkConst: result = 0 - of tkVar: result = 1 - of tkSymbol: result = 2 - of tkIf..tkFor: result = 3 - of tkLoop: result = 56 - else: result = -1 - test(0, 9) # test the call - -proc TestLoops() = - var - i, j: int - - while i >= 0: - if i mod 3 == 0: - break - i = i + 1 - while j == 13: - j = 13 - break - break - - while true: - break - - -var - glob: int - a: array[0..5, int] - -proc main() = - #glob = 0 - #loops( glob ) - var - res: int - s: string - #write(stdout, mymax(23, 45)) - write(stdout, "Hallo! Wie heisst du? ") - s = readLine(stdin) - # test the case statement - case s - of "Andreas": write(stdout, "Du bist mein Meister!\n") - of "Rumpf": write(stdout, "Du bist in der Familie meines Meisters!\n") - else: write(stdout, "ich kenne dich nicht!\n") - write(stdout, "Du heisst " & s & "\n") - -main() diff --git a/tests/stdlib/tmemfiles1.nim b/tests/stdlib/tmemfiles1.nim index 8b66dfcc1..33657256c 100644 --- a/tests/stdlib/tmemfiles1.nim +++ b/tests/stdlib/tmemfiles1.nim @@ -1,12 +1,11 @@ -discard """ - file: "tmemfiles1.nim" -""" import memfiles, os +import std/syncio + var mm: MemFile fn = "test.mmap" # Create a new file mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20) mm.close() -mm.close() +# mm.close() if fileExists(fn): removeFile(fn) diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim index d6cfa533a..c79f85ebf 100644 --- a/tests/stdlib/tmemfiles2.nim +++ b/tests/stdlib/tmemfiles2.nim @@ -1,9 +1,12 @@ discard """ - file: "tmemfiles2.nim" + disabled: "Windows" output: '''Full read size: 20 Half read size: 10 Data: Hello''' """ import memfiles, os +import std/syncio + + const fn = "test.mmap" var @@ -12,14 +15,16 @@ 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 mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1, allowRemap = true) -echo "Full read size: ",mm_full.size +let size = mm_full.size p = mm_full.mapMem(fmReadWrite, 20, 0) +echo "Full read size: ", size var p2 = cast[cstring](p) p2[0] = 'H' p2[1] = 'e' @@ -32,7 +37,7 @@ mm_full.close() # read half, and verify data change mm_half = memfiles.open(fn, mode = fmRead, mappedSize = 10) -echo "Half read size: ",mm_half.size, " Data: ", cast[cstring](mm_half.mem) +echo "Half read size: ", mm_half.size, " Data: ", cast[cstring](mm_half.mem) mm_half.close() if fileExists(fn): removeFile(fn) diff --git a/tests/stdlib/tmemlines.nim b/tests/stdlib/tmemlines.nim index 19821ea26..98e03b5bb 100644 --- a/tests/stdlib/tmemlines.nim +++ b/tests/stdlib/tmemlines.nim @@ -1,5 +1,9 @@ +discard """ +outputsub: "" +""" + import memfiles -var inp = memfiles.open("readme.txt") +var inp = memfiles.open("tests/stdlib/tmemlines.nim") for line in lines(inp): echo("#" & line & "#") close(inp) diff --git a/tests/stdlib/tmemlinesBuf.nim b/tests/stdlib/tmemlinesBuf.nim index 21edc2322..7bd89d4f2 100644 --- a/tests/stdlib/tmemlinesBuf.nim +++ b/tests/stdlib/tmemlinesBuf.nim @@ -1,6 +1,9 @@ -import memfiles -var inp = memfiles.open("readme.txt") -var buffer: TaintedString = "" +import std/[memfiles, assertions] +var inp = memfiles.open("tests/stdlib/tmemlinesBuf.nim") +var buffer: string = "" +var lineCount = 0 for line in lines(inp, buffer): - echo("#" & line & "#") + lineCount += 1 + close(inp) +doAssert lineCount == 9, $lineCount # this file's number of lines diff --git a/tests/stdlib/tmemmapstreams.nim b/tests/stdlib/tmemmapstreams.nim index 243574f1a..9cfae62c7 100644 --- a/tests/stdlib/tmemmapstreams.nim +++ b/tests/stdlib/tmemmapstreams.nim @@ -1,6 +1,6 @@ discard """ - file: "tmemmapstreams.nim" - output: '''Created size: 10 +output: ''' +Created size: 10 Position after writing: 5 Position after writing one char: 6 Peeked data: Hello @@ -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/tmemslices.nim b/tests/stdlib/tmemslices.nim index 951807cc4..c0d6d3960 100644 --- a/tests/stdlib/tmemslices.nim +++ b/tests/stdlib/tmemslices.nim @@ -1,5 +1,11 @@ +discard """ +outputsub: "rlwuiadtrnzb" +""" + +# chatever the sub pattern it will find itself + import memfiles -var inp = memfiles.open("readme.txt") +var inp = memfiles.open("tests/stdlib/tmemslices.nim") for mem in memSlices(inp): if mem.size > 3: echo("#" & $mem & "#") 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 5792b6282..f41963f02 100644 --- a/tests/stdlib/tmget.nim +++ b/tests/stdlib/tmget.nim @@ -1,20 +1,25 @@ 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 -Can't access 6 +2 +0 10 11 -Can't access 6 +0 10 11 Can't access 6 @@ -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]() @@ -85,7 +102,7 @@ block: except KeyError: echo "Can't access 6" echo x[5] - x[5] += 1 + x.inc 5, 1 var c = x[5] echo c @@ -97,14 +114,14 @@ block: except KeyError: echo "Can't access 6" echo x[5] - x[5] += 1 + x.inc 5, 1 var c = x[5] echo c import sets block: - var x = initSet[int]() + var x = initHashSet[int]() x.incl 5 try: echo x[6] 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 index c683647bc..8242beb83 100644 --- a/tests/stdlib/tnativesockets.nim +++ b/tests/stdlib/tnativesockets.nim @@ -1,8 +1,30 @@ -import nativesockets, unittest +discard """ + matrix: "--mm:refc; --mm:orc" +""" -suite "nativesockets": - test "getHostname": - let hostname = getHostname() - check hostname.len > 0 - check hostname.len < 64 +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 d364447da..27a6ac49c 100644 --- a/tests/stdlib/tnet.nim +++ b/tests/stdlib/tnet.nim @@ -1,51 +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) @@ -54,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 @@ -66,11 +107,11 @@ block: # "IpAddress/Sockaddr conversion" doAssert(ipaddr_1 == ipaddr_2) doAssert($ipaddr_1 == $ipaddr_2) - if sockaddr.ss_family == AF_INET.toInt: + if sockaddr.ss_family.cint == AF_INET.toInt: var sockaddr4: Sockaddr_in copyMem(addr sockaddr4, addr sockaddr, sizeof(sockaddr4)) fromSockAddr(sockaddr4, socklen, ipaddr_2, port_2) - elif sockaddr.ss_family == AF_INET6.toInt: + elif sockaddr.ss_family.cint == AF_INET6.toInt: var sockaddr6: Sockaddr_in6 copyMem(addr sockaddr6, addr sockaddr, sizeof(sockaddr6)) fromSockAddr(sockaddr6, socklen, ipaddr_2, port_2) 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 new file mode 100644 index 000000000..84f9ac464 --- /dev/null +++ b/tests/stdlib/tnetbind.nim @@ -0,0 +1,25 @@ +discard """ +matrix: "--mm:refc; --mm:orc" +joinable: false +""" + +#[ +joinable: false +otherwise: +Error: unhandled exception: Address already in use [OSError] +]# + +import net + +## Test for net.bindAddr + +proc test() = + # IPv4 TCP + newSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP).bindAddr(Port(1900), "0.0.0.0") + newSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP).bindAddr(Port(1901)) + + # IPv6 TCP + newSocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP).bindAddr(Port(1902), "::") + newSocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP).bindAddr(Port(1903)) + +test() 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 695150179..a1e147ad5 100644 --- a/tests/stdlib/tnetdial.nim +++ b/tests/stdlib/tnetdial.nim @@ -2,20 +2,20 @@ 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 const port = Port(28431) proc initIPv6Server(hostname: string, port: Port): AsyncFD = - let fd = newNativeSocket(AF_INET6) + 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,10 +39,10 @@ proc testThread() {.thread.} = fd.close() proc test() = + let serverFd = initIPv6Server("::1", port) var t: Thread[void] createThread(t, testThread) - let serverFd = initIPv6Server("::1", port) var done = false serverFd.accept().callback = proc(fut: Future[AsyncFD]) = @@ -58,4 +58,5 @@ proc test() = joinThread(t) +# this would cause #13132 `for i in 0..<10000: test()` test() diff --git a/tests/stdlib/tnilecho.nim b/tests/stdlib/tnilecho.nim deleted file mode 100644 index 147b5e492..000000000 --- a/tests/stdlib/tnilecho.nim +++ /dev/null @@ -1,2 +0,0 @@ -var x = @["1", nil, "3"] -doAssert $x == "@[1, nil, 3]" diff --git a/tests/stdlib/tnre.nim b/tests/stdlib/tnre.nim index fabbb69a8..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 'tests/testament/tester' a failed check() in the test suite will cause the exit +# 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/torderedtable.nim b/tests/stdlib/torderedtable.nim deleted file mode 100644 index 91a916930..000000000 --- a/tests/stdlib/torderedtable.nim +++ /dev/null @@ -1,18 +0,0 @@ -import tables, random -var t = initOrderedTable[int,string]() - -# this tests issue #5917 -var data = newSeq[int]() -for i in 0..<1000: - var x = random(1000) - if x notin t: data.add(x) - t[x] = "meh" - -# this checks that keys are re-inserted -# in order when table is enlarged. -var i = 0 -for k, v in t: - doAssert(k == data[i]) - doAssert(v == "meh") - inc(i) - diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index e6fbb0e51..611659fdb 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -1,13 +1,5 @@ discard """ - output: '''true -true -true -true -true -true -true -true -true + output: ''' All: __really_obscure_dir_name/are.x __really_obscure_dir_name/created @@ -27,115 +19,857 @@ __really_obscure_dir_name/created __really_obscure_dir_name/dirs __really_obscure_dir_name/some __really_obscure_dir_name/test -false -false -false -false -false -false -false -false -false -true -true Raises -true -true -true -true +Raises ''' + matrix: "--mm:refc; --mm:orc" + joinable: false """ # test os path creation, iteration, and deletion -import os, strutils +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"] + let dirs = @["some", "created", "test", "dirs"] -let files = @["these.txt", "are.x", "testing.r", "files.q"] -let dirs = @["some", "created", "test", "dirs"] + let dname = "__really_obscure_dir_name" + + createDir(dname) + doAssert dirExists(dname) -let dname = "__really_obscure_dir_name" + 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) -createDir(dname) -echo dirExists(dname) + # Test creating files and dirs + for dir in dirs: + createDir(dname/dir) + doAssert dirExists(dname/dir) -# Test creating files and dirs -for dir in dirs: - createDir(dname/dir) - echo dirExists(dname/dir) + for file in files: + let fh = open(dname/file, fmReadWrite) + fh.close() + doAssert fileExists(dname/file) -for file in files: - let fh = open(dname/file, fmReadWrite) - fh.close() - echo fileExists(dname/file) + echo "All:" -echo "All:" + template norm(x): untyped = + (when defined(windows): x.replace('\\', '/') else: x) -template norm(x): untyped = - (when defined(windows): x.replace('\\', '/') else: x) + for path in walkPattern(dname/"*"): + echo path.norm -for path in walkPattern(dname/"*"): - echo path.norm + echo "Files:" -echo "Files:" + for path in walkFiles(dname/"*"): + echo path.norm -for path in walkFiles(dname/"*"): - echo path.norm + echo "Dirs:" -echo "Dirs:" + for path in walkDirs(dname/"*"): + echo path.norm -for path in walkDirs(dname/"*"): - echo path.norm + # Test removal of files dirs + for dir in dirs: + removeDir(dname/dir) + doAssert: not dirExists(dname/dir) -# Test removal of files dirs -for dir in dirs: - removeDir(dname/dir) - echo dirExists(dname/dir) + for file in files: + removeFile(dname/file) + doAssert: not fileExists(dname/file) -for file in files: - removeFile(dname/file) - echo fileExists(dname/file) + removeDir(dname) + doAssert: not dirExists(dname) -removeDir(dname) -echo dirExists(dname) + # createDir should create recursive directories + createDir(dirs[0] / dirs[1]) + doAssert dirExists(dirs[0] / dirs[1]) # true + removeDir(dirs[0]) -# createDir should create recursive directories -createDir(dirs[0] / dirs[1]) -echo dirExists(dirs[0] / dirs[1]) # true -removeDir(dirs[0]) + # createDir should properly handle trailing separator + createDir(dname / "") + doAssert dirExists(dname) # true + removeDir(dname) -# createDir should properly handle trailing separator -createDir(dname / "") -echo dirExists(dname) # true -removeDir(dname) + # createDir should raise IOError if the path exists + # and is not a directory + open(dname, fmWrite).close + try: + createDir(dname) + except IOError: + echo "Raises" + removeFile(dname) -# createDir should raise IOError if the path exists -# and is not a directory -open(dname, fmWrite).close -try: + # removeFile should not remove directory createDir(dname) -except IOError: - echo "Raises" -removeFile(dname) + try: + removeFile(dname) + except OSError: + echo "Raises" + removeDir(dname) + + # test copyDir: + createDir("a/b") + open("a/b/file.txt", fmWrite).close + createDir("a/b/c") + open("a/b/c/fileC.txt", fmWrite).close + + copyDir("a", "../dest/a") + removeDir("a") + + doAssert dirExists("../dest/a/b") + doAssert fileExists("../dest/a/b/file.txt") + + doAssert fileExists("../dest/a/b/c/fileC.txt") + removeDir("../dest") + + # test copyDir: + # if separator at the end of a path + createDir("a/b") + open("a/file.txt", fmWrite).close + + copyDir("a/", "../dest/a/") + removeDir("a") + + doAssert dirExists("../dest/a/b") + 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) -# test copyDir: -createDir("a/b") -open("a/b/file.txt", fmWrite).close -createDir("a/b/c") -open("a/b/c/fileC.txt", fmWrite).close +block: # moveFile + let tempDir = getTempDir() / "D20210609T151608" + createDir(tempDir) + defer: removeDir(tempDir) -copyDir("a", "../dest/a") -removeDir("a") + 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") -echo dirExists("../dest/a/b") -echo fileExists("../dest/a/b/file.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") -echo fileExists("../dest/a/b/c/fileC.txt") -removeDir("../dest") + 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") -# Test get/set modification times -# Should support at least microsecond resolution import times -let tm = fromUnix(0) + 100.microseconds -writeFile("a", "") -setLastModificationTime("a", tm) -echo getLastModificationTime("a") == tm -removeFile("a") \ No newline at end of file +block modificationTime: + # Test get/set modification times + # Should support at least microsecond resolution + let tm = fromUnix(0) + 100.microseconds + writeFile("a", "") + setLastModificationTime("a", tm) + + when defined(macosx): + doAssert true + else: + doAssert getLastModificationTime("a") == tm + removeFile("a") + +block walkDirRec: + createDir("walkdir_test/a/b") + open("walkdir_test/a/b/file_1", fmWrite).close() + open("walkdir_test/a/file_2", fmWrite).close() + + for p in walkDirRec("walkdir_test"): + doAssert p.fileExists + doAssert p.startsWith("walkdir_test") + + var s: seq[string] + for p in walkDirRec("walkdir_test", {pcFile}, {pcDir}, relative = true): + s.add(p) + + doAssert s.len == 2 + doAssert "a" / "b" / "file_1" in s + doAssert "a" / "file_2" in s + + removeDir("walkdir_test") + +import std/sequtils + +block: # walkDir + doAssertRaises(OSError): + for a in walkDir("nonexistent", checkDir = true): discard + doAssertRaises(OSError): + for p in walkDirRec("nonexistent", checkDir = true): discard + + when not defined(windows): + block walkDirRelative: + createDir("walkdir_test") + createSymlink(".", "walkdir_test/c") + for k, p in walkDir("walkdir_test", true): + 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: + doAssert normalizedPath(".") == "." + doAssert normalizedPath("foo/..") == "." + doAssert normalizedPath("foo//../bar/.") == "bar" + doAssert normalizedPath("..") == ".." + doAssert normalizedPath("../") == ".." + doAssert normalizedPath("../..") == unixToNativePath"../.." + doAssert normalizedPath("../a/..") == ".." + doAssert normalizedPath("../a/../") == ".." + doAssert normalizedPath("./") == "." + + block absolute: + doAssert normalizedPath("/") == unixToNativePath"/" + doAssert normalizedPath("/.") == unixToNativePath"/" + doAssert normalizedPath("/..") == unixToNativePath"/.." + doAssert normalizedPath("/../") == unixToNativePath"/.." + doAssert normalizedPath("/../..") == unixToNativePath"/../.." + doAssert normalizedPath("/../../") == unixToNativePath"/../.." + doAssert normalizedPath("/../../../") == unixToNativePath"/../../.." + doAssert normalizedPath("/a/b/../../foo") == unixToNativePath"/foo" + doAssert normalizedPath("/a/b/../../../foo") == unixToNativePath"/../foo" + doAssert normalizedPath("/./") == unixToNativePath"/" + doAssert normalizedPath("//") == unixToNativePath"/" + doAssert normalizedPath("///") == unixToNativePath"/" + doAssert normalizedPath("/a//b") == unixToNativePath"/a/b" + doAssert normalizedPath("/a///b") == unixToNativePath"/a/b" + doAssert normalizedPath("/a/b/c/..") == unixToNativePath"/a/b" + doAssert normalizedPath("/a/b/c/../") == unixToNativePath"/a/b" + +block isHidden: + when defined(posix): + doAssert ".foo.txt".isHidden + doAssert "bar/.foo.ext".isHidden + doAssert not "bar".isHidden + doAssert not "foo/".isHidden + doAssert ".foo/.".isHidden + # Corner cases: `isHidden` is not yet `..` aware + doAssert not ".foo/..".isHidden + +block absolutePath: + doAssertRaises(ValueError): discard absolutePath("a", "b") + doAssert absolutePath("a") == getCurrentDir() / "a" + doAssert absolutePath("a", "/b") == "/b" / "a" + when defined(posix): + doAssert absolutePath("a", "/b/") == "/b" / "a" + doAssert absolutePath("a", "/b/c") == "/b/c" / "a" + doAssert absolutePath("/a", "b/") == "/a" + +block splitFile: + doAssert splitFile("") == ("", "", "") + doAssert splitFile("abc/") == ("abc", "", "") + doAssert splitFile("/") == ("/", "", "") + doAssert splitFile("./abc") == (".", "abc", "") + doAssert splitFile(".txt") == ("", ".txt", "") + doAssert splitFile("abc/.txt") == ("abc", ".txt", "") + doAssert splitFile("abc") == ("", "abc", "") + doAssert splitFile("abc.txt") == ("", "abc", ".txt") + doAssert splitFile("/abc.txt") == ("/", "abc", ".txt") + doAssert splitFile("/foo/abc.txt") == ("/foo", "abc", ".txt") + doAssert splitFile("/foo/abc.txt.gz") == ("/foo", "abc.txt", ".gz") + doAssert splitFile(".") == ("", ".", "") + doAssert splitFile("abc/.") == ("abc", ".", "") + doAssert splitFile("..") == ("", "..", "") + doAssert splitFile("a/..") == ("a", "..", "") + doAssert splitFile("/foo/abc....txt") == ("/foo", "abc...", ".txt") + +# execShellCmd is tested in tosproc + +block ospaths: + doAssert unixToNativePath("") == "" + doAssert unixToNativePath(".") == $CurDir + doAssert unixToNativePath("..") == $ParDir + doAssert isAbsolute(unixToNativePath("/")) + doAssert isAbsolute(unixToNativePath("/", "a")) + doAssert isAbsolute(unixToNativePath("/a")) + doAssert isAbsolute(unixToNativePath("/a", "a")) + doAssert isAbsolute(unixToNativePath("/a/b")) + doAssert isAbsolute(unixToNativePath("/a/b", "a")) + doAssert unixToNativePath("a/b") == joinPath("a", "b") + + when defined(macos): + doAssert unixToNativePath("./") == ":" + doAssert unixToNativePath("./abc") == ":abc" + doAssert unixToNativePath("../abc") == "::abc" + doAssert unixToNativePath("../../abc") == ":::abc" + doAssert unixToNativePath("/abc", "a") == "abc" + doAssert unixToNativePath("/abc/def", "a") == "abc:def" + elif doslikeFileSystem: + doAssert unixToNativePath("./") == ".\\" + doAssert unixToNativePath("./abc") == ".\\abc" + doAssert unixToNativePath("../abc") == "..\\abc" + doAssert unixToNativePath("../../abc") == "..\\..\\abc" + doAssert unixToNativePath("/abc", "a") == "a:\\abc" + doAssert unixToNativePath("/abc/def", "a") == "a:\\abc\\def" + else: + #Tests for unix + doAssert unixToNativePath("./") == "./" + doAssert unixToNativePath("./abc") == "./abc" + doAssert unixToNativePath("../abc") == "../abc" + doAssert unixToNativePath("../../abc") == "../../abc" + doAssert unixToNativePath("/abc", "a") == "/abc" + doAssert unixToNativePath("/abc/def", "a") == "/abc/def" + + block extractFilenameTest: + doAssert extractFilename("") == "" + when defined(posix): + doAssert extractFilename("foo/bar") == "bar" + doAssert extractFilename("foo/bar.txt") == "bar.txt" + doAssert extractFilename("foo/") == "" + doAssert extractFilename("/") == "" + when doslikeFileSystem: + doAssert extractFilename(r"foo\bar") == "bar" + doAssert extractFilename(r"foo\bar.txt") == "bar.txt" + doAssert extractFilename(r"foo\") == "" + doAssert extractFilename(r"C:\") == "" + + block lastPathPartTest: + doAssert lastPathPart("") == "" + when defined(posix): + doAssert lastPathPart("foo/bar.txt") == "bar.txt" + doAssert lastPathPart("foo/") == "foo" + doAssert lastPathPart("/") == "" + when doslikeFileSystem: + doAssert lastPathPart(r"foo\bar.txt") == "bar.txt" + doAssert lastPathPart(r"foo\") == "foo" + + template canon(x): untyped = normalizePath(x, '/') + doAssert canon"/foo/../bar" == "/bar" + doAssert canon"foo/../bar" == "bar" + + doAssert canon"/f/../bar///" == "/bar" + doAssert canon"f/..////bar" == "bar" + + doAssert canon"../bar" == "../bar" + doAssert canon"/../bar" == "/../bar" + + doAssert canon("foo/../../bar/") == "../bar" + doAssert canon("./bla/blob/") == "bla/blob" + doAssert canon(".hiddenFile") == ".hiddenFile" + doAssert canon("./bla/../../blob/./zoo.nim") == "../blob/zoo.nim" + + doAssert canon("C:/file/to/this/long") == "C:/file/to/this/long" + doAssert canon("") == "" + doAssert canon("foobar") == "foobar" + doAssert canon("f/////////") == "f" + + doAssert relativePath("/foo/bar//baz.nim", "/foo", '/') == "bar/baz.nim" + doAssert normalizePath("./foo//bar/../baz", '/') == "foo/baz" + + 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" + + # `//` 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" + doAssert relativePath("/foo", "/Foo", '/') == (when FileSystemCaseSensitive: "../foo" else: ".") + doAssert relativePath("/Foo", "/foo", '/') == (when FileSystemCaseSensitive: "../Foo" else: ".") + doAssert relativePath("/foo", "/fOO", '/') == (when FileSystemCaseSensitive: "../foo" else: ".") + doAssert relativePath("/foO", "/foo", '/') == (when FileSystemCaseSensitive: "../foO" else: ".") + + doAssert relativePath("foo", ".", '/') == "foo" + doAssert relativePath(".", ".", '/') == "." + doAssert relativePath("..", ".", '/') == ".." + + doAssert relativePath("foo", "foo") == "." + doAssert relativePath("", "foo") == "" + doAssert relativePath("././/foo", "foo//./") == "." + + doAssert relativePath(getCurrentDir() / "bar", "foo") == "../bar".unixToNativePath + doAssert relativePath("bar", getCurrentDir() / "foo") == "../bar".unixToNativePath + + when doslikeFileSystem: + doAssert relativePath(r"c:\foo.nim", r"C:\") == r"foo.nim" + doAssert relativePath(r"c:\foo\bar\baz.nim", r"c:\foo") == r"bar\baz.nim" + doAssert relativePath(r"c:\foo\bar\baz.nim", r"d:\foo") == r"c:\foo\bar\baz.nim" + doAssert relativePath(r"\foo\baz.nim", r"\foo") == r"baz.nim" + doAssert relativePath(r"\foo\bar\baz.nim", r"\bar") == r"..\foo\bar\baz.nim" + doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foo\bar") == r"baz.nim" + doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foO\bar") == r"baz.nim" + doAssert relativePath(r"\\foo\bar\baz.nim", r"\\bar\bar") == r"\\foo\bar\baz.nim" + doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foo\car") == r"\\foo\bar\baz.nim" + doAssert relativePath(r"\\foo\bar\baz.nim", r"\\goo\bar") == r"\\foo\bar\baz.nim" + doAssert relativePath(r"\\foo\bar\baz.nim", r"c:\") == r"\\foo\bar\baz.nim" + doAssert relativePath(r"\\foo\bar\baz.nim", r"\foo") == r"\\foo\bar\baz.nim" + doAssert relativePath(r"c:\foo.nim", r"\foo") == r"c:\foo.nim" + + doAssert joinPath("usr", "") == unixToNativePath"usr" + doAssert joinPath("", "lib") == "lib" + doAssert joinPath("", "/lib") == unixToNativePath"/lib" + doAssert joinPath("usr/", "/lib") == unixToNativePath"usr/lib" + doAssert joinPath("", "") == unixToNativePath"" # issue #13455 + doAssert joinPath("", "/") == unixToNativePath"/" + doAssert joinPath("/", "/") == unixToNativePath"/" + 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("zook/.", "abc") == unixToNativePath"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(".", "/lib") == unixToNativePath"./lib" + doAssert joinPath(".", "abc") == unixToNativePath"./abc" + + # cases related to issue #13455 + doAssert joinPath("foo", "", "") == "foo" + doAssert joinPath("foo", "") == "foo" + doAssert joinPath("foo/", "") == unixToNativePath"foo/" + doAssert joinPath("foo/", ".") == "foo" + doAssert joinPath("foo", "./") == unixToNativePath"foo/" + doAssert joinPath("foo", "", "bar/") == unixToNativePath"foo/bar/" + + # issue #13579 + doAssert joinPath("/foo", "../a") == unixToNativePath"/a" + doAssert joinPath("/foo/", "../a") == unixToNativePath"/a" + doAssert joinPath("/foo/.", "../a") == unixToNativePath"/a" + doAssert joinPath("/foo/.b", "../a") == unixToNativePath"/foo/a" + doAssert joinPath("/foo///", "..//a/") == unixToNativePath"/a/" + doAssert joinPath("foo/", "../a") == unixToNativePath"a" + + when doslikeFileSystem: + doAssert joinPath("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\", "..\\..\\VC\\vcvarsall.bat") == r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" + doAssert joinPath("C:\\foo", "..\\a") == r"C:\a" + doAssert joinPath("C:\\foo\\", "..\\a") == r"C:\a" + +block getTempDir: + block TMPDIR: + # TMPDIR env var is not used if either of these are defined. + when not (defined(tempDir) or defined(windows) or defined(android)): + if existsEnv("TMPDIR"): + let origTmpDir = getEnv("TMPDIR") + putEnv("TMPDIR", "/mytmp") + doAssert getTempDir() == "/mytmp/" + delEnv("TMPDIR") + doAssert getTempDir() == "/tmp/" + putEnv("TMPDIR", origTmpDir) + else: + doAssert getTempDir() == "/tmp/" + +block: # getCacheDir + doAssert getCacheDir().dirExists + +block isRelativeTo: + doAssert isRelativeTo("/foo", "/") + doAssert isRelativeTo("/foo/bar", "/foo") + doAssert isRelativeTo("foo/bar", "foo") + doAssert isRelativeTo("/foo/bar.nim", "/foo/bar.nim") + doAssert isRelativeTo("./foo/", "foo") + doAssert isRelativeTo("foo", "./foo/") + doAssert isRelativeTo(".", ".") + doAssert isRelativeTo("foo/bar", ".") + doAssert not isRelativeTo("foo/bar.nims", "foo/bar.nim") + doAssert not isRelativeTo("/foo2", "/foo") + +block: # quoteShellWindows + doAssert quoteShellWindows("aaa") == "aaa" + doAssert quoteShellWindows("aaa\"") == "aaa\\\"" + doAssert quoteShellWindows("") == "\"\"" + +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): + doAssert quoteShell("") == "''" + +block: # normalizePathEnd + # handle edge cases correctly: shouldn't affect whether path is + # absolute/relative + doAssert "".normalizePathEnd(true) == "" + doAssert "".normalizePathEnd(false) == "" + doAssert "/".normalizePathEnd(true) == $DirSep + doAssert "/".normalizePathEnd(false) == $DirSep + + when defined(posix): + doAssert "//".normalizePathEnd(false) == "/" + doAssert "foo.bar//".normalizePathEnd == "foo.bar" + doAssert "bar//".normalizePathEnd(trailingSep = true) == "bar/" + 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, + # but this is simplest. + doAssert r"D:\".normalizePathEnd == r"D:" + doAssert r"E:/".normalizePathEnd(trailingSep = true) == r"E:\" + doAssert "/".normalizePathEnd == r"\" + + +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) + doAssert not isValidFilename("con") + doAssert not isValidFilename("aux") + doAssert not isValidFilename("prn") + doAssert not isValidFilename("OwO|UwU") + doAssert not isValidFilename(" foo") + doAssert not isValidFilename("foo ") + doAssert not isValidFilename("foo.") + doAssert not isValidFilename("con.txt") + doAssert not isValidFilename("aux.bat") + doAssert not isValidFilename("prn.exe") + doAssert not isValidFilename("nim>.nim") + doAssert not isValidFilename(" foo.log") + # Positive Tests. + doAssert isValidFilename("abcd", maxLen = 42.Positive) + doAssert isValidFilename("c0n") + doAssert isValidFilename("foo.aux") + doAssert isValidFilename("bar.prn") + doAssert isValidFilename("OwO_UwU") + doAssert isValidFilename("cron") + doAssert isValidFilename("ux.bat") + doAssert isValidFilename("nim.nim") + doAssert isValidFilename("foo.log") + +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 + + 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 new file mode 100644 index 000000000..194deeb42 --- /dev/null +++ b/tests/stdlib/tos_unc.nim @@ -0,0 +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 new file mode 100644 index 000000000..da4f6252d --- /dev/null +++ b/tests/stdlib/tosproc.nim @@ -0,0 +1,316 @@ +discard """ +matrix: "--mm:refc; --mm:orc" +joinable: false +""" + +#[ +joinable: false +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: cint): void {.importc: "_exit", header: "<unistd.h>".} + import os + var a = 0 + proc fun(b = 0) = + a.inc + if a mod 10000000 == 0: # prevents optimizing it away + echo a + fun(b+1) + + proc main() = + let args = commandLineParams() + echo (msg: "child binary", pid: getCurrentProcessId()) + let arg = args[0] + echo (arg: arg) + 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": + if true: c_exit2(139) + of "quit_139": + # `exitStatusLikeShell` doesn't distinguish between a process that + # exit(139) and a process that gets killed with `SIGSEGV` because + # 139 = 11 + 128 = SIGSEGV + 128. + # However, as #10249 shows, this leads to bad debugging experience + # when a child process dies with SIGSEGV, leaving no trace of why it + # failed. The shell (and lldb debugger) solves that by inserting a + # helpful msg: `segmentation fault` when it detects a signal killed + # the child. + # todo: expose an API that will show more diagnostic, returning + # (exitCode, signal) instead of just `shellExitCode`. + if true: quit(139) + of "exit_recursion": # stack overflow by infinite recursion + fun() + echo a + of "exit_array": # bad array access + echo args[1] + main() + +elif defined(case_testfile2): + import strutils + let x = stdin.readLine() + echo x.parseInt + 5 + +elif defined(case_testfile3): + echo "start ta_out" + stdout.writeLine("to stdout") + stdout.flushFile() + stdout.writeLine("to stdout") + stdout.flushFile() + + stderr.writeLine("to stderr") + stderr.flushFile() + stderr.writeLine("to stderr") + stderr.flushFile() + + stdout.writeLine("to stdout") + stdout.flushFile() + stdout.writeLine("to stdout") + stdout.flushFile() + echo "end ta_out" + +elif defined(case_testfile4): + import system # we could remove that + quit(QuitFailure) + +else: # main driver + import stdtest/[specialpaths, unittest_light] + import os, osproc, strutils + const nim = getCurrentCompilerExe() + const sourcePath = currentSourcePath() + let dir = getCurrentDir() / "tests" / "osproc" + + template deferring(cleanup, body) = + try: body + finally: cleanup + + # we're testing `execShellCmd` so don't rely on it to compile test file + # note: this should be exported in posix.nim + proc c_system(cmd: cstring): cint {.importc: "system", header: "<stdlib.h>".} + + proc compileNimProg(opt: string, name: string): string = + result = buildDir / name.addFileExt(ExeExt) + let cmd = "$# c -o:$# --hints:off $# $#" % [nim.quoteShell, result.quoteShell, opt, sourcePath.quoteShell] + doAssert c_system(cmd) == 0, $cmd + doAssert result.fileExists + + block execShellCmdTest: + let output = compileNimProg("-d:release -d:case_testfile", "D20190111T024543") + + ## use it + template runTest(arg: string, expected: int) = + echo (arg2: arg, expected2: expected) + assertEquals execShellCmd(output & " " & arg), expected + + runTest("exit_0", 0) + runTest("exitnow_139", 139) + runTest("c_exit2_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 + let (_, err) = execCmdEx(nim & " c " & quoteShell(dir / "osproctest.nim")) + doAssert err == 0 + let exePath = dir / addFileExt("osproctest", ExeExt) + let outStr1 = execProcess(exePath, workingDir = dir, args = ["foo", + "b A r"], options = {}) + doAssert outStr1 == dir & "\nfoo\nb A r\n" + + const testDir = "t e st" + createDir(testDir) + doAssert dirExists(testDir) + let outStr2 = execProcess(exePath, workingDir = testDir, args = ["x yz"], + options = {}) + 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 + + 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: 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 = ("", -1) + var line = newStringOfCap(120) + while true: + if outp.readLine(line): + result[0].add(line) + result[0].add("\n") + else: + result[1] = peekExitCode(p) + if result[1] != -1: break + close(p) + + var result = startProcessTest("nim r --hints:off -", options = {}, input = "echo 3*4") + doAssert result == ("12\n", 0) + + block: # startProcess stdin (replaces old test `tstdin` + `ta_in`) + let output = compileNimProg("-d:case_testfile2", "D20200626T215919") + var p = startProcess(output, dir) # dir not needed though + p.inputStream.write("5\n") + p.inputStream.flush() + var line = "" + var s: seq[string] + while p.outputStream.readLine(line): + s.add line + doAssert s == @["10"] + + block: + let output = compileNimProg("-d:case_testfile3", "D20200626T221233") + var x = newStringOfCap(120) + block: # startProcess stdout poStdErrToStdOut (replaces old test `tstdout` + `ta_out`) + var p = startProcess(output, dir, options={poStdErrToStdOut}) + deferring: p.close() + do: + var sout: seq[string] + 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={}) + deferring: p.close() + do: + var serr, sout: seq[string] + 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"] + + block: # startProcess exit code (replaces old test `texitcode` + `tafalse`) + let output = compileNimProg("-d:case_testfile4", "D20200626T224758") + var p = startProcess(output, dir) + doAssert waitForExit(p) == QuitFailure + p = startProcess(output, dir) + var running = true + while running: + # xxx: avoid busyloop? + running = running(p) + doAssert waitForExit(p) == QuitFailure + + # make sure that first call to running() after process exit returns false + p = startProcess(output, dir) + for j in 0..<30: # refs #13449 + os.sleep(50) + if not running(p): break + doAssert not running(p) + doAssert waitForExit(p) == QuitFailure # avoid zombies + + import std/strtabs + block execProcessTest: + var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4") + stripLineEnd(result[0]) + doAssert result == ("12", 0) + when not defined(windows): + doAssert execCmdEx("ls --nonexistent").exitCode != 0 + when false: + # bug: on windows, this raises; on posix, passes + 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 7fc6c5d85..93b0317f7 100644 --- a/tests/stdlib/tosprocterminate.nim +++ b/tests/stdlib/tosprocterminate.nim @@ -1,7 +1,16 @@ -import os, osproc +discard """ + cmd: "nim $target $options -r $file" + targets: "c cpp" + matrix: "--mm:refc; --mm:orc" +""" -when defined(Windows): +import os, osproc, times, std / monotimes +import std/assertions + +when defined(windows): const ProgramWhichDoesNotEnd = "notepad" +elif defined(openbsd): + const ProgramWhichDoesNotEnd = "/bin/cat" else: const ProgramWhichDoesNotEnd = "/bin/sh" @@ -15,7 +24,21 @@ while process.running() and TimeToWait > 0: sleep(100) TimeToWait = TimeToWait - 100 -if process.running(): - echo("FAILED") -else: - echo("SUCCESS") +doAssert not process.running() +echo("stopped process") + +process.close() + +echo("starting " & ProgramWhichDoesNotEnd) +process = startProcess(ProgramWhichDoesNotEnd) +echo("process should be stopped after 2s") + +let start = getMonoTime() +discard process.waitForExit(2000) +let took = getMonoTime() - start + +doAssert not process.running() +# some additional time to account for overhead +doAssert took < initDuration(seconds = 3) + +echo("stopped process after ", took) 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 fc735f3eb..000000000 --- a/tests/stdlib/tparscfg.nim +++ /dev/null @@ -1,69 +0,0 @@ -discard """ -output: ''' -utf-8 -on -hello -lihf8515 -10214028 -lihaifeng@wxm.com -=== -charset=utf-8 -[Package] -name=hello ---threads:on -[Author] -name=lhf -qq=10214028 -email="lihaifeng@wxm.com" -=== -charset=utf-8 -[Package] -name=hello ---threads:on -[Author] -name=lihf8515 -qq=10214028 -''' -""" -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") -echo charset -echo threads -echo pname -echo name -echo qq -echo email - -echo "===" - -## Modifying a configuration file. -var dict3 = loadConfig(newStringStream(ss.data)) -dict3.setSectionKey("Author","name","lhf") -write(stdout, $dict3) - -echo "===" - -## Deleting a section key in a configuration file. -var dict4 = loadConfig(newStringStream(ss.data)) -dict4.delSectionKey("Author","email") -write(stdout, $dict4) - 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 126020ed6..cd582551d 100644 --- a/tests/stdlib/tparsesql.nim +++ b/tests/stdlib/tparsesql.nim @@ -1,11 +1,23 @@ discard """ - file: "tparsesql.nim" + matrix: "--mm:refc; --mm:orc" + targets: "c js" """ - import parsesql +import std/assertions + +doAssert treeRepr(parseSql("INSERT INTO STATS VALUES (10, 5.5); ") +) == """ -doAssert $parseSQL("SELECT foo FROM table;") == "select foo from table;" -doAssert $parseSQL(""" +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, @@ -21,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 @@ -49,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 ( @@ -117,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, @@ -169,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%') @@ -202,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 848fba2da..f3a9a9798 100644 --- a/tests/stdlib/tparsopt.nim +++ b/tests/stdlib/tparsopt.nim @@ -1,8 +1,16 @@ -# Test the new parseopt module +discard """ +disabled: true +""" + +# this file has a type in the name, and it does not really test +# parseopt module, because tester has no support to set arguments. Test the +# new parseopt module. Therefore it is disabled. import parseopt +import std/[assertions, syncio] + proc writeHelp() = writeLine(stdout, "Usage: tparsopt [options] filename [options]") @@ -21,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 e5b709a66..da3fc14b7 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -1,5 +1,9 @@ discard """ + matrix: "--mm:refc; --mm:orc" + targets: "c cpp js" output: ''' +PEG AST traversal output +------------------------ pkNonTerminal: Sum @(2, 3) pkSequence: (Product (('+' / '-') Product)*) pkNonTerminal: Product @(3, 7) @@ -26,53 +30,315 @@ pkNonTerminal: Sum @(2, 3) pkChar: '+' pkChar: '-' pkNonTerminal: Product @(3, 7) + +Event parser output +------------------- +@[5.0] ++ +@[5.0, 3.0] +@[8.0] + +/ +@[8.0, 2.0] +@[4.0] + +- +@[4.0, 7.0] +-* +@[4.0, 7.0, 22.0] +@[4.0, 154.0] +- +@[-150.0] ''' """ -import strutils, streams -import pegs +when defined(nimHasEffectsOf): + {.experimental: "strictEffects".} + +import std/[strutils, streams, pegs, assertions] const indent = " " let - pegSrc = """ -Expr <- Sum -Sum <- Product (('+' / '-') Product)* -Product <- Value (('*' / '/') Value)* -Value <- [0-9]+ / '(' Expr ')' - """ - pegAst: Peg = pegSrc.peg - -var - outp = newStringStream() - processed: seq[string] = @[] - -proc prt(outp: Stream, kind: PegKind, s: string; level: int = 0) = - outp.writeLine indent.repeat(level) & "$1: $2" % [$kind, s] - -proc recLoop(p: Peg, level: int = 0) = - case p.kind - of pkEmpty..pkWhitespace: - discard - of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: - outp.prt(p.kind, $p, level) - of pkChar, pkGreedyRepChar: - outp.prt(p.kind, $p, level) - of pkCharChoice, pkGreedyRepSet: - outp.prt(p.kind, $p, level) - of pkNonTerminal: - outp.prt(p.kind, - "$1 @($3, $4)" % [p.nt.name, $p.nt.rule.kind, $p.nt.line, $p.nt.col], level) - if not(p.nt.name in processed): - processed.add p.nt.name - p.nt.rule.recLoop level+1 - of pkBackRef..pkBackRefIgnoreStyle: - outp.prt(p.kind, $p, level) - else: - outp.prt(p.kind, $p, level) - for s in items(p): - s.recLoop level+1 - -pegAst.recLoop -echo outp.data \ No newline at end of file + pegAst = """ +Expr <- Sum +Sum <- Product (('+' / '-')Product)* +Product <- Value (('*' / '/')Value)* +Value <- [0-9]+ / '(' Expr ')' + """.peg + txt = "(5+3)/2-7*22" + +block: + var + outp = newStringStream() + processed: seq[string] = @[] + + proc prt(outp: Stream, kind: PegKind, s: string; level: int = 0) = + outp.writeLine indent.repeat(level) & "$1: $2" % [$kind, s] + + proc recLoop(p: Peg, level: int = 0) = + case p.kind + of pkEmpty..pkWhitespace: + discard + of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: + outp.prt(p.kind, $p, level) + of pkChar, pkGreedyRepChar: + outp.prt(p.kind, $p, level) + of pkCharChoice, pkGreedyRepSet: + outp.prt(p.kind, $p, level) + of pkNonTerminal: + outp.prt(p.kind, + "$1 @($3, $4)" % [p.nt.name, $p.nt.rule.kind, $p.nt.line, $p.nt.col], level) + if not(p.nt.name in processed): + processed.add p.nt.name + p.nt.rule.recLoop level+1 + of pkBackRef..pkBackRefIgnoreStyle: + outp.prt(p.kind, $p, level) + else: + outp.prt(p.kind, $p, level) + for s in items(p): + s.recLoop level+1 + + pegAst.recLoop + echo "PEG AST traversal output" + echo "------------------------" + echo outp.data + +block: + var + pStack {.threadvar.}: seq[string] + valStack {.threadvar.}: seq[float] + opStack {.threadvar.}: string + let + parseArithExpr = pegAst.eventParser: + pkNonTerminal: + enter: + pStack.add p.nt.name + leave: + pStack.setLen pStack.high + if length > 0: + let matchStr = s.substr(start, start+length-1) + case p.nt.name + of "Value": + try: + valStack.add matchStr.parseFloat + echo valStack + except ValueError: + discard + of "Sum", "Product": + try: + let val {.used.} = matchStr.parseFloat + except ValueError: + if valStack.len > 1 and opStack.len > 0: + valStack[^2] = case opStack[^1] + of '+': valStack[^2] + valStack[^1] + of '-': valStack[^2] - valStack[^1] + of '*': valStack[^2] * valStack[^1] + else: valStack[^2] / valStack[^1] + valStack.setLen valStack.high + echo valStack + opStack.setLen opStack.high + echo opStack + pkChar: + leave: + if length == 1 and "Value" != pStack[^1]: + let matchChar = s[start] + opStack.add matchChar + echo opStack + echo "Event parser output" + echo "-------------------" + let pLen = parseArithExpr(txt) + 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/tpermutations.nim b/tests/stdlib/tpermutations.nim deleted file mode 100644 index a6e07ded6..000000000 --- a/tests/stdlib/tpermutations.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - output: '''@[0, 2, 1] -@[1, 0, 2] -@[1, 2, 0] -@[2, 0, 1] -@[2, 1, 0] -@[2, 0, 1] -@[1, 2, 0] -@[1, 0, 2] -@[0, 2, 1] -@[0, 1, 2]''' -""" -import algorithm - -var v = @[0, 1, 2] -while v.nextPermutation(): - echo v -while v.prevPermutation(): - echo v diff --git a/tests/stdlib/tposix.nim b/tests/stdlib/tposix.nim index 229035d22..060482229 100644 --- a/tests/stdlib/tposix.nim +++ b/tests/stdlib/tposix.nim @@ -1,11 +1,17 @@ +discard """ + matrix: "--mm:refc; --mm:orc" + disabled: windows +""" + # Test Posix interface when not defined(windows): import posix + import std/[assertions, syncio] var - u: Tutsname + u: Utsname discard uname(u) @@ -14,3 +20,69 @@ when not defined(windows): 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 d18b468c8..000000000 --- a/tests/stdlib/tquit.nim +++ /dev/null @@ -1,6 +0,0 @@ -# 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 new file mode 100644 index 000000000..39637434d --- /dev/null +++ b/tests/stdlib/tre.nim @@ -0,0 +1,122 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/re +import std/assertions + +proc testAll() = + doAssert match("(a b c)", rex"\( .* \)") + doAssert match("WHiLe", re("while", {reIgnoreCase})) + + doAssert "0158787".match(re"\d+") + doAssert "ABC 0232".match(re"\w+\s+\d+") + doAssert "ABC".match(rex"\d+ | \w+") + + {.push warnings:off.} + doAssert matchLen("key", re"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b") == 3 + {.pop.} + + var pattern = re"[a-z0-9]+\s*=\s*[a-z0-9]+" + doAssert matchLen("key1= cal9", pattern) == 11 + + doAssert find("_____abc_______", re"abc") == 5 + doAssert findBounds("_____abc_______", re"abc") == (5,7) + + var matches: array[6, string] + if match("abcdefg", re"c(d)ef(g)", matches, 2): + doAssert matches[0] == "d" + doAssert matches[1] == "g" + else: + doAssert false + + if "abc" =~ re"(a)bcxyz|(\w+)": + doAssert matches[1] == "abc" + else: + doAssert false + + if "abc" =~ re"(cba)?.*": + doAssert matches[0] == "" + else: doAssert false + + if "abc" =~ re"().*": + doAssert matches[0] == "" + else: doAssert false + + doAssert "var1=key; var2=key2".endsWith(re"\w+=\w+") + doAssert("var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2") == + "var1<-keykey; var2<-key2key2") + doAssert("var1=key; var2=key2".replace(re"(\w+)=(\w+)", "$1<-$2$2") == + "$1<-$2$2; $1<-$2$2") + + var accum: seq[string] = @[] + for word in split("00232this02939is39an22example111", re"\d+"): + accum.add(word) + doAssert(accum == @["", "this", "is", "an", "example", ""]) + + accum = @[] + for word in split("00232this02939is39an22example111", re"\d+", maxsplit=2): + accum.add(word) + doAssert(accum == @["", "this", "is39an22example111"]) + + accum = @[] + for word in split("AAA : : BBB", re"\s*:\s*"): + accum.add(word) + doAssert(accum == @["AAA", "", "BBB"]) + + doAssert(split("abc", re"") == @["a", "b", "c"]) + doAssert(split("", re"") == @[]) + + doAssert(split("a;b;c", re";") == @["a", "b", "c"]) + doAssert(split(";a;b;c", re";") == @["", "a", "b", "c"]) + doAssert(split(";a;b;c;", re";") == @["", "a", "b", "c", ""]) + doAssert(split("a;b;c;", re";") == @["a", "b", "c", ""]) + doAssert(split("00232this02939is39an22example111", re"\d+", maxsplit=2) == @["", "this", "is39an22example111"]) + + + for x in findAll("abcdef", re"^{.}", 3): + doAssert x == "d" + accum = @[] + for x in findAll("abcdef", re".", 3): + accum.add(x) + doAssert(accum == @["d", "e", "f"]) + + doAssert("XYZ".find(re"^\d*") == 0) + doAssert("XYZ".match(re"^\d*") == true) + + block: + var matches: array[16, string] + if match("abcdefghijklmnop", re"(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)(n)(o)(p)", matches): + for i in 0..matches.high: + doAssert matches[i] == $chr(i + 'a'.ord) + else: + doAssert false + + block: # Buffer based RE + var cs: cstring = "_____abc_______" + doAssert(cs.find(re"abc", bufSize=15) == 5) + doAssert(cs.matchLen(re"_*abc", bufSize=15) == 8) + doAssert(cs.matchLen(re"abc", start=5, bufSize=15) == 3) + doAssert(cs.matchLen(re"abc", start=5, bufSize=7) == -1) + doAssert(cs.matchLen(re"abc_*", start=5, bufSize=10) == 5) + var accum: seq[string] = @[] + for x in cs.findAll(re"[a-z]", start=3, bufSize=15): + accum.add($x) + doAssert(accum == @["a","b","c"]) + + block: # bug #9306 + doAssert replace("bar", re"^", "foo") == "foobar" + 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 ae6714de1..9dd66cd60 100644 --- a/tests/stdlib/tregex.nim +++ b/tests/stdlib/tregex.nim @@ -1,6 +1,6 @@ discard """ - file: "tregex.nim" output: "key: keyAYes!" + matrix: "--mm:refc; --mm:orc" """ # Test the new regular expression module # which is based on the PCRE library @@ -12,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*(\#.*)": @@ -27,5 +27,3 @@ else: echo("Bug!") #OUT key: keyAYes! - - 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/treguse.nim b/tests/stdlib/treguse.nim deleted file mode 100644 index 3d09eb731..000000000 --- a/tests/stdlib/treguse.nim +++ /dev/null @@ -1,27 +0,0 @@ -discard """ - file: "treguse.nim" - output: "055this should be the casehugh" -""" -# Test the register usage of the virtual machine and -# the blocks in var statements - -proc main(a, b: int) = - var x = 0 - write(stdout, x) - if x == 0: - var y = 55 - write(stdout, y) - write(stdout, "this should be the case") - var input = "<no input>" - if input == "Andreas": - write(stdout, "wow") - else: - write(stdout, "hugh") - else: - var z = 66 - write(stdout, z) # "bug!") - -main(45, 1000) -#OUT 055this should be the casehugh - - diff --git a/tests/stdlib/treloop.nim b/tests/stdlib/treloop.nim deleted file mode 100644 index b4221525d..000000000 --- a/tests/stdlib/treloop.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ - output: '''@["(", "+", " 1", " 2", ")"]''' -""" - -import re - -let str = "(+ 1 2)" -var tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)""" -echo str.findAll(tokenRE) diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index 18fe7e054..3956b98f9 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -1,29 +1,328 @@ discard """ - file: "trepr.nim" - 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'}" + 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)) +# 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/trepr2.nim b/tests/stdlib/trepr2.nim deleted file mode 100644 index 300df565d..000000000 --- a/tests/stdlib/trepr2.nim +++ /dev/null @@ -1,32 +0,0 @@ -# test the new "repr" built-in proc - -type - TEnum = enum - en1, en2, en3, en4, en5, en6 - - TPoint {.final.} = object - x, y, z: int - s: array[0..1, string] - e: TEnum - -var - p: TPoint - q: ref TPoint - s: seq[ref TPoint] - -p.x = 0 -p.y = 13 -p.z = 45 -p.s[0] = "abc" -p.s[1] = "xyz" -p.e = en6 - -new(q) -q[] = p - -s = @[q, q, q, q] - -writeLine(stdout, repr(p)) -writeLine(stdout, repr(q)) -writeLine(stdout, repr(s)) -writeLine(stdout, repr(en4)) 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 new file mode 100644 index 000000000..ceab34bc9 --- /dev/null +++ b/tests/stdlib/trst.nim @@ -0,0 +1,1994 @@ +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, 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" + doAssert "<strong>test1</strong>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + removeFile("other.rst") + + test "Include starting from": + "other.rst".writeFile(""" +And this should **NOT** be visible in `docs.html` +OtherStart +*Visible* +""") + + let input = """ +.. include:: other.rst + :start-after: OtherStart +""" + check "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + removeFile("other.rst") + + test "Include everything before": + "other.rst".writeFile(""" +*Visible* +OtherEnd +And this should **NOT** be visible in `docs.html` +""") + + let input = """ +.. include:: other.rst + :end-before: OtherEnd +""" + doAssert "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + removeFile("other.rst") + + + test "Include everything between": + "other.rst".writeFile(""" +And this should **NOT** be visible in `docs.html` +OtherStart +*Visible* +OtherEnd +And this should **NOT** be visible in `docs.html` +""") + + let input = """ +.. include:: other.rst + :start-after: OtherStart + :end-before: OtherEnd +""" + check "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) + removeFile("other.rst") + + + test "Ignore premature ending string": + "other.rst".writeFile(""" + +OtherEnd +And this should **NOT** be visible in `docs.html` +OtherStart +*Visible* +OtherEnd +And this should **NOT** be visible in `docs.html` +""") + + let input = """ +.. include:: other.rst + :start-after: OtherStart + :end-before: OtherEnd +""" + 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 c702ccc2a..6253e7146 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1,7 +1,59 @@ +discard """ +matrix: "--mm:refc; --mm:orc" +outputsub: "" +""" + # tests for rstgen module. import ../../lib/packages/docutils/rstgen -import unittest +import ../../lib/packages/docutils/rst +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": @@ -16,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> @@ -27,7 +79,7 @@ suite "YAML syntax highlighting": <span class="Punctuation">?</span> <span class="StringLit">key</span> <span class="Punctuation">:</span> <span class="StringLit">value</span> <span class="Keyword">...</span></pre>""" - + test "Block scalars": let input = """.. code-block:: yaml a literal block scalar: | @@ -42,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> @@ -55,7 +107,7 @@ suite "YAML syntax highlighting": <span class="StringLit">another literal block scalar</span><span class="Punctuation">:</span> <span class="Command">|+</span> <span class="Comment"># comment after header</span><span class="LongStringLit"> allowed, since more indented than parent</span></pre>""" - + test "Directives": let input = """.. code-block:: yaml %YAML 1.2 @@ -68,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> @@ -89,15 +141,34 @@ 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> <span class="StringLit">more numbers</span><span class="Punctuation">:</span> <span class="Punctuation">[</span><span class="DecNumber">-783</span><span class="Punctuation">,</span> <span class="FloatNumber">11e78</span><span class="Punctuation">]</span><span class="Punctuation">,</span> <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 @@ -106,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> @@ -126,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> @@ -136,4 +207,1486 @@ suite "YAML syntax highlighting": <span class="DecNumber">-3</span> <span class="DecNumber">-4</span> <span class="StringLit">example.com/not/a#comment</span><span class="Punctuation">:</span> - <span class="StringLit">?not a map key</span></pre>""" \ No newline at end of file + <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": + 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/tsegfaults.nim b/tests/stdlib/tsegfaults.nim deleted file mode 100644 index 1d8508c52..000000000 --- a/tests/stdlib/tsegfaults.nim +++ /dev/null @@ -1,29 +0,0 @@ -discard """ - output: '''caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash!''' -""" - -import segfaults - -proc main = - try: - var x: ptr int - echo x[] - try: - raise newException(ValueError, "not a crash") - except ValueError: - discard - except NilAccessError: - echo "caught a crash!" - -for i in 0..10: - main() diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim new file mode 100644 index 000000000..1094ae233 --- /dev/null +++ b/tests/stdlib/tsequtils.nim @@ -0,0 +1,547 @@ +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` +var counter = 0 +proc identity[T](a: T): auto = + counter.inc + a + +block: # concat test + let + s1 = @[1, 2, 3] + s2 = @[4, 5] + s3 = @[6, 7] + total = concat(s1, s2, s3) + doAssert total == @[1, 2, 3, 4, 5, 6, 7] + +block: # count test + let + s1 = @[1, 2, 3, 2] + s2 = @['a', 'b', 'x', 'a'] + a1 = [1, 2, 3, 2] + a2 = ['a', 'b', 'x', 'a'] + r0 = count(s1, 0) + r1 = count(s1, 1) + r2 = count(s1, 2) + r3 = count(s2, 'y') + r4 = count(s2, 'x') + r5 = count(s2, 'a') + ar0 = count(a1, 0) + ar1 = count(a1, 1) + ar2 = count(a1, 2) + ar3 = count(a2, 'y') + ar4 = count(a2, 'x') + ar5 = count(a2, 'a') + 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 + a = @[1, 2, 3] + b: seq[int] = @[] + c = [1, 2, 3] + + doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] + doAssert a.cycle(0) == @[] + #doAssert a.cycle(-1) == @[] # will not compile! + doAssert b.cycle(3) == @[] + doAssert c.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] + doAssert c.cycle(0) == @[] + +block: # repeat tests + 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 + dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] + dup2 = @["a", "a", "c", "d", "d"] + dup3 = [1, 1, 3, 4, 2, 2, 8, 1, 4] + dup4 = ["a", "a", "c", "d", "d"] + unique1 = deduplicate(dup1) + unique2 = deduplicate(dup2) + unique3 = deduplicate(dup3) + unique4 = deduplicate(dup4) + unique5 = deduplicate(dup1.sorted, true) + unique6 = deduplicate(dup2, true) + unique7 = deduplicate(dup3.sorted, true) + unique8 = deduplicate(dup4, true) + 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 + short = @[1, 2, 3] + long = @[6, 5, 4, 3, 2, 1] + words = @["one", "two", "three"] + ashort = [1, 2, 3] + along = [6, 5, 4, 3, 2, 1] + awords = ["one", "two", "three"] + zip1 = zip(short, long) + zip2 = zip(short, words) + zip3 = zip(ashort, along) + 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) + 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) + 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] + doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]] + doAssert numbers.distribute(6)[0] == @[1, 2] + doAssert numbers.distribute(6)[5] == @[7] + let a = @[1, 2, 3, 4, 5, 6, 7] + doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]] + doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]] + doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]] + doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]] + doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]] + doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] + doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] + doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] + doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]] + doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]] + doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]] + doAssert a.distribute(6, false) == @[ + @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]] + doAssert a.distribute(8, false) == a.distribute(8, true) + doAssert a.distribute(90, false) == a.distribute(90, true) + var b = @[0] + for f in 1 .. 25: b.add(f) + doAssert b.distribute(5, true)[4].len == 5 + doAssert b.distribute(5, false)[4].len == 2 + +block: # map test + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + 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) + 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") + doAssert a == @["142", "242", "342", "442"] + +block: # filter proc test + let + colors = @["red", "yellow", "black"] + acolors = ["red", "yellow", "black"] + f1 = filter(colors, proc(x: string): bool = x.len < 6) + 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 + 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] + doAssert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == + @[4, 8, 4] + 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) + doAssert floats == @[13.0, 12.5, 10.1] + +block: # insert tests + var dest = @[1, 1, 1, 1, 1, 1, 1, 1] + let + 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) + 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]""" + +block: # filterIt test + let + 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) + 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') + 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] = @[] + 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] = @[] + 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] = @[] + 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] = @[] + 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: + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + oddNumbers = toSeq(filter(numeric) do (x: int) -> bool: + if x mod 2 == 1: + result = true) + doAssert oddNumbers == @[1, 3, 5, 7, 9] + + block: + doAssert [1, 2].toSeq == @[1, 2] + doAssert @[1, 2].toSeq == @[1, 2] + + doAssert @[1, 2].toSeq == @[1, 2] + doAssert toSeq(@[1, 2]) == @[1, 2] + + block: + iterator myIter(seed: int): auto = + for i in 0..<seed: + yield i + doAssert toSeq(myIter(2)) == @[0, 1] + + block: + iterator myIter(): auto {.inline.} = + 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 + + doAssert myIter.toSeq == @[1, 2] + doAssert toSeq(myIter) == @[1, 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] + + block: + 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 + counter = 0 + let ret = toSeq(@[1, 2, 3].identity().filter(proc (x: int): bool = x < 3)) + doAssert ret == @[1, 2] + doAssert counter == 1 +block: # foldl tests + let + numbers = @[5, 9, 11] + addition = foldl(numbers, a + b) + subtraction = foldl(numbers, a - b) + multiplication = foldl(numbers, a * b) + words = @["nim", "is", "cool"] + concatenation = foldl(words, a & b) + 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 + numbers = @[5, 9, 11] + addition = foldr(numbers, a + b) + subtraction = foldr(numbers, a - b) + multiplication = foldr(numbers, a * b) + words = @["nim", "is", "cool"] + concatenation = foldr(words, a & b) + 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 + counter = 0 + var + nums = @[1, 2, 3, 4] + strings = nums.identity.mapIt($(4 * it)) + doAssert counter == 1 + nums.applyIt(it * 3) + doAssert nums[0] + nums[3] == 15 + doAssert strings[2] == "12" + +block: # newSeqWith tests + var seq2D = newSeqWith(4, newSeq[bool](2)) + seq2D[0][0] = true + seq2D[1][0] = true + 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] + doAssert mapLiterals((1, ("abc"), 2), float, nested = false) == + (float(1), "abc", float(2)) + doAssert mapLiterals(([1], ("abc"), 2), `$`, nested = true) == + (["1"], "abc", "2") + +block: # mapIt with openArray + counter = 0 + proc foo(x: openArray[int]): seq[int] = x.mapIt(it * 10) + doAssert foo([identity(1), identity(2)]) == @[10, 20] + doAssert counter == 2 + +block: # mapIt with direct openArray + proc foo1(x: openArray[int]): seq[int] = x.mapIt(it * 10) + counter = 0 + doAssert foo1(openArray[int]([identity(1), identity(2)])) == @[10, 20] + doAssert counter == 2 + + # Corner cases (openArray literals should not be common) + template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10) + counter = 0 + doAssert foo2(openArray[int]([identity(1), identity(2)])) == @[10, 20] + doAssert counter == 2 + + counter = 0 + doAssert openArray[int]([identity(1), identity(2)]).mapIt(it) == @[1, 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 + # of elements) + doAssert: not compiles(mapIt(@[], it)) + doAssert: not compiles(mapIt([], it)) + doAssert newSeq[int](0).mapIt(it) == @[] + +block: # mapIt redifinition check, see https://github.com/nim-lang/Nim/issues/8580 + let s2 = [1, 2].mapIt(it) + doAssert s2 == @[1, 2] + +block: + counter = 0 + doAssert [1, 2].identity().mapIt(it*2).mapIt(it*10) == @[20, 40] + # https://github.com/nim-lang/Nim/issues/7187 test case + doAssert counter == 1 + +block: # mapIt with invalid RHS for `let` (#8566) + type X = enum + A, B + doAssert mapIt(X, $it) == @["A", "B"] + +block: + # bug #9093 + let inp = "a:b,c:d" + + let outp = inp.split(",").mapIt(it.split(":")) + doAssert outp == @[@["a", "b"], @["c", "d"]] + + +block: + proc iter(len: int): auto = + result = iterator(): int = + for i in 0..<len: + yield i + + # 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 new file mode 100644 index 000000000..10ad5f658 --- /dev/null +++ b/tests/stdlib/tsharedtable.nim @@ -0,0 +1,90 @@ +discard """ +matrix: "--mm:refc; --mm:orc" +output: ''' +''' +""" + +import sharedtables +import std/assertions + +block: + var table: SharedTable[int, int] + + init(table) + table[1] = 10 + doAssert table.mget(1) == 10 + doAssert table.mgetOrPut(3, 7) == 7 + doAssert table.mgetOrPut(3, 99) == 7 + deinitSharedTable(table) + +import sequtils, algorithm +proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted +template sortedItems(t: untyped): untyped = sorted(toSeq(t)) + +import tables # refs issue #13504 + +block: # we use Table as groundtruth, it's well tested elsewhere + template testDel(t, t0) = + template put2(i) = + t[i] = i + t0[i] = i + + template add2(i, val) = + t.add(i, val) + t0.add(i, val) + + template del2(i) = + t.del(i) + t0.del(i) + + template checkEquals() = + doAssert t.len == t0.len + for k,v in t0: + doAssert t.mgetOrPut(k, -1) == v # sanity check + doAssert t.mget(k) == v + + let n = 100 + let n2 = n*2 + let n3 = n*3 + let n4 = n*4 + let n5 = n*5 + + for i in 0..<n: + put2(i) + for i in 0..<n: + if i mod 3 == 0: + del2(i) + for i in n..<n2: + put2(i) + for i in 0..<n2: + if i mod 7 == 0: + del2(i) + + checkEquals() + + for i in n2..<n3: + t0[i] = -2 + doAssert t.mgetOrPut(i, -2) == -2 + doAssert t.mget(i) == -2 + + for i in 0..<n4: + let ok = i in t0 + if not ok: t0[i] = -i + doAssert t.hasKeyOrPut(i, -i) == ok + + checkEquals() + + for i in n4..<n5: + add2(i, i*10) + add2(i, i*11) + add2(i, i*12) + del2(i) + del2(i) + + checkEquals() + + var t: SharedTable[int, int] + init(t) # ideally should be auto-init + var t0: Table[int, int] + testDel(t, t0) + deinitSharedTable(t) diff --git a/tests/stdlib/tsince.nim b/tests/stdlib/tsince.nim new file mode 100644 index 000000000..a0a4229cb --- /dev/null +++ b/tests/stdlib/tsince.nim @@ -0,0 +1,32 @@ +import std/private/since +import std/assertions + +proc fun1(): int {.since: (1, 3).} = 12 +proc fun1Bad(): int {.since: (99, 3).} = 12 +proc fun2(): int {.since: (1, 3, 1).} = 12 +proc fun2Bad(): int {.since: (99, 3, 1).} = 12 + +doAssert fun1() == 12 +doAssert declared(fun1) +doAssert not declared(fun1Bad) + +doAssert fun2() == 12 +doAssert declared(fun2) +doAssert not declared(fun2Bad) + +var ok = false +since (1, 3): + ok = true +doAssert ok + +ok = false +since (1, 3, 1): + ok = true +doAssert ok + +since (99, 3): + doAssert false + +template fun3(): int {.since: (1, 3).} = 12 + +doAssert declared(fun3) diff --git a/tests/stdlib/tsinglylinkedring.nim b/tests/stdlib/tsinglylinkedring.nim deleted file mode 100644 index 93f0c69cd..000000000 --- a/tests/stdlib/tsinglylinkedring.nim +++ /dev/null @@ -1,29 +0,0 @@ -discard """ - output: '''[5] -[4, 5] -[3, 4, 5] -[2, 3, 4, 5] -[2, 3, 4, 5, 6] -[2, 3, 4, 5, 6, 7] -[2, 3, 4, 5, 6, 7, 8] -[1, 2, 3, 4, 5, 6, 7, 8]''' -""" -import lists - -var r = initSinglyLinkedRing[int]() -r.prepend(5) -echo r -r.prepend(4) -echo r -r.prepend(3) -echo r -r.prepend(2) -echo r -r.append(6) -echo r -r.append(7) -echo r -r.append(8) -echo r -r.prepend(1) -echo r 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 efe1d0b8b..32e004921 100644 --- a/tests/stdlib/tsortcall.nim +++ b/tests/stdlib/tsortcall.nim @@ -1,5 +1,69 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + import algorithm +import unittest + + +suite "test sort, sorted, and isSorted procs": + proc foosort(ships: var seq[int]) = sort(ships, system.cmp[int]) + + type + User = object + name: string + age: int + + func newUser(name: string, age: int): User = + result.name = name + result.age = age + + proc compareUsers(x, y: User): int = + if x.age == y.age: return 0 + if x.age < y.age: return -1 + return 1 + + setup: + var + unSortedIntSeq = @[1, 4, 3, 5, -1] + unSortedUserSeq = @[newUser("Andreas", 34), newUser("Alice", 12), newUser("Bob", 23)] + + let + sortedIntSeq = @[-1, 1, 3, 4, 5] + sortedUserSeq = @[newUser("Alice", 12), newUser("Bob", 23), newUser("Andreas", 34)] + + test "test the shortcut versions of sort, sorted, and isSorted": + check(not unSortedIntSeq.isSorted) + check sorted(unSortedIntSeq) == sortedIntSeq + check sorted(unSortedIntSeq).isSorted + + unSortedIntSeq.sort() + check unSortedIntSeq == sortedIntSeq + check unSortedIntSeq.isSorted + + test "test the shortcut versions with descending sort order": + check(not unSortedIntSeq.isSorted(SortOrder.Descending)) + check sorted(unSortedIntSeq, SortOrder.Descending) == reversed sortedIntSeq + check sorted(unSortedIntSeq).isSorted(SortOrder.Ascending) + + unSortedIntSeq.sort(SortOrder.Descending) + check unSortedIntSeq == reversed sortedIntSeq + check unSortedIntSeq.isSorted(SortOrder.Descending) -proc foosort(ships: var seq[int]) = sort(ships, system.cmp[int]) + test "test the versions that accept a custom compareUsers function": + check(not unSortedUserSeq.isSorted(compareUsers)) + check sorted(unSortedUserSeq, compareUsers) == sortedUserSeq + check sorted(unSortedUserSeq, compareUsers).isSorted(compareUsers) + unSortedUserSeq.sort(compareUsers) + check unSortedUserSeq == sortedUserSeq + check unSortedUserSeq.isSorted(compareUsers) + test "test the long versions with descending sort order": + check(not unSortedUserSeq.isSorted(compareUsers, SortOrder.Descending)) + check sorted(unSortedUserSeq, compareUsers, SortOrder.Descending) == reversed sortedUserSeq + check sorted(unSortedUserSeq, compareUsers, + SortOrder.Descending).isSorted(compareUsers, SortOrder.Descending) + unSortedUserSeq.sort(compareUsers, SortOrder.Descending) + check unSortedUserSeq == reversed sortedUserSeq + check unSortedUserSeq.isSorted(compareUsers, SortOrder.Descending) diff --git a/tests/stdlib/tsplit.nim b/tests/stdlib/tsplit.nim deleted file mode 100644 index 44da58aca..000000000 --- a/tests/stdlib/tsplit.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - file: "tsplit.nim" - output: "true" -""" -import strutils - -var s = "" -for w in split("|abc|xy|z", {'|'}): - s.add("#") - s.add(w) - -if s == "##abc#xy#z": - echo "true" -else: - echo "false" - -#OUT true - - - diff --git a/tests/stdlib/tsplit2.nim b/tests/stdlib/tsplit2.nim deleted file mode 100644 index 7fd9dda74..000000000 --- a/tests/stdlib/tsplit2.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - file: "tsplit2.nim" - output: "true" -""" -import strutils - -var s = "" -for w in split("|abc|xy|z", {'|'}): - s.add("#") - s.add(w) - -try: - discard "hello".split("") - echo "false" -except AssertionError: - echo "true" - -#OUT true - 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 4a7b2f7d7..6f123f21d 100644 --- a/tests/stdlib/tsqlparser.nim +++ b/tests/stdlib/tsqlparser.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:refc; --mm:orc" output: '''true''' """ @@ -6,7 +7,7 @@ discard """ import parsesql, streams, os -var tree = parseSql(newFileStream(getAppDir() / "somesql.sql"), "somesql") +var tree = parseSql(newFileStream(parentDir(currentSourcePath) / "somesql.sql"), "somesql") discard renderSql(tree) echo "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 new file mode 100644 index 000000000..b0f05d51d --- /dev/null +++ b/tests/stdlib/tstackframes.nim @@ -0,0 +1,34 @@ +import std/[strformat,os,osproc,assertions] +import stdtest/unittest_light + +proc main(opt: string, expected: string) = + const nim = getCurrentCompilerExe() + const file = currentSourcePath().parentDir / "mstackframes.nim" + let cmd = fmt"{nim} c -r --excessiveStackTrace:off --stacktraceMsgs:{opt} --hints:off {file}" + let (output, exitCode) = execCmdEx(cmd) + assertEquals output, expected + doAssert exitCode == 0 + +main("on"): """ +mstackframes.nim(38) mstackframes +mstackframes.nim(29) main + z: 0 + z: 1 +mstackframes.nim(20) main2 ("main2", 5, 1) +mstackframes.nim(20) main2 ("main2", 4, 2) +mstackframes.nim(20) main2 ("main2", 3, 3) +mstackframes.nim(19) main2 ("main2", 2, 4) +mstackframes.nim(18) bar ("bar ",) + +""" + +main("off"): """ +mstackframes.nim(38) mstackframes +mstackframes.nim(29) main +mstackframes.nim(20) main2 +mstackframes.nim(20) main2 +mstackframes.nim(20) main2 +mstackframes.nim(19) main2 +mstackframes.nim(18) bar + +""" 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 new file mode 100644 index 000000000..b7b806db8 --- /dev/null +++ b/tests/stdlib/tstdlib_issues.nim @@ -0,0 +1,110 @@ +discard """ +matrix: "--mm:refc; --mm:orc" +output: ''' +02 +1 +2 +3 +4 +5 +9 +b = true +123456789 +Second readLine raised an exception +123456789 +1 +2aaaaaaaa +3bbbbbbb +''' +""" + +import std/[terminal, colors, re, encodings, strutils, os, assertions, syncio] + + +block t9394: + let codeFg = ansiForegroundColorCode(colAliceBlue) + let codeBg = ansiBackgroundColorCode(colAliceBlue) + + doAssert codeFg == "\27[38;2;240;248;255m" + doAssert codeBg == "\27[48;2;240;248;255m" + + + +block t5382: + let regexp = re"^\/([0-9]{2})\.html$" + var matches: array[1, string] + discard "/02.html".find(regexp, matches) + echo matches[0] + + + +block tcount: + # bug #1845, #2224 + var arr = [3,2,1,5,4] + + # bubble sort + for i in low(arr)..high(arr): + for j in i+1..high(arr): # Error: unhandled exception: value out of range: 5 [RangeDefect] + if arr[i] > arr[j]: + let tmp = arr[i] + arr[i] = arr[j] + arr[j] = tmp + + for i in low(arr)..high(arr): + echo arr[i] + + # check this terminates: + for x in countdown('\255', '\0'): + discard + + + +block t8468: + when defined(windows): + var utf16to8 = open(destEncoding = "utf-16", srcEncoding = "utf-8") + var s = "some string" + var c = utf16to8.convert(s) + + var z = newStringOfCap(s.len * 2) + for x in s: + z.add x + z.add chr(0) + + doAssert z == c + + + +block t5349: + const fn = "file9char.txt" + writeFile(fn, "123456789") + + var f = syncio.open(fn) + echo getFileSize(f) + + var line = newString(10) + try: + let b = readLine(f, line) + echo "b = ", b + except: + echo "First readLine raised an exception" + echo line + + try: + line = readLine(f) + let b = readLine(f, line) + echo "b = ", b + except: + echo "Second readLine raised an exception" + echo line + f.close() + + removeFile(fn) + # bug #8961 + writeFile("test.txt", "1\C\L2aaaaaaaa\C\L3bbbbbbb") + + for line in lines("test.txt"): + echo line + +block t9456: + var f: File + f.close() diff --git a/tests/stdlib/tstdlib_various.nim b/tests/stdlib/tstdlib_various.nim new file mode 100644 index 000000000..bac5018fa --- /dev/null +++ b/tests/stdlib/tstdlib_various.nim @@ -0,0 +1,240 @@ +discard """ +matrix: "--mm:refc" +output: ''' +abc +def +definition +prefix +xyz +def +definition +Hi Andreas! How do you feel, Rumpf? + +@[0, 2, 1] +@[1, 0, 2] +@[1, 2, 0] +@[2, 0, 1] +@[2, 1, 0] +@[2, 0, 1] +@[1, 2, 0] +@[1, 0, 2] +@[0, 2, 1] +@[0, 1, 2] +055this should be the casehugh@["(", "+", " 1", " 2", ")"] +[5] +[4, 5] +[3, 4, 5] +[2, 3, 4, 5] +[2, 3, 4, 5, 6] +[1, 2, 3, 4, 5, 6] +<h1><a href="http://force7.de/nim">Nim</a></h1> +''' +""" + +import + 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] + r.incl "abc" + r.incl "xyz" + r.incl "def" + r.incl "definition" + r.incl "prefix" + doAssert r.contains"def" + #r.del "def" + + for w in r.items: + echo w + for w in r.itemsWithPrefix("de"): + echo w + + + +block testequivalence: + doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "equivalent or subset") + doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3]), "equivalent or subset") + doAssert((not(toHashSet(@[1,2,3]) <= toHashSet(@[1,2]))), "equivalent or subset") + doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "strict subset") + doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2,3]))), "strict subset") + doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2]))), "strict subset") + doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3,4]))), "==") + doAssert(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3]), "==") + doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2]))), "==") + + + +block tformat: + echo("Hi $1! How do you feel, $2?\n" % ["Andreas", "Rumpf"]) + + + +block tnilecho: + var x = @["1", "", "3"] + doAssert $x == """@["1", "", "3"]""" + + + +block torderedtable: + var t = initOrderedTable[int,string]() + + # this tests issue #5917 + var data = newSeq[int]() + for i in 0..<1000: + var x = rand(1000) + if x notin t: data.add(x) + t[x] = "meh" + + # this checks that keys are re-inserted + # in order when table is enlarged. + var i = 0 + for k, v in t: + doAssert(k == data[i]) + doAssert(v == "meh") + inc(i) + + + +block tpermutations: + var v = @[0, 1, 2] + while v.nextPermutation(): + echo v + while v.prevPermutation(): + echo v + + + +block treguse: + proc main(a, b: int) = + var x = 0 + write(stdout, x) + if x == 0: + var y = 55 + write(stdout, y) + write(stdout, "this should be the case") + var input = "<no input>" + if input == "Andreas": + write(stdout, "wow") + else: + write(stdout, "hugh") + else: + var z = 66 + write(stdout, z) # "bug!") + + main(45, 1000) + + + +block treloop: + let str = "(+ 1 2)" + var tokenRE = re"""[\s,]*(~@|[\[\]{}()'`~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('"`,;)]*)""" + echo str.findAll(tokenRE) + + + +block tropes: + var + r1 = rope("") + r2 = rope("123") + doAssert r1.len == 0 + doAssert r2.len == 3 + doAssert $r1 == "" + doAssert $r2 == "123" + + r1.add("123") + r2.add("456") + doAssert r1.len == 3 + doAssert r2.len == 6 + doAssert $r1 == "123" + doAssert $r2 == "123456" + doAssert $r1[1] == "2" + doAssert $r2[2] == "3" + + + +block tsegfaults: + when not defined(arm64): + var crashes = 0 + proc main = + try: + 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 + + + +block tsinglylinkedring: + var r = initSinglyLinkedRing[int]() + r.prepend(5) + echo r + r.prepend(4) + echo r + r.prepend(3) + echo r + r.prepend(2) + echo r + r.append(6) + echo r + r.prepend(1) + echo r + + + +block tsplit: + var s = "" + for w in split("|abc|xy|z", {'|'}): + s.add("#") + s.add(w) + + doAssert s == "##abc#xy#z" + + + +block tsplit2: + var s = "" + for w in split("|abc|xy|z", {'|'}): + s.add("#") + s.add(w) + + doAssert "true".split("") == @["true"] + + + +block tsqlparser: + # Just check that we can parse 'somesql' and render it without crashes. + var tree = parseSql(newFileStream( parentDir(currentSourcePath) / "somesql.sql"), "somesql") + discard renderSql(tree) + + + +block txmlgen: + var nim = "Nim" + echo h1(a(href="http://force7.de/nim", nim)) + + + +block txmltree: + var x = <>a(href="nim.de", newText("www.nim-test.de")) + + doAssert($x == "<a href=\"nim.de\">www.nim-test.de</a>") + doAssert(newText("foo").innerText == "foo") + doAssert(newEntity("bar").innerText == "bar") + doAssert(newComment("baz").innerText == "") + + let y = newXmlTree("x", [ + newText("foo"), + newXmlTree("y", [ + newText("bar") + ]) + ]) + doAssert(y.innerText == "foobar") 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 640565a27..60c63b450 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -1,7 +1,107 @@ -import streams +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 +''' +""" -var outp = newFileStream(stdout) -var inp = newFileStream(stdin) -write(outp, "Hello! What is your name?") -var line = readLine(inp) -write(outp, "Nice name: " & line) + +import std/[syncio, streams, assertions] + + +block tstreams: + var outp = newFileStream(stdout) + var inp = newFileStream(stdin) + writeLine(outp, "Hello! What is your name?") + var line = readLine(inp) + writeLine(outp, "Nice name: " & line) + + +block tstreams2: + var + fs = newFileStream("amissingfile.txt") + line = "" + echo "fs is: ",repr(fs) + if not isNil(fs): + while fs.readLine(line): + echo line + fs.close() + + +block tstreams3: + try: + var fs = openFileStream("shouldneverexist.txt") + except IOError: + echo "threw exception" + + static: + var s = newStringStream("I\nAM\nGROOT") + 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/tstreams2.nim b/tests/stdlib/tstreams2.nim deleted file mode 100644 index 90102d8e3..000000000 --- a/tests/stdlib/tstreams2.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - file: "tstreams2.nim" - output: '''fs is: nil''' -""" -import streams -var - fs = newFileStream("amissingfile.txt") - line = "" -echo "fs is: ",repr(fs) -if not isNil(fs): - while fs.readLine(line): - echo line - fs.close() diff --git a/tests/stdlib/tstreams3.nim b/tests/stdlib/tstreams3.nim deleted file mode 100644 index b2c9170e3..000000000 --- a/tests/stdlib/tstreams3.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ - file: "tstreams3.nim" - output: "threw exception" -""" -import streams - -try: - var fs = openFileStream("shouldneverexist.txt") -except IoError: - echo "threw exception" diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index db76899d4..ff406f898 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -1,56 +1,590 @@ discard """ - action: "run" + matrix: "--mm:refc; --mm:orc" """ -import strformat - -type Obj = object - -proc `$`(o: Obj): string = "foobar" - -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" +import genericstrformat +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 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 660746150..b9b3c78a3 100644 --- a/tests/stdlib/tstring.nim +++ b/tests/stdlib/tstring.nim @@ -1,79 +1,124 @@ discard """ - file: "tstring.nim" - 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(IndexError): - 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() + +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]()) + 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 new file mode 100644 index 000000000..ae7fd98ca --- /dev/null +++ b/tests/stdlib/tstrscans.nim @@ -0,0 +1,288 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/[strscans, strutils, assertions] + +block ParsePasswd: + proc parsePasswd(content: string): seq[string] = + result = @[] + var idx = 0 + while true: + var entry = "" + if scanp(content, idx, +(~{'\L', '\0'} -> entry.add($_)), '\L'): + result.add entry + else: + break + + 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 +nobody:x:65534:65534:nobody:/nonexistent:/bin/sh +messagebus:x:103:107::/var/run/dbus:/bin/false +""" + + 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", + "sys:x:3:3:sys:/dev:/bin/sh", + "nobody:x:65534:65534:nobody:/nonexistent:/bin/sh", + "messagebus:x:103:107::/var/run/dbus:/bin/false", + ] + doAssert etcPasswd.parsePasswd == parsedEtcPasswd + +block LastNot: + var idx : int + + idx = 0 + doAssert scanp("foo", idx, 'f', 'o', ~'a') + + idx = 0 + doAssert scanp("foo", idx, 'f', 'o', ~'o') == false + + idx = 0 + doAssert scanp("foox", idx, 'f', 'o', ~'o') == false + + idx = 0 + doAssert scanp("foox", idx, 'f', 'o', ~'a') + +block LastOptional: + var idx = 0 + doAssert scanp("foo", idx, 'f', 'o', 'o', ?'o') + +block Tuple: + var idx = 0 + doAssert scanp("foo", idx, ('f', 'o', 'o')) + +block NotWithOptional: + var idx : int + + idx = 0 + doAssert scanp("bc", idx, ~(?'b', 'c')) == false + + idx = 0 + doAssert scanp("c", idx, ~(?'b', 'c')) == false + + idx = 0 + doAssert scanp("b", idx, ~(?'b', 'c')) + +block NotEmpty: + var idx = 0 + doAssert scanp("", idx, ~()) == false + +block EmptyTuple: + var idx = 0 + doAssert scanp("ab", idx, 'a', (), 'b') + +block Arrow: + let text = "foo;bar;baz;" + var idx = 0 + 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 a248cc3b2..d261abe76 100644 --- a/tests/stdlib/tstrtabs.nim +++ b/tests/stdlib/tstrtabs.nim @@ -1,4 +1,95 @@ -import strtabs +discard """ +matrix: "--mm:refc; --mm:orc" +sortoutput: true +output: ''' +key1: value1 +key2: value2 +key_0: value0 +key_10: value10 +key_11: value11 +key_12: value12 +key_13: value13 +key_14: value14 +key_15: value15 +key_16: value16 +key_17: value17 +key_18: value18 +key_19: value19 +key_20: value20 +key_21: value21 +key_22: value22 +key_23: value23 +key_24: value24 +key_25: value25 +key_26: value26 +key_27: value27 +key_28: value28 +key_29: value29 +key_30: value30 +key_31: value31 +key_32: value32 +key_33: value33 +key_34: value34 +key_35: value35 +key_36: value36 +key_37: value37 +key_38: value38 +key_39: value39 +key_3: value3 +key_40: value40 +key_41: value41 +key_42: value42 +key_43: value43 +key_44: value44 +key_45: value45 +key_46: value46 +key_47: value47 +key_48: value48 +key_49: value49 +key_4: value4 +key_50: value50 +key_51: value51 +key_52: value52 +key_53: value53 +key_54: value54 +key_55: value55 +key_56: value56 +key_57: value57 +key_58: value58 +key_59: value59 +key_5: value5 +key_60: value60 +key_61: value61 +key_62: value62 +key_63: value63 +key_64: value64 +key_65: value65 +key_66: value66 +key_67: value67 +key_68: value68 +key_69: value69 +key_6: value6 +key_70: value70 +key_71: value71 +key_72: value72 +key_73: value73 +key_74: value74 +key_75: value75 +key_76: value76 +key_77: value77 +key_78: value78 +key_79: value79 +key_7: value7 +key_80: value80 +key_8: value8 +key_9: value9 +length of table 0 +length of table 81 +value1 = value2 +''' +""" + +import std/[strtabs, assertions, syncio] var tab = newStringTable({"key1": "val1", "key2": "val2"}, modeStyleInsensitive) @@ -9,4 +100,18 @@ for key, val in pairs(tab): writeLine(stdout, key, ": ", val) writeLine(stdout, "length of table ", $tab.len) -writeLine(stdout, `%`("$key1 = $key2; ${PATH}", tab, {useEnvironment})) +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 4d4081d39..000000000 --- a/tests/stdlib/tstrutil.nim +++ /dev/null @@ -1,306 +0,0 @@ -discard """ - file: "tstrutil.nim" - output: "ha/home/a1xyz/usr/bin" -""" -# test the new strutils module - -import - strutils - -import macros - -template rejectParse(e) = - try: - discard e - raise newException(AssertionError, "This was supposed to fail: $#!" % astToStr(e)) - except ValueError: discard - -proc testStrip() = - write(stdout, strip(" 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() - for p in split("/home/a1:xyz:/usr/bin", {':'}): - write(stdout, p) - -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 testIsAlphaNumeric = - assert isAlphaNumeric("abcdABC1234") == true - assert isAlphaNumeric("a") == true - assert isAlphaNumeric("abcABC?1234") == false - assert isAlphaNumeric("abcABC 1234") == false - assert isAlphaNumeric(".") == false - -testIsAlphaNumeric() - -proc testIsDigit = - assert isDigit("1") == true - assert isDigit("1234") == true - assert isDigit("abcABC?1234") == false - assert isDigit(".") == false - assert isDigit(":") == false - -testIsDigit() - -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', 13) == 10 - assert "0123456789ABCDEFGAH".rfind('H', 13) == -1 - assert "0123456789ABCDEFGAH".rfind("A") == 17 - assert "0123456789ABCDEFGAH".rfind("A", 13) == 10 - assert "0123456789ABCDEFGAH".rfind("H", 13) == -1 - assert "0123456789ABCDEFGAH".rfind({'A'..'C'}) == 17 - assert "0123456789ABCDEFGAH".rfind({'A'..'C'}, 13) == 12 - assert "0123456789ABCDEFGAH".rfind({'G'..'H'}, 13) == -1 - -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() -testCountLines() -testParseInts() - -assert(insertSep($1000_000) == "1_000_000") -assert(insertSep($232) == "232") -assert(insertSep($12345, ',') == "12,345") -assert(insertSep($0) == "0") - -assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffix") == 0) -assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffi1") == 1) -assert(editDistance("prefix__hallo_suffix", "prefix__HALLO_suffix") == 5) -assert(editDistance("prefix__hallo_suffix", "prefix__ha_suffix") == 3) -assert(editDistance("prefix__hallo_suffix", "prefix") == 14) -assert(editDistance("prefix__hallo_suffix", "suffix") == 14) -assert(editDistance("prefix__hallo_suffix", "prefix__hao_suffix") == 2) -assert(editDistance("main", "malign") == 2) - -assert "/1/2/3".rfind('/') == 4 -assert "/1/2/3".rfind('/', 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) == "") - -main() -#OUT ha/home/a1xyz/usr/bin 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/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 4ab3ba581..0f04168dc 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -1,123 +1,34 @@ -# test the new time module discard """ - file: "ttimes.nim" - output: '''[Suite] ttimes -''' + matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:on; --backend:js --jsbigint64:off -d:nimStringHash2" """ -import - times, os, strutils, unittest - -# $ date --date='@2147483647' -# Tue 19 Jan 03:14:07 GMT 2038 - -proc checkFormat(t: DateTime, format, expected: string) = - let actual = t.format(format) - if actual != expected: - echo "Formatting failure!" - echo "expected: ", expected - echo "actual : ", actual - doAssert false - -let t = fromUnix(2147483647).utc -t.checkFormat("ddd dd MMM hh:mm:ss yyyy", "Tue 19 Jan 03:14:07 2038") -t.checkFormat("ddd ddMMMhh:mm:ssyyyy", "Tue 19Jan03:14:072038") -t.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & - " ss t tt y yy yyy yyyy yyyyy z zz zzz", - "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 +0 +00 +00:00") - -t.checkFormat("yyyyMMddhhmmss", "20380119031407") - -# issue 7620 -let t7620_am = parse("4/15/2017 12:01:02 AM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc()) -t7620_am.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 AM +0") -let t7620_pm = parse("4/15/2017 12:01:02 PM +0", "M/d/yyyy' 'h:mm:ss' 'tt' 'z", utc()) -t7620_pm.checkFormat("M/d/yyyy' 'h:mm:ss' 'tt' 'z", "4/15/2017 12:01:02 PM +0") - -let t2 = fromUnix(160070789).utc # Mon 27 Jan 16:06:29 GMT 1975 -t2.checkFormat("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & - " ss t tt y yy yyy yyyy yyyyy z zz zzz", - "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 +0 +00 +00:00") - -var t4 = fromUnix(876124714).utc # Mon 6 Oct 08:58:34 BST 1997 -t4.checkFormat("M MM MMM MMMM", "10 10 Oct October") - -# Interval tests -(t4 - initTimeInterval(years = 2)).checkFormat("yyyy", "1995") -(t4 - initTimeInterval(years = 7, minutes = 34, seconds = 24)).checkFormat("yyyy mm ss", "1990 24 10") - -# checking dayOfWeek matches known days -doAssert getDayOfWeek(01, mJan, 0000) == dSat -doAssert getDayOfWeek(01, mJan, -0023) == dSat -doAssert getDayOfWeek(21, mSep, 1900) == dFri -doAssert getDayOfWeek(01, mJan, 1970) == dThu -doAssert getDayOfWeek(21, mSep, 1970) == dMon -doAssert getDayOfWeek(01, mJan, 2000) == dSat -doAssert getDayOfWeek(01, mJan, 2021) == dFri - -# toUnix tests with GM timezone -let t4L = fromUnix(876124714).utc -doAssert toUnix(toTime(t4L)) == 876124714 -doAssert toUnix(toTime(t4L)) + t4L.utcOffset == toUnix(toTime(t4)) - -# adding intervals -var - a1L = toUnix(toTime(t4L + initTimeInterval(hours = 1))) + t4L.utcOffset - a1G = toUnix(toTime(t4)) + 60 * 60 -doAssert a1L == a1G - -# subtracting intervals -a1L = toUnix(toTime(t4L - initTimeInterval(hours = 1))) + t4L.utcOffset -a1G = toUnix(toTime(t4)) - (60 * 60) -doAssert a1L == a1G - -# Comparison between Time objects should be detected by compiler -# as 'noSideEffect'. -proc cmpTimeNoSideEffect(t1: Time, t2: Time): bool {.noSideEffect.} = - result = t1 == t2 -doAssert cmpTimeNoSideEffect(0.fromUnix, 0.fromUnix) -# Additionally `==` generic for seq[T] has explicit 'noSideEffect' pragma -# so we can check above condition by comparing seq[Time] sequences -let seqA: seq[Time] = @[] -let seqB: seq[Time] = @[] -doAssert seqA == seqB - -for tz in [ - (0, "+0", "+00", "+00:00"), # UTC - (-3600, "+1", "+01", "+01:00"), # CET - (-39600, "+11", "+11", "+11:00"), # two digits - (-1800, "+0", "+00", "+00:30"), # half an hour - (7200, "-2", "-02", "-02:00"), # positive - (38700, "-10", "-10", "-10:45")]: # positive with three quaters hour - let ti = DateTime(month: mJan, monthday: 1, utcOffset: tz[0]) - doAssert ti.format("z") == tz[1] - doAssert ti.format("zz") == tz[2] - doAssert ti.format("zzz") == tz[3] - -block countLeapYears: - # 1920, 2004 and 2020 are leap years, and should be counted starting at the following year - doAssert countLeapYears(1920) + 1 == countLeapYears(1921) - doAssert countLeapYears(2004) + 1 == countLeapYears(2005) - doAssert countLeapYears(2020) + 1 == countLeapYears(2021) - -block timezoneConversion: - var l = now() - let u = l.utc - l = u.local - - doAssert l.timezone == local() - doAssert u.timezone == utc() +import times, strutils, unittest +import std/assertions + +when not defined(js): + import os + +proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} = + let offset = hours * 3600 + minutes * 60 + seconds + + proc zonedTimeFromAdjTime(adjTime: Time): ZonedTime = + result.isDst = false + result.utcOffset = offset + result.time = adjTime + initDuration(seconds = offset) + + proc zonedTimeFromTime(time: Time): ZonedTime = + result.isDst = false + result.utcOffset = offset + result.time = time + + newTimezone("", zonedTimeFromTime, zonedTimeFromAdjTime) template parseTest(s, f, sExpected: string, ydExpected: int) = let parsed = s.parse(f, utc()) parsedStr = $parsed check parsedStr == sExpected - if parsed.yearday != ydExpected: - echo s - echo parsed.repr - echo parsed.yearday, " exp: ", ydExpected - check(parsed.yearday == ydExpected) + check parsed.yearday == ydExpected template parseTestExcp(s, f: string) = expect ValueError: @@ -130,55 +41,60 @@ template parseTestTimeOnly(s, f, sExpected: string) = # explicit timezone offsets in all tests. template runTimezoneTests() = parseTest("Tuesday at 09:04am on Dec 15, 2015 +0", - "dddd at hh:mmtt on MMM d, yyyy z", "2015-12-15T09:04:00+00:00", 348) + "dddd 'at' hh:mmtt 'on' MMM d, yyyy z", "2015-12-15T09:04:00Z", 348) # ANSIC = "Mon Jan _2 15:04:05 2006" parseTest("Thu Jan 12 15:04:05 2006 +0", "ddd MMM dd HH:mm:ss yyyy z", - "2006-01-12T15:04:05+00:00", 11) + "2006-01-12T15:04:05Z", 11) # UnixDate = "Mon Jan _2 15:04:05 MST 2006" parseTest("Thu Jan 12 15:04:05 2006 +0", "ddd MMM dd HH:mm:ss yyyy z", - "2006-01-12T15:04:05+00:00", 11) + "2006-01-12T15:04:05Z", 11) # RubyDate = "Mon Jan 02 15:04:05 -0700 2006" parseTest("Mon Feb 29 15:04:05 -07:00 2016 +0", "ddd MMM dd HH:mm:ss zzz yyyy z", - "2016-02-29T15:04:05+00:00", 59) # leap day + "2016-02-29T15:04:05Z", 59) # leap day # RFC822 = "02 Jan 06 15:04 MST" parseTest("12 Jan 16 15:04 +0", "dd MMM yy HH:mm z", - "2016-01-12T15:04:00+00:00", 11) + "2016-01-12T15:04:00Z", 11) # RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone parseTest("01 Mar 16 15:04 -07:00", "dd MMM yy HH:mm zzz", - "2016-03-01T22:04:00+00:00", 60) # day after february in leap year + "2016-03-01T22:04:00Z", 60) # day after february in leap year # RFC850 = "Monday, 02-Jan-06 15:04:05 MST" parseTest("Monday, 12-Jan-06 15:04:05 +0", "dddd, dd-MMM-yy HH:mm:ss z", - "2006-01-12T15:04:05+00:00", 11) + "2006-01-12T15:04:05Z", 11) # RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" parseTest("Sun, 01 Mar 2015 15:04:05 +0", "ddd, dd MMM yyyy HH:mm:ss z", - "2015-03-01T15:04:05+00:00", 59) # day after february in non-leap year + "2015-03-01T15:04:05Z", 59) # day after february in non-leap year # RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone parseTest("Thu, 12 Jan 2006 15:04:05 -07:00", "ddd, dd MMM yyyy HH:mm:ss zzz", - "2006-01-12T22:04:05+00:00", 11) + "2006-01-12T22:04:05Z", 11) # RFC3339 = "2006-01-02T15:04:05Z07:00" - parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-ddTHH:mm:ssZzzz", - "2006-01-12T22:04:05+00:00", 11) parseTest("2006-01-12T15:04:05Z-07:00", "yyyy-MM-dd'T'HH:mm:ss'Z'zzz", - "2006-01-12T22:04:05+00:00", 11) + "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-ddTHH:mm:ss.999999999Zzzz", "2006-01-12T22:04:05+00:00", 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, - "2001-01-12T22:04:05+00:00", 11) + "2001-01-12T22:04:05Z", 11) + # timezone offset formats + parseTest("2001-01-12T15:04:05 +7", "yyyy-MM-dd'T'HH:mm:ss z", + "2001-01-12T08:04:05Z", 11) + parseTest("2001-01-12T15:04:05 +07", "yyyy-MM-dd'T'HH:mm:ss zz", + "2001-01-12T08:04:05Z", 11) + parseTest("2001-01-12T15:04:05 +07:00", "yyyy-MM-dd'T'HH:mm:ss zzz", + "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") - #when not defined(testing): - # echo "Kitchen: " & $s.parse(f) - # var ti = timeToTimeInfo(getTime()) - # echo "Todays date after decoding: ", ti - # var tint = timeToTimeInterval(getTime()) - # echo "Todays date after decoding to interval: ", tint # Bug with parse not setting DST properly if the current local DST differs from # the date being parsed. Need to test parse dates both in and out of DST. We - # are testing that be relying on the fact that tranforming a TimeInfo to a Time + # are testing that be relying on the fact that transforming a TimeInfo to a Time # and back again will correctly set the DST value. With the incorrect parse # behavior this will introduce a one hour offset from the named time and the # parsed time if the DST value differs between the current time and the date we @@ -195,48 +111,65 @@ template runTimezoneTests() = let parsedJan = parse("2016-01-05 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz") parsedJul = parse("2016-07-01 04:00:00+01:00", "yyyy-MM-dd HH:mm:sszzz") - doAssert toTime(parsedJan).toUnix == 1451962800 - doAssert toTime(parsedJul).toUnix == 1467342000 + check toTime(parsedJan).toUnix == 1451962800 + check toTime(parsedJul).toUnix == 1467342000 + +template usingTimezone(tz: string, body: untyped) = + when defined(linux) or defined(macosx): + let oldZone = getEnv("TZ") + putEnv("TZ", tz) + body + putEnv("TZ", oldZone) -suite "ttimes": +block: # ttimes # Generate tests for multiple timezone files where available # Set the TZ env var for each test - when defined(Linux) or defined(macosx): - const tz_dir = "/usr/share/zoneinfo" + when defined(linux) or defined(macosx): + let tz_dir = getEnv("TZDIR", "/usr/share/zoneinfo") const f = "yyyy-MM-dd HH:mm zzz" - - let orig_tz = getEnv("TZ") + var tz_cnt = 0 - for tz_fn in walkFiles(tz_dir & "/**/*"): - if symlinkExists(tz_fn) or tz_fn.endsWith(".tab") or - tz_fn.endsWith(".list"): + for timezone in walkFiles(tz_dir & "/**/*"): + if symlinkExists(timezone) or timezone.endsWith(".tab") or + timezone.endsWith(".list"): continue - test "test for " & tz_fn: - tz_cnt.inc - putEnv("TZ", tz_fn) - runTimezoneTests() + usingTimezone(timezone): + test "test for " & timezone: + tz_cnt.inc + runTimezoneTests() test "enough timezone files tested": check tz_cnt > 10 - test "dst handling": - putEnv("TZ", "Europe/Stockholm") - # In case of an impossible time, the time is moved to after the impossible time period - check initDateTime(26, mMar, 2017, 02, 30, 00).format(f) == "2017-03-26 03:30 +02:00" - # In case of an ambiguous time, the earlier time is choosen - check initDateTime(29, mOct, 2017, 02, 00, 00).format(f) == "2017-10-29 02:00 +02:00" + else: + # not on Linux or macosx: run in the local timezone only + test "parseTest": + runTimezoneTests() + + block: # dst handling + usingTimezone("Europe/Stockholm"): + # In case of an impossible time, the time is moved to after the + # impossible time period + check initDateTime(26, mMar, 2017, 02, 30, 00).format(f) == + "2017-03-26 03:30 +02:00" + # In case of an ambiguous time, the earlier time is chosen + check initDateTime(29, mOct, 2017, 02, 00, 00).format(f) == + "2017-10-29 02:00 +02:00" # These are just dates on either side of the dst switch - check initDateTime(29, mOct, 2017, 01, 00, 00).format(f) == "2017-10-29 01:00 +02:00" + check initDateTime(29, mOct, 2017, 01, 00, 00).format(f) == + "2017-10-29 01:00 +02:00" check initDateTime(29, mOct, 2017, 01, 00, 00).isDst - check initDateTime(29, mOct, 2017, 03, 01, 00).format(f) == "2017-10-29 03:01 +01:00" + check initDateTime(29, mOct, 2017, 03, 01, 00).format(f) == + "2017-10-29 03:01 +01:00" check (not initDateTime(29, mOct, 2017, 03, 01, 00).isDst) - - check initDateTime(21, mOct, 2017, 01, 00, 00).format(f) == "2017-10-21 01:00 +02:00" - test "issue #6520": - putEnv("TZ", "Europe/Stockholm") + check initDateTime(21, mOct, 2017, 01, 00, 00).format(f) == + "2017-10-21 01:00 +02:00" + + block: # issue #6520 + usingTimezone("Europe/Stockholm"): var local = fromUnix(1469275200).local var utc = fromUnix(1469275200).utc @@ -244,128 +177,138 @@ suite "ttimes": local.utcOffset = 0 check claimedOffset == utc.toTime - local.toTime - test "issue #5704": - putEnv("TZ", "Asia/Seoul") - let diff = parse("19700101-000000", "yyyyMMdd-hhmmss").toTime - parse("19000101-000000", "yyyyMMdd-hhmmss").toTime + 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": - putEnv("TZ", "Europe/Stockholm") + 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 "datetime before epoch": - check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52+00:00" - - test "adding/subtracting time across dst": - putenv("TZ", "Europe/Stockholm") + check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00" + 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" var dt2 = initDateTime(29, mOct, 2017, 02, 59, 59) check $(dt2 + 1.seconds) == "2017-10-29T02:00:00+01:00" - putEnv("TZ", orig_tz) + block: # datetime before epoch + check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52Z" - else: - # not on Linux or macosx: run in the local timezone only - test "parseTest": - runTimezoneTests() - - 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 "dynamic timezone": - proc staticOffset(offset: int): Timezone = - proc zoneInfoFromTz(adjTime: Time): ZonedTime = - result.isDst = false - result.utcOffset = offset - result.adjTime = adjTime - - proc zoneInfoFromUtc(time: Time): ZonedTime = - result.isDst = false - result.utcOffset = offset - result.adjTime = fromUnix(time.toUnix - offset) - - result.name = "" - result.zoneInfoFromTz = zoneInfoFromTz - result.zoneInfoFromUtc = zoneInfoFromUtc - - let tz = staticOffset(-9000) + block: # incorrect inputs: year (yyyy/uuuu) + parseTestExcp("-0001", "yyyy") + parseTestExcp("-0001", "YYYY") + parseTestExcp("1", "yyyy") + parseTestExcp("12345", "yyyy") + parseTestExcp("1", "uuuu") + parseTestExcp("12345", "uuuu") + parseTestExcp("-1 BC", "UUUU g") + + block: # incorrect inputs: invalid sign + parseTestExcp("+1", "YYYY") + parseTestExcp("+1", "dd") + parseTestExcp("+1", "MM") + parseTestExcp("+1", "hh") + parseTestExcp("+1", "mm") + parseTestExcp("+1", "ss") + + block: # _ as a separator + discard parse("2000_01_01", "YYYY'_'MM'_'dd") + + block: # dynamic timezone + let tz = staticTz(seconds = -9000) let dt = initDateTime(1, mJan, 2000, 12, 00, 00, tz) check dt.utcOffset == -9000 check dt.isDst == false check $dt == "2000-01-01T12:00:00+02:30" - check $dt.utc == "2000-01-01T09:30:00+00:00" + 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 "subtract months": + block: # TimeInterval + let t = fromUnix(876124714).utc # Mon 6 Oct 08:58:34 BST 1997 + # Interval tests + let t2 = t - 2.years + check t2.year == 1995 + let t3 = (t - 7.years - 34.minutes - 24.seconds) + check t3.year == 1990 + check t3.minute == 24 + check t3.second == 10 + check (t + 1.hours).toTime.toUnix == t.toTime.toUnix + 60 * 60 + check (t - 1.hours).toTime.toUnix == t.toTime.toUnix - 60 * 60 + + block: # TimeInterval - months var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc()) - check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00+00:00" + check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00Z" dt = initDateTime(15, mMar, 2017, 00, 00, 00, utc()) - check $(dt - initTimeInterval(months = 1)) == "2017-02-15T00:00:00+00:00" + check $(dt - initTimeInterval(months = 1)) == "2017-02-15T00:00:00Z" dt = initDateTime(31, mMar, 2017, 00, 00, 00, utc()) # This happens due to monthday overflow. It's consistent with Phobos. - check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00+00:00" + 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) check dt.nanosecond == convert(Milliseconds, Nanoseconds, 1) check d(seconds = 1, milliseconds = 500) * 2 == d(seconds = 3) check d(seconds = 3) div 2 == d(seconds = 1, milliseconds = 500) - check d(milliseconds = 1001).seconds == 1 + check d(milliseconds = 1001).inSeconds == 1 check d(seconds = 1, milliseconds = 500) - d(milliseconds = 1250) == d(milliseconds = 250) check d(seconds = 1, milliseconds = 1) < d(seconds = 1, milliseconds = 2) @@ -378,19 +321,19 @@ 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) discard initDateTime(1, mJan, 35_000, 12, 00, 00) # with duration/timeinterval - let dt = initDateTime(1, mJan, 35_000, 12, 00, 00, utc()) + + let dt = initDateTime(1, mJan, -35_000, 12, 00, 00, utc()) + initDuration(seconds = 1) check dt.second == 1 let dt2 = dt + 35_001.years - check $dt2 == "0001-01-01T12:00:01+00:00" + check $dt2 == "0001-01-01T12:00:01Z" - test "compare datetimes": + block: # compare datetimes var dt1 = now() var dt2 = dt1 check dt1 == dt2 @@ -398,32 +341,444 @@ 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 check now + convert(Seconds, Nanoseconds, 1).nanoseconds == now + 1.seconds check now + 1.weeks == now + 7.days check now - 1.seconds == now - 3.seconds + 2.seconds check now + 65.seconds == now + 1.minutes + 5.seconds check now + 60.minutes == now + 1.hours check now + 24.hours == now + 1.days - check now + 13.months == now + 1.years + 1.months + if not isSpecial: + check now + 13.months == now + 1.years + 1.months check toUnix(fromUnix(0) + 2.seconds) == 2 check toUnix(fromUnix(0) - 2.seconds) == -2 var ti1 = now + 1.years ti1 = ti1 - 1.years - check ti1 == now + if not isSpecial: + check ti1 == now ti1 = ti1 + 1.days - check ti1 == now + 1.days + if not isSpecial: + check ti1 == now + 1.days # Bug with adding a day to a Time let day = 24.hours let tomorrow = now + day check tomorrow - now == initDuration(days = 1) - - test "fromWinTime/toWinTime": - check 0.fromUnix.toWinTime.fromWinTime.toUnix == 0 - check (-1).fromWinTime.nanosecond == convert(Seconds, Nanoseconds, 1) - 100 - check -1.fromWinTime.toWinTime == -1 - # One nanosecond is discarded due to differences in time resolution - check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100 \ No newline at end of file + + # Disabled for JS because it fails due to precision errors + # (The JS target uses float64 for int64). + when not defined(js): + test "fromWinTime/toWinTime": + check 0.fromUnix.toWinTime.fromWinTime.toUnix == 0 + check (-1).fromWinTime.nanosecond == convert(Seconds, Nanoseconds, 1) - 100 + check (-1).fromWinTime.toWinTime == -1 + # One nanosecond is discarded due to differences in time resolution + check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100 + check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100 + + 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" + + block: # format + var dt = initDateTime(1, mJan, -0001, + 17, 01, 02, 123_456_789, + staticTz(hours = 1, minutes = 2, seconds = 3)) + check dt.format("d") == "1" + check dt.format("dd") == "01" + check dt.format("ddd") == "Fri" + check dt.format("dddd") == "Friday" + check dt.format("h") == "5" + check dt.format("hh") == "05" + check dt.format("H") == "17" + check dt.format("HH") == "17" + check dt.format("m") == "1" + check dt.format("mm") == "01" + check dt.format("M") == "1" + check dt.format("MM") == "01" + check dt.format("MMM") == "Jan" + check dt.format("MMMM") == "January" + check dt.format("s") == "2" + check dt.format("ss") == "02" + check dt.format("t") == "P" + check dt.format("tt") == "PM" + check dt.format("yy") == "02" + check dt.format("yyyy") == "0002" + check dt.format("YYYY") == "2" + check dt.format("uuuu") == "-0001" + check dt.format("UUUU") == "-1" + check dt.format("z") == "-1" + check dt.format("zz") == "-01" + check dt.format("zzz") == "-01:02" + check dt.format("zzzz") == "-01:02:03" + check dt.format("g") == "BC" + + check dt.format("fff") == "123" + check dt.format("ffffff") == "123456" + check dt.format("fffffffff") == "123456789" + dt.nanosecond = 1 + check dt.format("fff") == "000" + check dt.format("ffffff") == "000000" + check dt.format("fffffffff") == "000000001" + + dt.year = 12345 + check dt.format("yyyy") == "+12345" + check dt.format("uuuu") == "+12345" + dt.year = -12345 + check dt.format("yyyy") == "+12346" + check dt.format("uuuu") == "-12345" + + expect ValueError: + discard initTimeFormat("'") + + expect ValueError: + discard initTimeFormat("'foo") + + expect ValueError: + discard initTimeFormat("foo'") + + for tz in [ + (staticTz(seconds = 0), "+0", "+00", "+00:00"), # UTC + (staticTz(seconds = -3600), "+1", "+01", "+01:00"), # CET + (staticTz(seconds = -39600), "+11", "+11", "+11:00"), # two digits + (staticTz(seconds = -1800), "+0", "+00", "+00:30"), # half an hour + (staticTz(seconds = 7200), "-2", "-02", "-02:00"), # positive + (staticTz(seconds = 38700), "-10", "-10", "-10:45")]: # positive with three quaters hour + let dt = initDateTime(1, mJan, 2000, 00, 00, 00, tz[0]) + doAssert dt.format("z") == tz[1] + doAssert dt.format("zz") == tz[2] + doAssert dt.format("zzz") == tz[3] + + 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"], + ddd: ["Red", "Ora.", "Yel.", "Gre.", "Blu.", "Vio.", "Whi."], + dddd: ["Red", "Orange", "Yellow", "Green", "Blue", "Violet", "White"], + ) + var dt = initDateTime(5, mJan, 2010, 17, 01, 02, utc()) + check dt.format("d", loc) == "5" + check dt.format("dd", loc) == "05" + check dt.format("ddd", loc) == "Ora." + check dt.format("dddd", loc) == "Orange" + check dt.format("M", loc) == "1" + check dt.format("MM", loc) == "01" + check dt.format("MMM", loc) == "Fir" + check dt.format("MMMM", loc) == "Firsty" + + block: # parse + check $parse("20180101", "yyyyMMdd", utc()) == "2018-01-01T00:00:00Z" + parseTestExcp("+120180101", "yyyyMMdd") + + check parse("1", "YYYY", utc()).year == 1 + check parse("1 BC", "YYYY g", utc()).year == 0 + check parse("0001 BC", "yyyy g", utc()).year == 0 + check parse("+12345 BC", "yyyy g", utc()).year == -12344 + check parse("1 AD", "YYYY g", utc()).year == 1 + check parse("0001 AD", "yyyy g", utc()).year == 1 + check parse("+12345 AD", "yyyy g", utc()).year == 12345 + + check parse("-1", "UUUU", utc()).year == -1 + check parse("-0001", "uuuu", utc()).year == -1 + + discard parse("foobar", "'foobar'") + discard parse("foo'bar", "'foo''''bar'") + discard parse("'", "''") + + parseTestExcp("2000 A", "yyyy g") + + 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"], + ddd: ["Red", "Ora.", "Yel.", "Gre.", "Blu.", "Vio.", "Whi."], + dddd: ["Red", "Orange", "Yellow", "Green", "Blue", "Violet", "White"], + ) + 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" + + block: # timezoneConversion + var l = now() + let u = l.utc + l = u.local + + check l.timezone == local() + check u.timezone == utc() + + block: # getDayOfWeek + check getDayOfWeek(01, mJan, 0000) == dSat + check getDayOfWeek(01, mJan, -0023) == dSat + check getDayOfWeek(21, mSep, 1900) == dFri + check getDayOfWeek(01, mJan, 1970) == dThu + check getDayOfWeek(21, mSep, 1970) == dMon + check getDayOfWeek(01, mJan, 2000) == dSat + check getDayOfWeek(01, mJan, 2021) == dFri + + 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 + + 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 + + block: # between - empty interval + let x = now() + let y = x + doAssert x + between(x, y) == y + + 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 + + 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) + let y = initDateTime(29, mOct, 2018, 00, 00, 00) + doAssert between(x, y) == 24.hours + 30.minutes + doAssert x + between(x, y) == y + + 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"): + let x = initDateTime(24, mOct, 1987, 00, 00, 00) + let y = initDateTime(26, mOct, 1987, 23, 00, 00) + doAssert x + between(x, y) == y + doAssert y + between(y, x) == x + + 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 + + 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 + + block: # between - misc + block: + let x = initDateTime(31, mDec, 2000, 12, 00, 00, utc()) + let y = initDateTime(01, mJan, 2001, 00, 00, 00, utc()) + doAssert between(x, y) == 12.hours + + block: + let x = initDateTime(31, mDec, 2000, 12, 00, 00, utc()) + let y = initDateTime(02, mJan, 2001, 00, 00, 00, utc()) + doAssert between(x, y) == 1.days + 12.hours + + block: + let x = initDateTime(31, mDec, 1995, 00, 00, 00, utc()) + let y = initDateTime(01, mFeb, 2000, 00, 00, 00, utc()) + doAssert x + between(x, y) == y + + block: + let x = initDateTime(01, mDec, 1995, 00, 00, 00, utc()) + let y = initDateTime(31, mJan, 2000, 00, 00, 00, utc()) + doAssert x + between(x, y) == y + + block: + let x = initDateTime(31, mJan, 2000, 00, 00, 00, utc()) + let y = initDateTime(01, mFeb, 2000, 00, 00, 00, utc()) + doAssert x + between(x, y) == y + + block: + let x = initDateTime(01, mJan, 1995, 12, 00, 00, utc()) + let y = initDateTime(01, mFeb, 1995, 00, 00, 00, utc()) + doAssert between(x, y) == 4.weeks + 2.days + 12.hours + + block: + let x = initDateTime(31, mJan, 1995, 00, 00, 00, utc()) + let y = initDateTime(10, mFeb, 1995, 00, 00, 00, utc()) + doAssert x + between(x, y) == y + + block: + let x = initDateTime(31, mJan, 1995, 00, 00, 00, utc()) + let y = initDateTime(10, mMar, 1995, 00, 00, 00, utc()) + doAssert x + between(x, y) == y + doAssert between(x, y) == 1.months + 1.weeks + + block: # default DateTime https://github.com/nim-lang/RFCs/issues/211 + var num = 0 + for ai in Month: num.inc + check num == 12 + + var a: DateTime + check a == DateTime.default + check not a.isInitialized + check $a == "Uninitialized DateTime" + + expect(AssertionDefect): discard getDayOfWeek(a.monthday, a.month, a.year) + expect(AssertionDefect): discard a.toTime + expect(AssertionDefect): discard a.utc() + expect(AssertionDefect): discard a.local() + expect(AssertionDefect): discard a.inZone(utc()) + expect(AssertionDefect): discard a + initDuration(seconds = 1) + expect(AssertionDefect): discard a + initTimeInterval(seconds = 1) + expect(AssertionDefect): discard a.isLeapDay + expect(AssertionDefect): discard a < a + expect(AssertionDefect): discard a <= a + expect(AssertionDefect): discard getDateStr(a) + expect(AssertionDefect): discard getClockStr(a) + expect(AssertionDefect): discard a.format "yyyy" + expect(AssertionDefect): discard a.format initTimeFormat("yyyy") + expect(AssertionDefect): discard between(a, a) + + block: # inX procs + doAssert initDuration(seconds = 1).inSeconds == 1 + doAssert initDuration(seconds = -1).inSeconds == -1 + doAssert initDuration(seconds = -1, nanoseconds = 1).inSeconds == 0 + doAssert initDuration(nanoseconds = -1).inSeconds == 0 + 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 689453c76..653016ea9 100644 --- a/tests/stdlib/tunidecode.nim +++ b/tests/stdlib/tunidecode.nim @@ -1,12 +1,13 @@ discard """ cmd: "nim $target --hints:on -d:embedUnidecodeTable $options $file" - output: "Ausserst" """ import unidecode -loadUnidecodeTable("lib/pure/unidecode/unidecode.dat") +import std/unidecode # #14112 +import std/assertions -#assert unidecode("\x53\x17\x4E\xB0") == "Bei Jing" -echo unidecode("Äußerst") +loadUnidecodeTable("lib/pure/unidecode/unidecode.dat") +doAssert unidecode("北京") == "Bei Jing " +doAssert unidecode("Äußerst") == "Ausserst" diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index 86b9fd037..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 @@ -13,12 +15,16 @@ discard """ [Suite] bug #5784 -[Suite] test name filtering +[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 @@ -29,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)) @@ -45,17 +51,16 @@ test "unittest multiple requires": require(true) -import math, random -from strutils import parseInt +import std/random +from std/strutils import parseInt proc defectiveRobot() = - randomize() - case random(1..4) + 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, AssertionError: + expect IOError, OSError, ValueError, AssertionDefect: defectiveRobot() var @@ -123,38 +128,67 @@ suite "bug #5784": var obj: Obj check obj.isNil or obj.field == 0 -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") +type + SomeType = object + value: int + children: seq[SomeType] + +# bug #5252 + +proc `==`(a, b: SomeType): bool = + return a.value == b.value + +suite "test suite": + test "test": + let a = SomeType(value: 10) + let b = SomeType(value: 10) + + check(a == b) + +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 new file mode 100644 index 000000000..c29e0de01 --- /dev/null +++ b/tests/stdlib/tunittesttemplate.nim @@ -0,0 +1,25 @@ +discard """ + exitcode: 1 + outputsub: ''' + tunittesttemplate.nim(20, 12): Check failed: a.b == 2 + a.b was 0 + [FAILED] 1 +''' +""" + + + +# bug #6736 +import std/unittest + +type + A = object + b: int + +template t: untyped = + check(a.b == 2) + +suite "1": + test "1": + var a = A(b: 0) + t() 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 new file mode 100644 index 000000000..f9624ee5b --- /dev/null +++ b/tests/stdlib/tvarints.nim @@ -0,0 +1,73 @@ +discard """ + 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] + var got: uint64 + + for test in [0xFFFF_FFFF_FFFFF_FFFFu64, 77u64, 0u64, 10_000_000u64, uint64(high(int64)), + uint64(high(int32)), uint64(high(int32)), uint64(high(int64))]: + let wrLen = writeVu64(dest, test) + let rdLen = readVu64(dest, got) + doAssert wrLen == rdLen + doAssert got == test + + for test in 0u64..300u64: + let wrLen = writeVu64(dest, test) + let rdLen = readVu64(dest, got) + doAssert wrLen == rdLen + doAssert got == test + + # check this also works for floats: + for test in [0.0, 0.1, 2.0, +Inf, NegInf]: + let t = cast[uint64](test) + let wrLenB = writeVu64(dest, t) + let rdLenB = readVu64(dest, got) + doAssert wrLenB == rdLenB + doAssert cast[float64](got) == test + +block: + var hugeIntArray: array[9, byte] + var readedInt: uint64 + + 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: + 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/twalker.nim b/tests/stdlib/twalker.nim deleted file mode 100644 index 91c97df01..000000000 --- a/tests/stdlib/twalker.nim +++ /dev/null @@ -1,13 +0,0 @@ -# iterate over all files with a given filter: - -import - "../../lib/pure/os.nim", ../../ lib / pure / times - -proc main(filter: string) = - for filename in walkFiles(filter): - writeLine(stdout, filename) - - for key, val in envPairs(): - writeLine(stdout, key & '=' & val) - -main("*.nim") 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 new file mode 100644 index 000000000..3da230b5e --- /dev/null +++ b/tests/stdlib/twrapnils.nim @@ -0,0 +1,224 @@ +discard """ + matrix: "--mm:refc; --mm:orc" +""" + +import std/wrapnils +from std/options import get, isSome +import std/assertions + +proc checkNotZero(x: float): float = + doAssert x != 0 + x + +proc main() = + 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/txmlgen.nim b/tests/stdlib/txmlgen.nim deleted file mode 100644 index fa1dffe56..000000000 --- a/tests/stdlib/txmlgen.nim +++ /dev/null @@ -1,12 +0,0 @@ -discard """ - file: "txmlgen.nim" - output: "<h1><a href=\"http://force7.de/nim\">Nim</a></h1>" -""" -import htmlgen - -var nim = "Nim" -echo h1(a(href="http://force7.de/nim", nim)) - - - - diff --git a/tests/stdlib/txmltree.nim b/tests/stdlib/txmltree.nim index a849859e3..add12a3fc 100644 --- a/tests/stdlib/txmltree.nim +++ b/tests/stdlib/txmltree.nim @@ -1,27 +1,120 @@ discard """ - file: "txmltree.nim" - output: '''true -true -true -true -true -''' + matrix: "--mm:refc; --mm:orc" """ -import xmltree, strtabs +import std/[xmltree, assertions, xmlparser] -var x = <>a(href="nim.de", newText("www.nim-test.de")) -echo($x == "<a href=\"nim.de\">www.nim-test.de</a>") +block: + var + x: XmlNode -echo(newText("foo").innerText == "foo") -echo(newEntity("bar").innerText == "bar") -echo(newComment("baz").innerText == "") + x = <>a(href = "http://nim-lang.org", newText("Nim rules.")) + doAssert $x == """<a href="http://nim-lang.org">Nim rules.</a>""" -let y = newXmlTree("x", [ - newText("foo"), - newXmlTree("y", [ - newText("bar") + 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",[]) + ]) + ]) ]) -]) -echo(y.innerText == "foobar") + + 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 == "()" |