From tom at rubyforge.org Wed Nov 3 14:45:33 2004 From: tom at rubyforge.org (tom@rubyforge.org) Date: Wed Nov 3 14:45:35 2004 Subject: [Aversa-commits] aversa/www index.html Message-ID: <200411031945.iA3JjXdS028960@rubyforge.org> Update of /var/cvs/aversa/aversa/www In directory rubyforge.org:/tmp/cvs-serv28950 Modified Files: index.html Log Message: Mostly testing Mailman 'from' headers Index: index.html =================================================================== RCS file: /var/cvs/aversa/aversa/www/index.html,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** index.html 8 Sep 2004 14:36:25 -0000 1.7 --- index.html 3 Nov 2004 19:45:31 -0000 1.8 *************** *** 69,73 **** [tom@hal bt]$ ! Note that creating a new file make take some time if the file is large. For comparison, it takes Aversa about a quarter of a second to create a metainfo file for the 11 MB Ruby Windows Installer on my workstation. --- 69,73 ---- [tom@hal bt]$ ! Note that creating a new file may take some time if the file is large. For comparison, it takes Aversa about a quarter of a second to create a metainfo file for the 11 MB Ruby Windows Installer on my workstation. From teamikl at dsl092-150-242.wdc2.dsl.speakeasy.net Tue Nov 2 04:38:05 2004 From: teamikl at dsl092-150-242.wdc2.dsl.speakeasy.net (teamikl@dsl092-150-242.wdc2.dsl.speakeasy.net) Date: Wed Nov 3 14:47:12 2004 Subject: [Aversa-commits] aversa/lib/net/bittorrent ChangeLog metainfo.rb peerwireprotocol.rb storage.rb tracker.rb Message-ID: <200411020938.iA29c1dS007245@rubyforge.org> Update of /var/cvs/aversa/aversa/lib/net/bittorrent In directory rubyforge.org:/tmp/cvs-serv7216/lib/net/bittorrent Modified Files: ChangeLog metainfo.rb peerwireprotocol.rb storage.rb tracker.rb Log Message: Added code to handle multi files metainfo. Index: metainfo.rb =================================================================== RCS file: /var/cvs/aversa/aversa/lib/net/bittorrent/metainfo.rb,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** metainfo.rb 20 Sep 2004 03:11:46 -0000 1.8 --- metainfo.rb 2 Nov 2004 09:37:59 -0000 1.9 *************** *** 108,112 **** file['length'] = FileTest.size(name) info['files'].push(file) - p file end # TODO use Find#find to search directory tree. --- 108,111 ---- Index: peerwireprotocol.rb =================================================================== RCS file: /var/cvs/aversa/aversa/lib/net/bittorrent/peerwireprotocol.rb,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** peerwireprotocol.rb 13 Sep 2004 17:33:39 -0000 1.2 --- peerwireprotocol.rb 2 Nov 2004 09:37:59 -0000 1.3 *************** *** 1,5 **** --- 1,15 ---- ## # == NAME + # + # Net::BitTorrent::PeerWireProtocol class + # + # == DESCRIPTION + # + # The PeerWireProtocol class will provide send/recv packed messages + # for socket IO. # + # == NOTES + # + # Should do it with Mix-in ? # # == AUTHORS *************** *** 107,114 **** # : 20 bytes # ! # YouArentGonnaNeedIt: ! # I do hard-coding protocol name and reserved data. ! # I do not write the code for those extends, right now. ! # I may change the interface of this method when Spec is updated. def handshake(info_hash, peer_id="") protocol = [19].pack("C") + "BitTorrent protocol" --- 117,121 ---- # : 20 bytes # ! # - TODO the protocol name is a consts, move it. def handshake(info_hash, peer_id="") protocol = [19].pack("C") + "BitTorrent protocol" Index: tracker.rb =================================================================== RCS file: /var/cvs/aversa/aversa/lib/net/bittorrent/tracker.rb,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** tracker.rb 27 Sep 2004 07:19:43 -0000 1.6 --- tracker.rb 2 Nov 2004 09:37:59 -0000 1.7 *************** *** 1,12 **** ! # === NAME # # BitTorrent Tracker module. # ! # === EXAMPLES # # - TODO write examples for Tracker. # see ./test/net-bittorrent/tc_tracker.rb and ./lib/aversa/track.rb for now. # ! # === NOTES # # Tracker's Request/Response should extends HTTP's Request/Response class. --- 1,12 ---- ! # == NAME # # BitTorrent Tracker module. # ! # == EXAMPLES # # - TODO write examples for Tracker. # see ./test/net-bittorrent/tc_tracker.rb and ./lib/aversa/track.rb for now. # ! # == NOTES # # Tracker's Request/Response should extends HTTP's Request/Response class. Index: storage.rb =================================================================== RCS file: /var/cvs/aversa/aversa/lib/net/bittorrent/storage.rb,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** storage.rb 24 Sep 2004 15:53:32 -0000 1.5 --- storage.rb 2 Nov 2004 09:37:59 -0000 1.6 *************** *** 60,63 **** --- 60,64 ---- # prepare @files + # TODO tidy me if @metainfo['info'].key? 'files' @files = @metainfo['info']['files'].map{|file| *************** *** 66,79 **** } else ! @files = [{ 'path' => @metainfo['info']['name'], 'length' => @metainfo['info']['length'] }] end - - # Touch files unless file exists - # - # @files.each do |file| - # open(file['path'],'w').close unless File.exists? file['path'] - # end - end --- 67,73 ---- } else ! @files = [{ 'path' => @metainfo['info']['name'], 'length' => @metainfo['info']['length'] }] end end *************** *** 128,133 **** def length @files.inject(0) do |len, file| ! # path = File.join(@path, file['path']) ! file_path = file['path'] len += (File.exists? file_path) ? FileTest.size(file_path) : 0 end --- 122,126 ---- def length @files.inject(0) do |len, file| ! file_path = File.join(@path, file['path']) len += (File.exists? file_path) ? FileTest.size(file_path) : 0 end *************** *** 182,187 **** # def get_filehandle(file_name) ! # file_path = File.join(@path, file_name) ! file_path = file_name unless @handles.has_key? file_name --- 175,179 ---- # def get_filehandle(file_name) ! file_path = File.join(@path, file_name) unless @handles.has_key? file_name *************** *** 192,198 **** end ! return @handles[file_name] end - end end --- 184,189 ---- end ! @handles[file_name] end end end Index: ChangeLog =================================================================== RCS file: /var/cvs/aversa/aversa/lib/net/bittorrent/ChangeLog,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** ChangeLog 16 Sep 2004 14:36:27 -0000 1.3 --- ChangeLog 2 Nov 2004 09:37:59 -0000 1.4 *************** *** 1,4 **** --- 1,8 ---- # vim: noexpandtab tabstop=8 shiftwidth=8 + 2004-11-01 Ikkei Shimomura + * Tidy documents. + * Fixed path problem of Storage class. + 2004-09-16 Ikkei Shimomura * Added pack/unpack ip address and port. From teamikl at dsl092-150-242.wdc2.dsl.speakeasy.net Tue Nov 2 04:38:05 2004 From: teamikl at dsl092-150-242.wdc2.dsl.speakeasy.net (teamikl@dsl092-150-242.wdc2.dsl.speakeasy.net) Date: Wed Nov 3 14:47:12 2004 Subject: [Aversa-commits] aversa/test/aversa tc_track.rb Message-ID: <200411020938.iA29c1dS007246@rubyforge.org> Update of /var/cvs/aversa/aversa/test/aversa In directory rubyforge.org:/tmp/cvs-serv7216/test/aversa Modified Files: tc_track.rb Log Message: Added code to handle multi files metainfo. Index: tc_track.rb =================================================================== RCS file: /var/cvs/aversa/aversa/test/aversa/tc_track.rb,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** tc_track.rb 29 Sep 2004 01:02:56 -0000 1.4 --- tc_track.rb 2 Nov 2004 09:37:59 -0000 1.5 *************** *** 66,76 **** assert_equal(1800, res['interval']) - - # XXX This test should be changed after implement it. req['event'] = 'stopped' res = Response.parse(@http.get(req.to_s)) ! assert_equal(true, res.has_key?('failure reason')) ! assert_equal('This feature was not implemented, yet', ! res['failure reason']) # XXX This test should be changed after implement it. --- 66,72 ---- assert_equal(1800, res['interval']) req['event'] = 'stopped' res = Response.parse(@http.get(req.to_s)) ! assert_equal(true, res.has_key?('interval')) # XXX This test should be changed after implement it. From teamikl at dsl092-150-242.wdc2.dsl.speakeasy.net Tue Nov 2 04:38:05 2004 From: teamikl at dsl092-150-242.wdc2.dsl.speakeasy.net (teamikl@dsl092-150-242.wdc2.dsl.speakeasy.net) Date: Wed Nov 3 14:47:13 2004 Subject: [Aversa-commits] aversa/lib/aversa metainfo-view.rb track.rb Message-ID: <200411020938.iA29c1dS007244@rubyforge.org> Update of /var/cvs/aversa/aversa/lib/aversa In directory rubyforge.org:/tmp/cvs-serv7216/lib/aversa Modified Files: metainfo-view.rb track.rb Log Message: Added code to handle multi files metainfo. Index: track.rb =================================================================== RCS file: /var/cvs/aversa/aversa/lib/aversa/track.rb,v retrieving revision 1.18 retrieving revision 1.19 diff -C2 -d -r1.18 -r1.19 *** track.rb 17 Oct 2004 23:20:21 -0000 1.18 --- track.rb 2 Nov 2004 09:37:59 -0000 1.19 *************** *** 145,149 **** # Make sure the manner which should I use, for the case. # - TODO make class for dstate data ! # = TODO check input values. (How to use Taint mode and safe level) # def do_GET(req, res) --- 145,149 ---- # Make sure the manner which should I use, for the case. # - TODO make class for dstate data ! # - TODO check input values. (How to use Taint mode and safe level) # def do_GET(req, res) Index: metainfo-view.rb =================================================================== RCS file: /var/cvs/aversa/aversa/lib/aversa/metainfo-view.rb,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** metainfo-view.rb 29 Sep 2004 01:07:38 -0000 1.3 --- metainfo-view.rb 2 Nov 2004 09:37:59 -0000 1.4 *************** *** 47,51 **** puts " length : %d bytes" % metainfo.get_file_length puts " piece length : %d " % metainfo["info"]["piece length"] ! puts "-------------------" * 4 p metainfo if @dump --- 47,57 ---- puts " length : %d bytes" % metainfo.get_file_length puts " piece length : %d " % metainfo["info"]["piece length"] ! puts " files :" ! if metainfo["info"].has_key? "files" ! metainfo["info"]["files"].each do |file| ! puts " %-30s (%d)" % [file["path"].join, file["length"]] ! end ! end ! puts "-------------------" * 4 p metainfo if @dump From teamikl at rubyforge.org Thu Nov 25 04:29:16 2004 From: teamikl at rubyforge.org (teamikl@rubyforge.org) Date: Thu Nov 25 04:29:17 2004 Subject: [Aversa-commits] aversa/lib/aversa metainfo-view.rb Message-ID: <200411250929.iAP9TGdS006822@rubyforge.org> Update of /var/cvs/aversa/aversa/lib/aversa In directory rubyforge.org:/tmp/cvs-serv6810 Modified Files: metainfo-view.rb Log Message: Check some expections and show error messages for wrong arguments. Index: metainfo-view.rb =================================================================== RCS file: /var/cvs/aversa/aversa/lib/aversa/metainfo-view.rb,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** metainfo-view.rb 2 Nov 2004 09:37:59 -0000 1.4 --- metainfo-view.rb 25 Nov 2004 09:29:14 -0000 1.5 *************** *** 30,37 **** end ! def main(file='') ! @file = file ! metainfo = MetaInfo.new( @file ) puts "-------------------" * 4 --- 30,53 ---- end ! def main(file) ! ! @file ||= file ! unless File.exists? @file ! puts "File not found: #{@file}" ! exit 1 ! end ! ! begin ! metainfo = MetaInfo.new( @file ) ! rescue ! puts "Failed to load metainfo file" ! exit 1 ! end ! ! unless metainfo.valid? ! puts "#{@file} is not valid metainfo file that aversa can read." ! exit 1 ! end puts "-------------------" * 4 From teamikl at rubyforge.org Thu Nov 25 06:14:32 2004 From: teamikl at rubyforge.org (teamikl@rubyforge.org) Date: Thu Nov 25 06:14:34 2004 Subject: [Aversa-commits] aversa/lib/aversa track.rb Message-ID: <200411251114.iAPBEWdS011162@rubyforge.org> Update of /var/cvs/aversa/aversa/lib/aversa In directory rubyforge.org:/tmp/cvs-serv11150 Modified Files: track.rb Log Message: Fixed count complete/downloading/downloaded numbers Index: track.rb =================================================================== RCS file: /var/cvs/aversa/aversa/lib/aversa/track.rb,v retrieving revision 1.19 retrieving revision 1.20 diff -C2 -d -r1.19 -r1.20 *** track.rb 2 Nov 2004 09:37:59 -0000 1.19 --- track.rb 25 Nov 2004 11:14:30 -0000 1.20 *************** *** 237,240 **** --- 237,241 ---- db['completed'][info_hash] ||= 0 db['completed'][info_hash] += 1 + ret['peers'] = $cache[info_hash].values.dup when 'stopped' *************** *** 361,376 **** $cache.each do |key,val| num += 1 - completed_list = [] # How many peers complated and seeding. - downloading_list = [] # Total numbers how many downloaded. - for _key, _val in db['peers'][key] - if _val['left'] == 0 - completed_list << _val['ip'] - else - downloading_list << _val['ip'] - end - end ! completed = completed_list.uniq.length ! downloading = downloading_list.uniq.length downloaded = db['completed'][key] || 0 # confuse the name --- 362,371 ---- $cache.each do |key,val| num += 1 ! # How many peers complated and seeding. ! completed = db['peers'][key].select{|k,v| v['left'] == 0}.length ! # Current downloading peers ! downloading = db['peers'][key].select{|k,v| v['left'] > 0}.length ! # Total numbers how many downloaded. downloaded = db['completed'][key] || 0 # confuse the name *************** *** 378,384 **** table << "#{num}" table << "" << Digest::SHA1.hexdigest(key) << "" - table << "#{downloaded}" - table << "#{downloading}" table << "#{completed}" table << "\n" end --- 373,379 ---- table << "#{num}" table << "" << Digest::SHA1.hexdigest(key) << "" table << "#{completed}" + table << "#{downloading}" + table << "#{downloaded}" table << "\n" end From teamikl at rubyforge.org Thu Nov 25 06:46:19 2004 From: teamikl at rubyforge.org (teamikl@rubyforge.org) Date: Thu Nov 25 06:46:20 2004 Subject: [Aversa-commits] aversa/test/aversa tc_track.rb Message-ID: <200411251146.iAPBkJdS011579@rubyforge.org> Update of /var/cvs/aversa/aversa/test/aversa In directory rubyforge.org:/tmp/cvs-serv11575 Modified Files: tc_track.rb Log Message: Added testcase for Tracker /scrape Index: tc_track.rb =================================================================== RCS file: /var/cvs/aversa/aversa/test/aversa/tc_track.rb,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** tc_track.rb 2 Nov 2004 09:37:59 -0000 1.5 --- tc_track.rb 25 Nov 2004 11:46:17 -0000 1.6 *************** *** 80,86 **** req = Request.new(TRACKER_URI + '/scrape') res = Response.parse(@http.get(req.to_s)) ! assert_equal(true, res.has_key?('failure reason')) ! assert_equal('ScrapeHandler class is not implemented', ! res['failure reason']) end end --- 80,85 ---- req = Request.new(TRACKER_URI + '/scrape') res = Response.parse(@http.get(req.to_s)) ! assert_equal(true, res.has_key?('files')) ! assert_equal(Hash, res['files'].type) end end From teamikl at rubyforge.org Thu Nov 25 06:49:14 2004 From: teamikl at rubyforge.org (teamikl@rubyforge.org) Date: Thu Nov 25 06:49:17 2004 Subject: [Aversa-commits] aversa/lib/aversa track.rb Message-ID: <200411251149.iAPBnEdS011625@rubyforge.org> Update of /var/cvs/aversa/aversa/lib/aversa In directory rubyforge.org:/tmp/cvs-serv11599 Modified Files: track.rb Log Message: Implemented /scrape (without info_path argument) Index: track.rb =================================================================== RCS file: /var/cvs/aversa/aversa/lib/aversa/track.rb,v retrieving revision 1.20 retrieving revision 1.21 diff -C2 -d -r1.20 -r1.21 *** track.rb 25 Nov 2004 11:14:30 -0000 1.20 --- track.rb 25 Nov 2004 11:49:12 -0000 1.21 *************** *** 454,464 **** # /scrape # ! # TODO Implement /scrape - i.e., return status of all torrents ! # unless info_hash is passed in, in which case return only ! # status of that torrent ! # # # ret = { ! # 'files' => [ # info_hash => { 'complete' => int, # 'downloaded' => int, --- 454,462 ---- # /scrape # ! # return status of all torrents unless info_hash is passed in, ! # in which case return only status of that torrent # # ret = { ! # 'files' => { # info_hash => { 'complete' => int, # 'downloaded' => int, *************** *** 466,483 **** # }, # ... ! # ] # } # class ScrapeHandler < BTTrackerServlet def do_GET(req, res) - - raise NotImplementedError, 'ScrapeHandler class is not implemented' ret = Hash.new db = PStore.new(DSTATE_FILE) db.transaction do ! # Not sure until decide the db's data structure. ! # ret['files'] = db['completed'].to_a.dup end --- 464,488 ---- # }, # ... ! # } # } # class ScrapeHandler < BTTrackerServlet def do_GET(req, res) ret = Hash.new + ret['files'] = Hash.new db = PStore.new(DSTATE_FILE) db.transaction do ! $cache.each_key do |key| ! complete = db['peers'][key].select{|k,v| v['left'] == 0}.length ! incomplete = db['peers'][key].select{|k,v| v['left'] > 0}.length ! downloaded = db['completed'][key] || 0 ! ! ret['files'][key] = { ! 'complete' => complete, ! 'incomplete' => incomplete, ! 'downloaded' => downloaded } ! end end From teamikl at rubyforge.org Thu Nov 25 08:16:35 2004 From: teamikl at rubyforge.org (teamikl@rubyforge.org) Date: Thu Nov 25 08:16:38 2004 Subject: [Aversa-commits] aversa/lib/aversa track.rb Message-ID: <200411251316.iAPDGZdS015390@rubyforge.org> Update of /var/cvs/aversa/aversa/lib/aversa In directory rubyforge.org:/tmp/cvs-serv15386 Modified Files: track.rb Log Message: Added info_hash param for /scrape Index: track.rb =================================================================== RCS file: /var/cvs/aversa/aversa/lib/aversa/track.rb,v retrieving revision 1.21 retrieving revision 1.22 diff -C2 -d -r1.21 -r1.22 *** track.rb 25 Nov 2004 11:49:12 -0000 1.21 --- track.rb 25 Nov 2004 13:16:33 -0000 1.22 *************** *** 216,225 **** db['peers'][info_hash] ||= {} ! #if db['peers'][info_hash].has_key? peer_id and ! # db['peers'][info_hash][peer_id].has_key 'key' ! # unless db['peers'][info_hash][peer_id]['key'] == key ! # raise TrackerFailure, 'key did not match' ! # end ! #end $times[peer_id] = Time.now.to_i --- 216,220 ---- db['peers'][info_hash] ||= {} ! # TODO check 'key' $times[peer_id] = Time.now.to_i *************** *** 242,247 **** db['peers'][info_hash].delete(peer_id) $cache[info_hash].delete(peer_id) ! # raise NotImplementedError ! else raise 'No supported event' --- 237,242 ---- db['peers'][info_hash].delete(peer_id) $cache[info_hash].delete(peer_id) ! ! # TODO What should tracker return for stopped event. else raise 'No supported event' *************** *** 270,274 **** # # - TODO Read template from file - # - Confuse about counting downloaded/completed. # class IndexHandler < WEBrick::HTTPServlet::AbstractServlet --- 265,268 ---- *************** *** 471,493 **** ret = Hash.new ! ret['files'] = Hash.new db = PStore.new(DSTATE_FILE) db.transaction do ! $cache.each_key do |key| complete = db['peers'][key].select{|k,v| v['left'] == 0}.length incomplete = db['peers'][key].select{|k,v| v['left'] > 0}.length downloaded = db['completed'][key] || 0 ! ret['files'][key] = { ! 'complete' => complete, ! 'incomplete' => incomplete, ! 'downloaded' => downloaded } end end rescue NotImplementedError => err ret = { 'failure reason' => "#{err}" } ensure super(req, res, ret) --- 465,504 ---- ret = Hash.new ! files = Hash.new ! info_hash = req.query['info_hash'] ! ! unless info_hash.nil? ! raise TrackerFailure, 'Bad length of info hash' \ ! if info_hash.length != 20 ! ! raise TrackerFailure, 'Not found such info hash' \ ! unless $cache.has_key? info_hash ! end db = PStore.new(DSTATE_FILE) db.transaction do ! ! for key in $cache.keys ! next if (not info_hash.nil?) and (key != info_hash) ! complete = db['peers'][key].select{|k,v| v['left'] == 0}.length incomplete = db['peers'][key].select{|k,v| v['left'] > 0}.length downloaded = db['completed'][key] || 0 ! files[key] = { ! 'complete' => complete, ! 'incomplete' => incomplete, ! 'downloaded' => downloaded } end end + ret['files'] = files + rescue NotImplementedError => err ret = { 'failure reason' => "#{err}" } + rescue TrackerFailure => err + ret = { 'failure reason' => err.reason } + ensure super(req, res, ret) From teamikl at rubyforge.org Thu Nov 25 08:17:21 2004 From: teamikl at rubyforge.org (teamikl@rubyforge.org) Date: Thu Nov 25 08:17:23 2004 Subject: [Aversa-commits] aversa/test/aversa tc_track.rb Message-ID: <200411251317.iAPDHLdS015420@rubyforge.org> Update of /var/cvs/aversa/aversa/test/aversa In directory rubyforge.org:/tmp/cvs-serv15408 Modified Files: tc_track.rb Log Message: Added some test cases for /scrape Index: tc_track.rb =================================================================== RCS file: /var/cvs/aversa/aversa/test/aversa/tc_track.rb,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** tc_track.rb 25 Nov 2004 11:46:17 -0000 1.6 --- tc_track.rb 25 Nov 2004 13:17:19 -0000 1.7 *************** *** 76,85 **** ! # XXX This test should be changed after implement it. ! # /scrape ... now Not implemented ! req = Request.new(TRACKER_URI + '/scrape') ! res = Response.parse(@http.get(req.to_s)) assert_equal(true, res.has_key?('files')) ! assert_equal(Hash, res['files'].type) end end --- 76,106 ---- ! ## ! ## /scrape ! ## ! def _get_scrape(info_hash=nil) ! req = Request.new(TRACKER_URI + '/scrape') ! req['info_hash'] = info_hash unless info_hash.nil? ! return Response.parse(@http.get(req.to_s)) ! end ! ! res = _get_scrape() assert_equal(true, res.has_key?('files')) ! assert_equal(Hash, res['files'].class) ! ! # /scrape test with info hash param ! # This test depend to tracker has key 'XXXXXXXXXXXXXXXXXXXX' ! res = _get_scrape('X' * 20) ! assert_equal(true, res.has_key?('files')) ! ! # /scrape test with Bad length of info hash param ! res = _get_scrape('X' * 21) ! assert_equal(true, res.has_key?('failure reason')) ! ! # /scrape test with Non-exists info hash param ! res = _get_scrape('YYYYYYYYYYYYYYYYYYYY') ! assert_equal(true, res.has_key?('failure reason')) ! ! end end