Hi All, moving code from a test into a workspace and back is painful because Object does not implement assert:equals: or assert:equals:description: even though it implements assert:description:. So one has to edit out the equals: moving to the workspace and edit it back in again on the way back. I'd like to suggest implementing Object>>assert:equals: and Object>>assert:equals:description: but I won't, because someone is bound to criticise it as being too green or ill considered or some other depressing BS. _,,,^..^,,,_ best, Eliot |
Hi Eliot, sure, SUnit could put those extensions into Object. I wouldn't want to have those outside SUnit, though. Not sure why. I only consider #assert: a useful tool even outside test cases. Just to document an invariant in a method. Best, Marcel
|
Hi Eliot, Hi Marcel,
-1 from my side for this change. :-)
If we start adding #assert:equals: and #assert:equals:description: on Object, it will be only a question of time until we will also discuss #deny:, #should:raise:, and all the other assertion selectors an object. And because it's not every object's purpose to provide such testing mechanisms, this feels like a step in the wrong direction for me. Object is bloated enough already. Instead of defining new assertion selectors (which happens for the single purpose of improving exception messages as I suppose), we should rather inspect the stack of an AssertionFailure/TestFailure to generate precise error messages.
Also, I am against defining these methods as extension methods because I would bet that someone (for example myself) would misinterpret them and assume they are part of the usual Object protocol, and suddenly you have a bunch of new dependencies from production code to SUnit.
Instead, we might miss some proper tooling for your use case, Eliot. Why can't we exchange the class of a Workspace's receiver? Or thinking differently, why can't we support dynamic workspace bindings in every inspector (maybe opt-in)? Then you could just inspect TestCase new, maximize the evaluator pane in the inspector, and e voilá you have a TestCase workspace.
Here are some references that might be related to this issue in some way: http://forum.world.st/Modifying-a-method-s-bytecodes-is-it-supported-tp5114421p5114435.html (proposal for Workspace variables) http://forum.world.st/The-Inbox-SUnit-ct-127-mcz-td5114709.html (about the addition of new assertion selectors)
Best, Christoph
Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Freitag, 9. Oktober 2020 11:41:26 An: squeak-dev Betreff: Re: [squeak-dev] assert:equals: in Object
Hi Eliot,
sure, SUnit could put those extensions into Object. I wouldn't want to have those outside SUnit, though. Not sure why. I only consider #assert: a useful tool even outside test cases. Just to document an invariant in a method.
Best,
Marcel
Carpe Squeak!
|
Hi Christoph, still, it would be a shame if we would remove #assert:(description:) from Object, too. Then we couldn't document invariants in code anymore. So many trade-offs! :-O As you suggested, this could be better solved at the tool level. The "self" binding in workspaces has great potential. Bind it to an example test case, and this would work. :-) Best, Marcel
|
Hi Marcel,
> still, it would be a shame if we would remove #assert:(description:) from Object, too. Then we couldn't document invariants in code anymore. So many trade-offs! :-O Yes, I absolutely agree with that. Production assertions are a useful feature. But compared to SUnit, they are not meant to fail at any time, and if they fail, they should do so during development only, so I don't
think we have to optimize the meaningfulness of AssertionFailure messages (as opposed to TestFailures. See https://github.com/hpi-swa/smalltalkCI/issues/478 :-)).
> As you suggested, this could be better solved at the tool level. The "self" binding in workspaces has great potential. Bind it to an example test case, and this
would work. :-)
I do indeed think there is much potential for new ideas (and this would finally show me a reason why variable bindings can be useful as an orthogonal concept to instance variables). But how can we avoid redundancy between inspectors and workspaces at this
point? How do you think about supporting dynamic bindings in every inspector's evaluator pane via opt-in? Or maybe add an item into Inspector's window menu, "convert to Workspace"?
Best,
Christoph
Von: Squeak-dev <[hidden email]> im Auftrag von Taeumel, Marcel
Gesendet: Freitag, 9. Oktober 2020 14:40:43 An: squeak-dev Betreff: Re: [squeak-dev] assert:equals: in Object
Hi Christoph,
still, it would be a shame if we would remove #assert:(description:) from Object, too. Then we couldn't document invariants in code anymore. So many trade-offs! :-O
As you suggested, this could be better solved at the tool level. The "self" binding in workspaces has great potential. Bind it to an example test case, and this would work. :-)
Best,
Marcel
Carpe Squeak!
|
For starters, I would stick to the example Eliot just gave us. And not "dream away" too soon. ;-) Best, Marcel
|
In reply to this post by Christoph Thiede
On 10/9/20 2:34 PM, Thiede, Christoph wrote:
> Hi Eliot, Hi Marcel, -1 from my side for this change. :-) Me too. I think it's almost a case of Gun>>draw vs Shape>>draw -- assert: in Object is for invariant-specification. > Instead, we might miss some proper tooling for your use case, Eliot. > Why can't we exchange the class of a Workspace's receiver? I really like this line of thinking! I've been mulling over the idea of having an ActorWorkspace that exists within the context of a running Actor so it can react to events, "crash", etc. (It makes sense in context -- Erlangish (and Syndicateish) actors are deeper participants in their ecosystems than vanilla Actors are...) Anyway, having it by default be the case that Workspaces' code environments be TestCases seems very sensible to me. I also wanted to respond to this: > Production assertions are a useful feature. But compared to SUnit, > they are not meant to fail at any time, and if they fail, they > should do so during development only, I wanted to make sure we are all on the same page by suggesting a clarification here. I think, Christoph, that you *should* have meant :-) something like: "Assertions of invariants in production code [as opposed to just in testing code] are useful. But they MUST BE LEFT ACTIVE IN PRODUCTION CODE because otherwise it's like wearing a seatbelt only while you're on your own driveway, and taking it off as soon as you're on the street." Hopefully I'm right! :-) It just alarmed me a bit to see discussion of making the same mistake C makes, namely compiling away assertions in "release" builds, which is where you need them most... Cheers, Tony |
Hi Tony,
> Anyway, having it by default be the case that Workspaces' code environments be TestCases seems very sensible to me. What do you mean by this? Such a TestCase receiver environment should definitively be not the default, this would be very confusing!
> "Assertions of invariants in production code [as opposed to just in testing code] are useful. But they MUST BE LEFT ACTIVE IN PRODUCTION CODE
because otherwise it's like wearing a seatbelt only while you're on your own driveway, and taking it off as soon as you're on the street."
Absolutely! All I wanted to avoid is that we introduce an SUnit extension on Object and no one notices that it is an extension method and then will misuse #assert:equals: as an assertion in production code - which would break his or her package if it is
installed in another environment that misses the SUnit package. Are we on the same page now? :-)
Best,
Christoph
Von: Tony Garnock-Jones <[hidden email]>
Gesendet: Freitag, 9. Oktober 2020 16:33:12 An: The general-purpose Squeak developers list; Thiede, Christoph Betreff: Re: [squeak-dev] assert:equals: in Object On 10/9/20 2:34 PM, Thiede, Christoph wrote:
> Hi Eliot, Hi Marcel, -1 from my side for this change. :-) Me too. I think it's almost a case of Gun>>draw vs Shape>>draw -- assert: in Object is for invariant-specification. > Instead, we might miss some proper tooling for your use case, Eliot. > Why can't we exchange the class of a Workspace's receiver? I really like this line of thinking! I've been mulling over the idea of having an ActorWorkspace that exists within the context of a running Actor so it can react to events, "crash", etc. (It makes sense in context -- Erlangish (and Syndicateish) actors are deeper participants in their ecosystems than vanilla Actors are...) Anyway, having it by default be the case that Workspaces' code environments be TestCases seems very sensible to me. I also wanted to respond to this: > Production assertions are a useful feature. But compared to SUnit, > they are not meant to fail at any time, and if they fail, they > should do so during development only, I wanted to make sure we are all on the same page by suggesting a clarification here. I think, Christoph, that you *should* have meant :-) something like: "Assertions of invariants in production code [as opposed to just in testing code] are useful. But they MUST BE LEFT ACTIVE IN PRODUCTION CODE because otherwise it's like wearing a seatbelt only while you're on your own driveway, and taking it off as soon as you're on the street." Hopefully I'm right! :-) It just alarmed me a bit to see discussion of making the same mistake C makes, namely compiling away assertions in "release" builds, which is where you need them most... Cheers, Tony
Carpe Squeak!
|
Hi Christoph,
On 10/9/20 4:53 PM, Thiede, Christoph wrote: >> Anyway, having it by default be the case that Workspaces' > code environments be TestCases seems very sensible to me. > > What do you mean by this? Such a TestCase receiver environment should > definitively be not the default, this would be very confusing! Oh interesting! What kind of confusion do you foresee? I had just imagined that the only visible-ish change would be availability of TestCase instance-side methods on the "self" in the Workspace. >> "Assertions [...] MUST BE LEFT ACTIVE IN >> PRODUCTION CODE > > Are we on the same page now? :-) Yes, I think we are! :-) Thanks. Tony |
In reply to this post by Eliot Miranda-2
Hi Eliot,
On Fri, 9 Oct 2020, Eliot Miranda wrote: > Hi All, > > moving code from a test into a workspace and back is painful because Object does not implement assert:equals: or assert:equals:description: even though it implements assert:description:. So one has to edit out the > equals: moving to the workspace and edit it back in again on the way back. I'd like to suggest implementing Object>>assert:equals: and Object>>assert:equals:description: but I won't, because someone is bound to criticise it > as being too green or ill considered or some other depressing BS. In my opinion, it would be better to get rid of #assert:equals: and #assert:equals:description:. Many find the order of its arguments confusing, and they are only good to help tracking down what failed. If #assert: could give information about it's argument: the source code and probably the values of the variables in it, then #assert:equals: would simply become unnecessary. For example | x y | x := 'foo'. y := 1. self assert: x = y. would signal an exception with the message 'Assertion failed: x = y (x = ''foo'', y = 1)' instead of just 'Assertion failed'. Levente > _,,,^..^,,,_ > best, Eliot > > |
Hi Levente, On Fri, Oct 9, 2020 at 4:04 PM Levente Uzonyi <[hidden email]> wrote: Hi Eliot, I couldn't agree more. I *hate* assert:equals: being, for me, the wrong way round. We would need to provide an abstraction around extracting the parse tree for an argument, which shouldn't be too hard. Let me take a look at this and see if I can provide something simple and robust.
_,,,^..^,,,_ best, Eliot |
Hi Levente, On Mon, Oct 12, 2020 at 10:59 AM Eliot Miranda <[hidden email]> wrote:
Having thought about it a bit the problem is more difficult than it appears. If the form of an assert is self assert: exprA = exprB then in general only the result of exprA = exprB is available. exprA and exprB may have only been evaluated on the stack and not bound to variables. So no matter tha introspection makes it trivial to derive the parse node and present the text for the expression: 'exprA = exprB' there is no way to retrieve the values of either exprA or exprB because they have been consumed and replaced by the result of their comparison. This is what assert:equals: does; it binds both the actual and expected values to the arguments of assert:equals: and then performs the comparison. So, absent time reversal debugging machinery (which has costs and is unlikely to be attractive for general use in running test suites which are > 99% correct most of the time) we're stuck with assert:equals:. So things to think about are perhaps - a sibling of assert:equals: which takes its arguments in the opposite order - appealing to the SUnit community to reverse the order of arguments to assert:equals:, plus a rewrite rule that reversed the obvious cases where the first argument is a literal constant. - seeing if assert:equals: could infer the order of its arguments; this would require some analysis. For how many assert:equals: cases can it easily be determined if one or other of the arguments is the expected? How many cases are ambiguous? Are any undecidable? etc... - implementing "backwards in time" debugging for the Test Runner when one selects a failing or erroring case. This might be no more than instrumenting the method to place breakpoints before comparisons whose results are consumed by assert: variants and running up until the break. This could be somewhat analogous to toggle break on entry in that adding/removing the pre comparison breakpoints would be automatic. And one might be able to do it by mapping comparisons to comparisons for asserts. e.g. consider: self assert: a = b (along with >, <, >= et al) transformed into self assert: (a compareEqualForAssert: b) where compareEqualForAssert: answers a structured object (AssertResult?) holding onto a, b, and the result of the comparison. AssertResult would answer its result in response to value. So assert methods (that already send value to coerce blocks and booleans to booleans) could send value to the argument, and then if that was false, query to see if the argument was an AssertResult and xtract a more informative error message if so. All this is fun, and could function in the context of SUnit. It does nothing to answer my problem of running asserts in Workspaces. However, that was thoughtless on my part. All I have to remember to do is inspect an instance of a TestCase class and evaluate my doit in that inspector. So for the SocketTesxts if I had had my wits about me I would have simply inspected SocketTest new and then could use ll of SUnit's assert machinery in a doit in the inspector.
_,,,^..^,,,_ best, Eliot |
Eliot Miranda-2 wrote
> - appealing to the SUnit community to reverse the order of arguments to > assert:equals:, plus a rewrite rule that reversed the obvious cases where > the first argument is a literal constant. For what it is worth, Pharo has just swapped the parameters... which makes all "portable" test cases awkward. -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html |
Hi all, hi Eliot,
> > What do you mean by this? Such a TestCase receiver environment should definitively be not the default, this would be very confusing! >
> Oh interesting! What kind of confusion do you foresee? I had just imagined that the only visible-ish change would be availability of TestCase instance-side methods on the "self" in the
Workspace.
SUnit is just one of so many domains. Someone else might want to evaluate things in the context of Transcript (self showln: 'foo') or any other console host (name := self input: 'Your name?'. self output: 'Hello ' , name) etc. pp....
@AssertResults via reflection:
Highly interesting topic! I already had some thoughts about this last summer and I have an image containing some stubs for it. My basic idea was to pass only blocks to the #assert: method that can more easily be decompiled, reevaluated, and whatever you
wish. One then could identify the receiver "X" of the last message from the assertion block "X y: Z" and transform this message send into "X assert: #y: with: {Z}", where #assert:with: would be implemented on Object and could dispatch the assertion selector
to several extension methods. This would allow us, for example, to write
self assert: [myList noneSatisfy: [:ea | ea hasPlung]]
and TestCase >> #assert:, under the hood, would this transform into
self assert: [myList assert: #noneSatisfy: with: [:ea | ea even]]
and then, an extension method á la #assertNoneSatisfy: could be called from #assert:with: and instead of only signaling a generic AssertionFailure, we could see something like:
"No object in myList should satisfy [:ea | ea even], but 4 (at index 2) does satisfy."
As a plus, every package/framework developer could simply add support for customized AssertionFailures by overriding that #assert:with: and handling custom test selectors.
If we wanted to go one step further, we could even try to detect nested calls, e.g.:
self assert: [(myList anySatisfy: [:ea | ea hasPlung]]) and: [myList size even]]
"myList should satisfy myList size even but did not"
"myList should satisfy myList size even but size
is 5"
Ha, what a dream! And it shouldn't be terribly complicated, too ... For more inspiration, take a look at FluentAssertions for C#. :-)
(If anyone asks: I never realized this projects because I did not know what to do if one of these parameters has side effects, e.g. when a block is called. Any ideas?)
Best,
Christoph
Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Montag, 12. Oktober 2020 21:27:48 An: [hidden email] Betreff: Re: [squeak-dev] assert:equals: in Object Eliot Miranda-2 wrote
> - appealing to the SUnit community to reverse the order of arguments to > assert:equals:, plus a rewrite rule that reversed the obvious cases where > the first argument is a literal constant. For what it is worth, Pharo has just swapped the parameters... which makes all "portable" test cases awkward. -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html
Carpe Squeak!
|
In reply to this post by Jakob Reschke
On Mon, Oct 12, 2020 at 12:27 PM Jakob Reschke <[hidden email]> wrote: Eliot Miranda-2 wrote Then IMO we should follow suit. For once I agree with them ;-) -- _,,,^..^,,,_ best, Eliot |
> Then IMO we should follow suit. For once I agree with them ;-) Nooo ... :( I don't know their reasons, but inverting a long-existing convention from one day to another seems like a true nightmare to me. This would invalidate the semantics of every existing test case that was ever written for Squeak/Smalltalk.
Plus, IMHO it is often useful to order parameters by their complexity ascending (e.g., put blocks last) to provide a good writeability/readability of a message's senders. While the expected argument often is a constant, the actual argument mainly is the
result of a possibly complex operation, e.g.:
self assert: 42 equals: (myComputer
doComplexComputationWith: 6
and: 7
mode: #multiplyInt).
How would the opposite read like?
self assert: (myComputer
doComplexComputationWith: 6
and: 7
mode: #multiplyInt) equals: 42.
IMO, much less comprehensible.
Best,
Christoph
Von: Squeak-dev <[hidden email]> im Auftrag von Eliot Miranda <[hidden email]>
Gesendet: Dienstag, 13. Oktober 2020 04:02:48 An: The general-purpose Squeak developers list Betreff: Re: [squeak-dev] assert:equals: in Object On Mon, Oct 12, 2020 at 12:27 PM Jakob Reschke <[hidden email]> wrote:
Eliot Miranda-2 wrote Then IMO we should follow suit. For once I agree with them ;-)
-- _,,,^..^,,,_
best, Eliot
Carpe Squeak!
|
Hello
On 10/13/20, Thiede, Christoph <[hidden email]> wrote: >> Then IMO we should follow suit. For once I agree with them ;-) > > Nooo ... :( I don't know their reasons, but inverting a long-existing > convention from one day to another seems like a true nightmare to me. If I understand correctly Pharo has swapped assert:equals: in TestCase to equals:assert: in TestCase Is this so? If yes then the impact for changes is actually quite small for Squeak. It means that in addition to assert: expected equals: actual ^self assert: expected = actual description: [ self comparingStringBetween: expected and: actual ] a method equals: actual assert: expected ^assert: expected equals: actual is also needed in Squeak. > This would invalidate the semantics of every existing test case that was ever > written for Squeak/Smalltalk. Not the semantics which is the about the meaning. It is a minor syntax change (= change of structure/ form): the order of arguments in one or two methods. So one or two methods more in Squeak fixes this. > Plus, IMHO it is often useful to order parameters by their complexity > ascending (e.g., put blocks last) to provide a good writeability/readability > of a message's senders. While the expected argument often is a constant, the > actual argument mainly is the result of a possibly complex operation, e.g.: > > self assert: 42 equals: (myComputer > doComplexComputationWith: 6 > and: 7 > mode: #multiplyInt). > > How would the opposite read like? > > self assert: (myComputer > doComplexComputationWith: 6 > and: 7 > mode: #multiplyInt) equals: 42. > > IMO, much less comprehensible. I agree that the former self assert: 42 equals: (myComputer doComplexComputationWith: 6 and: 7 mode: #multiplyInt). is much more readable. This is what Squeak implements. Best wishes Hannes > Best, > Christoph > > ________________________________ > Von: Squeak-dev <[hidden email]> im Auftrag > von Eliot Miranda <[hidden email]> > Gesendet: Dienstag, 13. Oktober 2020 04:02:48 > An: The general-purpose Squeak developers list > Betreff: Re: [squeak-dev] assert:equals: in Object > > > > On Mon, Oct 12, 2020 at 12:27 PM Jakob Reschke > <[hidden email]<mailto:[hidden email]>> wrote: > Eliot Miranda-2 wrote >> - appealing to the SUnit community to reverse the order of arguments to >> assert:equals:, plus a rewrite rule that reversed the obvious cases where >> the first argument is a literal constant. > > For what it is worth, Pharo has just swapped the parameters... which makes > all "portable" test cases awkward. > > Then IMO we should follow suit. For once I agree with them ;-) > -- > Sent from: http://forum.world.st/Squeak-Dev-f45488.html > > > > -- > _,,,^..^,,,_ > best, Eliot > |
Hannes Hirzel wrote
> If I understand correctly Pharo has swapped > assert:equals: in TestCase > to > equals:assert: in TestCase > > Is this so? Unfortunately, no. From Pharo 6.1: TAssertable>>assert: actual equals: expected ^ self assert: expected = actual description: [self comparingStringBetween: actual and: expected] -- Sent from: http://forum.world.st/Squeak-Dev-f45488.html |
In reply to this post by Tony Garnock-Jones-5
On 10/9/20, Tony Garnock-Jones <[hidden email]> wrote:
> Hi Christoph, > > On 10/9/20 4:53 PM, Thiede, Christoph wrote: >>> Anyway, having it by default be the case that Workspaces' >> code environments be TestCases seems very sensible to me. >> >> What do you mean by this? Such a TestCase receiver environment should >> definitively be not the default, this would be very confusing! > > Oh interesting! What kind of confusion do you foresee? I had just > imagined that the only visible-ish change would be availability of > TestCase instance-side methods on the "self" in the Workspace. > >>> "Assertions [...] MUST BE LEFT ACTIVE IN >>> PRODUCTION CODE >> Are we on the same page now? :-) > > Yes, I think we are! :-) Thanks. > > Tony I consider that assertions are useful to have in the production code an an argument in favor of having assert:equals:description: implemented the class Object. A situation where SUnit and all the tests are not necessarily loaded. --Hannes |
> A situation where SUnit and all the tests are not necessarily loaded. Hmm... maybe we might consider adding an SUnit-Core? Well, not sure it makes sense to have an image without SUnit. But then you might want to also strip, e.g., network support to shrink the image file a little bit more. :-) Best, Marcel
|
Free forum by Nabble | Edit this page |