[Pharo-project] About testing that ensure works
Hi guys in RPackage there is a method named withOrganizer:do: defined as follows and I would like to test it to understand really if it does what it says. withOrganizer: aNewOrganizer do: aBlock "Perform an action locally to aNewOrganizer. Does not impact any other organizers." | old shouldRegisterAtTheEnd| [ old := self organizer. shouldRegisterAtTheEnd := false. old hasRegistered ifTrue: [ old unregister. shouldRegisterAtTheEnd := true. ]. self organizer: aNewOrganizer. aBlock cull: aNewOrganizer.] ensure: [ self organizer: old. shouldRegisterAtTheEnd ifTrue: [ self organizer register. ]. aNewOrganizer unregister. ] So I wrote two tests testWithDoIsCorrectlyReinstallingDefault "self debug: #testWithDoIsCorrectlyReinstallingDefault" | current empty | current := RPackageOrganizer default. empty := RPackageOrganizer basicNew initialize. RPackage withOrganizer: empty do: [ self assert: (SystemAnnouncer announcer hasSubscriber: empty). self deny: (SystemAnnouncer announcer hasSubscriber: current)]. self assert: (SystemAnnouncer announcer hasSubscriber: current). self deny: (SystemAnnouncer announcer hasSubscriber: empty) and testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt "self debug: #testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt" | current empty | current := RPackageOrganizer default. empty := RPackageOrganizer basicNew initialize. RPackage withOrganizer: empty do: [ self error. self assert: (SystemAnnouncer announcer hasSubscriber: empty). self deny: (SystemAnnouncer announcer hasSubscriber: current)]. self assert: (SystemAnnouncer announcer hasSubscriber: current). self deny: (SystemAnnouncer announcer hasSubscriber: empty) Now I would like to make sure that I raise an error or whaever to make sure that the ensure: block argument is executed. Does anybody have an idea how I can do that. because self error does not work. Stef
First question what i wanna ask is: - why do you need to play with two different instances of organizer, and especially in such manner (temporary swapping forth and back)? This code doesn't looks to be good. On 24 April 2011 00:44, Stéphane Ducasse <stephane.ducasse@inria.fr> wrote:
Hi guys
in RPackage there is a method named withOrganizer:do: Â defined as follows and I would like to test it to understand really if it does what it says.
withOrganizer: aNewOrganizer do: aBlock     "Perform an action locally to aNewOrganizer. Does not impact any other organizers."
    | old shouldRegisterAtTheEnd|     [     old := self organizer.     shouldRegisterAtTheEnd := false.     old hasRegistered ifTrue: [         old unregister.         shouldRegisterAtTheEnd := true.         ].     self organizer: aNewOrganizer.     aBlock cull: aNewOrganizer.] ensure: [         self organizer: old.         shouldRegisterAtTheEnd ifTrue: [             self organizer register.             ].         aNewOrganizer unregister.         ]
So I wrote two tests
testWithDoIsCorrectlyReinstallingDefault     "self debug: #testWithDoIsCorrectlyReinstallingDefault"
    | current empty |     current := RPackageOrganizer default.     empty := RPackageOrganizer basicNew initialize.     RPackage withOrganizer: empty         do: [ self assert: (SystemAnnouncer announcer hasSubscriber: empty).             self deny: (SystemAnnouncer announcer hasSubscriber: current)].     self assert: (SystemAnnouncer announcer hasSubscriber: current).     self deny: (SystemAnnouncer announcer hasSubscriber: empty)
and
testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt     "self debug: #testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt"
    | current empty |     current := RPackageOrganizer default.     empty := RPackageOrganizer basicNew initialize.     RPackage withOrganizer: empty         do: [ self error.             self assert: (SystemAnnouncer announcer hasSubscriber: empty).             self deny: (SystemAnnouncer announcer hasSubscriber: current)].     self assert: (SystemAnnouncer announcer hasSubscriber: current).     self deny: (SystemAnnouncer announcer hasSubscriber: empty)
