# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with this # work for additional information regarding copyright ownership. The ASF # licenses this file to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. require File.expand_path('../spec_helpers', File.dirname(__FILE__)) require 'stringio' Sandbox.require_optional_extension 'buildr/drb' describe Buildr::DRbApplication do module DRbHelper attr_accessor :app, :drb, :cfg def use_stdio(stdin = nil, stdout = nil, stderr = nil) stdin ||= StringIO.new stdout ||= StringIO.new stderr ||= StringIO.new cfg.update :in => stdin, :out => stdout, :err => stderr end def remote_run(*argv) cfg.update :argv => argv drb.remote_run(cfg) end def output cfg[:out].string end def write_buildfile(content = nil) write 'buildfile', content || %q{ define('foo') do rule '.rbc' => '.rb' do |t| $stdout.puts "#{t.name} from #{t.source}" end task('hello') do $stdout.puts 'hi' end task('empty') task('no') do task('empty').enhance ['delete_me'] task('empty') { $stdout.puts 'no' } end task('delete_me') task('create') do Rake::Task.define_task('created') rule '.rbc' => '.rb' do |t| $stdout.puts "#{t.name} from #{t.source}" end end task('exists') do $stdout.puts !!Buildr.application.lookup('created') end task('setopt', :name, :value) do |task, args| Buildr.application.options.send("#{args[:name]}=", args[:value]) end end } end end include DRbHelper before(:each) do @in, @out, @err = $stdin, $stdout, $stderr @cfg = { :dir => Dir.pwd, :argv => [], :in => @in, :out => @out, :err => @err } @drb = Buildr::DRbApplication.clone @drb.send :setup @app = Buildr.application end after(:each) do $stdin, $stdout, $stderr = @in, @out, @err end describe '.run' do it 'starts server if no server is running' do drb.should_receive(:connect).and_raise DRb::DRbConnError drb.should_receive(:run_server!) drb.should_not_receive(:run_client) drb.run end it 'connects to an already started server' do drb.should_receive(:connect).and_return "client" drb.should_receive(:run_client).with "client" drb.should_not_receive(:run_server!) drb.run end end describe '.remote_run' do describe 'stdout' do it 'is redirected to client' do use_stdio Buildr.application.should_receive(:remote_run) do $stdout.puts "HELLO" end remote_run output.should eql("HELLO\n") end end describe 'stderr' do it 'is redirected to client' do use_stdio Buildr.application.should_receive(:remote_run) do $stderr.puts "HELLO" end remote_run cfg[:err].string.should eql("HELLO\n") end end describe 'stdin' do it 'is redirected to client' do use_stdio cfg[:in].should_receive(:gets).and_return("HELLO\n") result = nil Buildr.application.should_receive(:remote_run) do result = $stdin.gets end remote_run result.should eql("HELLO\n") end end describe 'server ARGV' do it 'is replaced with client argv' do Buildr.application.should_receive(:remote_run) do ARGV.should eql(['hello']) end remote_run 'hello' end end describe 'without buildfile loaded' do before(:each) do app.instance_eval { @rakefile = nil } write_buildfile end it 'should load the buildfile' do app.should_receive(:top_level) lambda { remote_run }.should run_task('foo') end end describe 'with unmodified buildfile' do before(:each) do write_buildfile app.options.rakelib = [] app.send :load_buildfile drb.save_snapshot(app) end it 'should not reload the buildfile' do app.should_not_receive(:reload_buildfile) app.should_receive(:top_level) remote_run end it 'should not define projects again' do use_stdio lambda { 2.times { remote_run 'foo:hello' } }.should_not run_task('foo') output.should eql("hi\nhi\n") end it 'should restore task actions' do use_stdio remote_run 'foo:empty' output.should be_empty 2.times { remote_run 'foo:no' } remote_run 'foo:empty' actions = app.lookup('foo:empty').instance_eval { @actions } actions.should be_empty # as originally defined output.should be_empty end it 'should restore task prerequisites' do use_stdio remote_run 'foo:empty' output.should be_empty 2.times { remote_run 'foo:no' } remote_run 'foo:empty' pres = app.lookup('foo:empty').send(:prerequisites).map(&:to_s) pres.should be_empty # as originally defined output.should be_empty end it 'should drop runtime created tasks' do remote_run 'foo:create' app.lookup('created').should_not be_nil remote_run 'foo:empty' app.lookup('created').should be_nil end it 'should restore options' do remote_run 'foo:setopt[bar,baz]' app.options.bar.should eql("baz") remote_run 'foo:empty' app.options.bar.should be_nil end it 'should restore rules' do orig = app.instance_eval { @rules.size } remote_run 'foo:create' app.instance_eval { @rules.size }.should eql(orig + 1) remote_run 'foo:empty' app.instance_eval { @rules.size }.should eql(orig) end end describe 'with modified buildfile' do before(:each) do write_buildfile app.options.rakelib = [] app.send :load_buildfile drb.save_snapshot(app) app.instance_eval { @last_loaded = Time.now - 10 } write_buildfile %q{ rule '.rbc' => '.rb' do |t| $stdout.puts "#{t.name} from #{t.source}" end define('foo') do task('hello') do $stdout.puts 'bye' end task('empty') define('bar') do end end } end it 'should reload the buildfile' do app.should_receive(:reload_buildfile) app.should_receive(:top_level) remote_run end it 'should redefine projects' do lambda { remote_run }.should run_tasks('foo', 'foo:bar') end it 'should remove tasks deleted from buildfile' do app.lookup('foo:delete_me').should_not be_nil remote_run app.lookup('foo:delete_me').should be_nil end it 'should redefine tasks actions' do actions = app.lookup('foo:empty').instance_eval { @actions } actions.should be_empty # no action app.lookup('foo:no').invoke # enhance the empty task actions = app.lookup('foo:empty').instance_eval { @actions } actions.should_not be_empty remote_run # cause to reload the buildfile actions = app.lookup('foo:empty').instance_eval { @actions } actions.should be_empty # as defined on the new buildfile end it 'should redefine task prerequisites' do pres = app.lookup('foo:empty').send(:prerequisites).map(&:to_s) pres.should be_empty # no action app.lookup('foo:no').invoke # enhance the empty task pres = app.lookup('foo:empty').send(:prerequisites).map(&:to_s) pres.should_not be_empty remote_run # cause to reload the buildfile pres = app.lookup('foo:empty').send(:prerequisites).map(&:to_s) pres.should be_empty # as defined on the new buildfile end it 'should drop runtime created tasks' do app.lookup('foo:create').invoke app.lookup('created').should_not be_nil remote_run 'foo:empty' app.lookup('created').should be_nil end it 'should restore options' do app.options.bar = 'baz' remote_run 'foo:empty' app.options.bar.should be_nil end it 'should redefine rules' do orig = app.instance_eval { @rules.size } app.lookup('foo:create').invoke app.instance_eval { @rules.size }.should eql(orig + 1) remote_run 'foo:empty' app.instance_eval { @rules.size }.should eql(orig) end end end end