TRAVIS_OS_NAME=linux <<<<<< ENV .dir-locals.el LICENSE dub.sdl dub.selections.json reggaefile.d source/automem/allocator.d source/automem/array.d source/automem/package.d source/automem/ref_counted.d source/automem/traits.d source/automem/unique.d source/automem/utils.d source/automem/vector.d tests/ut/issues.d tests/ut/package.d tests/ut/ref_counted.d tests/ut/unique.d tests/ut/vector.d tests/ut_main.d travis_install.sh <<<<<< network # path=source-automem-ref_counted.lst |/** | A reference-counted smart pointer. | */ |module automem.ref_counted; | |import automem.traits: isAllocator; |import automem.unique: Unique; |import std.experimental.allocator: theAllocator, processAllocator; |import std.typecons: Flag; | | |alias RC = RefCounted; | |version (D_BetterC) | enum gcExists = false; |else | enum gcExists = true; | |/** | A reference-counted smart pointer similar to C++'s std::shared_ptr. | */ |struct RefCounted(RefCountedType, | Allocator = typeof(theAllocator), | Flag!"supportGC" supportGC = gcExists ? Flag!"supportGC".yes : Flag!"supportGC".no) | if(isAllocator!Allocator) |{ | | import std.traits: hasMember; | | enum isSingleton = hasMember!(Allocator, "instance"); | enum isTheAllocator = is(Allocator == typeof(theAllocator)); | enum isGlobal = isSingleton || isTheAllocator; | | alias Type = RefCountedType; | | static if(isGlobal) | /** | The allocator is a singleton, so no need to pass it in to the | constructor | */ 14| this(Args...)(auto ref Args args) { 14| this.makeObject!args(); | } | else | /** | Non-singleton allocator, must be passed in | */ 19| this(Args...)(Allocator allocator, auto ref Args args) { 19| _allocator = allocator; 19| this.makeObject!args(); | } | | static if(isGlobal) | /** | Factory method to enable construction of structs despite | structs not being able to have a constructor with no arguments. | */ | static typeof(this) construct(Args...)(auto ref Args args) { | static if (Args.length != 0) | return typeof(return)(args); | else { 1| typeof(return) ret; 1| ret.makeObject!()(); 1| return ret; | } | } | else | /** | Factory method. Not necessary with non-global allocator | but included for symmetry. | */ | static typeof(this) construct(Args...)(auto ref Allocator allocator, auto ref Args args) { | return typeof(return)(allocator, args); | } | | /// | this(this) { 15| if(_impl !is null) inc; | } | | /// | ~this() { 53| release; | } | | /** | Assign to an lvalue RefCounted | */ | void opAssign(ref RefCounted other) { | 6| if (_impl == other._impl) return; | 6| if(_impl !is null) release; | | static if(!isGlobal) 3| _allocator = other._allocator; | 4| _impl = other._impl; | 7| if(_impl !is null) inc; | } | | /** | Assign to an rvalue RefCounted | */ | void opAssign(RefCounted other) { | import std.algorithm: swap; 4| swap(_impl, other._impl); | static if(!isGlobal) 3| swap(_allocator, other._allocator); | } | | /** | Dereference the smart pointer and yield a reference | to the contained type. | */ | ref auto opUnary(string s)() inout if (s == "*") { 12| return _impl._get; | } | | /** | Prevent opSlice and opIndex from being hidden by Impl*. | This comment is deliberately not DDOC. | */ | auto ref opSlice(A...)(auto ref A args) | if (__traits(compiles, Type.init.opSlice(args))) | { 1| return _impl._get.opSlice(args); | } | /// ditto | auto ref opIndex(A...)(auto ref A args) | if (__traits(compiles, Type.init.opIndex(args))) | { 1| return _impl._get.opIndex(args); | } | /// ditto | auto ref opIndexAssign(A...)(auto ref A args) | if (__traits(compiles, Type.init.opIndexAssign(args))) | { 1| return _impl._get.opIndexAssign(args); | } | | alias _impl this; | |private: | | static struct Impl { | | static if(is(Type == shared)) | shared size_t _count; | else | size_t _count; | | static if(is(Type == class)) { | | align ((void*).alignof) | void[__traits(classInstanceSize, Type)] _rawMemory; | | } else | Type _object; | | | static if (is(Type == class)) { | | inout(Type) _get() inout 7| in(&this !is null) | do | { 7| return cast(inout(Type)) &_rawMemory[0]; | } | | inout(shared(Type)) _get() inout shared 1| in(&this !is null) | do | { 1| return cast(inout(shared(Type))) &_rawMemory[0]; | } | } else { // struct | | ref inout(Type) _get() inout 56| in(&this !is null) | do | { 55| return _object; | } | | ref inout(shared(Type)) _get() inout shared 3| in(&this !is null) | do | { 3| return _object; | } | } | | alias _get this; | } | | static if(isSingleton) | alias _allocator = Allocator.instance; | else static if(isTheAllocator) { | static if (is(Type == shared)) | // 'processAllocator' should be used for allocating | // memory shared across threads | alias _allocator = processAllocator; | else | alias _allocator = theAllocator; | } | else | Allocator _allocator; | | static if(is(Type == shared)) | alias ImplType = shared Impl; | else | alias ImplType = Impl; | | public ImplType* _impl; // has to be public or alias this doesn't work | | void allocateImpl() { | import std.traits: hasIndirections; | 35| _impl = cast(typeof(_impl)) _allocator.allocate(Impl.sizeof); 35| _impl._count = 1; | | static if (is(Type == class)) { | // class representation: | // void* classInfoPtr | // void* monitorPtr | // [] interfaces | // T... members | import core.memory: GC; | | // TypeInfo_Shared has no | static if(is(Type == shared)) { | auto flags() { 1| return (cast(TypeInfo_Class) typeid(Type).base).m_flags; | } | } else { | auto flags() { 2| return typeid(Type).m_flags; | } | } | | 3| if (supportGC && !(flags & TypeInfo_Class.ClassFlags.noPointers)) | // members have pointers: we have to watch the monitor | // and all members; skip the classInfoPtr 1| GC.addRange(cast(void*) &_impl._rawMemory[(void*).sizeof], | __traits(classInstanceSize, Type) - (void*).sizeof); | else | // representation doesn't have pointers, just watch the | // monitor pointer; skip the classInfoPtr | // need to watch the monitor pointer even if supportGC is false. 2| GC.addRange(cast(void*) &_impl._rawMemory[(void*).sizeof], (void*).sizeof); | } else static if (supportGC && hasIndirections!Type) { | import core.memory: GC; 1| GC.addRange(cast(void*) &_impl._object, Type.sizeof); | } | } | | void release() { | import std.traits : hasIndirections; | import core.memory : GC; | import automem.utils : destruct; | 65| if(_impl is null) return; 45| assert(_impl._count > 0, "Trying to release a RefCounted but ref count is 0 or less"); | 45| dec; | 45| if(_impl._count == 0) { 70| () @trusted { destruct(_impl._get); }(); | static if (is(Type == class)) { | // need to watch the monitor pointer even if supportGC is false. 6| () @trusted { GC.removeRange(cast(void*) &_impl._rawMemory[(void*).sizeof]); }(); | } else static if (supportGC && hasIndirections!Type) { 2| () @trusted { GC.removeRange(cast(void*) &_impl._object); }(); | } 70| auto memSlice = () @trusted { return (cast(void*) _impl)[0 .. Impl.sizeof]; }(); 70| () @trusted { _allocator.deallocate(memSlice); }(); | } | } | | void inc() { | static if(is(Type == shared)) { | import core.atomic: atomicOp; 0000000| _impl._count.atomicOp!"+="(1); | } else 10| ++_impl._count; | } | | void dec() { | static if(is(Type == shared)) { | import core.atomic: atomicOp; 4| _impl._count.atomicOp!"-="(1); | } else 41| --_impl._count; | } | |} | |private template makeObject(args...) |{ | void makeObject(Type, A)(ref RefCounted!(Type, A) rc) @trusted { | import std.conv: emplace; | import std.functional : forward; | import std.traits: Unqual; | 34| rc.allocateImpl; | | static if(is(Type == class)) 3| emplace!Type(cast(void[]) rc._impl._rawMemory[], forward!args); | else 31| emplace(&rc._impl._object, forward!args); | } |} | | | |auto refCounted(Type, Allocator)(Unique!(Type, Allocator) ptr) { | 1| RefCounted!(Type, Allocator) ret; | | static if(!ptr.isGlobal) 1| ret._allocator = ptr.allocator; | 1| ret.allocateImpl; 1| *ret = *ptr; | 1| return ret; |} source/automem/ref_counted.d is 98% covered <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-subpackages-runner-source-unit_threaded-runner-attrs.lst |/** | UDAs for decorating tests. | */ |module unit_threaded.runner.attrs; | |import unit_threaded.from; | |enum UnitTest; //opt-in to registration |enum DontTest; //opt-out of registration |enum Serial; //run tests in the module in one thread / serially | |alias SingleThreaded = Serial; | |///Hide test. Not run by default but can be run. |struct HiddenTest { | string reason; |} | |/// The suite fails if the test passes. |struct ShouldFail { | string reason; |} | |/// The suite fails unless the test throws T |struct ShouldFailWith(T: Throwable) { | alias Type = T; | string reason; |} | |/// Associate a name with a unittest block. |struct Name { | string value; |} | |/// Associates one or more tags with the test |struct Tags { 0000000| this(string[] values...) { this.values = values;} 0000000| this(string[] values) { this.values = values; } 0000000| this(string value) { this.values = [value]; } | string[] values; |} | |/** Automatically assign @Tags for each parameterized test | e.g. |--------------- |@Values("foo", "bar") @AutoTags unittest { ... } |// there are now two unit tests, one for "foo" with tag "foo" |// and one for "bar" with tag "bar" |--------------- | */ |enum AutoTags; | |/** Attachs these types to the a parametrized unit test. | The attached template function will be instantiated with | each type listed, e.g. | | ---------------- | @Types!(int, byte) void testInit(T)() { T.init.shouldEqual(0); } | ---------------- | | These would mean two testInit test runs. | | Normally this would be a template but I don't know how to write | * the UDA code to filter a template out | */ |struct Types(T...) { | alias types = T; | enum length = T.length; |} | | |/** | Used as a UDA for built-in unittests to enable value-parametrized tests. | Example: | ------- | @Values(1, 2, 3) unittest { assert(getValue!int % 2 == 0); } | ------- | The example above results in unit_threaded running the unit tests 3 times, | once for each value declared. | | See `getValue`. | */ |auto Values(T)(T[] values...) { | return ValuesImpl!T(values.dup); |} | |auto Values(R)(R values) if(from!"std.range.primitives".isInputRange!R) { | import std.range.primitives: ElementType; | import std.array: array; | return ValuesImpl!(ElementType!R)(values.array); |} | | |struct ValuesImpl(T) { | T[] values; |} | |/** | Retrieves the current test value of type T in a built-in unittest. | See `Values`. | */ |T getValue(T, int index = 0)() { | return ValueHolder!T.values[index]; |} | |package struct ValueHolder(T) { | static T[10] values; |} | | |enum Setup; |enum Shutdown; | |struct Flaky { | /// the number of times to run the test | enum defaultRetries = 10; | int retries = defaultRetries; |} ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/subpackages/runner/source/unit_threaded/runner/attrs.d is 0% covered <<<<<< EOF # path=source-automem-vector.lst |/** | Dynamic arrays with deterministic memory usage | akin to C++'s std::vector or Rust's std::vec::Vec | */ |module automem.vector; | | |import automem.traits: isGlobal; |import std.range.primitives: isInputRange; |import std.experimental.allocator: theAllocator; |import std.experimental.allocator.mallocator: Mallocator; | | |alias String = StringA!(typeof(theAllocator)); |alias StringM = StringA!Mallocator; | | |template StringA(A = typeof(theAllocator)) if(isAllocator!A) { | alias StringA = Vector!(immutable char, A); |} | |/** | Create a vector from a variadic list of elements, inferring the type of | the elements and the allocator | */ |auto vector(A = typeof(theAllocator), E) | (E[] elements...) | if(isAllocator!A && isGlobal!A) |{ 45| return Vector!(E, A)(elements); |} | |/// ditto |auto vector(A = typeof(theAllocator), E) | (A allocator, E[] elements...) | if(isAllocator!A && !isGlobal!A) |{ 9| return Vector!(E, A)(allocator, elements); |} | |/** | Create a vector from an input range, inferring the type of the elements | and the allocator. | */ |auto vector(A = typeof(theAllocator), R) | (R range) | if(isAllocator!A && isGlobal!A && isInputRange!R) |{ | import automem.vector: ElementType; 5| return Vector!(ElementType!R, A)(range); |} | | |/// ditto |auto vector(A = typeof(theAllocator), R) | (A allocator, R range) | if(isAllocator!A && !isGlobal!A && isInputRange!R) |{ | import automem.vector: ElementType; | return Vector!(ElementType!R, A)(allocator, range); |} | |/** | A dynamic array with deterministic memory usage | akin to C++'s std::vector or Rust's std::vec::Vec | */ |struct Vector(E, Allocator = typeof(theAllocator)) if(isAllocator!Allocator) { | | import automem.traits: isGlobal, isSingleton, isTheAllocator; | import std.traits: Unqual, isCopyable; | | alias MutE = Unqual!E; | enum isElementMutable = !is(E == immutable) && !is(E == const); | | static if(isGlobal!Allocator) { | 53| this(E[] elements...) { 53| fromElements(elements); | } | 5| this(R)(R range) if(isInputRangeOf!(R, E)) { | // reallocating is ok if only allocating now 10| () @trusted { this = range; }(); | } | | } else static if(isCopyable!Allocator) { | 9| this(Allocator allocator, E[] elements...) { 9| _allocator = allocator; 9| fromElements(elements); | } | | this(R)(Allocator allocator, R range) if(isInputRangeOf!(R, E)) { | _allocator = allocator; | this = range; | } | } else { | | this(R)(R range) if(isInputRangeOf!(R, E)) { | this = range; | } | | } | | this(this) scope { | 4| auto oldElements = _elements; 4| _elements = createVector(_elements.length); | | static if(isElementMutable) 4| _elements[0 .. length.toSizeT] = oldElements[0 .. length.toSizeT]; | else 0000000| () @trusted { 0000000| cast(MutE[])(_elements)[0 .. length.toSizeT] = oldElements[0 .. length.toSizeT]; | }(); | } | | ~this() { 79| free; | } | | /// Frees the memory and returns to .init | void free() scope { | import std.experimental.allocator: dispose; | | // dispose is @system for theAllocator 79| () @trusted { | static if(is(E == immutable)) 9| auto elements = cast(MutE[]) _elements; | else | alias elements = _elements; | 79| _allocator.dispose(elements); | }(); | 79| clear; | } | | static if(isElementMutable) { | /// Pops the front element off | void popFront() { 45| foreach(i; 0 .. length - 1) 10| _elements[i.toSizeT] = _elements[i.toSizeT + 1]; | 5| popBack; | } | } | | /// Pops the last element off | void popBack() { 7| --_length; | } | | /// If the vector is empty | bool empty() const { 3| return length == 0; | } | | /// The current length of the vector | @property long length() const { 1004| return _length; | } | | /// Set the length of the vector | @property void length(long newLength) { 3| if(capacity < newLength) reserve(newLength); 2| _length = newLength; | } | | /// The current memory capacity of the vector | long capacity() const { 106| return _elements.length; | } | | /// Clears the vector, resulting in an empty one | void clear() { 80| _length = 0; | } | | /// Reserve memory to avoid allocations when appending | void reserve(long newLength) { 9| expandMemory(newLength); | } | | static if(isElementMutable) { | | /// Shrink to fit the current length. Returns if shrunk. | bool shrink() scope { 1| return shrink(length); | } | | /** | Shrink to fit the new length given. Returns if shrunk. | Cannot be made @safe due to reallocation causing pointers | to dangle. | */ | bool shrink(long newLength) scope { | import std.experimental.allocator: shrinkArray; | 4| const delta = capacity - newLength; 4| const shrunk = _allocator.shrinkArray(_elements, delta.toSizeT); 4| _length = newLength; | 4| return shrunk; | } | } | | /// Access the ith element. Can throw RangeError. | ref inout(E) opIndex(long i) scope return inout { 631| if(i < 0 || i >= length) | mixin(throwBoundsException); 314| return _elements[i.toSizeT]; | } | | /// Returns a new vector after appending to the given vector. | Vector opBinary(string s, T)(auto ref T other) const | if(s == "~" && is(Unqual!T == Vector)) | { | import std.range: chain; | // opSlice is @system , but it's ok here because we're not | // returning the slice but concatenating. 6| return Vector(chain(() @trusted { return this[]; }(), 3| () @trusted { return other[]; }())); | } | | /// Assigns from a range. | void opAssign(R)(R range) scope if(isForwardRangeOf!(R, E)) { | import std.range.primitives: walkLength, save; | 6| expand(range.save.walkLength); | 6| long i = 0; 120| foreach(element; range) 36| _elements[toSizeT(i++)] = element; | } | | // make it an output range | | | /// Append to the vector | void opOpAssign(string op) | (E other) | scope | if(op == "~") | { 35| put(other); | } | | void put(E other) { | 36| expand(length + 1); | 36| const lastIndex = (length - 1).toSizeT; | | static if(isElementMutable) 35| _elements[lastIndex] = other; | else { 1| assert(_elements[lastIndex] == E.init, | "Assigning to non default initialised non mutable member"); | 2| () @trusted { mutableElements[lastIndex] = other; }(); | } | } | | /// Append to the vector from a range | void opOpAssign(string op, R) | (scope R range) | scope | if(op == "~" && isForwardRangeOf!(R, E)) | { 6| put(range); | } | | void put(R)(scope R range) if(isLengthRangeOf!(R, E)) { | import std.range.primitives: walkLength, save; | 7| long index = length; | | static if(hasLength!R) 7| const rangeLength = range.length; | else | const rangeLength = range.save.walkLength; | 7| expand(length + rangeLength); | 69| foreach(element; range) { 17| const safeIndex = toSizeT(index++); | static if(!isElementMutable) { 3| assert(_elements[safeIndex] == E.init, | "Assigning to non default initialised non mutable member"); | } | | static if(isElementMutable) 14| _elements[safeIndex] = element; | else { 3| assert(_elements[safeIndex] == E.init, | "Assigning to non default initialised non mutable member"); 6| () @trusted { mutableElements[safeIndex] = element; }(); | } | } | } | | /** | Return a forward range of the vector contents. | Negative `end` values work like in Python. | */ | auto range(this This)(in long start = 0, long end = -1) return scope 74| in(start >= 0) 74| in(end <= length) | do | { | import std.range.primitives: isForwardRange; | | static struct Range { | private This* self; | private long index; | private long end; | | Range save() { 1| return this; | } | | auto front() { 283| return (*self)[index]; | } | | void popFront() { 281| ++index; | } | | bool empty() const { 418| const comp = end < 0 ? length + end + 1 : end; 418| return index >= comp; | } | | auto length() const { 408| return self.length; | } | } | | static assert(isForwardRange!Range); | | // FIXME - why isn't &this @safe? 148| return Range(() @trusted { return &this; }(), | start, | end); | } | | /** | Returns a slice. | @system because the pointer in the slice might dangle. | */ | auto opSlice(this This)() @system scope return { 7| return _elements[0 .. length.toSizeT]; | } | | /** | Returns a slice. | @system because the pointer in the slice might dangle. | */ | auto opSlice(this This)(long start, long end) @system scope return { 16| if(start < 0 || start >= length) | mixin(throwBoundsException); | 16| if(end < 0 || end > length) | mixin(throwBoundsException); | 7| return _elements[start.toSizeT .. end.toSizeT]; | } | | long opDollar() const { 2| return length; | } | | static if(isElementMutable) { | /// Assign all elements to the given value | void opSliceAssign(E value) { 1| _elements[] = value; | } | } | | | static if(isElementMutable) { | /// Assign all elements in the given range to the given value | void opSliceAssign(E value, long start, long end) { 2| if(start < 0 || start >= length) | mixin(throwBoundsException); | 2| if(end < 0 || end >= length) | mixin(throwBoundsException); | 1| _elements[start.toSizeT .. end.toSizeT] = value; | } | } | | static if(isElementMutable) { | /// Assign all elements using the given operation and the given value | void opSliceOpAssign(string op)(E value) scope { 15| foreach(ref elt; _elements) | mixin(`elt ` ~ op ~ `= value;`); | } | } | | static if(isElementMutable) { | /// Assign all elements in the given range using the given operation and the given value | void opSliceOpAssign(string op)(E value, long start, long end) scope { 2| if(start < 0 || start >= length) | mixin(throwBoundsException); | 2| if(end < 0 || end >= length) | mixin(throwBoundsException); | 9| foreach(ref elt; _elements[start.toSizeT .. end.toSizeT]) | mixin(`elt ` ~ op ~ `= value;`); | } | } | | bool opCast(U)() const scope if(is(U == bool)) { 2| return length > 0; | } | | static if(is(Unqual!E == char)) { | | /** | Return a null-terminated C string | @system since appending is not @safe. | */ | auto stringz(this This)() return scope @system { 2| if(capacity == length) reserve(length + 1); | | static if(isElementMutable) { | _elements[length.toSizeT] = 0; | } else { 1| assert(_elements[length.toSizeT] == E.init || _elements[length.toSizeT] == 0, | "Assigning to non default initialised non mutable member"); | 2| () @trusted { mutableElements[length.toSizeT] = 0; }(); | } | 1| return &_elements[0]; | } | } | | auto ptr(this This)() return scope { 2| return &_elements[0]; | } | | bool opEquals(R)(R range) scope const | if(isInputRangeOf!(R, E)) | { | import std.array: empty, popFront, front; | 10| if(length == 0 && range.empty) return true; | 99| foreach(i; 0 .. length) { 29| if(range.empty) return false; 29| if(range.front != this[i]) return false; 21| range.popFront; | } | 4| return range.empty; | } | | bool opEquals(OtherAllocator)(auto ref scope const(Vector!(E, OtherAllocator)) other) const { 3| return this == other.range; | } | |private: | | E[] _elements; | long _length; | | static if(isSingleton!Allocator) | alias _allocator = Allocator.instance; | else static if(isTheAllocator!Allocator) | alias _allocator = theAllocator; | else | Allocator _allocator; | | E[] createVector(long length) scope { | import std.experimental.allocator: makeArray; | // theAllocator.makeArray is @system 152| return () @trusted { return _allocator.makeArray!E(length.toSizeT); }(); | } | | void fromElements(E[] elements) { | 62| _elements = createVector(elements.length); | | static if(isElementMutable) 54| _elements[] = elements[]; | else 16| () @trusted { (cast(MutE[]) _elements)[] = elements[]; }(); | 62| _length = elements.length; | } | | void expand(long newLength) scope { 49| expandMemory(newLength); 49| _length = newLength; | } | | | // @system since reallocating can cause pointers to dangle | void expandMemory(long newLength) scope @system { | import std.experimental.allocator: expandArray; | 58| if(newLength > capacity) { 37| if(length == 0) 10| _elements = createVector(newLength); | else { 27| const newCapacity = (newLength * 3) / 2; 27| const delta = newCapacity - capacity; 27| _allocator.expandArray(mutableElements, delta.toSizeT); | } | } | } | | ref MutE[] mutableElements() scope return @system { 32| auto ptr = &_elements; 32| return *(cast(MutE[]*) ptr); | } |} | | |static if (__VERSION__ >= 2082) { // version identifier D_Exceptions was added in 2.082 | version (D_Exceptions) | private enum haveExceptions = true; | else | private enum haveExceptions = false; |} else { | version (D_BetterC) | private enum haveExceptions = false; | else | private enum haveExceptions = true; |} | | |static if (haveExceptions) { | private static immutable boundsException = new BoundsException("Out of bounds index"); | private enum throwBoundsException = q{throw boundsException;}; | class BoundsException: Exception { | import std.exception: basicExceptionCtors; | | mixin basicExceptionCtors; | } |} else { | private enum throwBoundsException = q{assert(0, "Out of bounds index");}; |} | | |private template isInputRangeOf(R, E) { | import std.range.primitives: isInputRange; | enum isInputRangeOf = isInputRange!R && canAssignFrom!(R, E); |} | |private template isForwardRangeOf(R, E) { | import std.range.primitives: isForwardRange; | enum isForwardRangeOf = isForwardRange!R && canAssignFrom!(R, E); |} | | |private enum hasLength(R) = is(typeof({ | import std.traits: isIntegral; | auto length = R.init.length; | static assert(isIntegral!(typeof(length))); |})); | | |private enum isLengthRangeOf(R, E) = isForwardRangeOf!(R, E) || hasLength!R; | |private template canAssignFrom(R, E) { | enum canAssignFrom = is(typeof({ | import automem.vector: frontNoAutoDecode; | E element = R.init.frontNoAutoDecode; | })); |} | |private size_t toSizeT(long length) @safe @nogc pure nothrow { | static if(size_t.sizeof < long.sizeof) | assert(length < cast(long) size_t.max); 565| return cast(size_t) length; |} | |// Because autodecoding is fun |private template ElementType(R) { | import std.traits: isSomeString; | | static if(isSomeString!R) { | alias ElementType = typeof(R.init[0]); | } else { | import std.range.primitives: ElementType_ = ElementType; | alias ElementType = ElementType_!R; | } |} | |@("ElementType") |@safe pure unittest { | import automem.vector: ElementType; | static assert(is(ElementType!(int[]) == int)); | static assert(is(ElementType!(char[]) == char)); | static assert(is(ElementType!(wchar[]) == wchar)); | static assert(is(ElementType!(dchar[]) == dchar)); |} | | |// More fun with autodecoding |private auto frontNoAutoDecode(R)(R range) { | import std.traits: isSomeString; | | static if(isSomeString!R) | return range[0]; | else { | import std.range.primitives: front; | return range.front; | } |} | | |void checkAllocator(T)() { | import std.experimental.allocator: dispose, shrinkArray, makeArray, expandArray; | import std.traits: hasMember; | | static if(hasMember!(T, "instance")) | alias allocator = T.instance; | else | T allocator; | | void[] bytes; | allocator.dispose(bytes); | | int[] ints = allocator.makeArray!int(42); | | allocator.shrinkArray(ints, size_t.init); | allocator.expandArray(ints, size_t.init); |} |enum isAllocator(T) = is(typeof(checkAllocator!T)); | | |@("isAllocator") |@safe @nogc pure unittest { | import std.experimental.allocator.mallocator: Mallocator; | import test_allocator: TestAllocator; | | static assert( isAllocator!Mallocator); | static assert( isAllocator!TestAllocator); | static assert(!isAllocator!int); | static assert( isAllocator!(typeof(theAllocator))); |} source/automem/vector.d is 98% covered <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-subpackages-from-source-unit_threaded-from.lst |/** | Eliminate top-level imports. | */ |module unit_threaded.from; | |/** | Local imports everywhere. | */ |template from(string moduleName) { | mixin("import from = " ~ moduleName ~ ";"); |} ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/subpackages/from/source/unit_threaded/from.d has no code <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-subpackages-runner-source-unit_threaded-runner-reflection.lst |/** | Compile-time reflection to find unit tests and set their properties. | */ |module unit_threaded.runner.reflection; | | |import unit_threaded.from; | |/* | These standard library imports contain something important for the code below. | Unfortunately I don't know what they are so they're to prevent breakage. | */ |import std.traits; |import std.algorithm; |import std.array; | | |/** | An alternative to writing test functions by hand to avoid compile-time | performance penalties by using -unittest. | */ |mixin template Test(string testName, alias Body, size_t line = __LINE__) { | import std.format: format; | import unit_threaded.runner.attrs: Name, UnitTest; | import unit_threaded.runner.reflection: unittestFunctionName; | | enum unitTestCode = q{ | @UnitTest | @Name("%s") | void %s() { | | } | }.format(testName, unittestFunctionName(line)); | | //pragma(msg, unitTestCode); | mixin(unitTestCode); |} | | |string unittestFunctionName(size_t line = __LINE__) { | import std.conv: text; 0000000| return "unittest_L" ~ line.text; |} | |/// |alias TestFunction = void delegate(); | |/** | * Common data for test functions and test classes | */ |struct TestData { | string name; | TestFunction testFunction; ///only used for functions, null for classes | bool hidden; | bool shouldFail; | bool singleThreaded; | bool builtin; | string suffix; // append to end of getPath | string[] tags; | TypeInfo exceptionTypeInfo; // for ShouldFailWith | int flakyRetries = 0; | | /// The test's name | string getPath() const pure nothrow { 0000000| string path = name.dup; | import std.array: empty; 0000000| if(!suffix.empty) path ~= "." ~ suffix; 0000000| return path; | } | | /// If the test is a class | bool isTestClass() @safe const pure nothrow { 0000000| return testFunction is null; | } |} | | |/** | * Finds all test cases (functions, classes, built-in unittest blocks) | * Template parameters are module strings | */ |const(TestData)[] allTestData(MOD_STRINGS...)() | if(from!"std.meta".allSatisfy!(from!"std.traits".isSomeString, typeof(MOD_STRINGS))) |{ | import std.array: join; | import std.range : iota; | import std.format : format; | import std.algorithm : map; | | string getModulesString() { | string[] modules; | foreach(i, module_; MOD_STRINGS) modules ~= "module%d = %s".format(i, module_); | return modules.join(", "); | } | | enum modulesString = getModulesString; | mixin("import " ~ modulesString ~ ";"); | mixin("return allTestData!(" ~ | MOD_STRINGS.length.iota.map!(i => "module%d".format(i)).join(", ") ~ | ");"); |} | | |/** | * Finds all test cases (functions, classes, built-in unittest blocks) | * Template parameters are module symbols | */ |const(TestData)[] allTestData(MOD_SYMBOLS...)() | if(!from!"std.meta".anySatisfy!(from!"std.traits".isSomeString, typeof(MOD_SYMBOLS))) |{ | return | moduleTestClasses!MOD_SYMBOLS ~ | moduleTestFunctions!MOD_SYMBOLS ~ | moduleUnitTests!MOD_SYMBOLS; |} | | |private template Identity(T...) if(T.length > 0) { | static if(__traits(compiles, { alias x = T[0]; })) | alias Identity = T[0]; | else | enum Identity = T[0]; |} | | |/** | Names a test function / built-in unittest based on @Name or string UDAs | on it. If none are found, "returns" an empty string | */ |template TestNameFromAttr(alias testFunction) { | import unit_threaded.runner.attrs: Name; | import std.traits: getUDAs; | import std.meta: Filter; | | // i.e. if @("this is my name") appears | enum strAttrs = Filter!(isStringUDA, __traits(getAttributes, testFunction)); | | enum nameAttrs = getUDAs!(testFunction, Name); | static assert(nameAttrs.length < 2, "Only one @Name UDA allowed"); | | // strAttrs might be values to pass so only if the length is 1 is it a name | enum hasName = nameAttrs.length || strAttrs.length == 1; | | static if(hasName) { | static if(nameAttrs.length == 1) | enum TestNameFromAttr = nameAttrs[0].value; | else | enum TestNameFromAttr = strAttrs[0]; | } else | enum TestNameFromAttr = ""; |} | |/** | * Finds all built-in unittest blocks in the given modules. | * Recurses into structs, classes, and unions of the modules. | * | * @return An array of TestData structs | */ |TestData[] moduleUnitTests(modules...)() { | TestData[] ret; | static foreach(module_; modules) { | ret ~= moduleUnitTests_!module_; | } | return ret; |} | |/** | * Finds all built-in unittest blocks in the given module. | * Recurses into structs, classes, and unions of the module. | * | * @return An array of TestData structs | */ |private TestData[] moduleUnitTests_(alias module_)() { | | // Return a name for a unittest block. If no @Name UDA is found a name is | // created automatically, else the UDA is used. | // the weird name for the first template parameter is so that it doesn't clash | // with a package name | string unittestName(alias _theUnitTest, int index)() @safe nothrow { | import std.conv: text; | import std.algorithm: startsWith, endsWith; | import std.traits: fullyQualifiedName; | | enum prefix = fullyQualifiedName!(__traits(parent, _theUnitTest)) ~ "."; | enum nameFromAttr = TestNameFromAttr!_theUnitTest; | | // Establish a unique name for a unittest with no name | static if(nameFromAttr == "") { | // use the unittest name if available to allow for running unittests based | // on location | if(__traits(identifier, _theUnitTest).startsWith("__unittest_L")) { | const ret = prefix ~ __traits(identifier, _theUnitTest)[2 .. $]; | const suffix = "_C1"; | // simplify names for the common case where there's only one | // unittest per line | | return ret.endsWith(suffix) ? ret[0 .. $ - suffix.length] : ret; | } | | try | return prefix ~ "unittest" ~ index.text; | catch(Exception) | assert(false, text("Error converting ", index, " to string")); | | } else | return prefix ~ nameFromAttr; | } | | void function() getUDAFunction(alias composite, alias uda)() pure nothrow { | import std.traits: isSomeFunction, hasUDA; | | void function()[] ret; | foreach(memberStr; __traits(allMembers, composite)) { | static if(__traits(compiles, Identity!(__traits(getMember, composite, memberStr)))) { | alias member = Identity!(__traits(getMember, composite, memberStr)); | static if(__traits(compiles, &member)) { | static if(isSomeFunction!member && hasUDA!(member, uda)) { | ret ~= &member; | } | } | } | } | | return ret.length ? ret[0] : null; | } | | TestData[] testData; | | void addMemberUnittests(alias composite)() pure nothrow { | | import unit_threaded.runner.attrs; | import std.traits: hasUDA; | import std.meta: Filter, aliasSeqOf; | import std.algorithm: map, cartesianProduct; | | // weird name for hygiene reasons | foreach(index, eLtEstO; __traits(getUnitTests, composite)) { | | enum dontTest = hasUDA!(eLtEstO, DontTest); | | static if(!dontTest) { | | enum name = unittestName!(eLtEstO, index); | enum hidden = hasUDA!(eLtEstO, HiddenTest); | enum shouldFail = hasUDA!(eLtEstO, ShouldFail) || hasUDA!(eLtEstO, ShouldFailWith); | enum singleThreaded = hasUDA!(eLtEstO, Serial); | enum builtin = true; | enum suffix = ""; | | // let's check for @Values UDAs, which are actually of type ValuesImpl | enum isValues(alias T) = is(typeof(T)) && is(typeof(T):ValuesImpl!U, U); | alias valuesUDAs = Filter!(isValues, __traits(getAttributes, eLtEstO)); | | enum isTags(alias T) = is(typeof(T)) && is(typeof(T) == Tags); | enum tags = tagsFromAttrs!(Filter!(isTags, __traits(getAttributes, eLtEstO))); | enum exceptionTypeInfo = getExceptionTypeInfo!eLtEstO; | enum flakyRetries = getFlakyRetries!(eLtEstO); | | static if(valuesUDAs.length == 0) { | testData ~= TestData(name, | () { | auto setup = getUDAFunction!(composite, Setup); | auto shutdown = getUDAFunction!(composite, Shutdown); | | if(setup) setup(); | scope(exit) if(shutdown) shutdown(); | | eLtEstO(); | }, | hidden, | shouldFail, | singleThreaded, | builtin, | suffix, | tags, | exceptionTypeInfo, | flakyRetries); | } else { | import std.range; | | // cartesianProduct doesn't work with only one range, so in the usual case | // of only one @Values UDA, we bind to prod with a range of tuples, just | // as returned by cartesianProduct. | | static if(valuesUDAs.length == 1) { | import std.typecons; | enum prod = valuesUDAs[0].values.map!(a => tuple(a)); | } else { | mixin(`enum prod = cartesianProduct(` ~ valuesUDAs.length.iota.map! | (a => `valuesUDAs[` ~ guaranteedToString(a) ~ `].values`).join(", ") ~ `);`); | } | | foreach(comb; aliasSeqOf!prod) { | enum valuesName = valuesName(comb); | | static if(hasUDA!(eLtEstO, AutoTags)) | enum realTags = tags ~ valuesName.split(".").array; | else | enum realTags = tags; | | testData ~= TestData(name ~ "." ~ valuesName, | () { | foreach(i; aliasSeqOf!(comb.length.iota)) | ValueHolder!(typeof(comb[i])).values[i] = comb[i]; | eLtEstO(); | }, | hidden, | shouldFail, | singleThreaded, | builtin, | suffix, | realTags, | exceptionTypeInfo, | flakyRetries); | } | } | } | } | } | | | // Keeps track of mangled names of everything visited. | bool[string] visitedMembers; | | void addUnitTestsRecursively(alias composite)() pure nothrow { | | if (composite.mangleof in visitedMembers) | return; | | visitedMembers[composite.mangleof] = true; | addMemberUnittests!composite(); | | foreach(member; __traits(allMembers, composite)) { | | // isPrivate can't be used here. I don't know why. | static if(__traits(compiles, __traits(getProtection, __traits(getMember, module_, member)))) | enum notPrivate = __traits(getProtection, __traits(getMember, module_, member)) != "private"; | else | enum notPrivate = false; | | static if ( | notPrivate && | // If visibility of the member is deprecated, the next line still returns true | // and yet spills deprecation warning. If deprecation is turned into error, | // all works as intended. | __traits(compiles, __traits(getMember, composite, member)) && | __traits(compiles, __traits(allMembers, __traits(getMember, composite, member))) && | __traits(compiles, recurse!(__traits(getMember, composite, member))) | ) { | recurse!(__traits(getMember, composite, member)); | } | } | } | | void recurse(child)() pure nothrow { | static if (is(child == class) || is(child == struct) || is(child == union)) { | addUnitTestsRecursively!child; | } | } | | addUnitTestsRecursively!module_(); | return testData; |} | |private TypeInfo getExceptionTypeInfo(alias Test)() { | import unit_threaded.runner.attrs: ShouldFailWith; | import std.traits: hasUDA, getUDAs; | | static if(hasUDA!(Test, ShouldFailWith)) { | alias uda = getUDAs!(Test, ShouldFailWith)[0]; | return typeid(uda.Type); | } else | return null; |} | | |private string valuesName(T)(T tuple) { | import std.range: iota; | import std.meta: aliasSeqOf; | import std.array: join; | | string[] parts; | foreach(a; aliasSeqOf!(tuple.length.iota)) | parts ~= guaranteedToString(tuple[a]); | return parts.join("."); |} | |private string guaranteedToString(T)(T value) nothrow pure @safe { | import std.conv; | try | return value.to!string; | catch(Exception ex) | assert(0, "Could not convert value to string"); |} | |private string getValueAsString(T)(T value) nothrow pure @safe { | import std.conv; | try | return value.to!string; | catch(Exception ex) | assert(0, "Could not convert value to string"); |} | | |private template isStringUDA(alias T) { | import std.traits: isSomeString; | static if(__traits(compiles, isSomeString!(typeof(T)))) | enum isStringUDA = isSomeString!(typeof(T)); | else | enum isStringUDA = false; |} | |@safe pure unittest { | static assert(isStringUDA!"foo"); | static assert(!isStringUDA!5); |} | |private template isPrivate(alias module_, string moduleMember) { | alias ut_mmbr__ = Identity!(__traits(getMember, module_, moduleMember)); | | static if(__traits(compiles, __traits(getProtection, ut_mmbr__))) | enum isPrivate = __traits(getProtection, ut_mmbr__) == "private"; | else | enum isPrivate = true; |} | | |// if this member is a test function or class, given the predicate |private template PassesTestPred(alias module_, alias pred, string moduleMember) { | | static if(__traits(compiles, Identity!(__traits(getMember, module_, moduleMember)))) { | | import unit_threaded.runner.attrs: DontTest; | import std.traits: hasUDA; | | alias member = Identity!(__traits(getMember, module_, moduleMember)); | | static if(__traits(compiles, hasUDA!(member, DontTest))) | enum hasDontTest = hasUDA!(member, DontTest); | else | enum hasDontTest = false; | | enum PassesTestPred = | !isPrivate!(module_, moduleMember) && | pred!(module_, moduleMember) && | !hasDontTest; | | } else | enum PassesTestPred = false; |} | | |/** | * Finds all test classes (classes implementing a test() function) | * in the given module | */ |TestData[] moduleTestClasses(modules...)() pure nothrow { | | template isTestClass(alias module_, string moduleMember) { | import unit_threaded.runner.attrs: UnitTest; | import std.traits: isAggregateType, hasUDA; | | alias member = Identity!(__traits(getMember, module_, moduleMember)); | | static if(.isPrivate!(module_, moduleMember)) { | enum isTestClass = false; | } else static if(!__traits(compiles, isAggregateType!(member))) { | enum isTestClass = false; | } else static if(!isAggregateType!(member)) { | enum isTestClass = false; | } else static if(!__traits(compiles, { return new member; })) { | enum isTestClass = false; //can't new it, can't use it | } else { | enum hasUnitTest = hasUDA!(member, UnitTest); | enum hasTestMethod = __traits(hasMember, member, "test"); | | enum isTestClass = is(member == class) && (hasTestMethod || hasUnitTest); | } | } | | TestData[] ret; | | static foreach(module_; modules) { | ret ~= moduleTestData!(module_, isTestClass, memberTestData); | } | | return ret; |} | | |/** | * Finds all test functions in the given module. | * Returns an array of TestData structs | */ |TestData[] moduleTestFunctions(modules...)() { | | template isTestFunction(alias module_, string moduleMember) { | import unit_threaded.runner.attrs: UnitTest, Types; | import std.meta: AliasSeq; | import std.traits: isSomeFunction, hasUDA; | | alias member = Identity!(__traits(getMember, module_, moduleMember)); | | static if(.isPrivate!(module_, moduleMember)) { | enum isTestFunction = false; | } else static if(AliasSeq!(member).length != 1) { | enum isTestFunction = false; | } else static if(isSomeFunction!member) { | enum isTestFunction = | hasTestPrefix!(module_, moduleMember) || | hasUDA!(member, UnitTest); | } else static if(__traits(compiles, __traits(getAttributes, member))) { | // in this case we handle the possibility of a template function with | // the @Types UDA attached to it | enum hasTestName = | hasTestPrefix!(module_, moduleMember) || | hasUDA!(member, UnitTest); | enum isTestFunction = hasTestName && hasUDA!(member, Types); | } else { | enum isTestFunction = false; | } | } | | template hasTestPrefix(alias module_, string memberName) { | import std.uni: isUpper; | | alias member = Identity!(__traits(getMember, module_, memberName)); | | enum prefix = "test"; | enum minSize = prefix.length + 1; | | static if(memberName.length >= minSize && | memberName[0 .. prefix.length] == prefix && | isUpper(memberName[prefix.length])) { | enum hasTestPrefix = true; | } else { | enum hasTestPrefix = false; | } | } | | TestData[] ret; | | static foreach(module_; modules) { | ret ~= moduleTestData!(module_, isTestFunction, createFuncTestData); | } | | return ret; |} | | |/** | Get all the test functions for this module member. There might be more than one | when using parametrized unit tests. | | Examples: | ------ | void testFoo() {} // -> the array contains one element, testFoo | @(1, 2, 3) void testBar(int) {} // The array contains 3 elements, one for each UDA value | @Types!(int, float) void testBaz(T)() {} //The array contains 2 elements, one for each type | ------ |*/ |private TestData[] createFuncTestData(alias module_, string moduleMember)() { | import unit_threaded.runner.attrs; | import std.meta: aliasSeqOf, Alias; | import std.traits: hasUDA; | | alias testFunction = Alias!(__traits(getMember, module_, moduleMember)); | | enum isRegularFunction = __traits(compiles, &__traits(getMember, module_, moduleMember)); | | static if(isRegularFunction) { | | static if(arity!testFunction == 0) | return createRegularFuncTestData!(module_, moduleMember); | else | return createValueParamFuncTestData!(module_, moduleMember, testFunction); | | } else static if(hasUDA!(testFunction, Types)) { // template function with @Types | return createTypeParamFuncTestData!(module_, moduleMember, testFunction); | } else { | return []; | } |} | |private TestData[] createRegularFuncTestData(alias module_, string moduleMember)() { | import std.meta: Alias; | | alias member = Alias!(__traits(getMember, module_, moduleMember)); | enum func = &member; | | // the reason we're creating a lambda to call the function is that test functions | // are ordinary functions, but we're storing delegates | return [ memberTestData!member(() { func(); }) ]; //simple case, just call the function |} | |// for value parameterised tests |private TestData[] createValueParamFuncTestData(alias module_, string moduleMember, alias testFunction)() { | | import unit_threaded.runner.traits: GetAttributes; | import unit_threaded.runner.attrs: AutoTags; | import std.traits: Parameters; | import std.range: iota; | import std.algorithm: map; | import std.typecons: tuple; | import std.traits: arity, hasUDA; | import std.meta: aliasSeqOf, Alias; | | alias params = Parameters!testFunction; | alias member = Alias!(__traits(getMember, module_, moduleMember)); | | bool hasAttributesForAllParams() { | auto ret = true; | static foreach(P; params) { | static if(GetAttributes!(member, P).length == 0) ret = false; | } | return ret; | } | | static if(!hasAttributesForAllParams) { | import std.conv: text; | pragma(msg, text("Warning: ", __traits(identifier, testFunction), | " passes the criteria for a value-parameterized test function", | " but doesn't have the appropriate value UDAs.\n", | " Consider changing its name or annotating it with @DontTest")); | return []; | } else { | | static if(arity!testFunction == 1) { | // bind a range of tuples to prod just as cartesianProduct returns | enum prod = [GetAttributes!(member, params[0])].map!(a => tuple(a)); | } else { | import std.conv: text; | | mixin(`enum prod = cartesianProduct(` ~ params.length.iota.map! | (a => `[GetAttributes!(member, params[` ~ guaranteedToString(a) ~ `])]`).join(", ") ~ `);`); | } | | TestData[] testData; | foreach(comb; aliasSeqOf!prod) { | enum valuesName = valuesName(comb); | | static if(hasUDA!(member, AutoTags)) | enum extraTags = valuesName.split(".").array; | else | enum string[] extraTags = []; | | | testData ~= memberTestData!member( | // testFunction(value0, value1, ...) | () { testFunction(comb.expand); }, | valuesName, | extraTags, | ); | } | | return testData; | } |} | | |// template function with @Types |private TestData[] createTypeParamFuncTestData(alias module_, string moduleMember, alias testFunction) | () |{ | import unit_threaded.attrs: Types, AutoTags; | import std.traits: getUDAs, hasUDA; | | alias typesAttrs = getUDAs!(testFunction, Types); | static assert(typesAttrs.length > 0); | | TestData[] testData; | | // To get a cartesian product of all @Types on the function, we use a mixin | string nestedForEachMixin() { | import std.array: join, array; | import std.range: iota, retro; | import std.algorithm: map; | import std.conv: text; | import std.format: format; | | string[] lines; | | string indentation(size_t n) { | string ret; | foreach(i; 0 .. n) ret ~= " "; | return ret; | } | | // e.g. 3 -> [type0, type1, type2] | string typeVars() { | return typesAttrs.length.iota.map!(i => text(`type`, i)).join(`, `); | } | | // e.g. 3 -> [int, float, Foo] | string typeIds() { | return typesAttrs.length.iota.map!(i => text(`type`, i, `.stringof`)).join(` ~ "." ~ `); | } | | // nested static foreachs, one per attribute | lines ~= typesAttrs | .length | .iota | .map!(i => indentation(i) ~ `static foreach(type%s; typesAttrs[%s].types) {`.format(i, i)) | .array | ; | | lines ~= q{ | { | static if(hasUDA!(testFunction, AutoTags)) | enum extraTags = [type0.stringof]; // FIXME | else | enum string[] extraTags = []; | | testData ~= memberTestData!testFunction( | () { testFunction!(%s)(); }, | %s, | extraTags | ); | } | }.format(typeVars, typeIds); | | // close all static foreach braces | lines ~= typesAttrs | .length | .iota | .retro | .map!(i => indentation(i) ~ `}`) | .array | ; | | return lines.join("\n"); | } | | | | enum mixinStr = nestedForEachMixin; | //pragma(msg, "\n", mixinStr, "\n"); | mixin(mixinStr); | | return testData; |} | | |// this funtion returns TestData for either classes or test functions |// built-in unittest modules are handled by moduleUnitTests |// pred determines what qualifies as a test |// createTestData must return TestData[] |private TestData[] moduleTestData(alias module_, alias pred, alias createTestData)() pure { | TestData[] testData; | | foreach(moduleMember; __traits(allMembers, module_)) { | | static if(PassesTestPred!(module_, pred, moduleMember)) | testData ~= createTestData!(module_, moduleMember); | } | | return testData; | |} | |// Deprecated: here for backwards compatibility |// TestData for a member of a module (either a test function or a test class) |private TestData memberTestData | (alias module_, string moduleMember, string[] extraTags = []) | (TestFunction testFunction = null, string suffix = "") |{ | import std.meta: Alias; | alias member = Alias!(__traits(getMember, module_, moduleMember)); | return memberTestData!member(testFunction, suffix, extraTags); |} | | |// TestData for a member of a module (either a test function or a test class) |private TestData memberTestData(alias member) | (TestFunction testFunction, string suffix = "", string[] extraTags = []) |{ | import unit_threaded.runner.attrs; | import std.traits: hasUDA, getUDAs; | import std.meta: Alias; | | enum singleThreaded = hasUDA!(member, Serial); | enum builtin = false; | enum tags = tagsFromAttrs!(getUDAs!(member, Tags)); | enum exceptionTypeInfo = getExceptionTypeInfo!member; | enum shouldFail = hasUDA!(member, ShouldFail) || hasUDA!(member, ShouldFailWith); | enum flakyRetries = getFlakyRetries!member; | // change names if explicitly asked to with a @Name UDA | enum nameFromAttr = TestNameFromAttr!member; | | static if(nameFromAttr == "") | enum name = __traits(identifier, member); | else | enum name = nameFromAttr; | | alias module_ = Alias!(__traits(parent, member)); | | return TestData(fullyQualifiedName!module_~ "." ~ name, | testFunction, | hasUDA!(member, HiddenTest), | shouldFail, | singleThreaded, | builtin, | suffix, | tags ~ extraTags, | exceptionTypeInfo, | flakyRetries); |} | |private int getFlakyRetries(alias test)() { | import unit_threaded.runner.attrs: Flaky; | import std.traits: getUDAs; | import std.conv: text; | | alias flakies = getUDAs!(test, Flaky); | | static assert(flakies.length == 0 || flakies.length == 1, | text("Only 1 @Flaky allowed, found ", flakies.length, " on ", | __traits(identifier, test))); | | static if(flakies.length == 1) { | static if(is(flakies[0])) | return Flaky.defaultRetries; | else | return flakies[0].retries; | } else | return 0; |} | |string[] tagsFromAttrs(T...)() { | static assert(T.length <= 1, "@Tags can only be applied once"); | static if(T.length) | return T[0].values; | else | return []; |} ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/subpackages/runner/source/unit_threaded/runner/reflection.d is 0% covered <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-subpackages-runner-source-unit_threaded-runner-io.lst |/** | * IO related functions | */ | |module unit_threaded.runner.io; | |import unit_threaded.from; | |/** | * Write if debug output was enabled. | */ |void writelnUt(T...)(auto ref T args) { | debug { | import unit_threaded.runner.testcase: TestCase; | if(isDebugOutputEnabled) | TestCase.currentTest.getWriter.writeln(args); | } |} | | | |private shared(bool) _debugOutput = false; ///print debug msgs? |private shared(bool) _forceEscCodes = false; ///use ANSI escape codes anyway? |package bool _useEscCodes; |enum _escCodes = ["\033[31;1m", "\033[32;1m", "\033[33;1m", "\033[0;;m"]; | | | |package bool shouldUseEscCodes() { | version (Posix) { | import std.stdio: stdout; | import core.sys.posix.unistd: isatty; 0000000| return _forceEscCodes || isatty(stdout.fileno()) != 0; | } else | return false; |} | | |void enableDebugOutput(bool value = true) nothrow { 0000000| synchronized { 0000000| _debugOutput = value; | } |} | |package bool isDebugOutputEnabled() nothrow @trusted { 0000000| synchronized { 0000000| return _debugOutput; | } |} | |package void forceEscCodes() nothrow { 0000000| synchronized { 0000000| _forceEscCodes = true; | } |} | |interface Output { | void send(in string output) @safe; | void flush() @safe; |} | |private enum Colour { | red, | green, | yellow, | cancel, |} | |private string colour(alias C)(in string msg) { 0000000| return escCode(C) ~ msg ~ escCode(Colour.cancel); |} | |private alias green = colour!(Colour.green); |private alias red = colour!(Colour.red); |private alias yellow = colour!(Colour.yellow); | |/** | * Send escape code to the console | */ |private string escCode(in Colour code) @safe { 0000000| return _useEscCodes ? _escCodes[code] : ""; |} | | |/** | * Writes the args in a thread-safe manner. | */ |void write(T...)(Output output, auto ref T args) { | import std.conv: text; 0000000| output.send(text(args)); |} | |/** | * Writes the args in a thread-safe manner and appends a newline. | */ |void writeln(T...)(Output output, auto ref T args) { 0000000| write(output, args, "\n"); |} | |/** | * Writes the args in a thread-safe manner in green (POSIX only). | * and appends a newline. | */ |void writelnGreen(T...)(Output output, auto ref T args) { | import std.conv: text; 0000000| output.send(green(text(args) ~ "\n")); |} | |/** | * Writes the args in a thread-safe manner in red (POSIX only) | * and appends a newline. | */ |void writelnRed(T...)(Output output, auto ref T args) { 0000000| writeRed(output, args, "\n"); |} | |/** | * Writes the args in a thread-safe manner in red (POSIX only). | * and appends a newline. | */ |void writeRed(T...)(Output output, auto ref T args) { | import std.conv: text; 0000000| output.send(red(text(args))); |} | |/** | * Writes the args in a thread-safe manner in yellow (POSIX only). | * and appends a newline. | */ |void writeYellow(T...)(Output output, auto ref T args) { | import std.conv: text; 0000000| output.send(yellow(text(args))); |} | |/** | * Thread to output to stdout | */ |class WriterThread: Output { | | import std.concurrency: Tid; | | | /** | * Returns a reference to the only instance of this class. | */ | static WriterThread get() @trusted { | import std.concurrency: initOnce; | static __gshared WriterThread instance; 0000000| return initOnce!instance(new WriterThread); | } | | override void send(in string output) @safe { | | version(unitUnthreaded) { | import std.stdio: write; 0000000| write(output); | } else { | import std.concurrency: send, thisTid; | () @trusted { _tid.send(output, thisTid); }(); | } | } | | override void flush() @safe { | version(unitUnthreaded) {} | else { | import std.concurrency: send, thisTid; | () @trusted { _tid.send(Flush(), thisTid); }(); | } | } | | |private: | 0000000| this() { | version(unitUnthreaded) {} | else { | import std.concurrency: spawn, thisTid, receiveOnly, send; | import std.stdio: stdout, stderr; | _tid = spawn(&threadWriter!(stdout, stderr), thisTid); | _tid.send(ThreadWait()); | receiveOnly!ThreadStarted; | } | } | | | Tid _tid; |} | | |struct ThreadWait{}; |struct ThreadFinish{}; |struct ThreadStarted{}; |struct ThreadEnded{}; |struct Flush{}; | |version (Posix) { | enum nullFileName = "/dev/null"; |} else { | enum nullFileName = "NUL"; |} | | |void threadWriter(alias OUT, alias ERR)(from!"std.concurrency".Tid tid) |{ | import std.concurrency: receive, send, OwnerTerminated, Tid; | | auto done = false; | | auto saveStdout = OUT; | auto saveStderr = ERR; | | void restore() { | saveStdout.flush(); | OUT = saveStdout; | ERR = saveStderr; | } | | scope (failure) restore; | | if (!isDebugOutputEnabled()) { | OUT = typeof(OUT)(nullFileName, "w"); | ERR = typeof(ERR)(nullFileName, "w"); | } | | void actuallyPrint(in string msg) { | if(msg.length) saveStdout.write(msg); | } | | // the first thread to send output becomes the current | // until that thread sends a Flush message no other thread | // can print to stdout, so we store their outputs in the meanwhile | static struct ThreadOutput { | string currentOutput; | string[] outputs; | | void store(in string msg) { | currentOutput ~= msg; | } | | void flush() { | outputs ~= currentOutput; | currentOutput = ""; | } | } | ThreadOutput[Tid] outputs; | | Tid currentTid; | | while (!done) { | receive( | (string msg, Tid originTid) { | | if(currentTid == currentTid.init) { | currentTid = originTid; | | // it could be that this thread became the current thread but had output not yet printed | if(originTid in outputs) { | actuallyPrint(outputs[originTid].currentOutput); | outputs[originTid].currentOutput = ""; | } | } | | if(currentTid == originTid) | actuallyPrint(msg); | else { | if(originTid !in outputs) outputs[originTid] = typeof(outputs[originTid]).init; | outputs[originTid].store(msg); | } | }, | (ThreadWait w) { | tid.send(ThreadStarted()); | }, | (ThreadFinish f) { | done = true; | }, | (Flush f, Tid originTid) { | | if(originTid in outputs) outputs[originTid].flush; | | if(currentTid != currentTid.init && currentTid != originTid) | return; | | foreach(_, ref threadOutput; outputs) { | foreach(o; threadOutput.outputs) | actuallyPrint(o); | threadOutput.outputs = []; | } | | currentTid = currentTid.init; | }, | (OwnerTerminated trm) { | done = true; | } | ); | } | | restore; | tid.send(ThreadEnded()); |} ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/subpackages/runner/source/unit_threaded/runner/io.d is 0% covered <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-source-unit_threaded-attrs.lst |module unit_threaded.attrs; | |public import unit_threaded.runner.attrs; ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/source/unit_threaded/attrs.d has no code <<<<<< EOF # path=tests-ut-unique.lst |module ut.unique; | | |import ut; |import automem.unique; | | |mixin TestUtils; | | |/// |@("with struct and test allocator") |@system unittest { | 2| auto allocator = TestAllocator(); | { 2| const foo = Unique!(Struct, TestAllocator*)(&allocator, 5); 1| foo.twice.shouldEqual(10); 1| allocator.numAllocations.shouldEqual(1); 1| Struct.numStructs.shouldEqual(1); | } | 1| Struct.numStructs.shouldEqual(0); |} | | |@("with class and test allocator") |@system unittest { | 2| auto allocator = TestAllocator(); | { 2| const foo = Unique!(Class, TestAllocator*)(&allocator, 5); 1| foo.twice.shouldEqual(10); 1| allocator.numAllocations.shouldEqual(1); 1| Class.numClasses.shouldEqual(1); | } | 1| Class.numClasses.shouldEqual(0); |} | |/// |@("with struct and mallocator") |@safe unittest { | | import std.experimental.allocator.mallocator: Mallocator; | { 2| const foo = Unique!(Struct, Mallocator)(5); 1| foo.twice.shouldEqual(10); 1| Struct.numStructs.shouldEqual(1); | } | 1| Struct.numStructs.shouldEqual(0); |} | |@("default constructor") |@system unittest { 2| auto allocator = TestAllocator(); | 2| auto ptr = Unique!(Struct, TestAllocator*)(); 2| (cast(bool)ptr).shouldBeFalse; 1| ptr.get.shouldBeNull; | 1| ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 1| ptr.get.shouldNotBeNull; 1| ptr.get.twice.shouldEqual(10); 2| (cast(bool)ptr).shouldBeTrue; |} | |@(".init") |@system unittest { 2| auto allocator = TestAllocator(); | 2| Unique!(Struct, TestAllocator*) ptr; 2| (cast(bool)ptr).shouldBeFalse; 1| ptr.get.shouldBeNull; | 1| ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 1| ptr.get.shouldNotBeNull; 1| ptr.get.twice.shouldEqual(10); 2| (cast(bool)ptr).shouldBeTrue; |} | |@("move") |@system unittest { | import std.algorithm: move; | 2| auto allocator = TestAllocator(); 2| auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5); 2| Unique!(Struct, TestAllocator*) newPtr = oldPtr.move; 1| oldPtr.shouldBeNull; 1| newPtr.twice.shouldEqual(10); 1| Struct.numStructs.shouldEqual(1); |} | |@("copy") |@system unittest { 2| auto allocator = TestAllocator(); 2| auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5); 2| Unique!(Struct, TestAllocator*) newPtr; | // non-copyable | static assert(!__traits(compiles, newPtr = oldPtr)); |} | |@("construct base class") |@system unittest { 2| auto allocator = TestAllocator(); | { 2| Unique!(Object, TestAllocator*) bar = Unique!(Class, TestAllocator*)(&allocator, 5); 1| Class.numClasses.shouldEqual(1); | } | 1| Class.numClasses.shouldEqual(0); |} | |@("assign base class") |@system unittest { 2| auto allocator = TestAllocator(); | { 2| Unique!(Object, TestAllocator*) bar; 1| bar = Unique!(Class, TestAllocator*)(&allocator, 5); 1| Class.numClasses.shouldEqual(1); | } | 1| Class.numClasses.shouldEqual(0); |} | |@("Return Unique from function") |@system unittest { 2| auto allocator = TestAllocator(); | | auto produce(int i) { 1| return Unique!(Struct, TestAllocator*)(&allocator, i); | } | 2| auto ptr = produce(4); 1| ptr.twice.shouldEqual(8); |} | |@("unique") |@system unittest { 2| auto allocator = TestAllocator(); 2| auto oldPtr = Unique!(Struct, TestAllocator*)(&allocator, 5); 2| auto newPtr = oldPtr.unique; 1| newPtr.twice.shouldEqual(10); 1| oldPtr.shouldBeNull; |} | |@("@nogc") |@safe @nogc unittest { | | import std.experimental.allocator.mallocator: Mallocator; | | { 2| const ptr = Unique!(NoGcStruct, Mallocator)(5); | // shouldEqual isn't @nogc 1| assert(ptr.i == 5); 1| assert(NoGcStruct.numStructs == 1); | } | 1| assert(NoGcStruct.numStructs == 0); |} | |@("@nogc @safe") |@safe @nogc unittest { | 1| auto allocator = SafeAllocator(); | | { 2| const ptr = Unique!(NoGcStruct, SafeAllocator)(SafeAllocator(), 6); | // shouldEqual isn't @nogc 1| assert(ptr.i == 6); 1| assert(NoGcStruct.numStructs == 1); | } | 1| assert(NoGcStruct.numStructs == 0); |} | |@("deref") |@system unittest { | { 2| auto allocator = TestAllocator(); 2| auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 1| *ptr = Struct(13); 1| ptr.twice.shouldEqual(26); 1| Struct.numStructs.shouldEqual(1); | } 1| Struct.numStructs.shouldEqual(0); |} | |@("move from populated other unique") |@system unittest { | | import std.algorithm: move; | | { 2| auto allocator = TestAllocator(); | 2| auto ptr1 = Unique!(Struct, TestAllocator*)(&allocator, 5); 1| Struct.numStructs.shouldEqual(1); | | { 2| auto ptr2 = Unique!(Struct, TestAllocator*)(&allocator, 10); 1| Struct.numStructs.shouldEqual(2); 1| ptr1 = ptr2.move; 1| Struct.numStructs.shouldEqual(1); 1| ptr2.shouldBeNull; 1| ptr1.twice.shouldEqual(20); | } | | } | 1| Struct.numStructs.shouldEqual(0); |} | |@("assign to rvalue") |@system unittest { | | { 2| auto allocator = TestAllocator(); | 2| auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 5); 1| ptr = Unique!(Struct, TestAllocator*)(&allocator, 7); | 1| Struct.numStructs.shouldEqual(1); 1| ptr.twice.shouldEqual(14); | } | 1| Struct.numStructs.shouldEqual(0); |} | | |@("theAllocator") |@system unittest { 2| with(theTestAllocator){ 2| auto ptr = Unique!Struct(42); 1| (*ptr).shouldEqual(Struct(42)); 1| Struct.numStructs.shouldEqual(1); | } | 1| Struct.numStructs.shouldEqual(0); |} | | |@("@nogc class destructor") |@nogc unittest { | 1| auto allocator = SafeAllocator(); | | { 2| const ptr = Unique!(NoGcClass, SafeAllocator)(SafeAllocator(), 6); | // shouldEqual isn't @nogc 1| assert(ptr.i == 6); 1| assert(NoGcClass.numClasses == 1); | } | 1| assert(NoGcClass.numClasses == 0); |} | | |version(DIP1000) { | @("borrow") | @safe unittest { | | auto allocator = SafeAllocator(); | | { | const ptr = Unique!(Struct, SafeAllocator)(SafeAllocator(), 6); | scopeFunc(ptr.borrow).shouldEqual(18); | } | } | | private int scopeFunc(scope const(Struct)* s) @safe { | | return s.i * 3; | } |} tests/ut/unique.d is 100% covered <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-subpackages-runner-source-unit_threaded-runner-testcase.lst |/** | The different TestCase classes | */ |module unit_threaded.runner.testcase; | | |private shared(bool) _stacktrace = false; | |private void setStackTrace(bool value) @trusted nothrow @nogc { 0000000| synchronized { 0000000| _stacktrace = value; | } |} | |/// Let AssertError(s) propagate and thus dump a stacktrace. |public void enableStackTrace() @safe nothrow @nogc { 0000000| setStackTrace(true); |} | |/// (Default behavior) Catch AssertError(s) and thus allow all tests to be ran. |public void disableStackTrace() @safe nothrow @nogc { 0000000| setStackTrace(false); |} | |/** | * Class from which other test cases derive | */ |class TestCase { | | import unit_threaded.runner.io: Output; | | /** | * Returns: the name of the test | */ | string getPath() const pure nothrow { 0000000| return this.classinfo.name; | } | | /** | * Executes the test. | * Returns: array of failures (child classes may have more than 1) | */ | string[] opCall() { | static if(__VERSION__ >= 2077) | import std.datetime.stopwatch: StopWatch, AutoStart; | else | import std.datetime: StopWatch, AutoStart; | 0000000| currentTest = this; 0000000| auto sw = StopWatch(AutoStart.yes); 0000000| doTest(); 0000000| flushOutput(); 0000000| return _failed ? [getPath()] : []; | } | | /** | Certain child classes override this | */ 0000000| ulong numTestsRun() const { return 1; } 0000000| void showChrono() @safe pure nothrow { _showChrono = true; } 0000000| void setOutput(Output output) @safe pure nothrow { _output = output; } 0000000| void silence() @safe pure nothrow { _silent = true; } 0000000| bool shouldFail() @safe @nogc pure nothrow { return false; } | | |package: | | static TestCase currentTest; | Output _output; | | final Output getWriter() @safe { | import unit_threaded.runner.io: WriterThread; 0000000| return _output is null ? WriterThread.get : _output; | } | | |protected: | | abstract void test(); | void setup() { } ///override to run before test() | void shutdown() { } ///override to run after test() | | |private: | | bool _failed; | bool _silent; | bool _showChrono; | | final auto doTest() { | import std.conv: text; | import std.datetime: Duration; | static if(__VERSION__ >= 2077) | import std.datetime.stopwatch: StopWatch, AutoStart; | else | import std.datetime: StopWatch, AutoStart; | 0000000| auto sw = StopWatch(AutoStart.yes); 0000000| print(getPath() ~ ":\n"); 0000000| check(setup()); 0000000| if (!_failed) check(test()); 0000000| if (!_failed) check(shutdown()); 0000000| if(_failed) print("\n"); 0000000| if(_showChrono) print(text(" (", cast(Duration)sw.peek, ")\n\n")); 0000000| if(_failed) print("\n"); | } | | final bool check(E)(lazy E expression) { | import unit_threaded.exception: UnitTestException; | try { 0000000| expression(); | } catch(UnitTestException ex) { 0000000| fail(ex.toString()); | } catch(Throwable ex) { 0000000| fail("\n " ~ ex.toString() ~ "\n"); | } | 0000000| return !_failed; | } | | final void fail(in string msg) { 0000000| _failed = true; 0000000| print(msg); | } | | final void print(in string msg) { | import unit_threaded.runner.io: write; 0000000| if(!_silent) getWriter.write(msg); | } | | final void flushOutput() { 0000000| getWriter.flush; | } |} | |unittest |{ | enum Stage { setup, test, shutdown, none, } | | class TestForFailingStage : TestCase | { | Stage failedStage, currStage; | | this(Stage failedStage) | { | this.failedStage = failedStage; | } | | override void setup() | { | currStage = Stage.setup; | if (failedStage == currStage) assert(0); | } | | override void test() | { | currStage = Stage.test; | if (failedStage == currStage) assert(0); | } | | override void shutdown() | { | currStage = Stage.shutdown; | if (failedStage == currStage) assert(0); | } | } | | // the last stage of non failing test case is the shutdown stage | { | auto test = new TestForFailingStage(Stage.none); | test.silence; | test.doTest; | | assert(test.failedStage == Stage.none); | assert(test.currStage == Stage.shutdown); | } | | // if a test case fails at setup stage the last stage is setup one | { | auto test = new TestForFailingStage(Stage.setup); | test.silence; | test.doTest; | | assert(test.failedStage == Stage.setup); | assert(test.currStage == Stage.setup); | } | | // if a test case fails at test stage the last stage is test stage | { | auto test = new TestForFailingStage(Stage.test); | test.silence; | test.doTest; | | assert(test.failedStage == Stage.test); | assert(test.currStage == Stage.test); | } |} | |/** | A test that runs other tests. | */ |class CompositeTestCase: TestCase { 0000000| void add(TestCase t) { _tests ~= t;} | | void opOpAssign(string op : "~")(TestCase t) { 0000000| add(t); | } | | override string[] opCall() { | import std.algorithm: map, reduce; 0000000| return _tests.map!(a => a()).reduce!((a, b) => a ~ b); | } | 0000000| override void test() { assert(false, "CompositeTestCase.test should never be called"); } | | override ulong numTestsRun() const { 0000000| return _tests.length; | } | | package TestCase[] tests() @safe pure nothrow { 0000000| return _tests; | } | | override void showChrono() { 0000000| foreach(test; _tests) test.showChrono; | } | |private: | | TestCase[] _tests; |} | |/** | A test that should fail | */ |class ShouldFailTestCase: TestCase { 0000000| this(TestCase testCase, in TypeInfo exceptionTypeInfo) { 0000000| this.testCase = testCase; 0000000| this.exceptionTypeInfo = exceptionTypeInfo; | } | | override bool shouldFail() @safe @nogc pure nothrow { 0000000| return true; | } | | override string getPath() const pure nothrow { 0000000| return this.testCase.getPath; | } | | override void test() { | import unit_threaded.exception: UnitTestException; | import std.exception: enforce, collectException; | import std.conv: text; | 0000000| const ex = collectException!Throwable(testCase.test()); 0000000| enforce!UnitTestException(ex !is null, "Test '" ~ testCase.getPath ~ "' was expected to fail but did not"); 0000000| enforce!UnitTestException(exceptionTypeInfo is null || typeid(ex) == exceptionTypeInfo, 0000000| text("Test '", testCase.getPath, "' was expected to throw ", | exceptionTypeInfo, " but threw ", typeid(ex))); | } | |private: | | TestCase testCase; | const(TypeInfo) exceptionTypeInfo; |} | |/** | A test that is a regular function. | */ |class FunctionTestCase: TestCase { | | import unit_threaded.runner.reflection: TestData, TestFunction; | 0000000| this(in TestData data) pure nothrow { 0000000| _name = data.getPath; 0000000| _func = data.testFunction; | } | | override void test() { 0000000| _func(); | } | | override string getPath() const pure nothrow { 0000000| return _name; | } | | private string _name; | private TestFunction _func; |} | |/** | A test that is a `unittest` block. | */ |class BuiltinTestCase: FunctionTestCase { | | import unit_threaded.runner.reflection: TestData; | 0000000| this(in TestData data) pure nothrow { 0000000| super(data); | } | | override void test() { | import core.exception: AssertError; | | try 0000000| super.test(); | catch(AssertError e) { | import unit_threaded.exception: fail; 0000000| fail(_stacktrace? e.toString() : e.msg, e.file, e.line); | } | } |} | | |/** | A test that is expected to fail some of the time. | */ |class FlakyTestCase: TestCase { 0000000| this(TestCase testCase, int retries) { 0000000| this.testCase = testCase; 0000000| this.retries = retries; | } | | override string getPath() const pure nothrow { 0000000| return this.testCase.getPath; | } | | override void test() { | 0000000| foreach(i; 0 .. retries) { | try { 0000000| testCase.test; 0000000| break; | } catch(Throwable t) { 0000000| if(i == retries - 1) 0000000| throw t; | } | } | } | |private: | | TestCase testCase; | int retries; |} ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/subpackages/runner/source/unit_threaded/runner/testcase.d is 0% covered <<<<<< EOF # path=tests-ut-ref_counted.lst |module ut.ref_counted; | |import ut; |import automem.ref_counted; | |mixin TestUtils; | |/// |@("struct test allocator no copies") |@system unittest { 2| auto allocator = TestAllocator(); 1| Struct.numStructs.should == 0; | { 2| auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 1| Struct.numStructs.shouldEqual(1); | } 1| Struct.numStructs.shouldEqual(0); |} | |@("struct test allocator one lvalue assignment") |@system unittest { 2| auto allocator = TestAllocator(); | { 2| auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 1| Struct.numStructs.shouldEqual(1); | 2| RefCounted!(Struct, TestAllocator*) ptr2; 1| ptr2 = ptr1; 1| Struct.numStructs.shouldEqual(1); | } 1| Struct.numStructs.shouldEqual(0); |} | |@("struct test allocator one lvalue assignment from T.init") |@system unittest { | 2| auto allocator = TestAllocator(); | | { 2| RefCounted!(Struct, TestAllocator*) ptr1; 1| Struct.numStructs.shouldEqual(0); | 2| auto ptr2 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 1| Struct.numStructs.shouldEqual(1); | 1| ptr2 = ptr1; 1| Struct.numStructs.shouldEqual(0); | } | 1| Struct.numStructs.shouldEqual(0); |} | |@("struct test allocator one lvalue assignment both non-null") |@system unittest { | 2| auto allocator = TestAllocator(); | | { 2| auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 1| Struct.numStructs.shouldEqual(1); | 2| auto ptr2 = RefCounted!(Struct, TestAllocator*)(&allocator, 7); 1| Struct.numStructs.shouldEqual(2); | 1| ptr2 = ptr1; 1| Struct.numStructs.shouldEqual(1); | } | 1| Struct.numStructs.shouldEqual(0); |} | | | |@("struct test allocator one rvalue assignment test allocator") |@system unittest { 2| auto allocator = TestAllocator(); | { 2| RefCounted!(Struct, TestAllocator*) ptr; 1| ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 1| Struct.numStructs.shouldEqual(1); | } 1| Struct.numStructs.shouldEqual(0); |} | |@("struct test allocator one rvalue assignment mallocator") |@safe unittest { | import std.experimental.allocator.mallocator: Mallocator; | { 2| RefCounted!(Struct, Mallocator) ptr; 1| ptr = RefCounted!(Struct, Mallocator)(5); 1| Struct.numStructs.shouldEqual(1); | } 1| Struct.numStructs.shouldEqual(0); |} | | |@("struct test allocator one lvalue copy constructor") |@system unittest { 2| auto allocator = TestAllocator(); | { 2| auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 1| Struct.numStructs.shouldEqual(1); 2| auto ptr2 = ptr1; 1| Struct.numStructs.shouldEqual(1); | 1| ptr1.i.shouldEqual(5); 1| ptr2.i.shouldEqual(5); | } 1| Struct.numStructs.shouldEqual(0); |} | |@("struct test allocator one rvalue copy constructor") |@system unittest { 2| auto allocator = TestAllocator(); | { 2| auto ptr = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 1| Struct.numStructs.shouldEqual(1); | } 1| Struct.numStructs.shouldEqual(0); |} | |@("many copies made") |@system unittest { 2| auto allocator = TestAllocator(); | | // helper function for intrusive testing, in case the implementation | // ever changes | size_t refCount(T)(ref T ptr) { 7| return ptr._impl._count; | } | | { 2| auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 5); 1| Struct.numStructs.shouldEqual(1); | 2| auto ptr2 = ptr1; 1| Struct.numStructs.shouldEqual(1); | | { 2| auto ptr3 = ptr2; 1| Struct.numStructs.shouldEqual(1); | 1| refCount(ptr1).shouldEqual(3); 1| refCount(ptr2).shouldEqual(3); 1| refCount(ptr3).shouldEqual(3); | } | 1| Struct.numStructs.shouldEqual(1); 1| refCount(ptr1).shouldEqual(2); 1| refCount(ptr2).shouldEqual(2); | | auto produce() { 1| return RefCounted!(Struct, TestAllocator*)(&allocator, 3); | } | 1| ptr1 = produce; 1| Struct.numStructs.shouldEqual(2); 1| refCount(ptr1).shouldEqual(1); 1| refCount(ptr2).shouldEqual(1); | 1| ptr1.twice.shouldEqual(6); 1| ptr2.twice.shouldEqual(10); | } | 1| Struct.numStructs.shouldEqual(0); |} | |@("default allocator") |@system unittest { | { 2| auto ptr = RefCounted!Struct(5); 1| Struct.numStructs.shouldEqual(1); | } 1| Struct.numStructs.shouldEqual(0); |} | |@("default.struct.shared") |@system unittest { | { 2| auto ptr = RefCounted!(shared SharedStruct)(5); 1| SharedStruct.numStructs.shouldEqual(1); | } 1| SharedStruct.numStructs.shouldEqual(0); |} | | |@("default.class.shared") |@system unittest { | { 2| auto ptr = RefCounted!(shared SharedClass)(5); 1| SharedClass.numClasss.shouldEqual(1); | } 1| SharedClass.numClasss.shouldEqual(0); |} | | |@("deref") |@system unittest { 2| auto allocator = TestAllocator(); 2| auto rc1 = RefCounted!(int, TestAllocator*)(&allocator, 5); | 1| (*rc1).shouldEqual(5); 2| auto rc2 = rc1; 1| *rc2 = 42; 1| (*rc1).shouldEqual(42); |} | |@("swap") |@system unittest { | import std.algorithm: swap; 4| RefCounted!(int, TestAllocator*) rc1, rc2; 1| swap(rc1, rc2); |} | |@("phobos bug 6606") |@system unittest { | | union U { | size_t i; | void* p; | } | | struct S { | U u; | } | | alias SRC = RefCounted!(S, TestAllocator*); |} | |@("phobos bug 6436") |@system unittest |{ | static struct S { 1| this(ref int val, string file = __FILE__, size_t line = __LINE__) { 1| val.shouldEqual(3, file, line); 1| ++val; | } | } | 2| auto allocator = TestAllocator(); 1| int val = 3; 2| auto s = RefCounted!(S, TestAllocator*)(&allocator, val); 1| val.shouldEqual(4); |} | |@("assign from T") |@safe unittest { | import std.experimental.allocator.mallocator: Mallocator; | | { 2| auto a = RefCounted!(Struct, Mallocator)(3); 1| Struct.numStructs.shouldEqual(1); | 1| *a = Struct(5); 1| Struct.numStructs.shouldEqual(1); 1| (*a).shouldEqual(Struct(5)); | 2| RefCounted!(Struct, Mallocator) b; 1| b = a; 1| (*b).shouldEqual(Struct(5)); 1| Struct.numStructs.shouldEqual(1); | } | 1| Struct.numStructs.shouldEqual(0); |} | |@("assign self") |@system unittest { 2| auto allocator = TestAllocator(); | { 2| auto a = RefCounted!(Struct, TestAllocator*)(&allocator, 1); 1| a = a; 1| Struct.numStructs.shouldEqual(1); | } 1| Struct.numStructs.shouldEqual(0); |} | |@("SharedStruct") |@system unittest { 2| auto allocator = TestAllocator(); | { 2| auto ptr = RefCounted!(shared SharedStruct, TestAllocator*)(&allocator, 5); 1| SharedStruct.numStructs.shouldEqual(1); | } 1| SharedStruct.numStructs.shouldEqual(0); |} | |@("@nogc @safe") |@safe @nogc unittest { | 1| auto allocator = SafeAllocator(); | | { 2| const ptr = RefCounted!(NoGcStruct, SafeAllocator)(SafeAllocator(), 6); 1| assert(ptr.i == 6); 1| assert(NoGcStruct.numStructs == 1); | } | 1| assert(NoGcStruct.numStructs == 0); |} | | |@("const object") |@system unittest { 2| auto allocator = TestAllocator(); 2| auto ptr1 = RefCounted!(const Struct, TestAllocator*)(&allocator, 5); |} | | |@("theAllocator") |@system unittest { | 2| with(theTestAllocator) { 2| auto ptr = RefCounted!Struct(42); 1| (*ptr).shouldEqual(Struct(42)); 1| Struct.numStructs.shouldEqual(1); | } | 1| Struct.numStructs.shouldEqual(0); |} | | |@("threads Mallocator") |@system unittest { | import std.experimental.allocator.mallocator: Mallocator; | static assert(__traits(compiles, sendRefCounted!Mallocator(7))); |} | |@("threads SafeAllocator by value") |@system unittest { | // can't even use TestAllocator because it has indirections | // can't pass by pointer since it's an indirection 1| auto allocator = SafeAllocator(); | static assert(__traits(compiles, sendRefCounted!(SafeAllocator)(allocator, 7))); |} | |@("threads SafeAllocator by shared pointer") |@system unittest { | // can't even use TestAllocator because it has indirections | // can't only pass by pointer if shared 1| auto allocator = shared SafeAllocator(); | static assert(__traits(compiles, sendRefCounted!(shared SafeAllocator*)(&allocator, 7))); |} | |@("Construct RefCounted from Unique") |@system unittest { | import automem.unique: Unique; 2| auto allocator = TestAllocator(); 2| auto ptr = refCounted(Unique!(int, TestAllocator*)(&allocator, 42)); 1| (*ptr).shouldEqual(42); |} | |@("RefCounted with class") |@system unittest { 2| auto allocator = TestAllocator(); | { 1| writelnUt("Creating ptr"); 2| auto ptr = RefCounted!(Class, TestAllocator*)(&allocator, 33); 1| (*ptr).i.shouldEqual(33); 1| Class.numClasses.shouldEqual(1); | } 1| Class.numClasses.shouldEqual(0); |} | |@("@nogc class destructor") |@nogc unittest { | | import automem: Unique; | 1| auto allocator = SafeAllocator(); | | { 2| const ptr = Unique!(NoGcClass, SafeAllocator)(SafeAllocator(), 6); | // shouldEqual isn't @nogc 1| assert(ptr.i == 6); 1| assert(NoGcClass.numClasses == 1); | } | 1| assert(NoGcClass.numClasses == 0); |} | |@("RefCounted opSlice and opIndex") |@system unittest { | import std.mmfile: MmFile; 2| auto file = RefCounted!MmFile(null, MmFile.Mode.readWriteNew, 120, null); | // The type of file[0] should be ubyte, not Impl. | static assert(is(typeof(file[0]) == typeof(MmFile.init[0]))); | // opSlice should result in void[] not Impl[]. | static assert(is(typeof(file[0 .. size_t.max]) == typeof(MmFile.init[0 .. size_t.max]))); 1| ubyte[] data = cast(ubyte[]) file[0 .. cast(size_t) file.length]; 1| immutable ubyte b = file[1]; 1| file[1] = cast(ubyte) (b + 1); 1| assert(data[1] == cast(ubyte) (b + 1)); |} | |@("Construct RefCounted using global allocator for struct with zero-args ctor") |@system unittest { | struct S { | private ulong zeroArgsCtorTest = 3; | } 2| auto s = RefCounted!S.construct(); | static assert(is(typeof(s) == RefCounted!S)); 1| assert(s._impl !is null); 1| assert(s.zeroArgsCtorTest == 3); |} | | | |void sendRefCounted(Allocator, Args...)(Args args) { | import std.concurrency: spawn, send; | | auto tid = spawn(&threadFunc); | auto ptr = RefCounted!(shared SharedStruct, Allocator)(args); | | tid.send(ptr); |} | |void threadFunc() { | |} | |@("shared struct with indirection") |@system unittest { 2| auto s = RefCounted!(shared SharedStructWithIndirection)("foobar"); |} | | |@("copy from T.init") |unittest { | static struct X { | int i; | } | static struct Y { | RefCounted!X x; | } 2| Y y1; 2| Y y2; 1| y2 = y1; |} | | |@("number of allocations") |@safe unittest { | static TestAllocator allocator; 1| allocator.numAllocations.should == 0; | 2| auto ptr1 = RefCounted!(Struct, TestAllocator*)(&allocator, 77); 1| allocator.numAllocations.should == 1; | { 2| auto ptr2 = ptr1; 1| allocator.numAllocations.should == 1; | | { 2| auto ptr = ptr2; 1| allocator.numAllocations.should == 1; | } | | auto produce(int i) { 1| return typeof(ptr1)(&allocator, i); | } | 1| ptr1 = produce(99); 1| allocator.numAllocations.should == 2; | } | 1| allocator.numAllocations.should == 2; |} tests/ut/ref_counted.d is 100% covered <<<<<< EOF # path=source-automem-package.lst |/** |C++-style automatic memory management smart pointers for D using `std.experimental.allocator`. | |Unlike the C++ variants, the smart pointers themselves allocate the memory for the objects they contain. |That ensures the right allocator is used to dispose of the memory as well. | |Allocators are template arguments instead of using `theAllocator` so |that these smart pointers can be used in `@nogc` code. However, they |will default to `typeof(theAllocator)` for simplicity. The examples |above will be explicit. | |Another reason to have to pass in the type of allocator is to decide how it is to |be stored. Stateless allocators can be "stored" by value and imply zero-cost `Unique` pointers. |Singleton allocators such as Mallocator (that have an `instance` attribute/member function) |don't need to be passed in to the constructor. This is detected at compile-time as an example |of design by instrospection. | |`RefCounted` leverages D's type system by doing atomic reference counting *iff* the type of the contained |object is `shared`. Otherwise it's non-atomic. |*/ |module automem; | |public import automem.unique; |public import automem.ref_counted; |public import automem.vector; |public import automem.array; | | |@safe unittest { | | import std.algorithm: move; | | static struct Point { | int x; | int y; | } | | // set theAllocator as desired beforehand, e.g. | // theAllocator = allocatorObject(Mallocator.instance) | | { | // must pass arguments to initialise the contained object 2| auto u1 = Unique!Point(2, 3); 1| assert(*u1 == Point(2, 3)); 1| assert(u1.y == 3); | | // auto u2 = u1; // won't compile, can only move 3| typeof(u1) u2 = () @trusted { return u1.move; }(); 1| assert(cast(bool)u1 == false); // u1 is now empty | } | // memory freed for the Point structure created in the block | | { 2| auto s1 = RefCounted!Point(4, 5); 1| assert(*s1 == Point(4, 5)); 1| assert(s1.x == 4); | { 2| auto s2 = s1; // can be copied | } // ref count goes to 1 here | | } // ref count goes to 0 here, memory released | | { | import std.algorithm: map, equal; | import std.range: iota; | | // `vector` is also known as `array` 2| auto vec = vector(Point(1, 2), Point(3, 4), Point(5, 6)); 1| assert(equal(vec.range, [Point(1, 2), Point(3, 4), Point(5, 6)])); | | // reallocations are @system since old pointers can dangle 1| () @trusted { 1| vec.length = 1; 1| assert(equal(vec.range, [Point(1, 2)])); | 1| vec ~= Point(7, 8); 1| assert(equal(vec.range, [Point(1, 2), Point(7, 8)])); | 3| vec ~= 2.iota.map!(i => Point(i + 10, i + 11)); 1| assert(equal(vec.range, [Point(1, 2), Point(7, 8), Point(10, 11), Point(11, 12)])); | }(); | } // memory for the array released here |} | | |// @nogc test - must explicitly use the allocator for compile-time guarantees |@safe @nogc unittest { | import std.experimental.allocator.mallocator: Mallocator; | | static struct Point { | int x; | int y; | } | | { | // must pass arguments to initialise the contained object 2| auto u1 = Unique!(Point, Mallocator)(2, 3); 1| assert(*u1 == Point(2, 3)); 1| assert(u1.y == 3); | } | // memory freed for the Point structure created in the block | | // similarly for the other types |} source/automem/package.d is 100% covered <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-subpackages-runner-source-unit_threaded-runner-factory.lst |/** | Creates test cases from compile-time information. | */ |module unit_threaded.runner.factory; | |import unit_threaded.from; |import unit_threaded.runner.testcase: CompositeTestCase; | | |private CompositeTestCase[string] serialComposites; | |/** | * Creates tests cases from the given modules. | * If testsToRun is empty, it means run all tests. | */ |from!"unit_threaded.runner.testcase".TestCase[] createTestCases( | in from!"unit_threaded.runner.reflection".TestData[] testData, | in string[] testsToRun = []) |{ | import unit_threaded.runner.testcase: TestCase; | import std.algorithm: sort; | import std.array: array; | 0000000| serialComposites = null; 0000000| bool[TestCase] tests; 0000000| foreach(const data; testData) { 0000000| if(!isWantedTest(data, testsToRun)) continue; 0000000| auto test = createTestCase(data); 0000000| if(test !is null) tests[test] = true; //can be null if abtract base class | } | 0000000| return tests.keys.sort!((a, b) => a.getPath < b.getPath).array; |} | | |from!"unit_threaded.runner.testcase".TestCase createTestCase( | in from!"unit_threaded.runner.reflection".TestData testData) |{ | import unit_threaded.runner.testcase: TestCase; | import std.algorithm: splitter, reduce; | import std.array: array; | | TestCase createImpl() { | import unit_threaded.runner.testcase: | BuiltinTestCase, FunctionTestCase, ShouldFailTestCase, FlakyTestCase; | import std.conv: text; | 0000000| TestCase testCase; | 0000000| if(testData.isTestClass) 0000000| testCase = cast(TestCase) Object.factory(testData.name); | else 0000000| testCase = testData.builtin | ? new BuiltinTestCase(testData) | : new FunctionTestCase(testData); | | version(unitThreadedLight) {} | else | assert(testCase !is null, | text("Error creating test case with ", | testData.isTestClass ? "test class data: " : "data: ", | testData)); | 0000000| if(testData.shouldFail) { 0000000| testCase = new ShouldFailTestCase(testCase, testData.exceptionTypeInfo); 0000000| } else if(testData.flakyRetries > 0) 0000000| testCase = new FlakyTestCase(testCase, testData.flakyRetries); | 0000000| return testCase; | } | 0000000| auto testCase = createImpl(); | 0000000| if(testData.singleThreaded) { | // @Serial tests in the same module run sequentially. | // A CompositeTestCase is created for each module with at least | // one @Serial test and subsequent @Serial tests | // appended to it 0000000| const moduleName = testData.name.splitter(".") | .array[0 .. $ - 1]. 0000000| reduce!((a, b) => a ~ "." ~ b); | | // create one if not already there 0000000| if(moduleName !in serialComposites) { 0000000| serialComposites[moduleName] = new CompositeTestCase; | } | | // add the current test to the composite 0000000| serialComposites[moduleName] ~= testCase; 0000000| return serialComposites[moduleName]; | } | 0000000| assert(testCase !is null || testData.testFunction is null, | "Could not create TestCase object for test " ~ testData.name); | 0000000| return testCase; |} | | | |bool isWantedTest(in from!"unit_threaded.runner.reflection".TestData testData, | in string[] testsToRun) |{ | | import std.algorithm: filter, all, startsWith, canFind; | import std.array: array; | 0000000| bool isTag(in string t) { return t.startsWith("@") || t.startsWith("~@"); } | 0000000| auto normalToRun = testsToRun.filter!(a => !isTag(a)).array; 0000000| auto tagsToRun = testsToRun.filter!isTag; | | bool matchesTags(in string tag) { //runs all tests with the specified tags 0000000| assert(isTag(tag)); 0000000| return tag[0] == '@' && testData.tags.canFind(tag[1..$]) || 0000000| (!testData.hidden && tag.startsWith("~@") && !testData.tags.canFind(tag[2..$])); | } | 0000000| return isWantedNonTagTest(testData, normalToRun) && 0000000| (tagsToRun.empty || tagsToRun.all!(t => matchesTags(t))); |} | |private bool isWantedNonTagTest(in from!"unit_threaded.runner.reflection".TestData testData, | in string[] testsToRun) |{ | | import std.algorithm: any, startsWith, canFind; | 0000000| if(!testsToRun.length) return !testData.hidden; // all tests except the hidden ones | | bool matchesExactly(in string t) { 0000000| return t == testData.getPath; | } | | bool matchesPackage(in string t) { //runs all tests in package if it matches | with(testData) 0000000| return !hidden && getPath.length > t.length && 0000000| getPath.startsWith(t) && getPath[t.length .. $].canFind("."); | } | 0000000| return testsToRun.any!(a => matchesExactly(a) || matchesPackage(a)); |} | ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/subpackages/runner/source/unit_threaded/runner/factory.d is 0% covered <<<<<< EOF # path=tests-ut-issues.lst |module ut.issues; | | |import ut; |import automem; | | |private typeof(vector(1).range()) gVectorIntRange; | |version(AutomemAsan) {} |else { | | @ShouldFail("https://issues.dlang.org/show_bug.cgi?id=19752") | @("26") | @safe unittest { | static void escape() { | auto vec = vector(1, 2, 3); | gVectorIntRange = vec.range; | } | | static void stackSmash() { | long[4096] arr = 42; | } | | escape; | gVectorIntRange.length.should == 0; | stackSmash; | gVectorIntRange.length.should == 0; | } |} | | |@("27") |@safe unittest { 2| const str = String("foobar"); | 1| (str == "foobar").should == true; 1| (str == "barfoo").should == false; 1| (str == "quux").should == false; | 1| (str == String("foobar")).should == true; 1| (str == String("barfoo")).should == false; 1| (str == String("quux")).should == false; |} | | |@("37") |@safe unittest { | | static struct S { | int front; 20| void popFront() { ++front; } 22| @property bool empty() { return front >= 10; } | } | 2| auto rc = RefCounted!S(0); 32| foreach(i; *rc) {} | 2| auto un = Unique!S(0); 32| foreach(i; *un) {} |} | | |@("38") |@safe unittest { | | import core.exception: AssertError; | | static struct S { | int front = 0; | } | 2| auto rc = RefCounted!S(); 2| rc.front.shouldThrow!AssertError; |} tests/ut/issues.d is 100% covered <<<<<< EOF # path=source-automem-allocator.lst |/** | Custom versions of std.experimental.allocator functions (unfortunately) | */ |module automem.allocator; | |import automem.utils: destruct; | |/** | |Destroys and then deallocates (using $(D alloc)) the object pointed to by a |pointer, the class object referred to by a $(D class) or $(D interface) |reference, or an entire array. It is assumed the respective entities had been |allocated with the same allocator. | |*/ |void dispose(A, T)(auto ref A alloc, T* p) |{ | import std.traits: hasElaborateDestructor; | | static if (hasElaborateDestructor!T) | { 16| destruct(*p); | } 25| alloc.deallocate((cast(void*) p)[0 .. T.sizeof]); |} | |/// Ditto |void dispose(A, T)(auto ref A alloc, T p) |if (is(T == class) || is(T == interface)) |{ | 5| if (!p) return; | static if (is(T == interface)) | { | version(Windows) | { | import core.sys.windows.unknwn : IUnknown; | static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in " | ~ __PRETTY_FUNCTION__); | } | auto ob = cast(Object) p; | } | else | alias ob = p; 5| auto support = (cast(void*) ob)[0 .. typeid(ob).initializer.length]; | 5| destruct(p); | 5| alloc.deallocate(support); |} | |/// Ditto |void dispose(A, T)(auto ref A alloc, T[] array) |{ | import std.traits: hasElaborateDestructor; | | static if (hasElaborateDestructor!(typeof(array[0]))) | { | foreach (ref e; array) | { | destruct(e); | } | } | alloc.deallocate(array); |} source/automem/allocator.d is 100% covered <<<<<< EOF # path=-tmp-dub_test_root_4b535da8_1c36_4977_b5fe_5a8d91cb7189.lst |module dub_test_root; |import std.typetuple; |static import automem.allocator; |static import automem.array; |static import automem.ref_counted; |static import automem.traits; |static import automem.unique; |static import automem.utils; |static import automem.vector; |static import ut.issues; |static import ut.ref_counted; |static import ut.unique; |static import ut.vector; |alias allModules = TypeTuple!(automem.allocator, automem.array, automem.ref_counted, automem.traits, automem.unique, automem.utils, automem.vector, ut.issues, ut.ref_counted, ut.unique, ut.vector); | | import std.stdio; | import core.runtime; | 1| void main() { writeln("All unit tests have been run successfully."); } | shared static this() { | version (Have_tested) { | import tested; | import core.runtime; | import std.exception; | Runtime.moduleUnitTester = () => true; | //runUnitTests!app(new JsonTestResultWriter("results.json")); | enforce(runUnitTests!allModules(new ConsoleTestResultWriter), "Unit tests failed."); | } | } | /tmp/dub_test_root_4b535da8_1c36_4977_b5fe_5a8d91cb7189.d is 100% covered <<<<<< EOF # path=source-automem-utils.lst |module automem.utils; | |import std.traits : isStaticArray; | |// This is a destroy() copied and modified from |// druntime, to allow for destruction attribute inference | |void destruct(T)(T obj) if (is(T == class)) { 8| (cast(_finalizeType!T) &rt_finalize)(cast(void*) obj); |} | |void destruct(T)(T obj) if (is(T == interface)) { | destruct(cast(Object) obj); |} | |void destruct(T)(ref T obj) if (is(T == struct)) { | static if (__traits(hasMember, T, "__xdtor") && | __traits(isSame, T, __traits(parent, obj.__xdtor))) 37| obj.__xdtor; |} | |void destruct(T : U[n], U, size_t n)(ref T obj) if (!is(T == struct)) { | foreach_reverse (ref e; obj[]) | destruct(e); |} | |void destruct(T)(ref T obj) |if(!is(T == struct) && !is(T == class) && !is(T == interface) && !isStaticArray!T) { 2| obj = T.init; |} | |@("class dtor inference") |@safe @nogc pure unittest { | class A { ~this() @nogc {} } | class B : A { ~this() {} } | class C : B { ~this() @nogc {} } | | static assert( __traits(compiles, () @nogc { A a; destruct(a); })); | static assert(!__traits(compiles, () @nogc { B a; destruct(b); })); | static assert(!__traits(compiles, () @nogc { C a; destruct(c); })); |} | |@("class dtor inference with struct members") |@system @nogc pure unittest { | import std.traits: functionAttributes, FunctionAttribute; | import std.conv: text; | | struct A { ~this() @nogc {} } | struct B { ~this() {} } | class CA { A a; ~this() @nogc {} } | class CB { B b; ~this() @nogc {} } | | static assert( __traits(compiles, () @nogc { CA a; destruct(a); })); | static assert(!__traits(compiles, () @system @nogc { CB b; destruct(b); })); |} | |private: | |extern(C) void rt_finalize(void* p, bool det = true); | |// A slightly better hack than the one presented by |// https://www.auburnsounds.com/blog/2016-11-10_Running-D-without-its-runtime.html |// |// This template infers destruction attributes from the given |// class hierarchy. It actually may be incorrect, as by |// the current language rules derived class can still |// have weaker set of destruction attributes. |extern(C) |template _finalizeType(T) { | static if (is(T == Object)) { | alias _finalizeType = typeof(&rt_finalize); | } else { | import std.traits : BaseClassesTuple; | import std.meta : AliasSeq; | alias _finalizeType = typeof((void* p, bool det = true) { | // generate a body that calls all the destructors in the chain, | // compiler should infer the intersection of attributes | foreach (B; AliasSeq!(T, BaseClassesTuple!T)) { | // __dtor, i.e. B.~this | static if (__traits(hasMember, B, "__dtor")) | () { B obj; obj.__dtor; } (); | // __xdtor, i.e. dtors for all RAII members | static if (__traits(hasMember, B, "__xdtor")) | () { B obj; obj.__xdtor; } (); | } | }); | } |} source/automem/utils.d is 100% covered <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-subpackages-exception-source-unit_threaded-exception.lst |/** | Exception classes | */ |module unit_threaded.exception; | |void fail(const string output, const string file, in size_t line) @safe pure |{ 0000000| throw new UnitTestException([output], file, line); |} | |void fail(const string[] lines, const string file, in size_t line) @safe pure |{ 0000000| throw new UnitTestException(lines, file, line); |} | |/** | * An exception to signal that a test case has failed. | */ |class UnitTestException : Exception |{ 0000000| this(const string msg, string file = __FILE__, | in size_t line = __LINE__, Throwable next = null) @safe pure nothrow | { 0000000| this([msg], file, line, next); | } | 0000000| this(const string[] msgLines, string file = __FILE__, | in size_t line = __LINE__, Throwable next = null) @safe pure nothrow | { | import std.string: join; 0000000| super(msgLines.join("\n"), next, file.dup, line); 0000000| this.msgLines = msgLines.dup; | } | | override string toString() @safe const pure scope | { | import std.algorithm: map; | import std.array: join; 0000000| return () @trusted { return msgLines.map!(a => getOutputPrefix(file, line) ~ a).join("\n"); }(); | } | |private: | | const string[] msgLines; | | string getOutputPrefix(in string file, in size_t line) @safe const pure | { | import std.conv: to; 0000000| return " " ~ file ~ ":" ~ line.to!string ~ " - "; | } |} ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/subpackages/exception/source/unit_threaded/exception.d is 0% covered <<<<<< EOF # path=source-automem-unique.lst |/** | A unique pointer. | */ |module automem.unique; | |import automem.traits: isAllocator; |import std.experimental.allocator: theAllocator; |import std.typecons: Flag; | |version(AutomemTesting) { | import ut; | mixin TestUtils; |} | |version (D_BetterC) | enum gcExists = false; |else | enum gcExists = true; | |/** | A unique pointer similar to C++'s std::unique_ptr. | */ |struct Unique( | UniqueType, | Allocator = typeof(theAllocator()), | Flag!"supportGC" supportGC = gcExists ? Flag!"supportGC".yes : Flag!"supportGC".no |) | if(isAllocator!Allocator) |{ | | import std.traits: hasMember; | import std.typecons: Proxy; | | enum isSingleton = hasMember!(Allocator, "instance"); | enum isTheAllocator = is(Allocator == typeof(theAllocator)); | enum isGlobal = isSingleton || isTheAllocator; | | alias Type = UniqueType; | | static if(is(Type == class)) | alias Pointer = Type; | else | alias Pointer = Type*; | | static if(isGlobal) { | | /** | The allocator is global, so no need to pass it in to the constructor | */ 10| this(Args...)(auto ref Args args) { 10| this.makeObject!(supportGC, args)(); | } | | } else { | | /** | Non-singleton allocator, must be passed in | */ 20| this(Args...)(Allocator allocator, auto ref Args args) { 20| _allocator = allocator; 20| this.makeObject!(supportGC, args)(); | } | } | | | static if(isGlobal) | /** | Factory method so can construct with zero args. | */ | static typeof(this) construct(Args...)(auto ref Args args) { | static if (Args.length != 0) | return typeof(return)(args); | else { 1| typeof(return) ret; 1| ret.makeObject!(supportGC)(); 1| return ret; | } | } | else | /** | Factory method. Not necessary with non-global allocator | but included for symmetry. | */ | static typeof(this) construct(Args...)(auto ref Allocator allocator, auto ref Args args) { | return typeof(return)(allocator, args); | } | | /// 1| this(T)(Unique!(T, Allocator) other) if(is(T: Type)) { 1| moveFrom(other); | } | | /// | @disable this(this); | | /// | ~this() { 41| deleteObject; | } | | /** | Borrow the owned pointer. | Can be @safe with DIP1000 and if used in a scope fashion. | */ | auto borrow() inout { 6| return _object; | } | | alias get = borrow; // backwards compatibility | | /** | Releases ownership and transfers it to the returned | Unique object. | */ | Unique unique() { | import std.algorithm: move; 1| Unique u; 1| move(this, u); 1| assert(_object is null); 1| return u; | } | | /// release ownership | package Pointer release() { 1| auto ret = _object; 1| _object = null; 1| return ret; | } | | /// | package Allocator allocator() { 1| return _allocator; | } | | /** | "Truthiness" cast | */ | bool opCast(T)() const if(is(T == bool)) { 5| return _object !is null; | } | | /// Move from another smart pointer | void opAssign(T)(Unique!(T, Allocator) other) if(is(T: Type)) { 5| deleteObject; 5| moveFrom(other); | } | | mixin Proxy!_object; | |private: | | Pointer _object; | | static if(isSingleton) | alias _allocator = Allocator.instance; | else static if(isTheAllocator) | alias _allocator = theAllocator; | else | Allocator _allocator; | | void deleteObject() @safe { | import automem.allocator: dispose; | import std.traits: isPointer; | import std.traits : hasIndirections; | import core.memory : GC; | | static if(isPointer!Allocator) 47| assert(_object is null || _allocator !is null); | 106| if(_object !is null) () @trusted { _allocator.dispose(_object); }(); | static if (is(Type == class)) { | // need to watch the monitor pointer even if supportGC is false. 8| () @trusted { 8| auto repr = (cast(void*)_object)[0..__traits(classInstanceSize, Type)]; 8| GC.removeRange(&repr[(void*).sizeof]); | }(); | } else static if (supportGC && hasIndirections!Type) { | () @trusted { | GC.removeRange(_object); | }(); | } | } | | void moveFrom(T)(ref Unique!(T, Allocator) other) if(is(T: Type)) { 6| _object = other._object; 6| other._object = null; | | static if(!isGlobal) { | import std.algorithm: move; 6| _allocator = other._allocator.move; | } | } |} | | |/// |@("Construct Unique using global allocator for struct with zero-args ctor") |@system unittest { | struct S { | private ulong zeroArgsCtorTest = 3; | } 2| auto s = Unique!S.construct(); | static assert(is(typeof(s) == Unique!S)); 1| assert(s._object !is null); 1| assert(s.zeroArgsCtorTest == 3); |} | | |/// |@("release") |@system unittest { | import std.experimental.allocator: dispose; | import core.exception: AssertError; | | try { 2| auto allocator = TestAllocator(); 2| auto ptr = Unique!(Struct, TestAllocator*)(&allocator, 42); 1| ptr.release; 1| assert(Struct.numStructs == 1); | } catch(AssertError e) { // TestAllocator should throw due to memory leak | version(unitThreadedLight) {} | else | "Memory leak in TestAllocator".should.be in e.msg; 1| return; | } | 0000000| assert(0); // should throw above |} | | |private template makeObject(Flag!"supportGC" supportGC, args...) |{ | void makeObject(Type,A)(ref Unique!(Type, A) u) { | import std.experimental.allocator: make; | import std.functional : forward; | import std.traits : hasIndirections; | import core.memory : GC; | 62| u._object = () @trusted { return u._allocator.make!Type(forward!args); }(); | | static if (is(Type == class)) { 5| () @trusted { 5| auto repr = (cast(void*)u._object)[0..__traits(classInstanceSize, Type)]; 5| if (supportGC && !(typeid(Type).m_flags & TypeInfo_Class.ClassFlags.noPointers)) { 0000000| GC.addRange(&repr[(void*).sizeof], | __traits(classInstanceSize, Type) - (void*).sizeof); | } else { | // need to watch the monitor pointer even if supportGC is false. 5| GC.addRange(&repr[(void*).sizeof], (void*).sizeof); | } | }(); | } else static if (supportGC && hasIndirections!Type) { | () @trusted { | GC.addRange(u._object, Type.sizeof); | }(); | } | } |} source/automem/unique.d is 95% covered <<<<<< EOF # path=tests-ut-package.lst |module ut; | |public import unit_threaded; |public import unit_threaded.should: should; // FIXME |public import test_allocator: TestAllocator; | |mixin template TestUtils() { | import unit_threaded; | import test_allocator; | | /** | Returns an object that, while in scope, replaces whatever | theAllocator was with TestAllocator. | */ | auto theTestAllocator() { | static struct Context { | import std.experimental.allocator: theAllocator; | | TestAllocator testAllocator; | typeof(theAllocator) oldAllocator; | | static auto create() { | import std.experimental.allocator: allocatorObject; | | Context ctx; | | ctx.oldAllocator = theAllocator; | theAllocator = allocatorObject(ctx.testAllocator); | | return ctx; | } | | ~this() { | import std.experimental.allocator: dispose; | | // 2.079.0 changed the API - we check here | static if(__traits(compiles, testAllocator.dispose(theAllocator))) | testAllocator.dispose(theAllocator); | | theAllocator = oldAllocator; | } | } | | return Context.create; | } | | @Setup | void before() { | } | | @Shutdown | void after() { | reset; | } | | void reset() { | Struct.numStructs = 0; | Class.numClasses = 0; | SharedStruct.numStructs = 0; | NoGcStruct.numStructs = 0; | } | | | void _writelnUt(T...)(T args) { | try { | () @trusted { writelnUt(args); }(); | } catch(Exception ex) { | assert(false); | } | } | | private struct Struct { | int i; | static int numStructs = 0; | | this(int i) @safe nothrow { | this.i = i; | | ++numStructs; | _writelnUt("Struct ", &this, " normal ctor, i=", i, ", N=", numStructs); | } | | this(this) @safe nothrow { | ++numStructs; | _writelnUt("Struct ", &this, " postBlit ctor, i=", i, ", N=", numStructs); | } | | ~this() @safe nothrow const { | --numStructs; | _writelnUt("Struct ", &this, " dtor, i=", i, ", N=", numStructs); | } | | int twice() @safe pure const nothrow { | return i * 2; | } | } | | private struct SharedStruct { | int i; | static int numStructs = 0; | | this(int i) @safe nothrow shared { | this.i = i; | | ++numStructs; | try () @trusted { | _writelnUt("Struct normal ctor ", &this, ", i=", i, ", N=", numStructs); | }(); | catch(Exception ex) {} | } | | this(this) @safe nothrow shared { | ++numStructs; | try () @trusted { | _writelnUt("Struct postBlit ctor ", &this, ", i=", i, ", N=", numStructs); | }(); | catch(Exception ex) {} | } | | ~this() @safe nothrow { | --numStructs; | try () @trusted { _writelnUt("Struct dtor ", &this, ", i=", i, ", N=", numStructs); }(); | catch(Exception ex) {} | } | | int twice() @safe pure const nothrow shared { | return i * 2; | } | } | | private class Class { | int i; | static int numClasses = 0; | | this(int i) @safe nothrow { | this.i = i; | ++numClasses; | } | | ~this() @safe nothrow { | --numClasses; | } | | int twice() @safe pure const nothrow { | return i * 2; | } | } | | private class SharedClass { | int i; | static int numClasss = 0; | | this(int i) @safe nothrow shared { | this.i = i; | | ++numClasss; | try () @trusted { | _writelnUt("SharedClass normal ctor ", this, ", i=", i, ", N=", numClasss); | }(); | catch(Exception ex) {} | } | | ~this() @safe nothrow { | --numClasss; | try () @trusted { _writelnUt("SharedClass dtor ", this, ", i=", i, ", N=", numClasss); }(); | catch(Exception ex) {} | } | | int twice() @safe pure const nothrow shared { | return i * 2; | } | } | | | private struct SafeAllocator { | | import std.experimental.allocator.mallocator: Mallocator; | | void[] allocate(this T)(size_t i) @trusted nothrow @nogc { | return Mallocator.instance.allocate(i); | } | | void deallocate(this T)(void[] bytes) @trusted nothrow @nogc { | Mallocator.instance.deallocate(bytes); | } | } | | private struct NoGcStruct { | int i; | | static int numStructs = 0; | | this(int i) @safe @nogc nothrow { | this.i = i; | | ++numStructs; | } | | this(this) @safe @nogc nothrow { | ++numStructs; | } | | ~this() @safe @nogc nothrow { | --numStructs; | } | | } | | private class NoGcClass { | int i; | static int numClasses = 0; | | this(int i) @safe @nogc nothrow { | this.i = i; | ++numClasses; | } | | ~this() @safe @nogc nothrow { | --numClasses; | } | } | | private struct SharedStructWithIndirection { | string s; | this(string s) shared { | this.s = s; | } | } |} tests/ut/package.d has no code <<<<<< EOF # path=source-automem-traits.lst |module automem.traits; | | |void checkAllocator(T)() { | import std.experimental.allocator: make, dispose; | import std.traits: hasMember; | | static if(hasMember!(T, "instance")) | alias allocator = T.instance; | else | T allocator; | | int* i = allocator.make!int; | allocator.dispose(&i); | void[] bytes = allocator.allocate(size_t.init); | allocator.deallocate(bytes); |} | |enum isAllocator(T) = is(typeof(checkAllocator!T)); | | |@("isAllocator") |@safe @nogc pure unittest { | import std.experimental.allocator.mallocator: Mallocator; | import test_allocator: TestAllocator; | | static assert( isAllocator!Mallocator); | static assert( isAllocator!TestAllocator); | static assert(!isAllocator!int); |} | | |template isGlobal(Allocator) { | enum isGlobal = isSingleton!Allocator || isTheAllocator!Allocator; |} | |template isSingleton(Allocator) { | import std.traits: hasMember; | enum isSingleton = hasMember!(Allocator, "instance"); |} | |template isTheAllocator(Allocator) { | import std.experimental.allocator: theAllocator; | enum isTheAllocator = is(Allocator == typeof(theAllocator)); |} | |/** | Determines if a type is Unique. | */ |template isUnique(T) { | import automem.unique: Unique; | import std.traits: TemplateOf; | enum isUnique = __traits(isSame, TemplateOf!T, Unique); |} | |/// |@("isUnique") |@safe unittest { | import automem.unique: Unique; | | static struct Point { | int x; | int y; | } | 2| auto u = Unique!Point(2, 3); | static assert(isUnique!(typeof(u))); | 1| auto p = Point(2, 3); | static assert(!isUnique!(typeof(p))); |} | |/** | Determines if a type is RefCounted. | */ |template isRefCounted(T) { | import automem.ref_counted: RefCounted; | import std.traits: TemplateOf; | enum isRefCounted = __traits(isSame, TemplateOf!T, RefCounted); |} | |/// |@("isRefCounted") |@safe unittest { | import automem.ref_counted: RefCounted; | | static struct Point { | int x; | int y; | } | 2| auto s = RefCounted!Point(2, 3); | static assert(isRefCounted!(typeof(s))); | 1| auto p = Point(2, 3); | static assert(!isRefCounted!(typeof(p))); |} | | |/** | The target of a `Unique` or `RefCounted` pointer. | */ |template PointerTarget(T) | if (isUnique!T || isRefCounted!T) |{ | alias PointerTarget = T.Type; |} | |/// |@("Get the target of a Unique or RefCounter pointer") |@safe unittest { | import automem.unique: Unique; | import automem.ref_counted: RefCounted; | | static struct Point { | int x; | int y; | } | 2| auto u = Unique!Point(2, 3); | static assert(is(Point == PointerTarget!(typeof(u)))); | 2| auto s = RefCounted!Point(2, 3); | static assert(is(Point == PointerTarget!(typeof(s)))); |} | |/// |@("Mixing Unique and RefCounted pointers") |unittest { | import std.math : approxEqual; | import automem.unique: Unique; | import automem.ref_counted: RefCounted; | | static struct Point { | int x; | int y; | } | | static double distance(T, U)(auto ref T p1, auto ref U p2) | if (is(PointerTarget!T == Point) && | is(PointerTarget!U == Point)) | { | import std.conv : to; | import std.math : sqrt, pow; 4| return((pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2)).to!double.sqrt); | } | 1| int x1 = 2; 1| int y1 = 3; 1| int x2 = x1 + 3; 1| int y2 = y1 + 4; | 2| auto u_p1 = Unique!Point(x1, y1); 2| auto u_p2 = Unique!Point(x2, y2); 1| assert(approxEqual(distance(u_p1, u_p2), 5.0)); | 2| auto rc_p1 = RefCounted!Point(x1, y1); 2| auto rc_p2 = RefCounted!Point(x2, y2); 1| assert(approxEqual(distance(rc_p1, rc_p2), 5.0)); | 1| assert(approxEqual(distance(u_p1, rc_p2), 5.0)); 1| assert(approxEqual(distance(rc_p1, u_p2), 5.0)); |} source/automem/traits.d is 100% covered <<<<<< EOF # path=source-automem-array.lst |/** | Aliases for automem.vector | */ |module automem.array; | |public import automem.vector: array = vector; |public import automem.vector: Array = Vector; source/automem/array.d has no code <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-subpackages-runner-source-unit_threaded-runner-testsuite.lst |/** | * This module implements $(D TestSuite), an aggregator for $(D TestCase) | * objects to run all tests. | */ | |module unit_threaded.runner.testsuite; | |import unit_threaded.from; | |/* | * taskPool.amap only works with public functions, not closures. | */ |auto runTest(from!"unit_threaded.runner.testcase".TestCase test) |{ 0000000| return test(); |} | |/** | * Responsible for running tests and printing output. | */ |struct TestSuite |{ | import unit_threaded.runner.io: Output; | import unit_threaded.runner.options: Options; | import unit_threaded.runner.reflection: TestData; | import unit_threaded.runner.testcase: TestCase; | import std.datetime: Duration; | static if(__VERSION__ >= 2077) | import std.datetime.stopwatch: StopWatch; | else | import std.datetime: StopWatch; | | /** | * Params: | * options = The options to run tests with. | * testData = The information about the tests to run. | */ 0000000| this(in Options options, in TestData[] testData) { | import unit_threaded.runner.io: WriterThread; 0000000| this(options, testData, WriterThread.get); | } | | /** | * Params: | * options = The options to run tests with. | * testData = The information about the tests to run. | * output = Where to send text output. | */ 0000000| this(in Options options, in TestData[] testData, Output output) { | import unit_threaded.runner.factory: createTestCases; | 0000000| _options = options; 0000000| _testData = testData; 0000000| _output = output; 0000000| _testCases = createTestCases(testData, options.testsToRun); | } | | /** | * Runs all test cases. | * Returns: true if no test failed, false otherwise. | */ | bool run() { | | import unit_threaded.runner.io: writelnRed, writeln, writeRed, write, writeYellow, writelnGreen; | import std.algorithm: filter, count; | import std.conv: text; | 0000000| if (!_testCases.length) { 0000000| _output.writelnRed("Error! No tests to run for args: "); 0000000| _output.writeln(_options.testsToRun); 0000000| return false; | } | 0000000| immutable elapsed = doRun(); | 0000000| if (!numTestsRun) { 0000000| _output.writeln("Did not run any tests!!!"); 0000000| return false; | } | 0000000| _output.writeln("\nTime taken: ", elapsed); 0000000| _output.write(numTestsRun, " test(s) run, "); 0000000| const failuresStr = text(_failures.length, " failed"); 0000000| if (_failures.length) { 0000000| _output.writeRed(failuresStr); | } else { 0000000| _output.write(failuresStr); | } | | ulong numTestsWithAttr(string attr)() { 0000000| return _testData.filter!(a => mixin("a. " ~ attr)).count; | } | | void printHidden() { 0000000| const num = numTestsWithAttr!"hidden"; 0000000| if(!num) return; 0000000| _output.write(", "); 0000000| _output.writeYellow(num, " ", "hidden"); | } | | void printShouldFail() { 0000000| const total = _testCases.filter!(a => a.shouldFail).count; 0000000| long num = total; | 0000000| foreach(f; _failures) { 0000000| const data = _testData.filter!(a => a.getPath == f).front; 0000000| if(data.shouldFail) --num; | } | 0000000| if(!total) return; 0000000| _output.write(", "); 0000000| _output.writeYellow(num, "/", total, " ", "failing as expected"); | } | 0000000| printHidden(); 0000000| printShouldFail(); | 0000000| _output.writeln(".\n"); | 0000000| if(_options.random) 0000000| _output.writeln("Tests were run in random order. To repeat this run, use --seed ", _options.seed, "\n"); | 0000000| if (_failures.length) { 0000000| _output.writelnRed("Tests failed!\n"); 0000000| return false; //oops | } | 0000000| _output.writelnGreen("OK!\n"); | 0000000| return true; | } | |private: | | const(Options) _options; | const(TestData)[] _testData; | TestCase[] _testCases; | string[] _failures; | StopWatch _stopWatch; | Output _output; | | /** | * Runs the tests. | * Returns: how long it took to run. | */ | Duration doRun() { | | import std.algorithm: reduce; | import std.parallelism: taskPool; | 0000000| auto tests = getTests(); | 0000000| if(_options.showChrono) 0000000| foreach(test; tests) 0000000| test.showChrono; | 0000000| _stopWatch.start(); | 0000000| if (_options.multiThreaded) { 0000000| _failures = reduce!((a, b) => a ~ b)(_failures, taskPool.amap!runTest(tests)); | } else { 0000000| foreach (test; tests) { 0000000| _failures ~= test(); | } | } | 0000000| handleFailures(); | 0000000| _stopWatch.stop(); 0000000| return cast(Duration) _stopWatch.peek(); | } | | auto getTests() { | import unit_threaded.runner.io: writeln; | 0000000| auto tests = _testCases.dup; | 0000000| if (_options.random) { | import std.random; | 0000000| auto generator = Random(_options.seed); 0000000| tests.randomShuffle(generator); 0000000| _output.writeln("Running tests in random order. ", | "To repeat this run, use --seed ", _options.seed); | } | 0000000| return tests; | } | | void handleFailures() { | import unit_threaded.runner.io: writeln, writeRed, write; | import std.array: empty; | import std.algorithm: canFind; | 0000000| if (!_failures.empty) 0000000| _output.writeln(""); 0000000| foreach (failure; _failures) { 0000000| _output.write("Test ", (failure.canFind(" ") ? `'` ~ failure ~ `'` : failure), " "); 0000000| _output.writeRed("failed"); 0000000| _output.writeln("."); | } 0000000| if (!_failures.empty) 0000000| _output.writeln(""); | } | | @property ulong numTestsRun() @trusted const { | import std.algorithm: map, reduce; 0000000| return _testCases.map!(a => a.numTestsRun).reduce!((a, b) => a + b); | } |} ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/subpackages/runner/source/unit_threaded/runner/testsuite.d is 0% covered <<<<<< EOF # path=tests-ut-vector.lst |module ut.vector; | | |import ut; |import automem.vector; |import std.experimental.allocator.mallocator: Mallocator; |import test_allocator; | | |@("length") |@safe unittest { 1| vector("foo", "bar", "baz").length.should == 3; 1| vector("quux", "toto").length.should == 2; |} | |@("vector.int") |@safe unittest { 1| vector(1, 2, 3, 4, 5).range.should == [1, 2, 3, 4, 5]; 1| vector(2, 3, 4).range.should == [2, 3, 4]; 1| vector(2, 3, 4).range.should == [2, 3, 4]; |} | |@("vector.double") |@safe unittest { 1| vector(33.3).range.should == [33.3]; 1| vector(22.2, 77.7).range.should == [22.2, 77.7]; |} | |@("copying") |@safe unittest { 2| auto vec1 = vector(1, 2, 3); 2| () @trusted { vec1.reserve(10); }(); 2| auto vec2 = vec1; 1| vec1[1] = 7; | 1| vec1.range.should == [1, 7, 3]; 1| vec2.range.should == [1, 2, 3]; |} | |@("bounds check") |@safe unittest { | 2| auto vec = vector(1, 2, 3); 2| () @trusted { vec.reserve(10); }(); 2| vec[3].shouldThrow!BoundsException; 2| vec[-1].shouldThrow!BoundsException; 3| () @trusted { vec[0 .. 4].shouldThrow!BoundsException; }(); 2| () @trusted { vec[0.. 3]; }(); // shouldn't throw (see #45) |} | |@("extend") |@system unittest { | import std.algorithm: map; | 2| auto vec = vector(0, 1, 2, 3); | 1| vec ~= 4; 1| vec.range.should == [0, 1, 2, 3, 4]; | 1| vec ~= [5, 6]; 1| vec.range.should == [0, 1, 2, 3, 4, 5, 6]; | 3| vec ~= [1, 2].map!(a => a + 10); 1| vec.range.should == [0, 1, 2, 3, 4, 5, 6, 11, 12]; |} | | |@("put") |@system unittest { | import std.range: iota; | 2| auto vec = vector(0, 1, 2, 3); 1| vec.put(4); 1| vec.range.should == [0, 1, 2, 3, 4]; 1| vec.put(2.iota); 1| vec.range.should == [0, 1, 2, 3, 4, 0, 1]; |} | |@("append") |@system unittest { 2| auto vec1 = vector(0, 1, 2); 2| auto vec2 = vector(3, 4); | 2| auto vec3 = vec1 ~ vec2; 1| vec3.range.should == [0, 1, 2, 3, 4]; | 1| vec1[0] = 7; 1| vec2[0] = 9; 1| vec3.range.should == [0, 1, 2, 3, 4]; | | | // make sure capacity is larger 1| vec1 ~= 100; 1| vec1.capacity.shouldBeGreaterThan(vec1.length); 1| vec1.range.should == [7, 1, 2, 100]; | 1| vec2 ~= 200; 1| vec2.capacity.shouldBeGreaterThan(vec2.length); 1| vec2.range.should == [9, 4, 200]; | 1| (vec1 ~ vec2).range.should == [7, 1, 2, 100, 9, 4, 200]; 1| (vec1 ~ vector(11, 12, 13, 14, 15)).range.should == [7, 1, 2, 100, 11, 12, 13, 14, 15]; |} | |@("slice") |@system unittest { 2| const vec = vector(0, 1, 2, 3, 4, 5); 1| vec[].should == [0, 1, 2, 3, 4, 5]; 1| vec[1 .. 3].should == [1, 2]; 1| vec[1 .. 4].should == [1, 2, 3]; 1| vec[2 .. 5].should == [2, 3, 4]; 1| vec[1 .. $ - 1].should == [1, 2, 3, 4]; 1| vec[0 .. 6].should == [0, 1, 2, 3, 4, 5]; |} | |@("opDollar") |@system unittest { 2| auto vec = vector(0, 1, 2, 3, 4); 1| vec ~= 5; 1| vec ~= 6; 1| vec.capacity.shouldBeGreaterThan(vec.length); | 1| vec[1 .. $ - 1].should == [1, 2, 3, 4, 5]; |} | |@("assign") |@system unittest { | import std.range: iota; 2| auto vec = vector(10, 11, 12); 1| vec = 5.iota; 1| vec.range.should == [0, 1, 2, 3, 4]; |} | |@("construct from range") |@safe unittest { | import std.range: iota; 1| vector(5.iota).range.should == [0, 1, 2, 3, 4]; |} | | |@("popBack") |@safe unittest { 2| auto vec = vector(0, 1, 2); 1| vec.popBack; 1| vec.range.should == [0, 1]; |} | |@("popFront") |@safe unittest { 2| auto vec = vector(0, 1, 2, 3, 4); 1| vec.popFront; 1| vec.range.should == [1, 2, 3, 4]; 2| vec.empty.shouldBeFalse; | 19| foreach(i; 0 .. vec.length) vec.popFront; 2| vec.empty.shouldBeTrue; |} | | |@("opSliceAssign") |@safe unittest { 2| auto vec = vector("foo", "bar", "quux", "toto"); | 1| vec[] = "haha"; 1| vec.range.should == ["haha", "haha", "haha", "haha"]; | 1| vec[1..3] = "oops"; 1| vec.range.should == ["haha", "oops", "oops", "haha"]; |} | |@("opSliceOpAssign") |@safe unittest { 2| auto vec = vector("foo", "bar", "quux", "toto"); 1| vec[] ~= "oops"; 1| vec.range.should == ["foooops", "baroops", "quuxoops", "totooops"]; |} | |@("opSliceOpAssign range") |@safe unittest { 2| auto vec = vector("foo", "bar", "quux", "toto"); 1| vec[1..3] ~= "oops"; 1| vec.range.should == ["foo", "baroops", "quuxoops", "toto"]; |} | |@("clear") |@safe unittest { 2| auto vec = vector(0, 1, 2, 3); 1| vec.clear; 1| int[] empty; 1| vec.range.should ==(empty); |} | | |@("Mallocator elements") |@safe @nogc unittest { | import std.algorithm: equal; 2| auto vec = vector!Mallocator(0, 1, 2, 3); 1| int[4] exp = [0, 1, 2, 3]; 1| assert(equal(vec.range, exp[])); |} | |@("Mallocator range") |@safe @nogc unittest { | import std.algorithm: equal; | import std.range: iota; 2| auto vec = vector!Mallocator(iota(5)); 1| int[5] exp = [0, 1, 2, 3, 4]; 1| assert(equal(vec.range, exp[])); |} | | |@("theAllocator null") |@safe unittest { 2| Vector!int vec; |} | | |@("Mallocator null") |@safe @nogc unittest { 2| Vector!(int, Mallocator) vec; |} | | |@("escape.range") |@safe @nogc unittest { | | alias Ints = typeof(Vector!(int, Mallocator).init.range()); | 1| Ints ints1; 2| scope vec = vector!Mallocator(0, 1, 2, 3); 1| Ints ints2; | | static assert(!__traits(compiles, ints1 = vec.range)); 1| ints2 = vec.range; // should compile |} | | |@("escape.element") |@safe unittest { | 1| int i = 1; 1| int j = 2; | 1| int* oops; 2| scope vec = vector(&i, &j); 1| int* ok; | | static assert(!__traits(compiles, oops = vec[0])); 1| ok = vec[0]; |} | | |@("TestAllocator elements capacity") |@system unittest { | static TestAllocator allocator; | 2| auto vec = vector(&allocator, 0, 1, 2); 1| vec.range.should == [0, 1, 2]; | 1| vec ~= 3; 1| vec ~= 4; 1| vec ~= 5; 1| vec ~= 6; 1| vec ~= 7; 1| vec ~= 8; | 1| vec.range.should == [0, 1, 2, 3, 4, 5, 6, 7, 8]; 1| allocator.numAllocations.shouldBeSmallerThan(4); |} | |@("TestAllocator reserve") |@system unittest { | static TestAllocator allocator; | 2| auto vec = vector!(TestAllocator*, int)(&allocator); | 1| vec.reserve(5); 2| () @trusted { vec.empty.should == true; }(); | 1| vec ~= 0; 1| vec ~= 1; 1| vec ~= 2; 1| vec ~= 3; 1| vec ~= 4; | 1| vec.range.should == [0, 1, 2, 3, 4]; 1| allocator.numAllocations.should == 1; | 1| vec ~= 5; 1| vec.range.should == [0, 1, 2, 3, 4, 5]; 1| allocator.numAllocations.should == 2; |} | |@("TestAllocator shrink no length") |@system unittest { | static TestAllocator allocator; | 2| auto vec = vector!(TestAllocator*, int)(&allocator); 1| vec.reserve(10); | 1| vec ~= 0; 1| vec ~= 1; 1| vec ~= 2; 1| vec ~= 3; | 1| vec.length.should == 4; 1| vec.capacity.should == 10; | 1| vec.shrink; 1| vec.length.should == 4; 1| vec.capacity.should == 4; |} | |@("TestAllocator shrink negative number") |@system unittest { | static TestAllocator allocator; | 2| auto vec = vector(&allocator, 0); 1| vec ~= 1; 1| vec ~= 2; 1| vec ~= 3; 1| vec.capacity.shouldBeGreaterThan(vec.length); 1| const oldCapacity = vec.capacity; | 2| vec.shrink(-1).shouldBeFalse; 1| vec.capacity.should == oldCapacity; |} | |@("TestAllocator shrink larger than capacity") |@system unittest { | static TestAllocator allocator; | 2| auto vec = vector(&allocator, 0); 1| vec ~= 1; 1| vec ~= 2; 1| vec ~= 3; 1| vec.capacity.shouldBeGreaterThan(vec.length); 1| const oldCapacity = vec.capacity; | 2| vec.shrink(oldCapacity * 2).shouldBeFalse; 1| vec.capacity.should == oldCapacity; |} | | |@("TestAllocator shrink with length") |@system unittest { | static TestAllocator allocator; | 2| auto vec = vector(&allocator, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 1| vec.capacity.should == 10; | 1| vec.shrink(5); 1| vec.range.should == [0, 1, 2, 3, 4]; 1| vec.capacity.should == 5; | 1| vec ~= 5; 1| vec.range.should == [0, 1, 2, 3, 4, 5]; 1| allocator.numAllocations.should == 3; | 1| vec.reserve(10); 1| vec.length.should == 6; 1| vec.capacity.shouldBeGreaterThan(6); |} | |@("TestAllocator copy") |@safe unittest { | static TestAllocator allocator; | 2| auto vec1 = vector(&allocator, "foo", "bar", "baz"); 1| allocator.numAllocations.should == 1; | 2| auto vec2 = vec1; 1| allocator.numAllocations.should == 2; |} | |@("TestAllocator move") |@safe unittest { | static TestAllocator allocator; | 2| auto vec = vector(&allocator, "foo", "bar", "baz"); 1| allocator.numAllocations.should == 1; | 1| consumeVec(vec); 1| allocator.numAllocations.should == 1; |} | | |private void consumeVec(T)(auto ref T vec) { | |} | | |@("set length") |@system unittest { 2| Vector!int vec; 1| vec.length = 3; 1| vec.range.should == [0, 0, 0]; |} | | |@("foreach") |@safe unittest { 2| foreach(e; vector(7, 7, 7).range) { 0000000| e.should == 7; | } |} | | |@("equal") |@safe unittest { | import std.range: iota; | import std.algorithm: equal; | 2| auto v = vector(0, 1, 2, 3); 1| assert(equal(v.range, 4.iota)); |} | | |@("bool") |@safe unittest { 2| vector(0, 1, 2).shouldBeTrue; 2| Vector!int v; 1| if(v) { 0000000| assert(0); | } |} | |@("char") |@system unittest { | { 2| auto vec = vector('f', 'o', 'o'); 1| vec.range.should ==("foo"); 1| vec ~= 'b'; 1| vec ~= ['a', 'r']; 1| vec.range.should ==("foobar"); 1| vec ~= "quux"; 1| vec.range.should ==("foobarquux"); | } | | { 2| auto vec = vector("foo"); 1| vec.range.should ==("foo"); 1| vec.popBack; 1| vec.range.should ==("fo"); | } | | { 2| auto vec = vector("foo"); 1| vec ~= "bar"; 1| vec.range.should ==("foobar"); | } |} | | |@("immutable.append") |@system unittest { 2| Vector!(immutable int) vec; 1| vec ~= 42; 1| vec.range.should == [42]; |} | | |@("String") |@safe unittest { 2| foreach(c; String("oooooo").range) 0000000| c.should == 'o'; |} | |@("stringz") |@safe unittest { | import std.string: fromStringz; 2| auto str = vector("foobar"); 2| const strz = () @trusted { return str.stringz; }(); 2| const back = () @trusted { return fromStringz(strz); }(); 1| back.should == "foobar"; 1| str.range.should ==("foobar"); |} | | |@("ptr") |@safe unittest { 2| const vec = vector(0, 1, 2, 3); 1| takesScopePtr(vec.ptr); 2| () @trusted { vec.ptr[1].should == 1; }(); |} | |private void takesScopePtr(T)(scope const(T)* ptr) { | |} | | |@("StackFront") |@safe @nogc unittest { | import std.algorithm: equal; | import std.experimental.allocator.showcase: StackFront; | import std.experimental.allocator.mallocator: Mallocator; | | alias Allocator = StackFront!(1024, Mallocator); | | { 2| Vector!(int, Allocator) v; 2| () @trusted { v ~= 1; }(); | { 1| int[1] expected = [1]; 1| assert(equal(v.range, expected[])); | } | } | | { | static void fun(Allocator)(ref Allocator allocator) { | Vector!(int, Allocator) v; | } | } |} | | |version(Windows) {} |else { | @("mmapRegionList") | @system unittest { | import std.experimental.allocator.showcase: mmapRegionList; | import std.experimental.allocator.mallocator: Mallocator; | import automem.vector: isAllocator; | 2| auto v = vector(mmapRegionList(1024), 0, 1, 2); 1| v ~= 3; | } |} | | | |@("2d") |@safe unittest { 2| auto v = vector(vector(0, 0, 0), vector(1, 1, 1, 1)); 1| v[0].range.should == [0, 0, 0]; 1| v[1].range.should == [1, 1, 1, 1]; |} | | |@("toString") |@safe unittest { | import std.conv: text; 2| auto v = vector(1, 2, 3); 1| v.range.text.should == `[1, 2, 3]`; |} | | |@("return") |@system unittest { | | static auto fun() { 1| auto v = vector(1, 2, 3); 1| v ~= 4; 1| return v; | } | 2| auto v = fun; 1| v ~= 5; 1| v.range.should == [1, 2, 3, 4, 5]; |} | | |@("noconsume.range") |@safe unittest { | import std.algorithm: equal; | 2| scope v = vector(1, 2, 3); | | static void fun(R)(R range) { | import std.array: array; 1| assert(equal(range, [1, 2, 3])); | } | 1| fun(v.range); 1| assert(equal(v.range, [1, 2, 3])); |} | | |@("noconsume.foreach") |@safe unittest { 2| scope v = vector(1, 2, 3); 11| foreach(e; v.range) {} 1| v.range.should == [1, 2, 3]; |} | | |@("noconsume.map") |@safe unittest { | import std.algorithm: map; | 2| scope v = vector(1, 2, 3); 4| v.range.map!(a => a * 2).should == [2, 4, 6]; 1| v.range.should == [1, 2, 3]; |} | | |@("reserve") |@safe unittest { 2| scope vec = vector(1, 2, 3); 1| vec.range.should == [1, 2, 3]; 2| () @trusted { vec.reserve(10); }(); 1| vec.range.should == [1, 2, 3]; |} | | |@("range.reserve") |@safe unittest { 2| scope vec = vector(1, 2, 3); 1| scope range = vec.range; | 1| range.save.should == [1, 2, 3]; 2| () @trusted { vec.reserve(10); }(); | 1| range.should == [1, 2, 3]; |} | | |@("range.const") |@safe unittest { 2| const vec = vector(1, 2, 3); 1| vec.range.should == [1, 2, 3]; |} | | |@("range.bounds") |@safe unittest { 2| const vec = vector(1, 2, 3, 4, 5); 1| vec.range(1, 4).should == [2, 3, 4]; 1| vec.range(2, vec.length).should == [3, 4, 5]; 1| vec.range(2, -1).should == [3, 4, 5]; 1| vec.range(2, -2).should == [3, 4]; |} | | |@("equals") |@safe unittest { | import std.range: iota, only; | 2| const vec = vector(0, 1, 2); | 1| (vec == 3.iota).should == true; 1| (vec == 2.iota).should == false; 1| (vec == 4.iota).should == false; 1| (vec == only(0)).should == false; |} tests/ut/vector.d is 98% covered <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-source-unit_threaded-io.lst |module unit_threaded.io; | |public import unit_threaded.runner.io; ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/source/unit_threaded/io.d has no code <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-subpackages-runner-source-unit_threaded-runner-options.lst |/** | Run-time options. | */ |module unit_threaded.runner.options; | | |/// |struct Options { | bool multiThreaded; | string[] testsToRun; | bool debugOutput; | bool list; | bool exit; | bool forceEscCodes; | bool random; | uint seed; | bool stackTraces; | bool showChrono; |} | |/** | * Parses the command-line args and returns Options | */ |auto getOptions(string[] args) { | | import std.stdio: writeln; | import std.random: unpredictableSeed; | import std.getopt: getopt; | 0000000| bool single; 0000000| bool debugOutput; 0000000| bool help; 0000000| bool list; 0000000| bool forceEscCodes; 0000000| bool random; 0000000| uint seed = unpredictableSeed; 0000000| bool stackTraces; 0000000| bool showChrono; | 0000000| getopt(args, | "single|s", &single, //single-threaded | "debug|d", &debugOutput, //print debug output | "esccodes|e", &forceEscCodes, | "help|h", &help, | "list|l", &list, | "random|r", &random, | "seed", &seed, | "trace|t", &stackTraces, | "chrono|c", &showChrono, | ); | 0000000| if(help) { 0000000| writeln("Usage: ...\n", | "Options: \n", | " -h/--help: help\n", | " -s/--single: single-threaded\n", | " -l/--list: list tests\n", | " -d/--debug: enable debug output\n", | " -e/--esccodes: force ANSI escape codes even for !isatty\n", | " -r/--random: run tests in random order\n", | " --seed: set the seed for the random order\n", | " -t/--trace: enable stack traces\n", | " -c/--chrono: print execution time per test", | ); | } | 0000000| if(random) { 0000000| if(!single) writeln("-r implies -s, running in a single thread\n"); 0000000| single = true; | } | | version(unitUnthreaded) 0000000| single = true; | 0000000| immutable exit = help || list; 0000000| return Options(!single, args[1..$], debugOutput, list, exit, forceEscCodes, | random, seed, stackTraces, showChrono); |} ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/subpackages/runner/source/unit_threaded/runner/options.d is 0% covered <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-source-unit_threaded-reflection.lst |module unit_threaded.reflection; | |public import unit_threaded.runner.reflection; ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/source/unit_threaded/reflection.d has no code <<<<<< EOF # path=..-..-..-.dub-packages-test_allocator-0.3.2-test_allocator-source-test_allocator-package.lst |module test_allocator; | |// tracks allocations and throws in the destructor if there is a memory leak |// it also throws when there is an attempt to deallocate memory that wasn't |// allocated |struct TestAllocator { | import std.experimental.allocator.common: platformAlignment; | import std.experimental.allocator.mallocator: Mallocator; | | alias allocator = Mallocator.instance; | | @safe @nogc nothrow: | | private static struct ByteRange { | void* ptr; | size_t length; | inout(void)[] opSlice() @trusted @nogc nothrow pure inout { 1| return ptr[0 .. length]; | } | } | | private ByteRange[] _allocations; | private int _numAllocations; | | enum uint alignment = platformAlignment; | | void[] allocate(size_t numBytes) scope { | import std.experimental.allocator: makeArray, expandArray; | 60| ++_numAllocations; | 60| auto ret = allocator.allocate(numBytes); 60| if(ret.length == 0) return ret; | 60| auto newEntry = ByteRange(&ret[0], ret.length); | 60| if(_allocations is null) 40| _allocations = allocator.makeArray(1, newEntry); | else 40| () @trusted { allocator.expandArray(_allocations, 1, newEntry); }(); | 60| return ret; | } | | bool deallocate(void[] bytes) scope pure { | import std.algorithm: remove, canFind; | static if (__VERSION__ < 2077) | { | import core.stdc.stdio: sprintf; | alias pureSprintf = sprintf; | } | 264| bool pred(ByteRange other) { return other.ptr == bytes.ptr && other.length == bytes.length; } | | static char[1024] buffer; | | // @trusted because this is `scope` and we're taking the address of it 120| assert(() @trusted { return &this !is null; }(), "Attempting to deallocate when `this` is null"); | 60| if(!_allocations.canFind!pred) { | debug { 0000000| auto index = pureSprintf(&buffer[0], | "Unknown deallocate byte range.\nPtr: %p, length: %ld, allocations:\n", 0000000| () @trusted { return bytes.ptr; }(), bytes.length); 0000000| index = printAllocations(buffer, index); 0000000| buffer[index .. $] = 0; 0000000| assert(false, buffer[0 .. index]); | } | } | 60| _allocations = _allocations.remove!pred; | 120| return () @trusted { return allocator.deallocate(bytes); }(); | } | | bool deallocateAll() scope pure { 108| foreach(ref allocation; _allocations) { 1| deallocate(allocation[]); | } 35| return true; | } | | auto numAllocations() pure const scope { 16| return _numAllocations; | } | | ~this() pure { 35| verify; 34| finalise; | } | | private void finalise() scope pure { | import std.experimental.allocator: dispose; 35| deallocateAll; 70| () @trusted { allocator.dispose(_allocations); }(); | } | | void verify() scope pure { | static if (__VERSION__ < 2077) | { | import core.stdc.stdio: sprintf; | alias pureSprintf = sprintf; | } | | static char[1024] buffer; | 35| if(_allocations.length) { 1| int index; | debug { 1| index = pureSprintf(&buffer[0], "Memory leak in TestAllocator. Allocations:\n"); 1| index = printAllocations(buffer, index); 1| buffer[index .. $] = 0; | } | 1| finalise; // avoid asan leaks | 1| debug assert(false, buffer[0 .. index]); | } | } | | int printAllocations(int N)(ref char[N] buffer, int index = 0) pure const scope { | static if (__VERSION__ < 2077) | { | import core.stdc.stdio: sprintf; | alias pureSprintf = sprintf; | } | 1| index += pureSprintf(&buffer[index], "["); | 1| if(_allocations !is null) { 6| foreach(ref allocation; _allocations) { 1| index += pureSprintf(&buffer[index], "ByteRange(%p, %ld), ", | allocation.ptr, allocation.length); | } | } | 1| index += pureSprintf(&buffer[index], "]"); 1| return index; | } |} | |static if (__VERSION__ >= 2077) |{ | /* Private bits that allow sprintf to become pure */ | private int pureSprintf(A...)(scope char* s, scope const(char*) format, A va) | @trusted pure nothrow | { 4| const errnosave = fakePureErrno(); 4| int ret = fakePureSprintf(s, format, va); 4| fakePureErrno() = errnosave; 4| return ret; | } | | extern (C) private @system @nogc nothrow | { | ref int fakePureErrnoImpl() | { | import core.stdc.errno; 9| return errno(); | } | } | | extern (C) private pure @system @nogc nothrow | { | pragma(mangle, "fakePureErrnoImpl") ref int fakePureErrno(); | pragma(mangle, "sprintf") int fakePureSprintf(scope char* s, scope const(char*) format, ...); | } |} | |@safe @nogc nothrow unittest { | import std.experimental.allocator : allocatorObject; | import std.experimental.allocator.building_blocks.stats_collector; | import std.experimental.allocator.mallocator: Mallocator; | import std.conv : to; | | alias SCAlloc = StatsCollector!(TestAllocator, Options.bytesUsed); | | SCAlloc allocator; | auto buf = allocator.allocate(10); | allocator.deallocate(buf); | assert(allocator.bytesUsed == 0); |} | | |@safe @nogc nothrow unittest { | auto obj = TestAllocator(); | scope ptr = &obj; |} ../../../.dub/packages/test_allocator-0.3.2/test_allocator/source/test_allocator/package.d is 88% covered <<<<<< EOF # path=..-..-..-.dub-packages-unit-threaded-0.10.8-unit-threaded-source-unit_threaded-light.lst |/** | This module is an attempt to alleviate compile times by including the bare | minimum. The idea is that while the reporting usually done by unit-threaded | is welcome, it only really matters when tests fail. Otherwise, no news is | good news. | | Likewise, naming and selecting tests are features used when certain tests | fail. The usual way to run tests is to run all of them and be happy if | they all pass. | | This module makes it so that unit-threaded gets out of the way, and if | needed the full features can be turned on at the cost of compiling | much more slowly. | | There aren't even any template constraints on the `should` functions | to avoid imports as much as possible. | */ |module unit_threaded.light; | |alias UnitTestException = Exception; | | |/** | Dummy version so "normal" code compiles | */ |mixin template runTestsMain(Modules...) if(Modules.length > 0) { | int main() { | import unit_threaded.light: runTestsImpl; | return runTestsImpl; | } |} | |/** | Dummy version of runTests so "normal" code compiles. | */ |int runTests(T...)(in string[] args) { | return runTestsImpl; |} | |/// ditto |int runTests(T)(string[] args, T testData) { | return runTestsImpl; |} | |int runTestsImpl() { | import core.runtime: Runtime; | import core.stdc.stdio: printf; | | version(Posix) 0000000| printf("\033[32;1mOk\033[0;;m"); | else | printf("Ok"); | 0000000| printf(": All tests passed\n\n"); | 0000000| return 0; |} | | |/** | Dummy version so "normal" code compiles | */ |int[] allTestData(T...)() { | return []; |} | |/** | No-op version of writelnUt | */ |void writelnUt(T...)(auto ref T args) { | |} | |/** | Same as unit_threaded.property.check | */ |void check(alias F)(int numFuncCalls = 100, | in string file = __FILE__, in size_t line = __LINE__) @trusted { | import unit_threaded.property: utCheck = check; | utCheck!F(numFuncCalls, file, line); |} | |/** | Same as unit_threaded.property.checkCustom | */ |void checkCustom(alias Generator, alias Predicate) | (int numFuncCalls = 100, in string file = __FILE__, in size_t line = __LINE__) @trusted { | import unit_threaded.property: utCheckCustom = checkCustom; | utCheckCustom!(Generator, Predicate)(numFuncCalls, file, line); |} | | |/** | Generic output interface | */ |interface Output { | void send(in string output) @safe; | void flush() @safe; |} | |/** | Dummy version of unit_threaded.testcase.TestCase | */ |class TestCase { | abstract void test(); | void setup() {} | void shutdown() {} 0000000| static TestCase currentTest() { return new class TestCase { override void test() {}}; } 0000000| Output getWriter() { return new class Output { override void send(in string output) {} override void flush() {}}; } |} | | |/** | Same as unit_threaded.mock.mock | */ |auto mock(T)() { | import unit_threaded.mock: utMock = mock; | return utMock!T; |} | |/** | Same as unit_threaded.mock.mockStruct | */ |auto mockStruct(T...)(auto ref T returns) { | import unit_threaded.mock: utMockStruct = mockStruct; | return utMockStruct(returns); |} | |/** | Throw if condition is not true. | */ |void shouldBeTrue(E)(lazy E condition, in string file = __FILE__, in size_t line = __LINE__) { | assert_(cast(bool)condition(), file, line); |} | |/// Throw if condition not false. |void shouldBeFalse(E)(lazy E condition, in string file = __FILE__, in size_t line = __LINE__) { | assert_(!cast(bool)condition(), file, line); |} | |/// Assert value is equal to expected |void shouldEqual(V, E)(auto ref V value, auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { | | void checkInputRange(T)(auto ref const(T) _) @trusted { | auto obj = cast(T)_; | bool e = obj.empty; | auto f = obj.front; | obj.popFront; | } | enum isInputRange(T) = is(T: Elt[], Elt) || is(typeof(checkInputRange(T.init))); | | static if(is(V == class)) { | | import unit_threaded.should: isEqual; | assert_(isEqual(value, expected), file, line); | | } else static if(isInputRange!V && isInputRange!E) { | | auto ref unqual(OriginalType)(auto ref OriginalType obj) @trusted { | | // copied from std.traits | template Unqual(T) { | static if (is(T U == immutable U)) alias Unqual = U; | else static if (is(T U == shared inout const U)) alias Unqual = U; | else static if (is(T U == shared inout U)) alias Unqual = U; | else static if (is(T U == shared const U)) alias Unqual = U; | else static if (is(T U == shared U)) alias Unqual = U; | else static if (is(T U == inout const U)) alias Unqual = U; | else static if (is(T U == inout U)) alias Unqual = U; | else static if (is(T U == const U)) alias Unqual = U; | else alias Unqual = T; | } | | static if(__traits(compiles, obj[])) { | static if(!is(typeof(obj[]) == OriginalType)) { | return unqual(obj[]); | } else static if(__traits(compiles, cast(Unqual!OriginalType) obj)) { | return cast(Unqual!OriginalType) obj; | } else { | return obj; | } | } else static if(__traits(compiles, cast(Unqual!OriginalType) obj)) { | return cast(Unqual!OriginalType) obj; | } else | return obj; | } | | auto ref unvoid(OriginalType)(auto ref OriginalType obj) @trusted { | static if(is(OriginalType == void[])) | return cast(ubyte[]) obj; | else | return obj; | } | | import std.algorithm: equal; | assert_(equal(unvoid(unqual(value)), unvoid(unqual(expected))), file, line); | | } else { | assert_(cast(const)value == cast(const)expected, file, line); | } |} | | | |/// Assert value is not equal to expected. |void shouldNotEqual(V, E)(in auto ref V value, in auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { | assert_(value != expected, file, line); |} | |/// Assert value is null. |void shouldBeNull(T)(in auto ref T value, in string file = __FILE__, in size_t line = __LINE__) { | assert_(value is null, file, line); |} | |/// Assert value is not null |void shouldNotBeNull(T)(in auto ref T value, in string file = __FILE__, in size_t line = __LINE__) { | assert_(value !is null, file, line); |} | |enum isLikeAssociativeArray(T, K) = is(typeof({ | if(K.init in T) { } | if(K.init !in T) { } |})); |static assert(isLikeAssociativeArray!(string[string], string)); |static assert(!isLikeAssociativeArray!(string[string], int)); | | |/// Assert that value is in container. |void shouldBeIn(T, U)(in auto ref T value, in auto ref U container, in string file = __FILE__, in size_t line = __LINE__) | if(isLikeAssociativeArray!(U, T)) { | assert_(cast(bool)(value in container), file, line); |} | |/// ditto. |void shouldBeIn(T, U)(in auto ref T value, U container, in string file = __FILE__, in size_t line = __LINE__) | if (!isLikeAssociativeArray!(U, T)) |{ | import std.algorithm: find; | import std.array: empty; | assert_(!find(container, value).empty, file, line); |} | |/// Assert value is not in container. |void shouldNotBeIn(T, U)(in auto ref T value, in auto ref U container, in string file = __FILE__, in size_t line = __LINE__) | if(isLikeAssociativeArray!U) { | assert_(!cast(bool)(value in container), file, line); |} | |/// ditto. |void shouldNotBeIn(T, U)(in auto ref T value, U container, in string file = __FILE__, in size_t line = __LINE__) | if (!isLikeAssociativeArray!(U, T)) |{ | import std.algorithm: find; | import std.array: empty; | assert_(find(container, value).empty, file, line); |} | |/// Assert that expr throws. |void shouldThrow(T : Throwable = Exception, E) | (lazy E expr, in string file = __FILE__, in size_t line = __LINE__) { | auto threw = false; | () @trusted { | try { | expr(); | } catch(T _) { | threw = true; | } | }(); | assert_(threw, file, line); |} | |/// Assert that expr throws an Exception that must have the type E, derived types won't do. |void shouldThrowExactly(T : Throwable = Exception, E) | (lazy E expr, in string file = __FILE__, in size_t line = __LINE__) |{ | T throwable = null; | | () @trusted { | try { | expr(); | assert_(false, file, line); | } catch(T t) { | throwable = t; | } | }(); | | //Object.opEquals is @system and impure | const sameType = () @trusted { return throwable !is null && typeid(throwable) == typeid(T); }(); | assert_(sameType, file, line); | |} | |/// Assert that expr doesn't throw |void shouldNotThrow(T: Throwable = Exception, E) | (lazy E expr, in string file = __FILE__, in size_t line = __LINE__) { | () @trusted { | try | expr(); | catch(T _) | assert_(false, file, line); | }(); |} | |/// Assert that expr throws and the exception message is msg. |void shouldThrowWithMessage(T : Throwable = Exception, E)(lazy E expr, | string msg, | string file = __FILE__, | size_t line = __LINE__) { | T throwable = null; | | () @trusted { | try { | expr(); | } catch(T ex) { | throwable = ex; | } | }(); | | assert_(throwable !is null && throwable.msg == msg, file, line); |} | |/// Assert that value is approximately equal to expected. |void shouldApproxEqual(V, E)(in V value, in E expected, double maxRelDiff = 1e-2, double maxAbsDiff = 1e-5, string file = __FILE__, size_t line = __LINE__) { | import std.math: approxEqual; | assert_(approxEqual(value, expected, maxRelDiff, maxAbsDiff), file, line); |} | |/// assert that rng is empty. |void shouldBeEmpty(R)(in auto ref R rng, in string file = __FILE__, in size_t line = __LINE__) { | import std.range: isInputRange; | import std.traits: isAssociativeArray; | import std.array; | | static if(isInputRange!R) | assert_(rng.empty, file, line); | else static if(isAssociativeArray!R) | () @trusted { assert_(rng.keys.empty, file, line); }(); | else | static assert(false, "Cannot call shouldBeEmpty on " ~ R.stringof); |} | |/// Assert that rng is not empty. |void shouldNotBeEmpty(R)(in auto ref R rng, in string file = __FILE__, in size_t line = __LINE__) { | import std.range: isInputRange; | import std.traits: isAssociativeArray; | import std.array; | | static if(isInputRange!R) | assert_(!rnd.empty, file, line); | else static if(isAssociativeArray!R) | () @trusted { assert_(!rng.keys.empty, file, line); }(); | else | static assert(false, "Cannot call shouldBeEmpty on " ~ R.stringof); |} | |/// Assert that t should be greater than u. |void shouldBeGreaterThan(T, U)(in auto ref T t, in auto ref U u, | in string file = __FILE__, in size_t line = __LINE__) |{ | assert_(t > u, file, line); |} | |/// Assert that t should be smaller than u. |void shouldBeSmallerThan(T, U)(in auto ref T t, in auto ref U u, | in string file = __FILE__, in size_t line = __LINE__) |{ | assert_(t < u, file, line); |} | |/// Assert that value is the same set as expected (i.e. order doesn't matter) |void shouldBeSameSetAs(V, E)(in auto ref V value, in auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { | assert_(isSameSet(value, expected), file, line); |} | |/// Assert that value is not the same set as expected. |void shouldNotBeSameSetAs(V, E)(in auto ref V value, in auto ref E expected, in string file = __FILE__, in size_t line = __LINE__) { | assert_(!isSameSet(value, expected), file, line); |} | |private bool isSameSet(T, U)(in auto ref T t, in auto ref U u) { | import std.array: array; | import std.algorithm: canFind; | | //sort makes the element types have to implement opCmp | //instead, try one by one | auto ta = t.array; | auto ua = u.array; | if (ta.length != ua.length) return false; | foreach(element; ta) | { | if (!ua.canFind(element)) return false; | } | | return true; |} | |/// Assert that actual and expected represent the same JSON (i.e. formatting doesn't matter) |void shouldBeSameJsonAs(in string actual, | in string expected, | in string file = __FILE__, | in size_t line = __LINE__) | @trusted // not @safe pure due to parseJSON |{ | import std.json: parseJSON, JSONException; | | auto parse(in string str) { | try 0000000| return str.parseJSON; | catch(JSONException ex) { 0000000| assert_(false, "Failed to parse " ~ str, file, line); | } 0000000| assert(0); | } | 0000000| assert_(parse(actual) == parse(expected), file, line); |} | | |private void assert_(in bool value, in string file, in size_t line) @safe pure { 128| assert_(value, "Assertion failure", file, line); |} | |private void assert_(bool value, in string message, in string file, in size_t line) @trusted pure { 128| if(!value) 0000000| throw new Exception(message, file, line); |} | |void fail(in string output, in string file, in size_t line) @safe pure { 0000000| assert_(false, output, file, line); |} | | |auto should(E)(lazy E expr) { | | struct Should { | | bool opEquals(U)(auto ref U other, | in string file = __FILE__, | in size_t line = __LINE__) | { | expr.shouldEqual(other, file, line); | return true; | } | } | | return Should(); |} ../../../.dub/packages/unit-threaded-0.10.8/unit-threaded/source/unit_threaded/light.d is 15% covered <<<<<< EOF