Language contest ==> unit test framework from lisp to ruby

A few days ago I made a post (rubytalk:92963 ==> [2]) about the
development of a mini-framework in ruby related to unit testing and
inspired by Peter Seibel [1]. Unfortunately, I have got no comments
about it.

Anyway, after some feedback on another mailing list from Gabriele Renzi,
I have come up with the following version:

CUT HERE
def stack
caller[3…-1].map {|m| m[/test_.*(?=’)/]}.compact.reverse.join " "
end
def check(*tests)
tests.map! {|t| puts “#{(v=eval(t)) ? ‘pass’ : ‘FAIL’} …
(#{stack}) #{t.strip}”; v}
combine_results(*tests)
end
def combine_results(*tests)
tests.inject {|t1, t2| t1 && t2}
end

def test_plus
check(
“1 + 2 == 3”,
“1 + 2 + 3 == 6”,
"-1 + -3 == -4")
end
def test_multiply
check(
“2 * 2 == 4”,
“3 * 5 == 15”)
end

def test_aritmetic
combine_results(
test_plus,
test_multiply)
end

def test_math
test_aritmetic
end

result = test_math
puts "result=#{result}"
CUT HERE

As you can see, my ruby version looks more compact and more easily
undestandable to my non-lisper eyes than Seibel’s version.

I have challenged a few friends to build the most compact version of the
framework. There’s a perl fan, a few ruby guys and a smalltalker in the
arena.
Constraints:
*) the framework should express same functionality as Seibel’s version
as much as possible, nothing more, nothing less, given the peculiar
limitations of the chosen language
*) code should be “understandable” by the majority of participants
*) if a line is longer than 70 chars then it is automatically splitted
*) the program with the smallest amount of lines is the champion

My version is 10 lines long, +1 for a line longer than 70 chars = 11,
but I don’t really participate, I’m the judge (wow!) and I’m financing
the prize (some food, I think, probably pizza).

A ruby guy has already anticipated that he has reached a version which
is only 5 lines long. Inspired and challenged by his words I have come
up with another version that based on the definition above is only 5
lines long too:

