diff options
| author | Eugen Wissner <belka@caraus.de> | 2025-08-25 16:09:03 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2025-08-25 16:09:03 +0200 |
| commit | b8fa670c5a1e6e856ced5d1be7f04360e91e0c7d (patch) | |
| tree | 14cbbbc198a6bc2c0fb56219af375c6ddf95a4e1 /source | |
| parent | 720d259cfc1db61d2deadca8b3e7751182d789b2 (diff) | |
| download | tanya-b8fa670c5a1e6e856ced5d1be7f04360e91e0c7d.tar.gz | |
Merge os, middle and meta subpackages
Diffstat (limited to 'source')
| -rw-r--r-- | source/tanya/algorithm/iteration.d | 4 | ||||
| -rw-r--r-- | source/tanya/algorithm/mutation.d | 5 | ||||
| -rw-r--r-- | source/tanya/container/array.d | 4 | ||||
| -rw-r--r-- | source/tanya/container/buffer.d | 2 | ||||
| -rw-r--r-- | source/tanya/container/entry.d | 4 | ||||
| -rw-r--r-- | source/tanya/container/hashtable.d | 4 | ||||
| -rw-r--r-- | source/tanya/container/list.d | 4 | ||||
| -rw-r--r-- | source/tanya/container/set.d | 4 | ||||
| -rw-r--r-- | source/tanya/container/string.d | 4 | ||||
| -rw-r--r-- | source/tanya/conv.d | 5 | ||||
| -rw-r--r-- | source/tanya/format.d | 9 | ||||
| -rw-r--r-- | source/tanya/hash/lookup.d | 4 | ||||
| -rw-r--r-- | source/tanya/math/package.d | 434 | ||||
| -rw-r--r-- | source/tanya/net/iface.d | 4 | ||||
| -rw-r--r-- | source/tanya/net/inet.d | 4 | ||||
| -rw-r--r-- | source/tanya/net/ip.d | 4 | ||||
| -rw-r--r-- | source/tanya/range/adapter.d | 4 | ||||
| -rw-r--r-- | source/tanya/range/primitive.d | 4 | ||||
| -rw-r--r-- | source/tanya/test/assertion.d | 106 | ||||
| -rw-r--r-- | source/tanya/test/package.d | 18 | ||||
| -rw-r--r-- | source/tanya/test/stub.d | 397 |
21 files changed, 559 insertions, 469 deletions
diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d index 41c5f8b..df1027d 100644 --- a/source/tanya/algorithm/iteration.d +++ b/source/tanya/algorithm/iteration.d @@ -20,10 +20,10 @@ */ module tanya.algorithm.iteration; -import std.traits : Unqual, isMutable; +import std.traits; import std.typecons; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; import tanya.range; private struct SingletonByValue(E) diff --git a/source/tanya/algorithm/mutation.d b/source/tanya/algorithm/mutation.d index e1e7c22..1a1bb56 100644 --- a/source/tanya/algorithm/mutation.d +++ b/source/tanya/algorithm/mutation.d @@ -14,11 +14,10 @@ */ module tanya.algorithm.mutation; -import std.traits : Unqual, hasElaborateAssign, hasElaborateCopyConstructor, hasElaborateDestructor, isAssignable, - isDynamicArray; +import std.traits; static import tanya.memory.lifetime; static import tanya.memory.op; -import tanya.meta.trait; +import tanya.meta; import tanya.range; /** diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d index b3b3455..b6c9c5d 100644 --- a/source/tanya/container/array.d +++ b/source/tanya/container/array.d @@ -18,11 +18,11 @@ import core.checkedint; import std.algorithm.comparison; import std.algorithm.iteration; import std.algorithm.mutation : bringToFront; -import std.traits : PointerTarget, Unqual, hasElaborateDestructor, isImplicitlyConvertible, isCopyable; +import std.traits; import tanya.algorithm.mutation; import tanya.memory.allocator; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; import tanya.range; /** diff --git a/source/tanya/container/buffer.d b/source/tanya/container/buffer.d index 35d83a5..ec8cbf0 100644 --- a/source/tanya/container/buffer.d +++ b/source/tanya/container/buffer.d @@ -16,7 +16,7 @@ module tanya.container.buffer; import std.traits : isScalarType; import tanya.memory.allocator; -import tanya.meta.trait; +import tanya.meta; version (unittest) { diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d index 52b3e6a..c4d74c2 100644 --- a/source/tanya/container/entry.d +++ b/source/tanya/container/entry.d @@ -14,11 +14,11 @@ */ module tanya.container.entry; -import std.traits : Unqual, hasElaborateDestructor; +import std.traits; import tanya.container.array; import tanya.memory.allocator; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; package struct SEntry(T) { diff --git a/source/tanya/container/hashtable.d b/source/tanya/container/hashtable.d index d74783c..5e9a4e4 100644 --- a/source/tanya/container/hashtable.d +++ b/source/tanya/container/hashtable.d @@ -15,14 +15,14 @@ module tanya.container.hashtable; import std.algorithm.iteration; -import std.traits : CopyConstness, Unqual, ifTestable, isMutable; +import std.traits; import tanya.algorithm.mutation; import tanya.container.array; import tanya.container.entry; import tanya.hash.lookup; import tanya.memory.allocator; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; import tanya.range.primitive; /** diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 23af88d..ddef391 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -17,11 +17,11 @@ module tanya.container.list; import std.algorithm.comparison; import std.algorithm.iteration; -import std.traits : Unqual, isImplicitlyConvertible, isCopyable; +import std.traits; import tanya.container.entry; import tanya.memory.allocator; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; import tanya.range.array; import tanya.range.primitive; diff --git a/source/tanya/container/set.d b/source/tanya/container/set.d index 67fc66a..5a4f64c 100644 --- a/source/tanya/container/set.d +++ b/source/tanya/container/set.d @@ -15,13 +15,13 @@ */ module tanya.container.set; -import std.traits : CopyConstness, Unqual, ifTestable, isImplicitlyConvertible, isMutable; +import std.traits; import tanya.container.array; import tanya.container.entry; import tanya.hash.lookup; import tanya.memory.allocator; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; import tanya.range.primitive; /** diff --git a/source/tanya/container/string.d b/source/tanya/container/string.d index 71328f2..10db553 100644 --- a/source/tanya/container/string.d +++ b/source/tanya/container/string.d @@ -28,12 +28,12 @@ module tanya.container.string; import std.algorithm.comparison; import std.algorithm.mutation : bringToFront; -import std.traits : CopyConstness, Unqual, isInstanceOf, isSomeChar, isNarrowString; +import std.traits; import tanya.algorithm.mutation; import tanya.hash.lookup; import tanya.memory.allocator; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; import tanya.range.array; import tanya.range.primitive; diff --git a/source/tanya/conv.d b/source/tanya/conv.d index 8f46bd3..3a75d62 100644 --- a/source/tanya/conv.d +++ b/source/tanya/conv.d @@ -14,11 +14,10 @@ */ module tanya.conv; -import std.traits : Unsigned, isNumeric, Largest, Unqual, EnumMembers, isFloatingPoint, isSomeChar, isSigned, - isUnsigned, isIntegral, isSomeString; +import std.traits; import tanya.container.string; import tanya.memory.allocator; -import tanya.meta.trait; +import tanya.meta; import tanya.range; /** diff --git a/source/tanya/format.d b/source/tanya/format.d index 0b031d1..089ed49 100644 --- a/source/tanya/format.d +++ b/source/tanya/format.d @@ -49,12 +49,13 @@ module tanya.format; import std.algorithm.comparison; import std.ascii; -import std.traits : Unqual, isPointer, isSomeChar, isFloatingPoint, isSomeFunction, isIntegral, isSomeString; +import std.math : signbit; +import std.meta; +import std.traits; import tanya.container.string; import tanya.math; static import tanya.memory.op; -import tanya.meta.metafunction; -import tanya.meta.trait; +import tanya.meta; import tanya.range; // Returns the last part of buffer with converted number. @@ -1952,7 +1953,7 @@ private const(char)[] real2String(double value, const FloatBits!double bits = { value }; exponent = (bits.integral >> 52) & 0x7ff; - sign = signBit(value); + sign = !!signbit(value); if (sign) { value = -value; diff --git a/source/tanya/hash/lookup.d b/source/tanya/hash/lookup.d index 706cca5..eb26caa 100644 --- a/source/tanya/hash/lookup.d +++ b/source/tanya/hash/lookup.d @@ -14,8 +14,8 @@ */ module tanya.hash.lookup; -import std.traits : isScalarType, isPointer, isSomeChar, isArray, isIntegral, isBoolean; -import tanya.meta.trait; +import std.traits; +import tanya.meta; import tanya.range.primitive; private struct Hasher diff --git a/source/tanya/math/package.d b/source/tanya/math/package.d index ef0592e..66923b6 100644 --- a/source/tanya/math/package.d +++ b/source/tanya/math/package.d @@ -22,8 +22,8 @@ module tanya.math; import std.math; -import std.traits : Unqual, isFloatingPoint; -import tanya.meta.trait; +import std.traits; +import tanya.meta; /// Floating-point number precisions according to IEEE-754. enum IEEEPrecision : ubyte @@ -113,433 +113,3 @@ package(tanya) union FloatBits(F) static assert(false, "Unsupported IEEE 754 floating point precision"); } } - -/** - * Floating-point number classifications. - */ -enum FloatingPointClass : ubyte -{ - /** - * Not a Number. - * - * See_Also: $(D_PSYMBOL isNaN). - */ - nan, - - /// Zero. - zero, - - /** - * Infinity. - * - * See_Also: $(D_PSYMBOL isInfinity). - */ - infinite, - - /** - * Denormalized number. - * - * See_Also: $(D_PSYMBOL isSubnormal). - */ - subnormal, - - /** - * Normalized number. - * - * See_Also: $(D_PSYMBOL isNormal). - */ - normal, -} - -/** - * Returns whether $(D_PARAM x) is a NaN, zero, infinity, subnormal or - * normalized number. - * - * This function doesn't distinguish between negative and positive infinity, - * negative and positive NaN or negative and positive zero. - * - * Params: - * F = Type of the floating point number. - * x = Floating point number. - * - * Returns: Classification of $(D_PARAM x). - */ -FloatingPointClass classify(F)(F x) -if (isFloatingPoint!F) -{ - if (x == 0) - { - return FloatingPointClass.zero; - } - FloatBits!F bits; - bits.floating = abs(x); - - static if (ieeePrecision!F == IEEEPrecision.single) - { - if (bits.integral > bits.expMask) - { - return FloatingPointClass.nan; - } - else if (bits.integral == bits.expMask) - { - return FloatingPointClass.infinite; - } - else if (bits.integral < (1 << 23)) - { - return FloatingPointClass.subnormal; - } - } - else static if (ieeePrecision!F == IEEEPrecision.double_) - { - if (bits.integral > bits.expMask) - { - return FloatingPointClass.nan; - } - else if (bits.integral == bits.expMask) - { - return FloatingPointClass.infinite; - } - else if (bits.integral < (1L << 52)) - { - return FloatingPointClass.subnormal; - } - } - else static if (ieeePrecision!F == IEEEPrecision.doubleExtended) - { - if (bits.exp == bits.expMask) - { - if ((bits.mantissa & bits.mantissaMask) == 0) - { - return FloatingPointClass.infinite; - } - else - { - return FloatingPointClass.nan; - } - } - else if (bits.exp == 0) - { - return FloatingPointClass.subnormal; - } - else if (bits.mantissa < (1L << 63)) // "Unnormal". - { - return FloatingPointClass.nan; - } - } - - return FloatingPointClass.normal; -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(classify(0.0) == FloatingPointClass.zero); - assert(classify(double.nan) == FloatingPointClass.nan); - assert(classify(double.infinity) == FloatingPointClass.infinite); - assert(classify(-double.infinity) == FloatingPointClass.infinite); - assert(classify(1.4) == FloatingPointClass.normal); - assert(classify(1.11254e-307 / 10) == FloatingPointClass.subnormal); - - assert(classify(0.0f) == FloatingPointClass.zero); - assert(classify(float.nan) == FloatingPointClass.nan); - assert(classify(float.infinity) == FloatingPointClass.infinite); - assert(classify(-float.infinity) == FloatingPointClass.infinite); - assert(classify(0.3) == FloatingPointClass.normal); - assert(classify(5.87747e-38f / 10) == FloatingPointClass.subnormal); - - assert(classify(0.0L) == FloatingPointClass.zero); - assert(classify(real.nan) == FloatingPointClass.nan); - assert(classify(real.infinity) == FloatingPointClass.infinite); - assert(classify(-real.infinity) == FloatingPointClass.infinite); -} - -/** - * Determines whether $(D_PARAM x) is a finite number. - * - * Params: - * F = Type of the floating point number. - * x = Floating point number. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM x) is a finite number, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isInfinity). - */ -bool isFinite(F)(F x) -if (isFloatingPoint!F) -{ - FloatBits!F bits; - static if (ieeePrecision!F == IEEEPrecision.single - || ieeePrecision!F == IEEEPrecision.double_) - { - bits.floating = x; - bits.integral &= bits.expMask; - return bits.integral != bits.expMask; - } - else static if (ieeePrecision!F == IEEEPrecision.doubleExtended) - { - bits.floating = abs(x); - return (bits.exp != bits.expMask) - && (bits.exp == 0 || bits.mantissa >= (1L << 63)); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(!isFinite(float.infinity)); - assert(!isFinite(-double.infinity)); - assert(isFinite(0.0)); - assert(!isFinite(float.nan)); - assert(isFinite(5.87747e-38f / 10)); - assert(isFinite(1.11254e-307 / 10)); - assert(isFinite(0.5)); -} - -/** - * Determines whether $(D_PARAM x) is $(B n)ot $(B a) $(B n)umber (NaN). - * - * Params: - * F = Type of the floating point number. - * x = Floating point number. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM x) is not a number, - * $(D_KEYWORD false) otherwise. - */ -bool isNaN(F)(F x) -if (isFloatingPoint!F) -{ - FloatBits!F bits; - bits.floating = abs(x); - - static if (ieeePrecision!F == IEEEPrecision.single - || ieeePrecision!F == IEEEPrecision.double_) - { - return bits.integral > bits.expMask; - } - else static if (ieeePrecision!F == IEEEPrecision.doubleExtended) - { - const maskedMantissa = bits.mantissa & bits.mantissaMask; - if ((bits.exp == bits.expMask && maskedMantissa != 0) - || ((bits.exp != 0) && (bits.mantissa < (1L << 63)))) - { - return true; - } - return false; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isNaN(float.init)); - assert(isNaN(double.init)); - assert(isNaN(real.init)); -} - -/** - * Determines whether $(D_PARAM x) is a positive or negative infinity. - * - * Params: - * F = Type of the floating point number. - * x = Floating point number. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM x) is infinity, $(D_KEYWORD false) - * otherwise. - * - * See_Also: $(D_PSYMBOL isFinite). - */ -bool isInfinity(F)(F x) -if (isFloatingPoint!F) -{ - FloatBits!F bits; - bits.floating = abs(x); - static if (ieeePrecision!F == IEEEPrecision.single - || ieeePrecision!F == IEEEPrecision.double_) - { - return bits.integral == bits.expMask; - } - else static if (ieeePrecision!F == IEEEPrecision.doubleExtended) - { - return (bits.exp == bits.expMask) - && ((bits.mantissa & bits.mantissaMask) == 0); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(isInfinity(float.infinity)); - assert(isInfinity(-float.infinity)); - assert(isInfinity(double.infinity)); - assert(isInfinity(-double.infinity)); - assert(isInfinity(real.infinity)); - assert(isInfinity(-real.infinity)); -} - -/** - * Determines whether $(D_PARAM x) is a denormilized number or not. - * - * Denormalized number is a number between `0` and `1` that cannot be - * represented as - * - * <pre> - * m*2<sup>e</sup> - * </pre> - * - * where $(I m) is the mantissa and $(I e) is an exponent that fits into the - * exponent field of the type $(D_PARAM F). - * - * `0` is neither normalized nor denormalized. - * - * Params: - * F = Type of the floating point number. - * x = Floating point number. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM x) is a denormilized number, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isNormal). - */ -bool isSubnormal(F)(F x) -if (isFloatingPoint!F) -{ - FloatBits!F bits; - bits.floating = abs(x); - static if (ieeePrecision!F == IEEEPrecision.single) - { - return bits.integral < (1 << 23) && bits.integral > 0; - } - else static if (ieeePrecision!F == IEEEPrecision.double_) - { - return bits.integral < (1L << 52) && bits.integral > 0; - } - else static if (ieeePrecision!F == IEEEPrecision.doubleExtended) - { - return bits.exp == 0 && bits.mantissa != 0; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(!isSubnormal(0.0f)); - assert(!isSubnormal(float.nan)); - assert(!isSubnormal(float.infinity)); - assert(!isSubnormal(0.3f)); - assert(isSubnormal(5.87747e-38f / 10)); - - assert(!isSubnormal(0.0)); - assert(!isSubnormal(double.nan)); - assert(!isSubnormal(double.infinity)); - assert(!isSubnormal(1.4)); - assert(isSubnormal(1.11254e-307 / 10)); - - assert(!isSubnormal(0.0L)); - assert(!isSubnormal(real.nan)); - assert(!isSubnormal(real.infinity)); -} - -/** - * Determines whether $(D_PARAM x) is a normilized number or not. - * - * Normalized number is a number that can be represented as - * - * <pre> - * m*2<sup>e</sup> - * </pre> - * - * where $(I m) is the mantissa and $(I e) is an exponent that fits into the - * exponent field of the type $(D_PARAM F). - * - * `0` is neither normalized nor denormalized. - * - * Params: - * F = Type of the floating point number. - * x = Floating point number. - * - * Returns: $(D_KEYWORD true) if $(D_PARAM x) is a normilized number, - * $(D_KEYWORD false) otherwise. - * - * See_Also: $(D_PSYMBOL isSubnormal). - */ -bool isNormal(F)(F x) -if (isFloatingPoint!F) -{ - static if (ieeePrecision!F == IEEEPrecision.single - || ieeePrecision!F == IEEEPrecision.double_) - { - FloatBits!F bits; - bits.floating = x; - bits.integral &= bits.expMask; - return bits.integral != 0 && bits.integral != bits.expMask; - } - else static if (ieeePrecision!F == IEEEPrecision.doubleExtended) - { - return classify(x) == FloatingPointClass.normal; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(!isNormal(0.0f)); - assert(!isNormal(float.nan)); - assert(!isNormal(float.infinity)); - assert(isNormal(0.3f)); - assert(!isNormal(5.87747e-38f / 10)); - - assert(!isNormal(0.0)); - assert(!isNormal(double.nan)); - assert(!isNormal(double.infinity)); - assert(isNormal(1.4)); - assert(!isNormal(1.11254e-307 / 10)); - - assert(!isNormal(0.0L)); - assert(!isNormal(real.nan)); - assert(!isNormal(real.infinity)); -} - -/** - * Determines whether the sign bit of $(D_PARAM x) is set or not. - * - * If the sign bit, $(D_PARAM x) is a negative number, otherwise positive. - * - * Params: - * F = Type of the floating point number. - * x = Floating point number. - * - * Returns: $(D_KEYWORD true) if the sign bit of $(D_PARAM x) is set, - * $(D_KEYWORD false) otherwise. - */ -bool signBit(F)(F x) -if (isFloatingPoint!F) -{ - FloatBits!F bits; - bits.floating = x; - static if (ieeePrecision!F == IEEEPrecision.single) - { - return (bits.integral & (1 << 31)) != 0; - } - else static if (ieeePrecision!F == IEEEPrecision.double_) - { - return (bits.integral & (1L << 63)) != 0; - } - else static if (ieeePrecision!F == IEEEPrecision.doubleExtended) - { - return (bits.exp & (1 << 15)) != 0; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - assert(signBit(-1.0f)); - assert(!signBit(1.0f)); - - assert(signBit(-1.0)); - assert(!signBit(1.0)); - - assert(signBit(-1.0L)); - assert(!signBit(1.0L)); -} diff --git a/source/tanya/net/iface.d b/source/tanya/net/iface.d index 8443cb4..c910b29 100644 --- a/source/tanya/net/iface.d +++ b/source/tanya/net/iface.d @@ -14,10 +14,10 @@ */ module tanya.net.iface; -import std.traits : Unqual; +import std.traits; import tanya.algorithm.mutation; import tanya.container.string; -import tanya.meta.trait; +import tanya.meta; import tanya.range; version (Windows) diff --git a/source/tanya/net/inet.d b/source/tanya/net/inet.d index 314a90c..141fd77 100644 --- a/source/tanya/net/inet.d +++ b/source/tanya/net/inet.d @@ -14,8 +14,8 @@ */ module tanya.net.inet; -import std.traits : Unqual, isUnsigned; -import tanya.meta.trait; +import std.traits; +import tanya.meta; import tanya.range; /** diff --git a/source/tanya/net/ip.d b/source/tanya/net/ip.d index 73ef23d..87268bd 100644 --- a/source/tanya/net/ip.d +++ b/source/tanya/net/ip.d @@ -18,14 +18,14 @@ import std.algorithm.comparison; import std.ascii; import std.sumtype; import std.typecons; -import std.traits : Unqual; +import std.traits; import tanya.algorithm.iteration; import tanya.algorithm.mutation; import tanya.container.string; import tanya.conv; import tanya.format; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; import tanya.net.iface; import tanya.net.inet; import tanya.range; diff --git a/source/tanya/range/adapter.d b/source/tanya/range/adapter.d index d30b24c..287e868 100644 --- a/source/tanya/range/adapter.d +++ b/source/tanya/range/adapter.d @@ -14,10 +14,10 @@ */ module tanya.range.adapter; -import std.traits : hasMember, isArray; +import std.traits; import tanya.algorithm.mutation; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; import tanya.range; private mixin template InserterCtor() diff --git a/source/tanya/range/primitive.d b/source/tanya/range/primitive.d index aa8c0cf..d8abe3d 100644 --- a/source/tanya/range/primitive.d +++ b/source/tanya/range/primitive.d @@ -15,9 +15,9 @@ module tanya.range.primitive; import std.algorithm.comparison; -import std.traits : FunctionAttribute, ReturnType, hasElaborateCopyConstructor, functionAttributes; +import std.traits; import tanya.memory.lifetime; -import tanya.meta.trait; +import tanya.meta; import tanya.range.array; /** diff --git a/source/tanya/test/assertion.d b/source/tanya/test/assertion.d new file mode 100644 index 0000000..9b146c8 --- /dev/null +++ b/source/tanya/test/assertion.d @@ -0,0 +1,106 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Additional assertions. + * + * This module provides functions that assert whether a given expression + * satisfies some complex condition, that can't be tested with + * $(D_KEYWORD assert) in a single line. Internally all the functions + * just evaluate the expression and call $(D_KEYWORD assert). + * + * The functions can cause segmentation fault if the module is compiled + * in production mode and the condition fails. + * + * Copyright: Eugene Wissner 2017-2025. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/assertion.d, + * tanya/test/assertion.d) + */ +module tanya.test.assertion; + +import std.traits; +import tanya.memory.allocator; +import tanya.meta; + +/** + * Asserts whether the function $(D_PARAM expr) throws an exception of type + * $(D_PARAM E). If it does, the exception is catched and properly destroyed. + * If it doesn't, an assertion error is thrown. If the exception doesn't match + * $(D_PARAM E) type, it isn't catched and escapes. + * + * Params: + * E = Expected exception type. + * T = Throwing function type. + * Args = Argument types of the throwing function. + * expr = Throwing function. + * args = Arguments for $(D_PARAM expr). + */ +void assertThrown(E : Exception, T, Args...)(T expr, auto ref Args args) +if (isSomeFunction!T) +{ + try + { + cast(void) expr(args); + assert(false, "Expected exception not thrown"); + } + catch (E exception) + { + defaultAllocator.dispose(exception); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + // If you want to test that an expression throws, you can wrap it into an + // arrow function. + static struct CtorThrows + { + this(int i) @nogc pure @safe + { + throw defaultAllocator.make!Exception(); + } + } + assertThrown!Exception(() => CtorThrows(8)); +} + +/** + * Asserts that the function $(D_PARAM expr) doesn't throw. + * + * If it does, the thrown exception is catched, properly destroyed and an + * assertion error is thrown instead. + * + * Params: + * T = Tested function type. + * Args = Argument types of $(D_PARAM expr). + * expr = Tested function. + * args = Arguments for $(D_PARAM expr). + */ +void assertNotThrown(T, Args...)(T expr, auto ref Args args) +if (isSomeFunction!T) +{ + try + { + cast(void) expr(args); + } + catch (Exception exception) + { + defaultAllocator.dispose(exception); + assert(false, "Unexpected exception thrown"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + // If you want to test that an expression doesn't throw, you can wrap it + // into an arrow function. + static struct S + { + } + assertNotThrown(() => S()); +} diff --git a/source/tanya/test/package.d b/source/tanya/test/package.d new file mode 100644 index 0000000..f316ddf --- /dev/null +++ b/source/tanya/test/package.d @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test suite for $(D_KEYWORD unittest)-blocks. + * + * Copyright: Eugene Wissner 2017-2025. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/package.d, + * tanya/test/package.d) + */ +module tanya.test; + +public import tanya.test.assertion; +public import tanya.test.stub; diff --git a/source/tanya/test/stub.d b/source/tanya/test/stub.d new file mode 100644 index 0000000..1741bbb --- /dev/null +++ b/source/tanya/test/stub.d @@ -0,0 +1,397 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Range and generic type generators. + * + * Copyright: Eugene Wissner 2018-2025. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/test/stub.d, + * tanya/test/stub.d) + */ +module tanya.test.stub; + +/** + * Attribute signalizing that the generated range should contain the given + * number of elements. + * + * $(D_PSYMBOL Count) should be always specified with some value and not as a + * type, so $(D_INLINECODE Count(1)) instead just $(D_INLINECODE Count), + * otherwise you can just omit $(D_PSYMBOL Count) and it will default to 0. + * + * $(D_PSYMBOL Count) doesn't generate `.length` property - use + * $(D_PSYMBOL Length) for that. + * + * If neither $(D_PSYMBOL Length) nor $(D_PSYMBOL Infinite) is given, + * $(D_ILNINECODE Count(0)) is assumed. + * + * This attribute conflicts with $(D_PSYMBOL Infinite) and $(D_PSYMBOL Length). + */ +struct Count +{ + /// Original range length. + size_t count = 0; + + @disable this(); + + /** + * Constructs the attribute with the given length. + * + * Params: + * count = Original range length. + */ + this(size_t count) @nogc nothrow pure @safe + { + this.count = count; + } +} + +/** + * Attribute signalizing that the generated range should be infinite. + * + * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Length). + */ +struct Infinite +{ +} + +/** + * Generates `.length` property for the range. + * + * The length of the range can be specified as a constructor argument, + * otherwise it is 0. + * + * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Infinite). + */ +struct Length +{ + /// Original range length. + size_t length = 0; +} + +/** + * Attribute signalizing that the generated range should return values by + * reference. + * + * This atribute affects the return values of `.front`, `.back` and `[]`. + */ +struct WithLvalueElements +{ +} + +/** + * Generates an input range. + * + * Params: + * E = Element type. + */ +mixin template InputRangeStub(E = int) +{ + import std.traits : hasUDA, getUDAs; + import std.meta : Alias; + + /* + * Aliases for the attribute lookups to access them faster + */ + private enum bool infinite = hasUDA!(typeof(this), Infinite); + private enum bool withLvalueElements = hasUDA!(typeof(this), + WithLvalueElements); + private alias Count = getUDAs!(typeof(this), .Count); + private alias Length = getUDAs!(typeof(this), .Length); + + static if (Count.length != 0) + { + private enum size_t count = Count[0].count; + + static assert (!infinite, + "Range cannot have count and be infinite at the same time"); + static assert (Length.length == 0, + "Range cannot have count and length at the same time"); + } + else static if (Length.length != 0) + { + private enum size_t count = Length[0]().length; + + static assert (!infinite, + "Range cannot have length and be infinite at the same time"); + } + else static if (!infinite) + { + private enum size_t count = 0; + } + + /* + * Member generation + */ + static if (infinite) + { + enum bool empty = false; + } + else + { + private size_t length_ = count; + + @property bool empty() const @nogc nothrow pure @safe + { + return this.length_ == 0; + } + } + + static if (withLvalueElements) + { + private E* element; // Pointer to enable range copying in save() + } + + void popFront() @nogc nothrow pure @safe + in + { + assert(!empty); + } + do + { + static if (!infinite) + { + --this.length_; + } + } + + static if (withLvalueElements) + { + ref E front() @nogc nothrow pure @safe + in + { + assert(!empty); + } + do + { + return *this.element; + } + } + else + { + E front() @nogc nothrow pure @safe + in + { + assert(!empty); + } + do + { + return E.init; + } + } + + static if (Length.length != 0) + { + size_t length() const @nogc nothrow pure @safe + { + return this.length_; + } + } +} + +/** + * Generates a forward range. + * + * This mixin includes input range primitives as well, but can be combined with + * $(D_PSYMBOL RandomAccessRangeStub). + * + * Params: + * E = Element type. + */ +mixin template ForwardRangeStub(E = int) +{ + static if (!is(typeof(this.InputRangeMixin) == void)) + { + mixin InputRangeStub!E InputRangeMixin; + } + + auto save() @nogc nothrow pure @safe + { + return this; + } +} + +/** + * Generates a bidirectional range. + * + * This mixin includes forward range primitives as well, but can be combined with + * $(D_PSYMBOL RandomAccessRangeStub). + * + * Params: + * E = Element type. + */ +mixin template BidirectionalRangeStub(E = int) +{ + mixin ForwardRangeStub!E; + + void popBack() @nogc nothrow pure @safe + in + { + assert(!empty); + } + do + { + static if (!infinite) + { + --this.length_; + } + } + + static if (withLvalueElements) + { + ref E back() @nogc nothrow pure @safe + in + { + assert(!empty); + } + do + { + return *this.element; + } + } + else + { + E back() @nogc nothrow pure @safe + in + { + assert(!empty); + } + do + { + return E.init; + } + } +} + +/** + * Generates a random-access range. + * + * This mixin includes input range primitives as well, but can be combined with + * $(D_PSYMBOL ForwardRangeStub) or $(D_PSYMBOL BidirectionalRangeStub). + * + * Note that a random-access range also requires $(D_PSYMBOL Length) or + * $(D_PARAM Infinite) by definition. + * + * Params: + * E = Element type. + */ +mixin template RandomAccessRangeStub(E = int) +{ + static if (!is(typeof(this.InputRangeMixin) == void)) + { + mixin InputRangeStub!E InputRangeMixin; + } + + static if (withLvalueElements) + { + ref E opIndex(size_t) @nogc nothrow pure @safe + { + return *this.element; + } + } + else + { + E opIndex(size_t) @nogc nothrow pure @safe + { + return E.init; + } + } +} + +/** + * Struct with a disabled postblit constructor. + * + * $(D_PSYMBOL NonCopyable) can be used as an attribute for + * $(D_PSYMBOL StructStub) or as a standalone type. + */ +struct NonCopyable +{ + @disable this(this); +} + +/** + * Struct with an elaborate destructor. + * + * $(D_PSYMBOL WithDtor) can be used as an attribute for + * $(D_PSYMBOL StructStub) or as a standalone type. + * + * When used as a standalone object the constructor of $(D_PSYMBOL WithDtor) + * accepts an additional `counter` argument, which is incremented by the + * destructor. $(D_PSYMBOL WithDtor) stores a pointer to the passed variable, + * so the variable can be investigated after the struct isn't available + * anymore. + */ +struct WithDtor +{ + size_t* counter; + + this(ref size_t counter) @nogc nothrow pure @trusted + { + this.counter = &counter; + } + + ~this() @nogc nothrow pure @safe + { + if (this.counter !is null) + { + ++*this.counter; + } + } +} + +/** + * Struct supporting hashing. + * + * $(D_PSYMBOL Hashable) can be used as an attribute for + * $(D_PSYMBOL StructStub) or as a standalone type. + * + * The constructor accepts an additional parameter, which is returned by the + * `toHash()`-function. `0U` is returned if no hash value is given. + */ +struct Hashable +{ + size_t hash; + + size_t toHash() const @nogc nothrow pure @safe + { + return this.hash; + } +} + +/** + * Generates a $(D_KEYWORD struct) with common functionality. + * + * To specify the needed functionality use user-defined attributes on the + * $(D_KEYWORD struct) $(D_PSYMBOL StructStub) is mixed in. + * + * Supported attributes are: $(D_PSYMBOL NonCopyable), $(D_PSYMBOL Hashable), + * $(D_PSYMBOL WithDtor). + */ +mixin template StructStub() +{ + import std.traits : hasUDA, getUDAs; + + static if (hasUDA!(typeof(this), NonCopyable)) + { + @disable this(this); + } + + private alias Hashable = getUDAs!(typeof(this), .Hashable); + static if (Hashable.length > 0) + { + size_t toHash() const @nogc nothrow pure @safe + { + return Hashable[0]().hash; + } + } + + static if (hasUDA!(typeof(this), WithDtor)) + { + ~this() @nogc nothrow pure @safe + { + } + } +} |