Now I would like to make sure that I raise an error or whaever to make sure that the ensure: block argument is executed. Does anybody have an idea how I can do that. because self error does not work.
Stef
-- Best regards, Igor Stasenko AKA sig.
First question what i wanna ask is: - why do you need to play with two different instances of organizer, and especially in such manner (temporary swapping forth and back)?
This code doesn't looks to be good.
Igor can you reply to my question instead of questioning because it just slows me down. Now the answer: when we run the tests we do not run them in the default organizer because if something wrong happens then this is simply harakiri. So all the tests are performed in a separated created on the fly organizer. And I think that this is good to do it like that else I have to have a global variable and the rest. This design is the result of changes doru pushed me to do and they are good. Now this good is bad and not written my me and I'm trying to clean it and this is why I need to write tests because I want the Package code to be bullet proof. Stef
On 24 April 2011 00:44, Stéphane Ducasse <stephane.ducasse@inria.fr> wrote:
Hi guys
in RPackage there is a method named withOrganizer:do: defined as follows and I would like to test it to understand really if it does what it says.
withOrganizer: aNewOrganizer do: aBlock "Perform an action locally to aNewOrganizer. Does not impact any other organizers."
| old shouldRegisterAtTheEnd| [ old := self organizer. shouldRegisterAtTheEnd := false. old hasRegistered ifTrue: [ old unregister. shouldRegisterAtTheEnd := true. ]. self organizer: aNewOrganizer. aBlock cull: aNewOrganizer.] ensure: [ self organizer: old. shouldRegisterAtTheEnd ifTrue: [ self organizer register. ]. aNewOrganizer unregister. ]
So I wrote two tests
testWithDoIsCorrectlyReinstallingDefault "self debug: #testWithDoIsCorrectlyReinstallingDefault"
| current empty | current := RPackageOrganizer default. empty := RPackageOrganizer basicNew initialize. RPackage withOrganizer: empty do: [ self assert: (SystemAnnouncer announcer hasSubscriber: empty). self deny: (SystemAnnouncer announcer hasSubscriber: current)]. self assert: (SystemAnnouncer announcer hasSubscriber: current). self deny: (SystemAnnouncer announcer hasSubscriber: empty)
and
testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt "self debug: #testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt"
| current empty | current := RPackageOrganizer default. empty := RPackageOrganizer basicNew initialize. RPackage withOrganizer: empty do: [ self error. self assert: (SystemAnnouncer announcer hasSubscriber: empty). self deny: (SystemAnnouncer announcer hasSubscriber: current)]. self assert: (SystemAnnouncer announcer hasSubscriber: current). self deny: (SystemAnnouncer announcer hasSubscriber: empty)
Now I would like to make sure that I raise an error or whaever to make sure that the ensure: block argument is executed. Does anybody have an idea how I can do that. because self error does not work.
Stef
-- Best regards, Igor Stasenko AKA sig.
On 24 April 2011 11:59, Stéphane Ducasse <stephane.ducasse@inria.fr> wrote:
First question what i wanna ask is: - why do you need to play with two different instances of organizer, and especially in such manner (temporary swapping forth and back)?
This code doesn't looks to be good.
Igor can you reply to my question instead of questioning because it just slows me down.
Now the answer:     when we run  the tests we do not run them in the default organizer because if something wrong happens     then this is simply harakiri.     So all the tests are performed in a separated created on the fly organizer.     And I think that this is good to do it like that else I have to have a global variable and the rest.     This design is the result of changes doru pushed me to do and they are good.     Now this good is bad and not written my me and I'm trying to clean it and this is why I need to     write tests because I want the Package code to be bullet proof.
i am puzzled. You wanna to be sure that ensure block are executed? It is. However if error was triggered during ensure block evaluation, it may not complete everything you put there. And for test you can use non-local return to trigger ensure block: testWithDoIsCorrectlyReinstallingDefault "self debug: #testWithDoIsCorrectlyReinstallingDefault" | current empty | current := RPackageOrganizer default. empty := RPackageOrganizer basicNew initialize. self doWith:current and:empty. self assert: (SystemAnnouncer announcer hasSubscriber: current). self deny: (SystemAnnouncer announcer hasSubscriber: empty) doWith:current and:empty RPackage withOrganizer: empty do: [ ^ self ]. Or with error: testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt "self debug: #testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt" | current empty | current := RPackageOrganizer default. empty := RPackageOrganizer basicNew initialize. [ RPackage withOrganizer: empty do: [ self error ]. ] on: Error do: [:ex | ]. self assert: (SystemAnnouncer announcer hasSubscriber: current). self deny: (SystemAnnouncer announcer hasSubscriber: empty)
Stef
On 24 April 2011 00:44, Stéphane Ducasse <stephane.ducasse@inria.fr> wrote:
Hi guys
in RPackage there is a method named withOrganizer:do: Â defined as follows and I would like to test it to understand really if it does what it says.
withOrganizer: aNewOrganizer do: aBlock     "Perform an action locally to aNewOrganizer. Does not impact any other organizers."
    | old shouldRegisterAtTheEnd|     [     old := self organizer.     shouldRegisterAtTheEnd := false.     old hasRegistered ifTrue: [         old unregister.         shouldRegisterAtTheEnd := true.         ].     self organizer: aNewOrganizer.     aBlock cull: aNewOrganizer.] ensure: [         self organizer: old.         shouldRegisterAtTheEnd ifTrue: [             self organizer register.             ].         aNewOrganizer unregister.         ]
