From: Piergiuliano Bossi Date: 2004-03-03T23:59:49+09:00 Subject: Ruby won, of course (was: language contest ==> unit test framework from lisp to ruby) 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] - 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