www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A technique to mock "static interfaces" (e.g. isInputRange)

reply Atila Neves <atila.neves gmail.com> writes:
There was talk in the forum of making it easier to come up 
instantiations of say, an input range for testing purposes. That 
got me thinking of how mocking frameworks make it easy to pass in 
dependencies without having to write a whole new "test double" 
type oneself. How would one do that for what I've started calling 
"static interfaces"? How would one mock an input range?

There's no way to inspect the code inside the lambda used in 
isInputRange or any other similar template constraint (.codeof 
would be awesome, but alas it doesn't exist), but a regular OOP 
interface you can reflect on... and there's even one called 
InputRange in Phobos... hmmm.

The result is in the link below. The implementation is a bit 
horrible because I cowboyed it. I should probably figure out how 
to make it more template mixin and less of the string variety, 
but I was on a roll. Anyway, take a look at the unit test at the 
bottom first and complain about my crappy implementation later:


https://gist.github.com/atilaneves/b40c4d030c70686ffa3b8543018f6a7e


If you have an interface already I guess you could just mock 
that, but then you wouldn't be able to test templated code with 
it. This technique would fix that problem.


Interesting? Crazy? Worth adding to unit-threaded? Phobos (after 
much cleaning up)?


Atila
May 25 2016
next sibling parent reply Alex Parrill <initrd.gz gmail.com> writes:
On Wednesday, 25 May 2016 at 21:38:23 UTC, Atila Neves wrote:
 There was talk in the forum of making it easier to come up 
 instantiations of say, an input range for testing purposes. 
 That got me thinking of how mocking frameworks make it easy to 
 pass in dependencies without having to write a whole new "test 
 double" type oneself. How would one do that for what I've 
 started calling "static interfaces"? How would one mock an 
 input range?

 There's no way to inspect the code inside the lambda used in 
 isInputRange or any other similar template constraint (.codeof 
 would be awesome, but alas it doesn't exist), but a regular OOP 
 interface you can reflect on... and there's even one called 
 InputRange in Phobos... hmmm.

 The result is in the link below. The implementation is a bit 
 horrible because I cowboyed it. I should probably figure out 
 how to make it more template mixin and less of the string 
 variety, but I was on a roll. Anyway, take a look at the unit 
 test at the bottom first and complain about my crappy 
 implementation later:


 https://gist.github.com/atilaneves/b40c4d030c70686ffa3b8543018f6a7e


 If you have an interface already I guess you could just mock 
 that, but then you wouldn't be able to test templated code with 
 it. This technique would fix that problem.


 Interesting? Crazy? Worth adding to unit-threaded? Phobos 
 (after much cleaning up)?


 Atila
Have you looked at std.typecons.AutoImplement at all? http://dlang.org/phobos/std_typecons.html#.AutoImplement It seems to do something similar to what you're doing, though it generates a subclass rather than a struct (for the purposes of testing contracts and stuff, I don't think it matters too much).
May 25 2016
parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 25 May 2016 at 21:52:37 UTC, Alex Parrill wrote:
 On Wednesday, 25 May 2016 at 21:38:23 UTC, Atila Neves wrote:
 [...]
Have you looked at std.typecons.AutoImplement at all? http://dlang.org/phobos/std_typecons.html#.AutoImplement
I'd never seen it before, thanks! Doesn't do the same thing - I was trying to get a struct that could be passed to generic algorithms, and that could be programmed like a regular mock. My example only shows return value programming, but it was a quick and dirty prototype. Atila
May 26 2016
parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Thursday, 26 May 2016 at 09:40:26 UTC, Atila Neves wrote:
 On Wednesday, 25 May 2016 at 21:52:37 UTC, Alex Parrill wrote:
 Have you looked at std.typecons.AutoImplement at all?
I'd never seen it before, thanks!
I recall adding on a wishlist somewhere that every week or something that a video or article is made about these things. Preferably a video. It could be them talking about design decisions of certain features in D, or more likely exposure to an entire module in D that covers the basics and when you'd use each function/feature/template and why. I'm not talking you need an hour long video or something, maybe like the introduction to the STL format, or even rapid firing and showing quick premade examples/use-cases of why it was used (for optimal or problem solving reasons). The video could be say 10 minutes long. There's so much in the library I would have to go through and try to remember and I know very little of it. Exposing myself to it feels like a chore sometimes, and often I don't.
May 29 2016
parent Seb <seb wilzba.ch> writes:
On Sunday, 29 May 2016 at 22:20:05 UTC, Era Scarecrow wrote:
 On Thursday, 26 May 2016 at 09:40:26 UTC, Atila Neves wrote:
 On Wednesday, 25 May 2016 at 21:52:37 UTC, Alex Parrill wrote:
 Have you looked at std.typecons.AutoImplement at all?
I'd never seen it before, thanks!
I recall adding on a wishlist somewhere that every week or something that a video or article is made about these things. Preferably a video. It could be them talking about design decisions of certain features in D, or more likely exposure to an entire module in D that covers the basics and when you'd use each function/feature/template and why. I'm not talking you need an hour long video or something, maybe like the introduction to the STL format, or even rapid firing and showing quick premade examples/use-cases of why it was used (for optimal or problem solving reasons). The video could be say 10 minutes long. There's so much in the library I would have to go through and try to remember and I know very little of it. Exposing myself to it feels like a chore sometimes, and often I don't.
Do you know about Adam's This Week in D? http://arsdnet.net/this-week-in-d/2016-may-22.html Vladimir also tries to maintain his Planet of D http://planet.dsource.org/ But the main problem is just time (videos even take more time), maybe we should have a user-based newsletter or forum where everyone can submit articles.
May 29 2016
prev sibling next sibling parent reply Robert burner Schadek <rburners gmail.com> writes:
+1 having a look at AutoImplement

For testing ranges, I usually use 
https://github.com/dlang/phobos/blob/master/std/internal/test/dummyrange.d
even though its internal ;-)

anyway, more and better testing always good.

p.s. I think at some point we have to build some (the perfect) 
mocking framework with dependency injection and all the other 
awesome stuff. But before that can happen we have to lose the 
fear of classes and OO. Please no OT!
May 25 2016
parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 25 May 2016 at 22:07:07 UTC, Robert burner Schadek 
wrote:
 +1 having a look at AutoImplement

 For testing ranges, I usually use 
 https://github.com/dlang/phobos/blob/master/std/internal/test/dummyrange.d
 even though its internal ;-)
