Re: RFC: patch statement

2017-04-03 Thread Minty Fresh via Digitalmars-d

On Monday, 3 April 2017 at 11:16:57 UTC, Dejan Lekic wrote:
I know people her do not like to see proposals that change (add 
stuff to) the language. However, I strongly feel that for the 
testing purposes D should provide means to patch any object (no 
matter whether it is final or not!). Therefore I wonder what 
people think of adding a `patch(obj) {}` or perhaps change the 
semantics of the `with(obj) {}` so unittest writers can modify 
the object and set values.


The patch keyword would work ONLY inside unittest {} blocks AND 
inside functions annotated with @test annotation.


Imagine we have:

int myFun(Person person) { /* some logic here */ }

unittest {
  auto p = new Person() /* does not really matter which 
constructor we use */

  patch(p) {
// here we can modify ANY attribute, no matter whether it 
is private or public

p.fname = "Nikola"
p.sname = "Tesla"
  }
  auto res = myFun(p)
  // do some assertions here
}

Similarly:

@test
void test_myFun() {
  // same code as in the unittest above.
}

I do not even know if patch() {} statement is even possible, 
that is the whole point of writing this, so people can 
enlighten me... :)


As I said in the introduction paragraph, for this purpose the 
semantics of the with statement could be changed, but I prefer 
a different keyword (patch) to be honest.


It looks like what you're trying to do is set up object mocks for 
unit testing.
In general, I find that well designed libraries provide such 
tools for testing, either in the form of factory functions or 
some other means of constructing mocks for test builds. I try to 
follow such patterns myself.


Getting back to the immediate subject:
You can already grant write access to whatever attributes with a 
bit of conditional compilation. Notably, defining accessors that 
exist only for unittest builds.


You could even go so far as to define a generalized one,
ie.
version(unittest)
{
void patch(string attr, T)(Person p, T value)
{
__traits(getMember, p, attr) = value;
}
}

And then, elsewhere:

// Given p is some Person.
p.patch!"fname" = "Nikola";
p.patch!"sname" = "Tesla";

So long as this is defined in the same module as the type, it'll 
be able to access protected and private fields.


Re: RFC: patch statement

2017-04-03 Thread Biotronic via Digitalmars-d

On Monday, 3 April 2017 at 11:16:57 UTC, Dejan Lekic wrote:
I know people her do not like to see proposals that change (add 
stuff to) the language. However, I strongly feel that for the 
testing purposes D should provide means to patch any object (no 
matter whether it is final or not!). Therefore I wonder what 
people think of adding a `patch(obj) {}` or perhaps change the 
semantics of the `with(obj) {}` so unittest writers can modify 
the object and set values.


The patch keyword would work ONLY inside unittest {} blocks AND 
inside functions annotated with @test annotation.


Imagine we have:

int myFun(Person person) { /* some logic here */ }

unittest {
  auto p = new Person() /* does not really matter which 
constructor we use */

  patch(p) {
// here we can modify ANY attribute, no matter whether it 
is private or public

p.fname = "Nikola"
p.sname = "Tesla"
  }
  auto res = myFun(p)
  // do some assertions here
}

Similarly:

@test
void test_myFun() {
  // same code as in the unittest above.
}

I do not even know if patch() {} statement is even possible, 
that is the whole point of writing this, so people can 
enlighten me... :)


As I said in the introduction paragraph, for this purpose the 
semantics of the with statement could be changed, but I prefer 
a different keyword (patch) to be honest.


We can already do that. Proof of concept:


// ===
module bar;

class A {
private int n;

public int GetN() {
return n;
}
}

// ===
module foo;
import bar;

void main() {
import std.stdio : writeln;
auto a = new A();
assert(a.GetN() == 0);
with (patch(a)) { // Look ma, magic!
n = 3;
}
assert(a.GetN() == 3);
}

auto patch(T)(T value) {
return Patch!T(value);
}

struct Patch(T) {
T _payload;

mixin PatchFields!T;
}

mixin template PatchFields(T, int __n = -1) {
static if (__n == -1) {
mixin PatchFields!(T, T.tupleof.length-1);
} else {
enum name = __traits(identifier, T.tupleof[__n]);

mixin("auto "~name~"(U)(U value) {
_payload.tupleof[__n] = value;
}");

static if (__n > 0) {
mixin PatchFields!(T, __n-1);
}
}
}


RFC: patch statement

2017-04-03 Thread Dejan Lekic via Digitalmars-d
I know people her do not like to see proposals that change (add 
stuff to) the language. However, I strongly feel that for the 
testing purposes D should provide means to patch any object (no 
matter whether it is final or not!). Therefore I wonder what 
people think of adding a `patch(obj) {}` or perhaps change the 
semantics of the `with(obj) {}` so unittest writers can modify 
the object and set values.


The patch keyword would work ONLY inside unittest {} blocks AND 
inside functions annotated with @test annotation.


Imagine we have:

int myFun(Person person) { /* some logic here */ }

unittest {
  auto p = new Person() /* does not really matter which 
constructor we use */

  patch(p) {
// here we can modify ANY attribute, no matter whether it is 
private or public

p.fname = "Nikola"
p.sname = "Tesla"
  }
  auto res = myFun(p)
  // do some assertions here
}

Similarly:

@test
void test_myFun() {
  // same code as in the unittest above.
}

I do not even know if patch() {} statement is even possible, that 
is the whole point of writing this, so people can enlighten me... 
:)


As I said in the introduction paragraph, for this purpose the 
semantics of the with statement could be changed, but I prefer a 
different keyword (patch) to be honest.