TestCase question

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

TestCase question

wernerk
hi,
i have a testsuite that is a subclass of TestCase with this method:

testStupid
|a|
a :=#(1).
self assert: a first isNumber .
a :=a at:1 put: nil.

if i run the test once, it succeeds, but but when i run it a second time
it fails. this seems to be sligthly counterintuitive, or how do i have
to understand it?

werner

Reply | Threaded
Open this post in threaded view
|

Re: TestCase question

Sven Van Caekenberghe
Hi Werner,

On 12 Apr 2011, at 23:47, Werner Kassens wrote:

> hi,
> i have a testsuite that is a subclass of TestCase with this method:
>
> testStupid
> |a|
> a :=#(1).
> self assert: a first isNumber .
> a :=a at:1 put: nil.
>
> if i run the test once, it succeeds, but but when i run it a second time it fails. this seems to be sligthly counterintuitive, or how do i have to understand it?
>
> werner

You are destructively modifying a constant, the literal array #(1), of which there is one instance attached to #testStupid'd compiled method.

Sven


Reply | Threaded
Open this post in threaded view
|

Re: TestCase question

simondenier
In reply to this post by wernerk

On 12 avr. 2011, at 23:47, Werner Kassens wrote:

> hi,
> i have a testsuite that is a subclass of TestCase with this method:
>
> testStupid
> |a|
> a :=#(1).
> self assert: a first isNumber .
> a :=a at:1 put: nil.
>
> if i run the test once, it succeeds, but but when i run it a second time it fails. this seems to be sligthly counterintuitive, or how do i have to understand it?


Interesting, I would never have guessed the test would be broken at first sight. So maybe some people enlightened in the ways of the compiler can provide a better explanation than me. Here is my guess:

#(1) is a special construct which creates a compile-time array. The compile-time makes the trick: in short the array is more or less encoded directly into the bytecodes of the method as a constant.
But modifying the array with #at:put: directly modifies the bytecodes in the compiled method itself. So you create a side-effect directly into your method.

Am I the only one to find this a bit dangerous and not intuitive? Is it a well-known behavior?

A correct way to fix that would be to use a dynamic array {1} or define a setUp method which would initialize the array before each run.

--
Simon Denier




Reply | Threaded
Open this post in threaded view
|

Re: TestCase question

wernerk
ah, got it!
thanks
werner

Reply | Threaded
Open this post in threaded view
|

Re: TestCase question

patmaddox
In reply to this post by simondenier
On Apr 12, 2011, at 3:24 PM, Simon Denier wrote:

>
> On 12 avr. 2011, at 23:47, Werner Kassens wrote:
>
>> hi,
>> i have a testsuite that is a subclass of TestCase with this method:
>>
>> testStupid
>> |a|
>> a :=#(1).
>> self assert: a first isNumber .
>> a :=a at:1 put: nil.
>>
>> if i run the test once, it succeeds, but but when i run it a second time it fails. this seems to be sligthly counterintuitive, or how do i have to understand it?
>
>
> Interesting, I would never have guessed the test would be broken at first sight. So maybe some people enlightened in the ways of the compiler can provide a better explanation than me. Here is my guess:
>
> #(1) is a special construct which creates a compile-time array. The compile-time makes the trick: in short the array is more or less encoded directly into the bytecodes of the method as a constant.
> But modifying the array with #at:put: directly modifies the bytecodes in the compiled method itself. So you create a side-effect directly into your method.
>
> Am I the only one to find this a bit dangerous and not intuitive? Is it a well-known behavior?

I would never have guessed that in a million years.

Pat

Reply | Threaded
Open this post in threaded view
|

Re: TestCase question

Stéphane Ducasse
>>>
>>
>>
>> Interesting, I would never have guessed the test would be broken at first sight. So maybe some people enlightened in the ways of the compiler can provide a better explanation than me. Here is my guess:
>>
>> #(1) is a special construct which creates a compile-time array. The compile-time makes the trick: in short the array is more or less encoded directly into the bytecodes of the method as a constant.
>> But modifying the array with #at:put: directly modifies the bytecodes in the compiled method itself. So you create a side-effect directly into your method.
>>
>> Am I the only one to find this a bit dangerous and not intuitive? Is it a well-known behavior?
>
> I would never have guessed that in a million years.
welcome to the time traveling machine