Internal, and not really a mock. I used a range as an example - anything with an accompanying interface would be supported.
 anyway, more and better testing always good.
Indeed :)
 p.s. I think at some point we have to build some (the perfect) 
 mocking framework with dependency injection and all the other 
 awesome stuff. But before that can happen we have to lose the 
 fear of classes and OO. Please no OT!
What's wrong with dmocks revived (I've never used it)? I'm not afraid of classes, I was just trying to extend mocking to static interfaces since there's so much emphasis on them in D. Regular mocking is boring and easily doable :P Atila
May 26 2016
parent Robert burner Schadek <rburners gmail.com> writes:
On Thursday, 26 May 2016 at 09:42:59 UTC, Atila Neves wrote:
 Internal, and not really a mock. I used a range as an example - 
 anything with an accompanying interface would be supported.
I see, I thought as much
 What's wrong with dmocks revived (I've never used it)? I'm not 
 afraid of classes, I was just trying to extend mocking to 
 static interfaces since there's so much emphasis on them in D. 
 Regular mocking is boring and easily doable :P
I was not clear. I mean, D has a community has to get past its GC Angst.
May 26 2016
prev sibling next sibling parent reply Gary Willoughby <dev nomad.so> writes:
On Wednesday, 25 May 2016 at 21:38:23 UTC, Atila Neves wrote:
 There was talk in the forum of making it easier to come up 
 instantiations of say, an input range for testing purposes. 
 That got me thinking of how mocking frameworks make it easy to 
 pass in dependencies without having to write a whole new "test 
 double" type oneself. How would one do that for what I've 
 started calling "static interfaces"? How would one mock an 
 input range?
I have a mocking framework here: https://github.com/nomad-software/dunit Maybe that is useful? The only problem with mine is that it needs to be injected inside an interface. Like this: interface InputRange(T) { property T front(); void popFront(); property bool empty(); mixin Mockable!(InputRange!(T)); } auto mock = InputRange.getMock()
May 26 2016
parent Atila Neves <atila.neves gmail.com> writes:
On Thursday, 26 May 2016 at 08:08:01 UTC, Gary Willoughby wrote:
 On Wednesday, 25 May 2016 at 21:38:23 UTC, Atila Neves wrote:
 There was talk in the forum of making it easier to come up 
 instantiations of say, an input range for testing purposes. 
 That got me thinking of how mocking frameworks make it easy to 
 pass in dependencies without having to write a whole new "test 
 double" type oneself. How would one do that for what I've 
 started calling "static interfaces"? How would one mock an 
 input range?