CUT HERE
def check(tests)
stack = caller.map {|m| m[/\w
(?=’)/]}.compact.reverse.join " "
tests.inject(true) {|ret, t| puts “#{(v=eval(t)) ? ‘pass’ : ‘FAIL’}
… (#{stack}) #{t.strip}”; ret && v}
end
CUT HERE

Doing this way there is no need of combine_results method, and several
tests may be combined in one method simply using ‘&&’ (or ‘and’) like
the following:

CUT HERE
def test_aritmetic
test_plus &&
test_multiply
end
CUT HERE

Contest deadline is next Monday, 1st of March.
I’ll let you know the results, of course, but I’m curious to have your
early feedback too, if possible.
Ciao, Giuliano

[1] - http://tinyurl.com/2b99d
[2] - http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/92963

···


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at last

Result: ruby won with the following version

CUT HERE
def combine(*results) not results.flatten().include? false end
def check (*all) combine all.collect{|t|c =caller-[caller.last]
c.collect!{|l|(l.include? “rut.rb”)? nil : l.sub(/.*in/, ‘’)}
puts (eval t)?“pass …#{c} #{t}”:“FAIL …#{c} #{t}”;eval t} end
CUT HERE

(my version would be more compact if body method follows def directly, I
should have thought about it).

Just for fun, here are perl and smalltalk participants.

Perl solution makes use of closures.
CUT HERE
01: sub makeTest {
02: my $testname = shift;
03: my @tests = map { ref $_ ? $_ : makeTestRunner($) } @;
04: return sub { my $suitename=shift; our $level++; my $result=1;
05: map { $result &= $_->($suitename.$testname.’ ') } @tests;
06: --$level == 0 ? print $result ? “T”:“F”,“\n” : $result; } }
07: sub makeTestRunner {
08: my $code = shift; return sub { my $result = eval $code;
09: print $result?“pass”:“fail”," … ( “.shift().”) $code\n";
10: $result ? 1:0 } } “End of conTest :-)”;
CUT HERE

Smalltalk solution is based on recursion.
CUT HERE
Trigo class >> check: what
what size = 0 ifTrue: [^true] ifFalse: [^(Trigo report: (what at: 1))
& (Trigo check: (what copyFrom: 2 to: what size))]
Trigo class >> filter
^((thisContext sendersTo: nil)
select: [:each | ‘test’ match: each printString]) reverse
Trigo class >> report: aTest

result eval |
eval := Compiler evaluate: aTest.
eval ifTrue:[result:=‘: pass’] ifFalse:[result:=‘: FAIL’].
Trigo filter do:[:each|Transcript show:each printString,’ ’ ].
Transcript show: aTest, result, '' withCRs.^eval
CUT HERE

This contest was a real fun!
Ciao, Giuliano

Piergiuliano Bossi wrote:

···

A few days ago I made a post (rubytalk:92963 ==> [2]) about the
development of a mini-framework in ruby related to unit testing and
inspired by Peter Seibel [1]. Unfortunately, I have got no comments
about it.

Anyway, after some feedback on another mailing list from Gabriele Renzi,
I have come up with the following version:

CUT HERE
def stack
caller[3…-1].map {|m| m[/test_.*(?=')/]}.compact.reverse.join " "
end
def check(*tests)
tests.map! {|t| puts “#{(v=eval(t)) ? ‘pass’ : ‘FAIL’} …
(#{stack}) #{t.strip}”; v}
combine_results(*tests)
end
def combine_results(*tests)
tests.inject {|t1, t2| t1 && t2}
end

def test_plus
check(
“1 + 2 == 3”,
“1 + 2 + 3 == 6”,
“-1 + -3 == -4”)
end
def test_multiply
check(
“2 * 2 == 4”,
“3 * 5 == 15”)
end

def test_aritmetic
combine_results(
test_plus,
test_multiply)
end

def test_math
test_aritmetic
end

result = test_math
puts “result=#{result}”
CUT HERE

As you can see, my ruby version looks more compact and more easily
undestandable to my non-lisper eyes than Seibel’s version.

I have challenged a few friends to build the most compact version of the
framework. There’s a perl fan, a few ruby guys and a smalltalker in the
arena.
Constraints:
*) the framework should express same functionality as Seibel’s version
as much as possible, nothing more, nothing less, given the peculiar
limitations of the chosen language
*) code should be “understandable” by the majority of participants
*) if a line is longer than 70 chars then it is automatically splitted
*) the program with the smallest amount of lines is the champion

My version is 10 lines long, +1 for a line longer than 70 chars = 11,
but I don’t really participate, I’m the judge (wow!) and I’m financing
the prize (some food, I think, probably pizza).

A ruby guy has already anticipated that he has reached a version which
is only 5 lines long. Inspired and challenged by his words I have come
up with another version that based on the definition above is only 5
lines long too:

CUT HERE
def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.compact.reverse.join " "
tests.inject(true) {|ret, t| puts “#{(v=eval(t)) ? ‘pass’ : ‘FAIL’}
… (#{stack}) #{t.strip}”; ret && v}
end
CUT HERE

Doing this way there is no need of combine_results method, and several
tests may be combined in one method simply using ‘&&’ (or ‘and’) like
the following:

CUT HERE
def test_aritmetic
test_plus &&
test_multiply
end
CUT HERE

Contest deadline is next Monday, 1st of March.
I’ll let you know the results, of course, but I’m curious to have your
early feedback too, if possible.
Ciao, Giuliano

[1] - Practical: Building a Unit Test Framework
[2] - http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/92963


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at last

“Piergiuliano Bossi” p_bossi_AGAINST_SPAM@tiscali.it schrieb im
Newsbeitrag news:c24rte$da0$1@lacerta.tiscalinet.it…

Result: ruby won with the following version

CUT HERE
def combine(*results) not results.flatten().include? false end
def check (*all) combine all.collect{|t|c =caller-[caller.last]
c.collect!{|l|(l.include? “rut.rb”)? nil : l.sub(/.*in/, ‘’)}
puts (eval t)?“pass …#{c} #{t}”:“FAIL …#{c} #{t}”;eval t} end
CUT HERE

IMHO Ruby can do better (also removing some duplicate evaluation):

def check (*all) c=caller[0…-1].map{|l|l.include?(“rut.rb”)? nil :
l.sub(/.*in/, ‘’)}.compact
all.collect{|t|res=eval t; puts “#{res ?“pass”:“FAIL”} …#{c}
#{t}”;res}.all? end

irb(main):078:0> check( “5+3”, “4>3” )
pass …g’g’ 5+3
pass …g’g’ 4>3
=> true
irb(main):079:0> check( “5+3”, “4>3”, “4<3” )
pass …g’g’ 5+3
pass …g’g’ 4>3
FAIL …g’g’ 4<3
=> false

Or did I miss something?

robert

(my version would be more compact if body method follows def directly, I
should have thought about it).

Just for fun, here are perl and smalltalk participants.

Perl solution makes use of closures.
CUT HERE
01: sub makeTest {
02: my $testname = shift;
03: my @tests = map { ref $_ ? $_ : makeTestRunner($) } @;
04: return sub { my $suitename=shift; our $level++; my $result=1;
05: map { $result &= $_->($suitename.$testname.’ ') } @tests;
06: --$level == 0 ? print $result ? “T”:“F”,“\n” : $result; } }
07: sub makeTestRunner {
08: my $code = shift; return sub { my $result = eval $code;
09: print $result?“pass”:“fail”," … ( “.shift().”) $code\n";
10: $result ? 1:0 } } “End of conTest :-)”;
CUT HERE

Smalltalk solution is based on recursion.
CUT HERE
Trigo class >> check: what
what size = 0 ifTrue: [^true] ifFalse: [^(Trigo report: (what at: 1))
& (Trigo check: (what copyFrom: 2 to: what size))]
Trigo class >> filter
^((thisContext sendersTo: nil)
select: [:each | ‘test’ match: each printString]) reverse
Trigo class >> report: aTest

result eval |
eval := Compiler evaluate: aTest.
eval ifTrue:[result:=‘: pass’] ifFalse:[result:=‘: FAIL’].
Trigo filter do:[:each|Transcript show:each printString,’ ’ ].
Transcript show: aTest, result, '' withCRs.^eval
CUT HERE

This contest was a real fun!
Ciao, Giuliano

Piergiuliano Bossi wrote:

A few days ago I made a post (rubytalk:92963 ==> [2]) about the
development of a mini-framework in ruby related to unit testing and
inspired by Peter Seibel [1]. Unfortunately, I have got no comments
about it.

Anyway, after some feedback on another mailing list from Gabriele
Renzi,
I have come up with the following version:

CUT HERE
def stack
caller[3…-1].map {|m| m[/test_.*(?=')/]}.compact.reverse.join " "
end
def check(*tests)
tests.map! {|t| puts “#{(v=eval(t)) ? ‘pass’ : ‘FAIL’} …
(#{stack}) #{t.strip}”; v}
combine_results(*tests)
end
def combine_results(*tests)
tests.inject {|t1, t2| t1 && t2}
end

def test_plus
check(
“1 + 2 == 3”,
“1 + 2 + 3 == 6”,
“-1 + -3 == -4”)
end
def test_multiply
check(
“2 * 2 == 4”,
“3 * 5 == 15”)
end

def test_aritmetic
combine_results(
test_plus,
test_multiply)
end

def test_math
test_aritmetic
end

result = test_math
puts “result=#{result}”
CUT HERE

As you can see, my ruby version looks more compact and more easily
undestandable to my non-lisper eyes than Seibel’s version.

I have challenged a few friends to build the most compact version of
the
framework. There’s a perl fan, a few ruby guys and a smalltalker in
the
arena.
Constraints:
*) the framework should express same functionality as Seibel’s version
as much as possible, nothing more, nothing less, given the peculiar
limitations of the chosen language
*) code should be “understandable” by the majority of participants
*) if a line is longer than 70 chars then it is automatically splitted
*) the program with the smallest amount of lines is the champion

My version is 10 lines long, +1 for a line longer than 70 chars = 11,
but I don’t really participate, I’m the judge (wow!) and I’m financing
the prize (some food, I think, probably pizza).

A ruby guy has already anticipated that he has reached a version which
is only 5 lines long. Inspired and challenged by his words I have come
up with another version that based on the definition above is only 5
lines long too:

CUT HERE
def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.compact.reverse.join " "
tests.inject(true) {|ret, t| puts “#{(v=eval(t)) ? ‘pass’ :
‘FAIL’}
… (#{stack}) #{t.strip}”; ret && v}
end
CUT HERE

Doing this way there is no need of combine_results method, and several
tests may be combined in one method simply using ‘&&’ (or ‘and’) like
the following:

CUT HERE
def test_aritmetic
test_plus &&
test_multiply
end
CUT HERE

Contest deadline is next Monday, 1st of March.
I’ll let you know the results, of course, but I’m curious to have your
early feedback too, if possible.
Ciao, Giuliano

[1] - Practical: Building a Unit Test Framework
[2] -
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/92963


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at
last

···

Robert Klemme wrote:

“Piergiuliano Bossi” p_bossi_AGAINST_SPAM@tiscali.it schrieb im
Newsbeitrag news:c24rte$da0$1@lacerta.tiscalinet.it…

Result: ruby won with the following version

CUT HERE
def combine(*results) not results.flatten().include? false end
def check (*all) combine all.collect{|t|c =caller-[caller.last]
c.collect!{|l|(l.include? “rut.rb”)? nil : l.sub(/.*in/, ‘’)}
puts (eval t)?“pass …#{c} #{t}”:“FAIL …#{c} #{t}”;eval t} end
CUT HERE

IMHO Ruby can do better (also removing some duplicate evaluation):

def check (*all) c=caller[0…-1].map{|l|l.include?(“rut.rb”)? nil :
l.sub(/.*in/, ‘’)}.compact
all.collect{|t|res=eval t; puts “#{res ?“pass”:“FAIL”} …#{c}
#{t}”;res}.all? end

irb(main):078:0> check( “5+3”, “4>3” )
pass …g’g’ 5+3
pass …g’g’ 4>3
=> true
irb(main):079:0> check( “5+3”, “4>3”, “4<3” )
pass …g’g’ 5+3
pass …g’g’ 4>3
FAIL …g’g’ 4<3
=> false

Or did I miss something?

You didn’t miss anything. Infact in my version (rubytalk:93734) I have
done a single evaluation just like what you did. I also prefer my
version of caller filtering because it looks much more understandable to me.

I have included it here once again just for your own convenience.

CUT HERE
def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.compact.reverse.join " "
tests.inject(true) {|ret, t| puts “#{(v=eval(t)) ? ‘pass’ : ‘FAIL’}
… (#{stack}) #{t.strip}”; ret && v}
end
CUT HERE

What do you think about it?

Thanks for your feedback.
Ciao, Giuliano

···


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at last

“Piergiuliano Bossi” p_bossi_AGAINST_SPAM@tiscali.it schrieb im
Newsbeitrag news:c24vu6$jj0$1@lacerta.tiscalinet.it…

Robert Klemme wrote:

“Piergiuliano Bossi” p_bossi_AGAINST_SPAM@tiscali.it schrieb im
Newsbeitrag news:c24rte$da0$1@lacerta.tiscalinet.it…

Result: ruby won with the following version

CUT HERE
def combine(*results) not results.flatten().include? false end
def check (*all) combine all.collect{|t|c =caller-[caller.last]
c.collect!{|l|(l.include? “rut.rb”)? nil : l.sub(/.*in/, ‘’)}
puts (eval t)?“pass …#{c} #{t}”:“FAIL …#{c} #{t}”;eval t} end
CUT HERE

IMHO Ruby can do better (also removing some duplicate evaluation):

def check (*all) c=caller[0…-1].map{|l|l.include?(“rut.rb”)? nil :
l.sub(/.*in/, ‘’)}.compact
all.collect{|t|res=eval t; puts “#{res ?“pass”:“FAIL”} …#{c}
#{t}”;res}.all? end

irb(main):078:0> check( “5+3”, “4>3” )
pass …g’g’ 5+3
pass …g’g’ 4>3
=> true
irb(main):079:0> check( “5+3”, “4>3”, “4<3” )
pass …g’g’ 5+3
pass …g’g’ 4>3
FAIL …g’g’ 4<3
=> false

Or did I miss something?

You didn’t miss anything. Infact in my version (rubytalk:93734) I have
done a single evaluation just like what you did. I also prefer my
version of caller filtering because it looks much more understandable to
me.

I have included it here once again just for your own convenience.

CUT HERE
def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.compact.reverse.join " "
tests.inject(true) {|ret, t| puts “#{(v=eval(t)) ? ‘pass’ :
‘FAIL’}
… (#{stack}) #{t.strip}”; ret && v}
end
CUT HERE

What do you think about it?

  • you’re omitting the file info as far as I can see
  • I’d move the v=eval(t) out of the string replacement, no need to have it
    there.
  • #inject is even more performant since you omit the intermediate array of
    results.

Thanks for your feedback.

You’re welcome!

robert

Ciao, Giuliano


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at
last

···

Robert Klemme wrote:

CUT HERE
def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.compact.reverse.join " "
tests.inject(true) {|ret, t| puts "#{(v=eval(t)) ? ‘pass’ :

‘FAIL’}

… (#{stack}) #{t.strip}"; ret && v}
end
CUT HERE

What do you think about it?

  • you’re omitting the file info as far as I can see

Yes, but that’s not really needed, because:
stack = caller.map {|m| m[/\w*(?=')/]}.compact.reverse.join " "
does everything you need.

  • I’d move the v=eval(t) out of the string replacement, no need to have it
    there.

You are right. It’s a bit longer, but much better in terms of
understandability.

  • #inject is even more performant since you omit the intermediate array of
    results.

Yes, even if the combine_results method performs all the check anyway,
while inject appears to stop as soon as a false value is reached. Not a
big issue, in any case.

Ciao, Giuliano

···


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at last

“Piergiuliano Bossi” p_bossi_AGAINST_SPAM@tiscali.it schrieb im
Newsbeitrag news:c26okp$q0i$1@lacerta.tiscalinet.it…

Robert Klemme wrote:

CUT HERE
def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.compact.reverse.join " "
tests.inject(true) {|ret, t| puts "#{(v=eval(t)) ? ‘pass’ :

‘FAIL’}

… (#{stack}) #{t.strip}"; ret && v}
end
CUT HERE

What do you think about it?

  • you’re omitting the file info as far as I can see

Yes, but that’s not really needed, because:
stack = caller.map {|m| m[/\w*(?=')/]}.compact.reverse.join " "
does everything you need.

  • I’d move the v=eval(t) out of the string replacement, no need to
    have it
    there.

You are right. It’s a bit longer, but much better in terms of
understandability.

  • #inject is even more performant since you omit the intermediate
    array of
    results.

Yes, even if the combine_results method performs all the check anyway,
while inject appears to stop as soon as a false value is reached. Not a
big issue, in any case.

inject doesn’t stop prematurely unless you include a break or return
(which you didn’t):

irb(main):007:0> (1…10).inject(true){|acc,v|p v; acc && v < 5}
1
2
3
4
5
6
7
8
9
10
=> false

robert

Ciao, Giuliano


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at
last

···

Robert Klemme wrote:

  • #inject is even more performant since you omit the intermediate

array of

results.

Yes, even if the combine_results method performs all the check anyway,
while inject appears to stop as soon as a false value is reached. Not a
big issue, in any case.

inject doesn’t stop prematurely unless you include a break or return
(which you didn’t):

You are right, my fault. Fact is that having eliminated combine_results
method I now combine values using ‘&&’ (or ‘and’), which in turn
performs a short-circuit evaluation ==> if the failing test is in the
first group combined through ‘&&’ the second group will never be evaluated.

I start thinking that combine_results cannot be avoided…

Thanks again.
Ciao, Giuliano

···


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at last

“Piergiuliano Bossi” p_bossi_AGAINST_SPAM@tiscali.it schrieb im
Newsbeitrag news:c27fab$p4h$1@lacerta.tiscalinet.it…

Robert Klemme wrote:

  • #inject is even more performant since you omit the intermediate

array of

results.

Yes, even if the combine_results method performs all the check anyway,
while inject appears to stop as soon as a false value is reached. Not
a
big issue, in any case.

inject doesn’t stop prematurely unless you include a break or return
(which you didn’t):

You are right, my fault. Fact is that having eliminated combine_results
method I now combine values using ‘&&’ (or ‘and’), which in turn
performs a short-circuit evaluation ==> if the failing test is in the
first group combined through ‘&&’ the second group will never be
evaluated.

I start thinking that combine_results cannot be avoided…

I’m afraid you’re a bit confused now. :slight_smile: What do you want
combine_results for? It does not make a difference, because you eval’ed t
before. And even if you didn’t, switching order is sufficient. You did
(with the slight modification of pulling v=eval(t) out of the string):

def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.compact.reverse.join " "
tests.inject(true) {|ret, t| v=eval(t); puts “#{v ? ‘pass’ : ‘FAIL’}
… (#{stack}) #{t.strip}”; ret && v}
end

It doesn’t matter whether “ret && v” short circuits or not, because you
did eval it already. And even if you didn’t you could do “eval(t) && ret”
which would always eval.

Regards

robert

Robert Klemme wrote:

You are right, my fault. Fact is that having eliminated combine_results
method I now combine values using ‘&&’ (or ‘and’), which in turn
performs a short-circuit evaluation ==> if the failing test is in the
first group combined through ‘&&’ the second group will never be

evaluated.

I start thinking that combine_results cannot be avoided…

I’m afraid you’re a bit confused now. :slight_smile: What do you want
combine_results for? It does not make a difference, because you eval’ed t
before. And even if you didn’t, switching order is sufficient. You did
(with the slight modification of pulling v=eval(t) out of the string):

def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.compact.reverse.join " "
tests.inject(true) {|ret, t| v=eval(t); puts “#{v ? ‘pass’ : ‘FAIL’}
… (#{stack}) #{t.strip}”; ret && v}
end

It doesn’t matter whether “ret && v” short circuits or not, because you
did eval it already. And even if you didn’t you could do “eval(t) && ret”
which would always eval.

Ok, I have to repost the code, otherwise we don’t understand each other.

CUT HERE
def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.reverse.join(" ").strip
tests.inject(true) {|ret, t| puts “#{(v=eval(t))?‘pass’:‘FAIL’} …
(#{stack}) #{t}”; ret && v}
end

def test_plus
check(
“1 + 2 == 3”,
“1 + 2 + 3 == 6”,
“-1 + -3 == -4”)
end
def test_multiply
check(
“2 * 2 == 4”,
“3 * 5 == 15”)
end

def test_aritmetic
test_plus &&
test_multiply
end

def test_math
test_aritmetic
end

result = test_math
puts “result=#{result}”
CUT HERE

I’m talking of the ‘&&’ inside test_aritmetic: using inject directly
instead of combine_results makes executing the first group of test only.

Try to change first test from “1 + 2 == 3” to “1 + 1 == 3” ==> you will get:
CUT HERE
FAIL … (test_math test_aritmetic test_plus) 1 + 1 == 3
pass … (test_math test_aritmetic test_plus) 1 + 2 + 3 == 6
pass … (test_math test_aritmetic test_plus) -1 + -3 == -4
result=false
CUT HERE

instead of

CUT HERE
FAIL … (test_math test_aritmetic test_plus) 1 + 1 == 3
pass … (test_math test_aritmetic test_plus) 1 + 2 + 3 == 6
pass … (test_math test_aritmetic test_plus) -1 + -3 == -4
pass … (test_math test_aritmetic test_multiply) 2 * 2 == 4
pass … (test_math test_aritmetic test_multiply) 3 * 5 == 15
result=false
CUT HERE

The former is wrong.

In order to fix this I should use some form of inject in test_aritmetic
too, creating a duplication that can be removed reintroducing
combine_results only. That method was defined as:

CUT HERE
def combine_results(*tests)
tests.inject {|t1, t2| t1 && t2}
end
CUT HERE

I hope that now it is clear. :slight_smile:

Ciao, Giuliano

···


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at last

“Piergiuliano Bossi” p_bossi_AGAINST_SPAM@tiscali.it schrieb im
Newsbeitrag news:c27mo1$7nr$1@lacerta.tiscalinet.it…

Robert Klemme wrote:

You are right, my fault. Fact is that having eliminated
combine_results
method I now combine values using ‘&&’ (or ‘and’), which in turn
performs a short-circuit evaluation ==> if the failing test is in the
first group combined through ‘&&’ the second group will never be

evaluated.

I start thinking that combine_results cannot be avoided…

I’m afraid you’re a bit confused now. :slight_smile: What do you want
combine_results for? It does not make a difference, because you
eval’ed t
before. And even if you didn’t, switching order is sufficient. You
did
(with the slight modification of pulling v=eval(t) out of the string):

def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.compact.reverse.join " "
tests.inject(true) {|ret, t| v=eval(t); puts “#{v ? ‘pass’ :
‘FAIL’}
… (#{stack}) #{t.strip}”; ret && v}
end

It doesn’t matter whether “ret && v” short circuits or not, because
you
did eval it already. And even if you didn’t you could do “eval(t) &&
ret”
which would always eval.

Ok, I have to repost the code, otherwise we don’t understand each other.

CUT HERE
def check(tests)
stack = caller.map {|m| m[/\w
(?=')/]}.reverse.join(" ").strip
tests.inject(true) {|ret, t| puts “#{(v=eval(t))?‘pass’:‘FAIL’} …
(#{stack}) #{t}”; ret && v}
end

def test_plus
check(
“1 + 2 == 3”,
“1 + 2 + 3 == 6”,
“-1 + -3 == -4”)
end
def test_multiply
check(
“2 * 2 == 4”,
“3 * 5 == 15”)
end

def test_aritmetic
test_plus &&
test_multiply
end

def test_math
test_aritmetic
end

result = test_math
puts “result=#{result}”
CUT HERE

I’m talking of the ‘&&’ inside test_aritmetic: using inject directly
instead of combine_results makes executing the first group of test only.

Try to change first test from “1 + 2 == 3” to “1 + 1 == 3” ==> you will
get:
CUT HERE
FAIL … (test_math test_aritmetic test_plus) 1 + 1 == 3
pass … (test_math test_aritmetic test_plus) 1 + 2 + 3 == 6
pass … (test_math test_aritmetic test_plus) -1 + -3 == -4
result=false
CUT HERE

instead of

CUT HERE
FAIL … (test_math test_aritmetic test_plus) 1 + 1 == 3
pass … (test_math test_aritmetic test_plus) 1 + 2 + 3 == 6
pass … (test_math test_aritmetic test_plus) -1 + -3 == -4
pass … (test_math test_aritmetic test_multiply) 2 * 2 == 4
pass … (test_math test_aritmetic test_multiply) 3 * 5 == 15
result=false
CUT HERE

The former is wrong.

In order to fix this I should use some form of inject in test_aritmetic
too, creating a duplication that can be removed reintroducing
combine_results only. That method was defined as:

CUT HERE
def combine_results(*tests)
tests.inject {|t1, t2| t1 && t2}
end
CUT HERE

I hope that now it is clear. :slight_smile:

Yes, it is. But:

def combine_results(*tests)
tests.all?
end

And:

you still don’t need combine_results():

def test_aritmetic
[ test_plus, test_multiply ].all?
end

:-))

Regards

robert

Robert Klemme wrote:

def combine_results(*tests)
tests.all?
end

And:

you still don’t need combine_results():

def test_aritmetic
[ test_plus, test_multiply ].all?
end

:-))

GOSH!! I didn’t know anything about all? method!!

Of course it is described in [1], but not in [2] or [3] (my primary
sources). I guess this could be a good reason to build ri, even if I’m
on Win.

Thank you very VERY much! :slight_smile:

Giuliano

[1] http://www.whytheluckystiff.net/articles/2003/08/04/rubyOneEightOh
[2] Programming Ruby
[3] http://www.eng.cse.dmu.ac.uk/~hgs/ruby/RUBY_SNAPSHOT_RDOC/index.html

···


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at last

“Piergiuliano Bossi” p_bossi_AGAINST_SPAM@tiscali.it schrieb im
Newsbeitrag news:c29r88$586$1@lacerta.tiscalinet.it…

Robert Klemme wrote:

def combine_results(*tests)
tests.all?
end

And:

you still don’t need combine_results():

def test_aritmetic
[ test_plus, test_multiply ].all?
end

:-))

GOSH!! I didn’t know anything about all? method!!

:-)))

Of course it is described in [1], but not in [2] or [3] (my primary
sources). I guess this could be a good reason to build ri, even if I’m
on Win.

Thank you very VERY much! :slight_smile:

You’re welcome.

robert

Giuliano

[1] http://www.whytheluckystiff.net/articles/2003/08/04/rubyOneEightOh
[2] Programming Ruby
[3] http://www.eng.cse.dmu.ac.uk/~hgs/ruby/RUBY_SNAPSHOT_RDOC/index.html


If you want to send me an email in the address you have to write ‘p’,
then a dot, followed by ‘bossi’ at ‘quinary’, another dot and ‘com’ at
last

···