From: <jh...@us...> - 2009-11-06 00:47:46
|
Revision: 137 http://etch.svn.sourceforge.net/etch/?rev=137&view=rev Author: jheiss Date: 2009-11-06 00:47:38 +0000 (Fri, 06 Nov 2009) Log Message: ----------- Fix bug where specific dependency structures requested in specific ways on the command line might never complete due to the client and server never getting on the same page such that a complete dependency tree can be calculated. Add test cases for same. See code comments for details. Modified Paths: -------------- trunk/server/lib/etch.rb trunk/test/options.rb Modified: trunk/server/lib/etch.rb =================================================================== --- trunk/server/lib/etch.rb 2009-11-06 00:43:46 UTC (rev 136) +++ trunk/server/lib/etch.rb 2009-11-06 00:47:38 UTC (rev 137) @@ -305,12 +305,43 @@ r = generate_commands(Etch.xmltext(dependcommand), request) proceed = proceed && r end + if !proceed @dlogger.debug "One or more dependencies of #{file} need data from client" + end + + # Make sure we have the original contents for this file + original_file = nil + if request[:files] && request[:files][file] && request[:files][file][:orig] + original_file = request[:files][file][:orig] + else + @dlogger.debug "Need original contents of #{file} from client" + proceed = false + end + + if !proceed + # If any file dependency failed to generate (due to a need for orig + # contents from the client) then we need to tell the client to request + # all of the files in the dependency tree again. + # + # For example, we have afile which depends on bfile and cfile. The + # user requests afile and bfile on the command line. The client sends + # sums for afile and bfile. The server sees the need for cfile's sum, so + # it sends back contents for bfile and a sum request for cfile and afile + # (since afile depends on bfile). The client sends sums for afile and + # cfile. The server sends back contents for cfile, and a sum request for + # bfile and afile. This repeats forever as the server isn't smart enough + # to ask for everything it needs and the client isn't smart enough to send + # everything. + depends.each { |depend| @need_orig[depend] = true } + # Tell the client to request this file again @need_orig[file] = true + # Strip this file's config down to the bare necessities filter_xml_completely!(config_xml, ['depend', 'setup']) + + # And hit the eject button generation_status = false throw :generate_done end @@ -341,24 +372,6 @@ end end end - - # Make sure we have the original contents for this file - original_file = nil - if request[:files] && request[:files][file] && request[:files][file][:orig] - original_file = request[:files][file][:orig] - else - @dlogger.debug "Need original contents of #{file} from client" - @need_orig[file] = true - # If there are setup commands defined for this file we need to - # pass those back along with our request for the original file, - # as the setup commands may be needed to create the original - # file on the node. - filter_xml_completely!(config_xml, ['depend', 'setup']) - # Nothing more can be done until we have the original file from - # the client - generation_status = false - throw :generate_done - end # Pull out any local requests local_requests = nil @@ -852,6 +865,7 @@ # If we encounter either failure or success we set it to false or :success. catch :generate_done do # Generate any other commands that this command depends on + dependfiles = [] proceed = true Etch.xmleach(commands_xml, '/commands/depend') do |depend| @dlogger.debug "Generating command dependency #{Etch.xmltext(depend)}" @@ -861,10 +875,17 @@ # Also generate any files that this command depends on Etch.xmleach(commands_xml, '/commands/dependfile') do |dependfile| @dlogger.debug "Generating file dependency #{Etch.xmltext(dependfile)}" + dependfiles << Etch.xmltext(dependfile) r = generate_file(Etch.xmltext(dependfile), request) proceed = proceed && r end if !proceed + @dlogger.debug "One or more dependencies of #{command} need data from client" + # If any file dependency failed to generate (due to a need for orig + # contents from the client) then we need to tell the client to request + # all of the files in the dependency tree again. See the big comment + # in generate_file for further explanation. + dependfiles.each { |dependfile| @need_orig[dependfile] = true } # Try again next time @retrycommands[command] = true generation_status = false Modified: trunk/test/options.rb =================================================================== --- trunk/test/options.rb 2009-11-06 00:43:46 UTC (rev 136) +++ trunk/test/options.rb 2009-11-06 00:47:38 UTC (rev 137) @@ -247,6 +247,183 @@ assert_equal(origcontents, get_file_contents(cmdtargetfile3), testname + ' cmd 3') end + def test_file_requests_with_depends + # + # Test that the user can request specific files and commands on the + # command line with a dependency structure such that, in the right + # circumstances, a poor implementation never completes because it + # alternately sends/requests the orig sum for the two dependencies, never + # sending both at the same time. + # + # For example, we have afile which depends on bfile and cfile. The user + # requests afile and bfile on the command line. The client sends sums for + # afile and bfile. The server sees the need for cfile's sum, so it sends + # back contents for bfile and a sum request for cfile and afile (since + # afile depends on bfile). The client sends sums for afile and cfile. + # The server sends back contents for cfile, and a sum request for bfile + # and afile. This repeats forever as the server isn't smart enough to ask + # for everything it needs and the client isn't smart enough to send + # everything. + # + # Yup, had this bug at one point. + # + testname = 'command line file requests with depends' + + targetfile2 = Tempfile.new('etchtest').path + targetfile3 = Tempfile.new('etchtest').path + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <depend>#{targetfile2}</depend> + <depend>#{targetfile3}</depend> + <file> + <warning_file></warning_file> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + FileUtils.mkdir_p("#{@repodir}/source/#{targetfile2}") + File.open("#{@repodir}/source/#{targetfile2}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file></warning_file> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + FileUtils.mkdir_p("#{@repodir}/source/#{targetfile3}") + File.open("#{@repodir}/source/#{targetfile3}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file></warning_file> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + + sourcecontents = "Test #{testname}\n" + [@targetfile, targetfile2, targetfile3].each do |target| + File.open("#{@repodir}/source/#{target}/source", 'w') do |file| + file.write(sourcecontents) + end + end + + # Run etch + #puts "Running '#{testname}' test" + run_etch(@port, @testbase, false, "#{@targetfile} #{targetfile2}") + + # Verify that all were created + assert_equal(sourcecontents, get_file_contents(@targetfile), testname + ' filesonly file 1') + assert_equal(sourcecontents, get_file_contents(targetfile2), testname + ' filesonly file 2') + assert_equal(sourcecontents, get_file_contents(targetfile3), testname + ' filesonly file 3') + end + + def test_mixed_requests_with_depends + # + # Similar to previous test, but mixing file and command requests + # + testname = 'mixed command line requests with depends' + + targetfile2 = Tempfile.new('etchtest').path + targetfile3 = Tempfile.new('etchtest').path + FileUtils.mkdir_p("#{@repodir}/source/#{@targetfile}") + File.open("#{@repodir}/source/#{@targetfile}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <depend>#{targetfile2}</depend> + <depend>#{targetfile3}</depend> + <file> + <warning_file></warning_file> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + FileUtils.mkdir_p("#{@repodir}/source/#{targetfile2}") + File.open("#{@repodir}/source/#{targetfile2}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file></warning_file> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + FileUtils.mkdir_p("#{@repodir}/source/#{targetfile3}") + File.open("#{@repodir}/source/#{targetfile3}/config.xml", 'w') do |file| + file.puts <<-EOF + <config> + <file> + <warning_file></warning_file> + <source> + <plain>source</plain> + </source> + </file> + </config> + EOF + end + + sourcecontents = "Test #{testname}\n" + [@targetfile, targetfile2, targetfile3].each do |target| + File.open("#{@repodir}/source/#{target}/source", 'w') do |file| + file.write(sourcecontents) + end + end + + cmdtargetfile1 = Tempfile.new('etchtest').path + FileUtils.mkdir_p("#{@repodir}/commands/etchtest1") + File.open("#{@repodir}/commands/etchtest1/commands.xml", 'w') do |file| + file.puts <<-EOF + <commands> + <dependfile>#{targetfile2}</dependfile> + <dependfile>#{targetfile3}</dependfile> + <step> + <guard> + <exec>grep '#{testname}' #{cmdtargetfile1}</exec> + </guard> + <command> + <exec>printf '#{testname}' >> #{cmdtargetfile1}</exec> + </command> + </step> + </commands> + EOF + end + + # Put some text into the original files. + origcontents = "This is the original text\n" + [@targetfile, targetfile2, targetfile3, cmdtargetfile1].each do |target| + File.open(target, 'w') do |file| + file.write(origcontents) + end + end + + # Run etch + #puts "Running '#{testname}' test" + run_etch(@port, @testbase, false, "etchtest1 #{targetfile2}") + + # Verify that all were created + assert_equal(origcontents + testname, get_file_contents(cmdtargetfile1), testname + ' cmdandfile cmd 1') + assert_equal(sourcecontents, get_file_contents(targetfile2), testname + ' cmdandfile file 2') + assert_equal(sourcecontents, get_file_contents(targetfile3), testname + ' cmdandfile file 3') + end + def teardown stop_server(@pid) remove_repository(@repodir) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |