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

P

Piergiuliano Bossi

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
 
P

Piergiuliano Bossi

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 said:
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
 
R

Robert Klemme

Piergiuliano Bossi said:
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 said:
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
 
P

Piergiuliano Bossi

Robert said:
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
 
R

Robert Klemme

Piergiuliano Bossi said:
Robert said:
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
 
P

Piergiuliano Bossi

Robert said:
**********************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
 
R

Robert Klemme

Piergiuliano Bossi said:
Robert said:
**********************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
 
P

Piergiuliano Bossi

Robert said:
array of



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
 
R

Robert Klemme

Piergiuliano Bossi said:
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. :) 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
 
P

Piergiuliano Bossi

Robert said:
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. :) 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. :)

Ciao, Giuliano
 
R

Robert Klemme

Piergiuliano Bossi said:
Robert said:
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. :) 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. :)

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
 
P

Piergiuliano Bossi

Robert said:
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! :)

Giuliano


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

Robert Klemme

Piergiuliano Bossi said:
Robert said:
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! :)

You're welcome.

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top