it is a bit ugly and nobody should rely on such bad behavior.
Consider that it should not exist.

Stef
Reply | Threaded
Open this post in threaded view
|

Re: TestCase question

Stéphane Ducasse
In reply to this post by simondenier
>
>> hi,
>> i have a testsuite that is a subclass of TestCase with this method:
>>
>> testStupid
>> |a|
>> a :=#(1).
>> self assert: a first isNumber .
>> a :=a at:1 put: nil.
>>
>> if i run the test once, it succeeds, but but when i run it a second time it fails. this seems to be sligthly counterintuitive, or how do i have to understand it?
>
>
> Interesting, I would never have guessed the test would be broken at first sight. So maybe some people enlightened in the ways of the compiler can provide a better explanation than me. Here is my guess:
>
> #(1) is a special construct which creates a compile-time array. The compile-time makes the trick: in short the array is more or less encoded directly into the bytecodes of the method as a constant.
> But modifying the array with #at:put: directly modifies the bytecodes in the compiled method itself. So you create a side-effect directly into your method.
>
> Am I the only one to find this a bit dangerous and not intuitive? Is it a well-known behavior?

It is clearly ugly.
Here what you see is a modification of the literal object that is stored in the compiled method literal
frame. This is a well know limit of Smalltalk implementation. Having immutable literal objects may solve this problem.

You get the same if you modify a string since this is a container of characters.
This is like the optimization of similar literal strings that breaks the fact that
        'foo' == 'foo' return false.
normally yes this is false but when on the same method you get the same literal.



Stef
Reply | Threaded
Open this post in threaded view
|

Re: TestCase question

Marcus Denker-4
In reply to this post by simondenier

On Apr 13, 2011, at 5:31 AM, Pat Maddox wrote:

> On Apr 12, 2011, at 3:24 PM, Simon Denier wrote:
>
>>
>> On 12 avr. 2011, at 23:47, Werner Kassens wrote:
>>
>>> hi,
>>> i have a testsuite that is a subclass of TestCase with this method:
>>>
>>> testStupid
>>> |a|
>>> a :=#(1).
>>> self assert: a first isNumber .
>>> a :=a at:1 put: nil.
>>>
>>> if i run the test once, it succeeds, but but when i run it a second time it fails. this seems to be sligthly counterintuitive, or how do i have to understand it?
>>
>>
>> Interesting, I would never have guessed the test would be broken at first sight. So maybe some people enlightened in the ways of the compiler can provide a better explanation than me. Here is my guess:
>>
>> #(1) is a special construct which creates a compile-time array. The compile-time makes the trick: in short the array is more or less encoded directly into the bytecodes of the method as a constant.
>> But modifying the array with #at:put: directly modifies the bytecodes in the compiled method itself. So you create a side-effect directly into your method.
>>
>> Am I the only one to find this a bit dangerous and not intuitive? Is it a well-known behavior?
>
> I would never have guessed that in a million years.
>


I like this method:

mystery
        'mystery' isString ifTrue: ['mystery'   become: {0}].
        'mystery'  at: 1 put: 'mystery'  first + 1.
        ^ 'mystery' first.

--
Marcus Denker  -- http://www.marcusdenker.de
INRIA Lille -- Nord Europe. Team RMoD.


Reply | Threaded
Open this post in threaded view
|

Re: TestCase question

Sven Van Caekenberghe

On 13 Apr 2011, at 10:00, Marcus Denker wrote:

> I like this method:
>
> mystery
> 'mystery' isString ifTrue: ['mystery'   become: {0}].
> 'mystery'  at: 1 put: 'mystery'  first + 1.
> ^ 'mystery' first.

And you were trying to help newcomers or scare them off ? ;-)

This is actually a problem in many languages.
It is always better to not touch literals (constants is a dangerour word here).