digitalmars.D - A technique to mock "static interfaces" (e.g. isInputRange)
- Atila Neves (23/23) May 25 2016 There was talk in the forum of making it easier to come up
- Alex Parrill (6/31) May 25 2016 Have you looked at std.typecons.AutoImplement at all?
- Atila Neves (7/11) May 26 2016 I'd never seen it before, thanks! Doesn't do the same thing - I
- Era Scarecrow (14/17) May 29 2016 I recall adding on a wishlist somewhere that every week or
- Seb (8/27) May 29 2016 Do you know about Adam's This Week in D?
- Robert burner Schadek (9/9) May 25 2016 +1 having a look at AutoImplement
- Atila Neves (10/19) May 26 2016 Internal, and not really a mock. I used a range as an example -
- Robert burner Schadek (4/10) May 26 2016 I was not clear. I mean, D has a community has to get past its GC
- Gary Willoughby (12/19) May 26 2016 I have a mocking framework here:
- Atila Neves (3/15) May 26 2016 That's a showstopper for me.
- Jacob Carlborg (16/18) May 26 2016 Interesting, yes. Crazy, not enough :). Here's an example of replacing
- Atila Neves (3/9) May 27 2016 I get an SSL warning for that link.
- Jacob Carlborg (93/94) May 27 2016 Hmm, here's the code inline:
- Atila Neves (3/8) May 29 2016 Yep, that's definitely crazier than I what I posted. Nifty!
- Jacob Carlborg (5/6) May 30 2016 Should add that the code for replacing functions is from flectioned [1].
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
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)? AtilaHave you looked at std.typecons.AutoImplement at all? 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
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: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[...]Have you looked at std.typecons.AutoImplement at all?
May 26 2016
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: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.Have you looked at std.typecons.AutoImplement at all?I'd never seen it before, thanks!
May 29 2016
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: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.On Wednesday, 25 May 2016 at 21:52:37 UTC, Alex Parrill wrote: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.Have you looked at std.typecons.AutoImplement at all?I'd never seen it before, thanks!
May 29 2016
+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
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
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 muchWhat'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 :PI was not clear. I mean, D has a community has to get past its GC Angst.
May 26 2016
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
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:That's a showstopper for me. AtilaThere 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:
May 26 2016
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
On Thursday, 26 May 2016 at 20:03:50 UTC, Jacob Carlborg wrote:On 2016-05-25 23:38, Atila Neves wrote:I get an SSL warning for that link. Atila[...]Interesting, yes. Crazy, not enough :). Here's an example of replacing functions and replacing methods on individual objects [1]. [...]
May 27 2016
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
On Friday, 27 May 2016 at 18:49:12 UTC, Jacob Carlborg wrote:On 2016-05-27 15:12, Atila Neves wrote:Yep, that's definitely crazier than I what I posted. Nifty! Atila[...]Hmm, here's the code inline: module red; [...]
May 29 2016
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