I have a mocking framework here: https://github.com/nomad-software/dunit Maybe that is useful? The only problem with mine is that it needs to be injected inside an interface. Like this:
That's a showstopper for me. Atila
May 26 2016
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2016-05-25 23:38, Atila Neves wrote:

 Interesting? Crazy? Worth adding to unit-threaded? Phobos (after much
 cleaning up)?
Interesting, yes. Crazy, not enough :). Here's an example of replacing functions and replacing methods on individual objects [1]. I'm trying to do something like you can do in Ruby where there are mocking frameworks allowing you to replace any method with a new implementation, both on a class and object level. Not sure how bad it actually is to do something like this :) One advantage of this technique is that the code does not need to be adapted for testing. For example, no need to make a function into a template just to be able to pass in a mock object/struct. Or if a function you would like to test writes to a file as a side effect for some reason. Just replace the "write" function with a dummy function that does nothing to avoid creating the file when running the test. [1] https://dpaste.dzfl.pl/bfd933702ed6 -- /Jacob Carlborg
May 26 2016
parent reply Atila Neves <atila.neves gmail.com> writes:
On Thursday, 26 May 2016 at 20:03:50 UTC, Jacob Carlborg wrote:
 On 2016-05-25 23:38, Atila Neves wrote:

 [...]
Interesting, yes. Crazy, not enough :). Here's an example of replacing functions and replacing methods on individual objects [1]. [...]
I get an SSL warning for that link. Atila
May 27 2016
parent reply Jacob Carlborg <doob me.com> writes:
On 2016-05-27 15:12, Atila Neves wrote:

 I get an SSL warning for that link.
Hmm, here's the code inline: module red; import std.stdio; import core.sys.posix.sys.mman : mprotect, PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC; import core.stdc.errno : errno; import core.stdc.string : memmove, memcpy; extern (C) int getpagesize(); void procWrite(void* pos, const ref ubyte[6] data) { void* addr = pos; size_t page = getpagesize(); addr -= (cast(size_t)addr) % page; if(0 != mprotect(addr, page, PROT_READ | PROT_WRITE | PROT_EXEC)){ return; // error } memmove(pos, data.ptr, data.length); mprotect(pos, page, PROT_READ | PROT_EXEC); } ubyte[6] redirect(void* from, void* to) { // compute ASM ubyte[6] cmd; // // sanity checks if(((from <= to) && (to <= from+5)) || ((to <= from) && (from <= to+5))) { return cmd; //throw new Exception("illegal source-destination combination"); } cmd[0] = 0xE9; // jmp cmd[5] = 0xC3; // retn size_t new_dest = cast(size_t) to; new_dest = new_dest - (cast(size_t)from + 5); int offset = cast(int)cast(ptrdiff_t)new_dest; cmd[1 .. 1 + offset.sizeof] = (cast(ubyte*)&offset)[0 .. offset.sizeof]; // // save original ubyte[6] original = (cast(ubyte*)from)[0 .. cmd.length]; // write asm procWrite(from, cmd); return original; // return null; } void restoreRedirection(void* addr, const ref ubyte[6] data) { procWrite(addr, data); } void a() { writeln("a"); } void b() { writeln("b"); } void bar(Foo foo) { writeln("bar"); } class Foo { void foo() { writeln("foo"); } } void replaceMethod() { auto f = new Foo; auto f2 = new Foo; auto vtbl = (cast(void**)f.__vptr)[0 .. 6].dup; vtbl[5] = &bar; f.__vptr = cast(immutable(void*)*) vtbl.ptr; f.foo(); f2.foo(); } void replaceFunction() { a(); auto r = redirect(&a, &b); a(); restoreRedirection(&a, r); a(); } void main() { replaceMethod(); replaceFunction(); } -- /Jacob Carlborg
May 27 2016
parent reply Atila Neves <atila.neves gmail.com> writes:
On Friday, 27 May 2016 at 18:49:12 UTC, Jacob Carlborg wrote:
 On 2016-05-27 15:12, Atila Neves wrote:

 [...]
Hmm, here's the code inline: module red; [...]
Yep, that's definitely crazier than I what I posted. Nifty! Atila
May 29 2016
parent Jacob Carlborg <doob me.com> writes:
On 2016-05-29 22:03, Atila Neves wrote:

 Yep, that's definitely crazier than I what I posted. Nifty!
Should add that the code for replacing functions is from flectioned [1]. [1] http://dsource.org/projects/flectioned -- /Jacob Carlborg
May 30 2016