Idea and proof of concept: Accept for Test
Hi, Some code is very âactiveâ, executed all the time, e.g. collections, graphics⦠or imagine if you work on the compiler or debugger. It would really be nice if we could test a change before accepting it. Something like âAccept for Testâ where magically the original method does not change, yet, when running tests, the version of the code we accepted for testing is executed. So how do we implement that? 1) menu in the browser. I did a quick hack, a Suggestions AST menu shows for all nodes (to be replaced with the âcorrectâ way later). SugsSuggestion subclass: #SugsAcceptForTest instanceVariableNames: '' classVariableNames: '' package: 'SmartSuggestions-Suggestionâ label ^'Accept for testâ So, now we can do everything in the #execute method. What do we need? 1) How we know that we are in a test? â> CurrentExecutionEnvironment value isTest 2) can we compile the buffer even if there are syntax errors (kind of what I expect to happen in non-accepted code)? â> yes, remember, we use the same Parser for syntax highlighting and we extended code-generation for SyntaxError Nodes in the past (it raises a syntax error at runtime, isnât that kind off odd in a fun way?). newMethod := context selectedClass compiler source: context code; options: #(+ optionParseErrors); compile. 3) Now how to we âhook intoâ the execution of the original unchanged method? We use a MetaLink. We install it as a before link on the RBMethodNode, the meta-object is a block. This block gets as arguments the thisContext and the arguments of the original method and then just does what you want it do do: if we are in test, return the result of the execution of the method we just compiled. (Closures are fun, we can just reference the outer temp newMethod, no need to store it anywhere else explicitly): [ :aContext :args | CurrentExecutionEnvironment value isTest ifTrue: [ aContext return: (newMethod valueWithReceiver: aContext receiver arguments: args) ] ]; Thus, all the code is this: execute | newMethod metaLink | âwe compile a new method from the not-accepted text editor. Allow syntax errors" newMethod := context selectedClass compiler source: context code; options: #(+ optionParseErrors); compile. "the link executes the method we just created and returns" metaLink := MetaLink new metaObject: [ :aContext :args | CurrentExecutionEnvironment value isTest ifTrue: [ aContext return: (newMethod valueWithReceiver: aContext receiver arguments: args) ] ]; selector: #value:value:; arguments: #(context arguments). context selectedMethod ast link: metaLink Cleanup we do not care as everything will be GCed as soon as we accept the method for real. And it works! I did not test it too much, but it seems to do what it is supposed to do. (but more tests and testing are for sure needed) https://github.com/pharo-project/pharo/pull/1182 Next step: move it to a better place in the menu. Marcus
Very cool. Impressive combination of unique Pharo features.
On 4 Apr 2018, at 18:02, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
Some code is very âactiveâ, executed all the time, e.g. collections, graphics⦠or imagine if you work on the compiler or debugger.
It would really be nice if we could test a change before accepting it. Something like âAccept for Testâ where magically the original method does not change, yet, when running tests, the version of the code we accepted for testing is executed.
So how do we implement that?
1) menu in the browser. I did a quick hack, a Suggestions AST menu shows for all nodes (to be replaced with the âcorrectâ way later).
SugsSuggestion subclass: #SugsAcceptForTest instanceVariableNames: '' classVariableNames: '' package: 'SmartSuggestions-Suggestionâ
label ^'Accept for testâ
So, now we can do everything in the #execute method.
What do we need?
1) How we know that we are in a test? â> CurrentExecutionEnvironment value isTest 2) can we compile the buffer even if there are syntax errors (kind of what I expect to happen in non-accepted code)? â> yes, remember, we use the same Parser for syntax highlighting and we extended code-generation for SyntaxError Nodes in the past (it raises a syntax error at runtime, isnât that kind off odd in a fun way?).
newMethod := context selectedClass compiler source: context code; options: #(+ optionParseErrors); compile.
3) Now how to we âhook intoâ the execution of the original unchanged method? We use a MetaLink. We install it as a before link on the RBMethodNode, the meta-object is a block. This block gets as arguments the thisContext and the arguments of the original method and then just does what you want it do do: if we are in test, return the result of the execution of the method we just compiled. (Closures are fun, we can just reference the outer temp newMethod, no need to store it anywhere else explicitly):
[ :aContext :args | CurrentExecutionEnvironment value isTest ifTrue: [ aContext return: (newMethod valueWithReceiver: aContext receiver arguments: args) ] ];
Thus, all the code is this:
execute | newMethod metaLink | âwe compile a new method from the not-accepted text editor. Allow syntax errors" newMethod := context selectedClass compiler source: context code; options: #(+ optionParseErrors); compile. "the link executes the method we just created and returns" metaLink := MetaLink new metaObject: [ :aContext :args | CurrentExecutionEnvironment value isTest ifTrue: [ aContext return: (newMethod valueWithReceiver: aContext receiver arguments: args) ] ]; selector: #value:value:; arguments: #(context arguments). context selectedMethod ast link: metaLink
Cleanup we do not care as everything will be GCed as soon as we accept the method for real.
And it works! I did not test it too much, but it seems to do what it is supposed to do. (but more tests and testing are for sure needed) https://github.com/pharo-project/pharo/pull/1182
Next step: move it to a better place in the menu.
Marcus
Yes this is cool. I was wondering what if you try and modify the Meta-Link architecture itself, but my brain did not reach this second meta level... 2018-04-04 21:50 GMT+02:00 Sven Van Caekenberghe <sven@stfx.eu>:
Very cool. Impressive combination of unique Pharo features.
On 4 Apr 2018, at 18:02, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
Some code is very âactiveâ, executed all the time, e.g. collections, graphics⦠or imagine if you work on the compiler or debugger.
It would really be nice if we could test a change before accepting it. Something like âAccept for Testâ where magically the original method does not change, yet, when running tests, the version of the code we accepted for testing is executed.
So how do we implement that?
1) menu in the browser. I did a quick hack, a Suggestions AST menu shows for all nodes (to be replaced with the âcorrectâ way later).
SugsSuggestion subclass: #SugsAcceptForTest instanceVariableNames: '' classVariableNames: '' package: 'SmartSuggestions-Suggestionâ
label ^'Accept for testâ
So, now we can do everything in the #execute method.
What do we need?
1) How we know that we are in a test? â> CurrentExecutionEnvironment value isTest 2) can we compile the buffer even if there are syntax errors (kind of what I expect to happen in non-accepted code)? â> yes, remember, we use the same Parser for syntax highlighting and we extended code-generation for SyntaxError Nodes in the past (it raises a syntax error at runtime, isnât that kind off odd in a fun way?).
newMethod := context selectedClass compiler source: context code; options: #(+ optionParseErrors); compile.
3) Now how to we âhook intoâ the execution of the original unchanged method? We use a MetaLink. We install it as a before link on the RBMethodNode, the meta-object is a block. This block gets as arguments the thisContext and the arguments of the original method and then just does what you want it do do: if we are in test, return the result of the execution of the method we just compiled. (Closures are fun, we can just reference the outer temp newMethod, no need to store it anywhere else explicitly):
[ :aContext :args | CurrentExecutionEnvironment value isTest ifTrue: [ aContext return: (newMethod valueWithReceiver: aContext receiver arguments: args) ] ];
Thus, all the code is this:
execute | newMethod metaLink | âwe compile a new method from the not-accepted text editor. Allow syntax errors" newMethod := context selectedClass compiler source: context code; options: #(+ optionParseErrors); compile. "the link executes the method we just created and returns" metaLink := MetaLink new metaObject: [ :aContext :args | CurrentExecutionEnvironment value isTest ifTrue: [ aContext return: (newMethod valueWithReceiver: aContext receiver arguments: args) ] ]; selector: #value:value:; arguments: #(context arguments). context selectedMethod ast link: metaLink
Cleanup we do not care as everything will be GCed as soon as we accept the method for real.
And it works! I did not test it too much, but it seems to do what it is supposed to do. (but more tests and testing are for sure needed) https://github.com/pharo-project/pharo/pull/1182
Next step: move it to a better place in the menu.
Marcus
On 4 Apr 2018, at 22:41, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> wrote:
Yes this is cool. I was wondering what if you try and modify the Meta-Link architecture itself, but my brain did not reach this second meta level...
for now all Links are ignored that are put in the MetaLink itself⦠but that was more a bootstrap thing. Now that we added more fine-grained control and better recession stopping, this can for sure be simplified. But for âmeta useâ to put a knot in the brain, the next level is not that, but instead âjustâ using links to install links at runtime or Links that enable/disable other links. Marcus
On 4 Apr 2018, at 21:50, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Very cool. Impressive combination of unique Pharo features.
It shows, too, that âdoing an experiment on topâ and then writing a paper is one thing, but that true progress happens if you can actually integrate the results back into the system. Marcus
On 5 Apr 2018, at 09:09, Marcus Denker <marcus.denker@inria.fr> wrote:
On 4 Apr 2018, at 21:50, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Very cool. Impressive combination of unique Pharo features.
It shows, too, that âdoing an experiment on topâ and then writing a paper is one thing, but that true progress happens if you can actually integrate the results back into the system.
And it leads immediately (at least for me) that I start to get lots of ideas⦠so now we accept a single method for test. Why not⦠(random ideas) -> For un-accepted code: Why not do something like this automatically at every keystroke? That is, after syntax highlight, we can just compile a method and store it as a method property on the original method (cost almost nothing). Then all those for an âimplicit layer" that e.g we can very easily activate for the dynamic extended of the exection of an expression in the playground. You could even open a playground that default to that. There can be a button in the browser ârun not acceptedâ. -> extend it so that you actually âaccept for layer Xâ, that is, you give a name to groups of these. -> and of course implement it that the whole group an be accepted as an atomic operation Marcus
On 5. Apr 2018, at 08:31, Marcus Denker <marcus.denker@inria.fr> wrote:
And it leads immediately (at least for me) that I start to get lots of ideasâ¦
so now we accept a single method for test.
Why not⦠(random ideas)
nice! Do we have this for collecting testcases and expected output as well? The tricky part might be to put the Class Under Test into the right state (and condense all duplicate test cases). holger
you should use that as an example in the reflectivity paper. On Wed, Apr 4, 2018 at 6:02 PM, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
Some code is very âactiveâ, executed all the time, e.g. collections, graphics⦠or imagine if you work on the compiler or debugger.
It would really be nice if we could test a change before accepting it. Something like âAccept for Testâ where magically the original method does not change, yet, when running tests, the version of the code we accepted for testing is executed.
So how do we implement that?
1) menu in the browser. I did a quick hack, a Suggestions AST menu shows for all nodes (to be replaced with the âcorrectâ way later).
SugsSuggestion subclass: #SugsAcceptForTest instanceVariableNames: '' classVariableNames: '' package: 'SmartSuggestions-Suggestionâ
label ^'Accept for testâ
So, now we can do everything in the #execute method.
What do we need?
1) How we know that we are in a test? â> CurrentExecutionEnvironment value isTest 2) can we compile the buffer even if there are syntax errors (kind of what I expect to happen in non-accepted code)? â> yes, remember, we use the same Parser for syntax highlighting and we extended code-generation for SyntaxError Nodes in the past (it raises a syntax error at runtime, isnât that kind off odd in a fun way?).
newMethod := context selectedClass compiler source: context code; options: #(+ optionParseErrors); compile.
3) Now how to we âhook intoâ the execution of the original unchanged method? We use a MetaLink. We install it as a before link on the RBMethodNode, the meta-object is a block. This block gets as arguments the thisContext and the arguments of the original method and then just does what you want it do do: if we are in test, return the result of the execution of the method we just compiled. (Closures are fun, we can just reference the outer temp newMethod, no need to store it anywhere else explicitly):
[ :aContext :args | CurrentExecutionEnvironment value isTest ifTrue: [ aContext return: (newMethod valueWithReceiver: aContext receiver arguments: args) ] ];
Thus, all the code is this:
execute | newMethod metaLink | âwe compile a new method from the not-accepted text editor. Allow syntax errors" newMethod := context selectedClass compiler source: context code; options: #(+ optionParseErrors); compile. "the link executes the method we just created and returns" metaLink := MetaLink new metaObject: [ :aContext :args | CurrentExecutionEnvironment value isTest ifTrue: [ aContext return: (newMethod valueWithReceiver: aContext receiver arguments: args) ] ]; selector: #value:value:; arguments: #(context arguments). context selectedMethod ast link: metaLink
Cleanup we do not care as everything will be GCed as soon as we accept the method for real.
And it works! I did not test it too much, but it seems to do what it is supposed to do. (but more tests and testing are for sure needed) https://github.com/pharo-project/pharo/pull/1182
Next step: move it to a better place in the menu.
Marcus
On 4 Apr 2018, at 18:02, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
Some code is very âactiveâ, executed all the time, e.g. collections, graphics⦠or imagine if you work on the compiler or debugger.
It would really be nice if we could test a change before accepting it. Something like âAccept for Testâ where magically the original method does not change, yet, when running tests, the version of the code we accepted for testing is executed.
here is a version done using a Calypso command: https://github.com/pharo-project/pharo/pull/1372 missing: -we need to have some visual clue in the footer - a tool that lists all âaccepted for testâ method with the possibility to âcommitâ all at once. Marcus
Hi Marcus. I commented on github. 2018-05-16 18:34 GMT+03:00 Marcus Denker <marcus.denker@inria.fr>:
On 4 Apr 2018, at 18:02, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
Some code is very âactiveâ, executed all the time, e.g. collections, graphics⦠or imagine if you work on the compiler or debugger.
It would really be nice if we could test a change before accepting it. Something like âAccept for Testâ where magically the original method does not change, yet, when running tests, the version of the code we accepted for testing is executed.
here is a version done using a Calypso command:
https://github.com/pharo-project/pharo/pull/1372
missing: -we need to have some visual clue in the footer - a tool that lists all âaccepted for testâ method with the possibility to âcommitâ all at once.
We will have it almost for free with Calypso as soon as this new metalink will be managed similar way as breakpoints. "Commit" is of course needs to be implemented.
Marcus
On 16 May 2018, at 18:21, Denis Kudriashov <dionisiydk@gmail.com> wrote:
Hi Marcus. I commented on github.
2018-05-16 18:34 GMT+03:00 Marcus Denker <marcus.denker@inria.fr <mailto:marcus.denker@inria.fr>>:
On 4 Apr 2018, at 18:02, Marcus Denker <marcus.denker@inria.fr <mailto:marcus.denker@inria.fr>> wrote:
Hi,
Some code is very âactiveâ, executed all the time, e.g. collections, graphics⦠or imagine if you work on the compiler or debugger.
It would really be nice if we could test a change before accepting it. Something like âAccept for Testâ where magically the original method does not change, yet, when running tests, the version of the code we accepted for testing is executed.
here is a version done using a Calypso command:
https://github.com/pharo-project/pharo/pull/1372 <https://github.com/pharo-project/pharo/pull/1372>
missing: -we need to have some visual clue in the footer - a tool that lists all âaccepted for testâ method with the possibility to âcommitâ all at once.
We will have it almost for free with Calypso as soon as this new metalink will be managed similar way as breakpoints. "Commit" is of course needs to be implemented.
I really wonder if this is the same as breakpoint. E.g. it only applies to the whole method, it is not an âactionâ, so a guter icon seems to be not the right visualization. To me the fact that it uses a meta-link is purely an implementation artefact, other than for breakpoints, watchpoints, where we annotate the AST with actions⦠Marcus
2018-05-17 10:53 GMT+03:00 Marcus Denker <marcus.denker@inria.fr>:
On 16 May 2018, at 18:21, Denis Kudriashov <dionisiydk@gmail.com> wrote:
Hi Marcus. I commented on github.
2018-05-16 18:34 GMT+03:00 Marcus Denker <marcus.denker@inria.fr>:
On 4 Apr 2018, at 18:02, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
Some code is very âactiveâ, executed all the time, e.g. collections, graphics⦠or imagine if you work on the compiler or debugger.
It would really be nice if we could test a change before accepting it. Something like âAccept for Testâ where magically the original method does not change, yet, when running tests, the version of the code we accepted for testing is executed.
here is a version done using a Calypso command:
https://github.com/pharo-project/pharo/pull/1372
missing: -we need to have some visual clue in the footer - a tool that lists all âaccepted for testâ method with the possibility to âcommitâ all at once.
We will have it almost for free with Calypso as soon as this new metalink will be managed similar way as breakpoints. "Commit" is of course needs to be implemented.
I really wonder if this is the same as breakpoint. E.g. it only applies to the whole method, it is not an âactionâ, so a guter icon seems to be not the right visualization.
To me the fact that it uses a meta-link is purely an implementation artefact, other than for breakpoints, watchpoints, where we annotate the AST with actionsâ¦
I only speak about management implementation. The way how registries of breakpoints and watchpoints are maintained and used by tools. They all needs some kind of reification of meta information to allow users distinguish between them. Even if we will avoid all these caches/registries, we will still need mechanizm to detect that this given metalink is breakpoint and not watchpoint. So I just want to have reusable mechanizm to manage all these kinds of meta information which is driven by metalinks. To me "MethodCodeForTest" is just another kind of them. Tools will need to detect them in methods and to distinguish them from breakpoints and others.
Marcus
To me the fact that it uses a meta-link is purely an implementation artefact, other than for breakpoints, watchpoints, where we annotate the AST with actionsâ¦
I only speak about management implementation. The way how registries of breakpoints and watchpoints are maintained and used by tools. They all needs some kind of reification of meta information to allow users distinguish between them. Even if we will avoid all these caches/registries, we will still need mechanizm to detect that this given metalink is breakpoint and not watchpoint.
So I just want to have reusable mechanizm to manage all these kinds of meta information which is driven by metalinks. To me "MethodCodeForTest" is just another kind of them. Tools will need to detect them in methods and to distinguish them from breakpoints and others.
I will first do the infrastructure for the breakpoints/watchpoints, then we see if it can be generalised. Marcus
You guys are awesome! Sent from my iPhone
On 17 May 2018, at 11:01, Marcus Denker <marcus.denker@inria.fr> wrote:
To me the fact that it uses a meta-link is purely an implementation artefact, other than for breakpoints, watchpoints, where we annotate the AST with actionsâ¦
I only speak about management implementation. The way how registries of breakpoints and watchpoints are maintained and used by tools. They all needs some kind of reification of meta information to allow users distinguish between them. Even if we will avoid all these caches/registries, we will still need mechanizm to detect that this given metalink is breakpoint and not watchpoint.
So I just want to have reusable mechanizm to manage all these kinds of meta information which is driven by metalinks. To me "MethodCodeForTest" is just another kind of them. Tools will need to detect them in methods and to distinguish them from breakpoints and others.
I will first do the infrastructure for the breakpoints/watchpoints, then we see if it can be generalised.
Marcus
2018-05-17 10:53 GMT+03:00 Marcus Denker <marcus.denker@inria.fr>:
On 16 May 2018, at 18:21, Denis Kudriashov <dionisiydk@gmail.com> wrote:
Hi Marcus. I commented on github.
2018-05-16 18:34 GMT+03:00 Marcus Denker <marcus.denker@inria.fr>:
On 4 Apr 2018, at 18:02, Marcus Denker <marcus.denker@inria.fr> wrote:
Hi,
Some code is very âactiveâ, executed all the time, e.g. collections, graphics⦠or imagine if you work on the compiler or debugger.
It would really be nice if we could test a change before accepting it. Something like âAccept for Testâ where magically the original method does not change, yet, when running tests, the version of the code we accepted for testing is executed.
here is a version done using a Calypso command:
https://github.com/pharo-project/pharo/pull/1372
missing: -we need to have some visual clue in the footer - a tool that lists all âaccepted for testâ method with the possibility to âcommitâ all at once.
We will have it almost for free with Calypso as soon as this new metalink will be managed similar way as breakpoints. "Commit" is of course needs to be implemented.
I really wonder if this is the same as breakpoint. E.g. it only applies to the whole method, it is not an âactionâ, so a guter icon seems to be not the right visualization.
Hm, I would like to see indication in method table that some methods have "uncommited" changes. But it is not necessary should be icon. It can be star prefix in name like in dirty tabs.
To me the fact that it uses a meta-link is purely an implementation artefact, other than for breakpoints, watchpoints, where we annotate the AST with actionsâ¦
Marcus
On 17 May 2018, at 11:46, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2018-05-17 10:53 GMT+03:00 Marcus Denker <marcus.denker@inria.fr <mailto:marcus.denker@inria.fr>>: On 16 May 2018, at 18:21, Denis Kudriashov <dionisiydk@gmail.com <mailto:dionisiydk@gmail.com>> wrote:
Hi Marcus. I commented on github.
2018-05-16 18:34 GMT+03:00 Marcus Denker <marcus.denker@inria.fr <mailto:marcus.denker@inria.fr>>:
On 4 Apr 2018, at 18:02, Marcus Denker <marcus.denker@inria.fr <mailto:marcus.denker@inria.fr>> wrote:
Hi,
Some code is very âactiveâ, executed all the time, e.g. collections, graphics⦠or imagine if you work on the compiler or debugger.
It would really be nice if we could test a change before accepting it. Something like âAccept for Testâ where magically the original method does not change, yet, when running tests, the version of the code we accepted for testing is executed.
here is a version done using a Calypso command:
https://github.com/pharo-project/pharo/pull/1372 <https://github.com/pharo-project/pharo/pull/1372>
missing: -we need to have some visual clue in the footer - a tool that lists all âaccepted for testâ method with the possibility to âcommitâ all at once.
We will have it almost for free with Calypso as soon as this new metalink will be managed similar way as breakpoints. "Commit" is of course needs to be implemented.
I really wonder if this is the same as breakpoint. E.g. it only applies to the whole method, it is not an âactionâ, so a guter icon seems to be not the right visualization.
Hm, I would like to see indication in method table that some methods have "uncommited" changes. But it is not necessary should be icon. It can be star prefix in name like in dirty tabs.
Yes, an indication is important (with some UI to just forget the non-accepted code). Could be a prefix in the tab⦠or/and something in the footer? In the footer I guess it would be easy to add a menu with some options (browse all un-accepted, accept, undo) Marcus
2018-05-17 13:00 GMT+03:00 Marcus Denker <marcus.denker@inria.fr>:
I really wonder if this is the same as breakpoint. E.g. it only applies to
the whole method, it is not an âactionâ, so a guter icon seems to be not the right visualization.
Hm, I would like to see indication in method table that some methods have "uncommited" changes. But it is not necessary should be icon. It can be star prefix in name like in dirty tabs.
Yes, an indication is important (with some UI to just forget the non-accepted code).
Could be a prefix in the tab⦠or/and something in the footer? In the footer I guess it would be easy to add a menu with some options (browse all un-accepted, accept, undo)
Yes. And such method can open editor always in dirty state. Many options. Another issue is how to work with breakpoints and others. What the target method for them? original or attached one?
Marcus
On 17 May 2018, at 12:59, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2018-05-17 13:00 GMT+03:00 Marcus Denker <marcus.denker@inria.fr <mailto:marcus.denker@inria.fr>>:
I really wonder if this is the same as breakpoint. E.g. it only applies to the whole method, it is not an âactionâ, so a guter icon seems to be not the right visualization.
Hm, I would like to see indication in method table that some methods have "uncommited" changes. But it is not necessary should be icon. It can be star prefix in name like in dirty tabs.
Yes, an indication is important (with some UI to just forget the non-accepted code).
Could be a prefix in the tab⦠or/and something in the footer? In the footer I guess it would be easy to add a menu with some options (browse all un-accepted, accept, undo)
Yes. And such method can open editor always in dirty state. Many options.
Another issue is how to work with breakpoints and others. What the target method for them? original or attached one?
oh⦠good question. As we recompile, I guess that old breakpoints should not be active, but one should be able to define new ones⦠(but how to visualise that?). If one thinks this through, what we really want is maybe a full-blown âLayerâ system⦠But: this is a lot of work. With even more open Questions :-) Marcus
participants (7)
-
Denis Kudriashov -
Holger Freyther -
Marcus Denker -
Nicolas Cellier -
Stephane Ducasse -
Sven Van Caekenberghe -
Tim Mackinnon