So I wrote two tests
testWithDoIsCorrectlyReinstallingDefault     "self debug: #testWithDoIsCorrectlyReinstallingDefault"
    | current empty |     current := RPackageOrganizer default.     empty := RPackageOrganizer basicNew initialize.     RPackage withOrganizer: empty         do: [ self assert: (SystemAnnouncer announcer hasSubscriber: empty).             self deny: (SystemAnnouncer announcer hasSubscriber: current)].     self assert: (SystemAnnouncer announcer hasSubscriber: current).     self deny: (SystemAnnouncer announcer hasSubscriber: empty)
and
testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt     "self debug: #testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt"
    | current empty |     current := RPackageOrganizer default.     empty := RPackageOrganizer basicNew initialize.     RPackage withOrganizer: empty         do: [ self error.             self assert: (SystemAnnouncer announcer hasSubscriber: empty).             self deny: (SystemAnnouncer announcer hasSubscriber: current)].     self assert: (SystemAnnouncer announcer hasSubscriber: current).     self deny: (SystemAnnouncer announcer hasSubscriber: empty)
Now I would like to make sure that I raise an error or whaever to make sure that the ensure: block argument is executed. Does anybody have an idea how I can do that. because self error does not work.
Stef
-- Best regards, Igor Stasenko AKA sig.
-- Best regards, Igor Stasenko AKA sig.
i am puzzled. You wanna to be sure that ensure block are executed? It is.
I want to write a test that shows and tests the withOrganizer:do: semantics. In particular that the ensure is executed and that the context set by the withOrganizer: is only present in the do: block.
However if error was triggered during ensure block evaluation, it may not complete everything you put there.
Sure this is normal.
And for test you can use non-local return to trigger ensure block:
| current empty | current := RPackageOrganizer default. empty := RPackageOrganizer basicNew initialize.
[ RPackage withOrganizer: empty do: [ self error ]. ] on: Error do: [:ex | ].
self assert: (SystemAnnouncer announcer hasSubscriber: current). self deny: (SystemAnnouncer announcer hasSubscriber: empty)
Yes this is what I was thinking to do. because a self error alone was not. Stef
On 24.04.2011 12:05, Stéphane Ducasse wrote:
i am puzzled. You wanna to be sure that ensure block are executed? It is. I want to write a test that shows and tests the withOrganizer:do: semantics. In particular that the ensure is executed and that the context set by the withOrganizer: is only present in the do: block.
In other words, another use case probably written more cleanly using the non-existing Announcer >> #suspend: subscriber while: aBlock ;) Cheers, Henry
good suggestion. I will check that. Stef On Apr 26, 2011, at 3:11 PM, Henrik Sperre Johansen wrote:
On 24.04.2011 12:05, Stéphane Ducasse wrote:
i am puzzled. You wanna to be sure that ensure block are executed? It is. I want to write a test that shows and tests the withOrganizer:do: semantics. In particular that the ensure is executed and that the context set by the withOrganizer: is only present in the do: block.
In other words, another use case probably written more cleanly using the non-existing Announcer >> #suspend: subscriber while: aBlock
;)
Cheers, Henry
On 26 April 2011 15:11, Henrik Sperre Johansen <henrik.s.johansen@veloxit.no> wrote:
On 24.04.2011 12:05, Stéphane Ducasse wrote:
i am puzzled. You wanna to be sure that ensure block are executed? It is.
I want to write a test that shows and tests the withOrganizer:do: semantics. In particular that the ensure is executed and that the context set by the withOrganizer: is only present in the do: block.
In other words, another use case probably written more cleanly using the non-existing Announcer >> #suspend: subscriber while: aBlock
What is potential implementation? Announcer >>suspend: subscriber while: aBlock | subscriptions | subscriptions := OrderedCollection new. registry subscriptionsOf: subscriber do: [:each | subscriptions add: each ]. registry removeSubscriber: subscriber. ^ aBlock ensure: [ subscriptions do: [:ea | registry add: ea ] ] The problem is that its not doing exactly what is said: - it suspends existing subscriptions instead of suspending subscriber. The difference between those are subtle but important: a suspended subscriber should not receive any announcements during block evaluation, even if it may want to subscribe to new ones during given block evaluation. And the code above not handling this case , as well as it doesn't handling the case when subscriber may want to occasionally unsubscribe from some announcements during block evaluation. So, for doing this correctly, a subscription registry should keep a list of suspended subscribers somewhere, and then in #deliver: it should check if subscription's subscriber are not in that list, and only then let it deliver an announcement. The problem is that delivery time will slowdown considerably, because for every subscription you should check if it's subscriber are not in 'suspended subscribers' list. So, it is really depends on intent. Suspending a concrete subscription will require adding 'suspended' flag to subscription state. But suspending a concrete subscriber and to guarantee that it won't receive any announcements (even if it will subscribe for a new ones during suspension time), can't be solved by having 'suspended' ivar on subscription. You have to manage suspended subscribers list separately. -- Best regards, Igor Stasenko AKA sig.
On 26.04.2011 17:37, Igor Stasenko wrote:
On 26 April 2011 15:11, Henrik Sperre Johansen <henrik.s.johansen@veloxit.no> wrote:
On 24.04.2011 12:05, Stéphane Ducasse wrote:
i am puzzled. You wanna to be sure that ensure block are executed? It is. I want to write a test that shows and tests the withOrganizer:do: semantics. In particular that the ensure is executed and that the context set by the withOrganizer: is only present in the do: block.
In other words, another use case probably written more cleanly using the non-existing Announcer>> #suspend: subscriber while: aBlock
What is potential implementation?
Announcer>>suspend: subscriber while: aBlock | subscriptions |
subscriptions := OrderedCollection new.
registry subscriptionsOf: subscriber do: [:each | subscriptions add: each ]. registry removeSubscriber: subscriber.
^ aBlock ensure: [ subscriptions do: [:ea | registry add: ea ] ]
The problem is that its not doing exactly what is said: - it suspends existing subscriptions instead of suspending subscriber.
The difference between those are subtle but important: a suspended subscriber should not receive any announcements during block evaluation, even if it may want to subscribe to new ones during given block evaluation. And the code above not handling this case , as well as it doesn't handling the case when subscriber may want to occasionally unsubscribe from some announcements during block evaluation.
So, for doing this correctly, a subscription registry should keep a list of suspended subscribers somewhere, and then in #deliver: it should check if subscription's subscriber are not in that list, and only then let it deliver an announcement.
The problem is that delivery time will slowdown considerably, because for every subscription you should check if it's subscriber are not in 'suspended subscribers' list.
So, it is really depends on intent. Suspending a concrete subscription will require adding 'suspended' flag to subscription state. But suspending a concrete subscriber and to guarantee that it won't receive any announcements (even if it will subscribe for a new ones during suspension time), can't be solved by having 'suspended' ivar on subscription. You have to manage suspended subscribers list separately.
That'a a rather farfetched usecase it'd be nice to note in a method comment is NOT supported at the moment, nothing more imho :) Simply providing something like: suspend(SubscriptionsOf): subscriber while: aBlock |oldSubs| oldSubs := subscriptionRegistry removeSubscriber: subscriber. aBlock ensure: [oldSubs do: [ :each | subscriptionRegistry add: each]] is better than nothing. Cheers, Henry
I see. Subtle. May be a solution not slowing everything but more limited is the way to go.
i am puzzled. You wanna to be sure that ensure block are executed? It is.
I want to write a test that shows and tests the withOrganizer:do: semantics. In particular that the ensure is executed and that the context set by the withOrganizer: is only present in the do: block.
In other words, another use case probably written more cleanly using the non-existing Announcer >> #suspend: subscriber while: aBlock
What is potential implementation?
Announcer >>suspend: subscriber while: aBlock | subscriptions |
subscriptions := OrderedCollection new.
registry subscriptionsOf: subscriber do: [:each | subscriptions add: each ]. registry removeSubscriber: subscriber.
^ aBlock ensure: [ subscriptions do: [:ea | registry add: ea ] ]
The problem is that its not doing exactly what is said: - it suspends existing subscriptions instead of suspending subscriber.
The difference between those are subtle but important: a suspended subscriber should not receive any announcements during block evaluation, even if it may want to subscribe to new ones during given block evaluation. And the code above not handling this case , as well as it doesn't handling the case when subscriber may want to occasionally unsubscribe from some announcements during block evaluation.
So, for doing this correctly, a subscription registry should keep a list of suspended subscribers somewhere, and then in #deliver: it should check if subscription's subscriber are not in that list, and only then let it deliver an announcement.
The problem is that delivery time will slowdown considerably, because for every subscription you should check if it's subscriber are not in 'suspended subscribers' list.
So, it is really depends on intent. Suspending a concrete subscription will require adding 'suspended' flag to subscription state. But suspending a concrete subscriber and to guarantee that it won't receive any announcements (even if it will subscribe for a new ones during suspension time), can't be solved by having 'suspended' ivar on subscription. You have to manage suspended subscribers list separately.
-- Best regards, Igor Stasenko AKA sig.
testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt "self debug: #testWithDoIsCorrectlyReinstallingDefaultEvenIfHalt"
| current empty | current := RPackageOrganizer default. empty := RPackageOrganizer basicNew initialize.
[ RPackage withOrganizer: empty do: [ self error ]. ] on: Error do: [:ex | ].
self assert: (SystemAnnouncer announcer hasSubscriber: current). self deny: (SystemAnnouncer announcer hasSubscriber: empty)
Igor but how can I specify that I want an error to occur in the do: and that I do not want to trap it but that the ensure should trap it. Is the ensure execute if I trap the error like that? Stef
here is a simpler case do: aBlock x := 5. [aBlock value] ensure: [x :=3] testDo "self run: #testDo" self do: [ self error]. self assert: x = 3 and I do not understand why ensure is not executed :( because I always want the ensure to happen not only on ifCurtailed: Stef
On 24 April 2011 13:26, Stéphane Ducasse <stephane.ducasse@inria.fr> wrote:
here is a simpler case
do: aBlock
    x := 5.     [aBlock value]  ensure: [x :=3]
testDo     "self run: #testDo"
    self do: [ self error].     self assert: x = 3
and I do not understand why ensure is not executed :( because I always want the ensure to happen not only on ifCurtailed:
error are not causing ensure block to trigger. only after you either abandon or proceed, an ensure block will be executed.
Stef
-- Best regards, Igor Stasenko AKA sig.
On Apr 24, 2011, at 12:43 PM, Igor Stasenko wrote:
On 24 April 2011 13:26, Stéphane Ducasse <stephane.ducasse@inria.fr> wrote:
here is a simpler case
do: aBlock
x := 5. [aBlock value] ensure: [x :=3]
testDo "self run: #testDo"
self do: [ self error]. self assert: x = 3
and I do not understand why ensure is not executed :( because I always want the ensure to happen not only on ifCurtailed:
error are not causing ensure block to trigger.
so what is causing them to trigger. I always thought ensure: was really giving that warranty. So this is really scary to me. I wrote that and this is green. testDo2 "self run: #testDo2 1 run, 1 passes, 0 expected failures, 0 failures, 0 errors, 0 unexpected passes" [self do: [ self error] ] on: Error do: [:ex | ]. self assert: x = 3. To me it means that I should not use ensure because it makes no sense to wrap the withOrganizer in a on: do: block outside. So I do not see the real use of ensure: Shit..... Stef
only after you either abandon or proceed, an ensure block will be executed.
Stef
-- Best regards, Igor Stasenko AKA sig.
I read what I wrote on the exception chapter and also the comments of the ensure: method and I do not get why
error are not causing ensure block to trigger. only after you either abandon or proceed, an ensure block will be executed.
to me it completely defeats the purpose of ensure: or ifCurtailed: Evaluate a termination block after evaluating the receiver, regardless of whether the receiver's evaluation completes. So when should ensure: be used? if I get an error because of a file problem will it be triggered? I do not see how this is working in that case Stef
How can I ensure that really in case of error or not a piece of code is executed. Now I'm totally confused by the semantics of ensure: So I wrote that: testOnDo "self run: #testOnDo " self doOnDo: [ self error]. self assert: x = 3. x := 77. self doOnDo: [x = 21]. self assert: x = 3 ensure x := 3 doOnDo: aBlock x := 5. [aBlock value. self ensure] on: Error do: [:ex | self ensure].
Ok I got it I ws confused by SUnit interference testDo "self run: #testDo" self do: [ Error signal]. self assert: x = 3 self run: #testDo -> red tests self debug: #testDo + proceed -> green tests
On 24 April 2011 15:13, Stéphane Ducasse <stephane.ducasse@inria.fr> wrote:
Ok I got it I ws confused by SUnit interference
:))
testDo     "self run: #testDo"
    self do: [ Error signal].     self assert: x = 3
self run: #testDo -> red tests
self debug: #testDo + proceed -> green tests
Yes, sunit interrupts execution at the moment when error was triggered, before unwinding the stack. -- Best regards, Igor Stasenko AKA sig.
On 24.04.2011 14:13, Stéphane Ducasse wrote:
Ok I got it I ws confused by SUnit interference
testDo "self run: #testDo"
self do: [ Error signal]. self assert: x = 3
self run: #testDo -> red tests
self debug: #testDo + proceed -> green tests
The ensure runs, but at that point the test already failed, see it says "1 errors", not "1 failures" :) To see it manually, you could put Transcript show: 'Foo' or some such in the ensure block instead, and notice how the Transcript is written :) Cheers, Henry ps. It's easier to just write aBlock ensure: [blabla] instead of [aBlock value] ensure: [blabla] ... if aBlock REALLY is a block, and not aValuable, of course :=
participants (3)
-
Henrik Sperre Johansen -
Igor Stasenko -
Stéphane Ducasse