require 'date' require File.expand_path(File.dirname(__FILE__) + '/../test_helper') require 'email_notifier' class ProjectTest < Test::Unit::TestCase include FileSandbox def setup @svn = Object.new @project = Project.new("lemmings") @project.source_control = @svn end def test_default_scheduler assert_equal PollingScheduler, @project.scheduler.class end def test_builds in_sandbox do |sandbox| @project.path = sandbox.root sandbox.new :file => "build-1/build_status.success" sandbox.new :file => "build-10/build_status.success" sandbox.new :file => "build-3/build_status.failure" sandbox.new :file => "build-5/build_status.success" sandbox.new :file => "build-5.2/build_status.success" sandbox.new :file => "build-5.12/build_status.success" assert_equal("1 - success, 3 - failure, 5 - success, 5.2 - success, 5.12 - success, 10 - success", @project.builds.collect {|b| "#{b.label} - #{b.status}"}.join(", ")) assert_equal('10', @project.last_build.label) end end def test_project_should_know_last_complete_build in_sandbox do |sandbox| @project.path = sandbox.root sandbox.new :file => "build-1/build_status.success" sandbox.new :file => "build-2/build_status.failure" sandbox.new :file => "build-3/build_status.incomplete" assert_equal('2', @project.last_complete_build.label) end end def test_builds_should_return_empty_array_when_project_has_no_builds in_sandbox do |sandbox| @project.path = sandbox.root assert_equal [], @project.builds end end def test_should_build_with_no_logs in_sandbox do |sandbox| @project.path = sandbox.root revision = new_revision(5) build = new_mock_build('5') build.stubs(:artifacts_directory).returns(sandbox.root) @project.stubs(:builds).returns([]) @project.stubs(:config_modified?).returns(false) @svn.expects(:latest_revision).returns(revision) @svn.expects(:update).with(@project, revision) build.expects(:run) @project.build_if_necessary end end def test_build_if_necessary_should_generate_events in_sandbox do |sandbox| @project.path = sandbox.root revision = new_revision(5) build = new_mock_build('5') build.stubs(:artifacts_directory).returns(sandbox.root) @project.stubs(:builds).returns([]) @project.stubs(:config_modified?).returns(false) @svn.expects(:latest_revision).returns(revision) @svn.expects(:update).with(@project, revision) build.expects(:run) # event expectations listener = Object.new listener.expects(:polling_source_control) listener.expects(:new_revisions_detected).with([revision]) listener.expects(:build_started).with(build) listener.expects(:build_finished).with(build) listener.expects(:sleeping) @project.add_plugin listener @project.build_if_necessary end end def test_build_should_generate_event_when_build_loop_crashes in_sandbox do |sandbox| @project.path = sandbox.root @project.expects(:builds).returns([]) error = StandardError.new @svn.expects(:latest_revision).raises(error) # event expectations listener = Object.new listener.expects(:polling_source_control) listener.expects(:build_loop_failed).with(error) @project.add_plugin listener assert_raises(error) { @project.build_if_necessary } end end def test_build_should_generate_event_when_build_is_broken in_sandbox do |sandbox| @project.path = sandbox.root successful_build = stub_build(1) successful_build.stubs(:successful?).returns(true) successful_build.stubs(:failed?).returns(false) new_build = new_mock_build('2') new_build.stubs(:successful?).returns(false) new_build.stubs(:failed?).returns(true) new_build.expects(:run) @project.expects(:last_build).returns(successful_build) @project.stubs(:builds).returns([successful_build]) @project.stubs(:log_changeset) @svn.stubs(:update) # event expectations listener = Object.new listener.expects(:build_started) listener.expects(:build_finished) listener.expects(:build_broken) @project.add_plugin listener @project.build([new_revision(2)]) end end def test_build_should_detect_config_modifications in_sandbox do |sandbox| @project.path = sandbox.root revision = new_revision(1) @svn.expects(:update).with(@project, revision) do |*args| sandbox.new :file => 'work/cruise_config.rb' true end FileUtils.mkdir_p 'build-1' mock_build = Object.new Build.expects(:new).returns(mock_build) mock_build.expects(:artifacts_directory).returns('build-1') mock_build.expects(:abort) listener = Object.new listener.expects(:configuration_modified) @project.add_plugin listener assert_throws(:reload_project) { @project.build([revision]) } end end def test_notify_should_create_plugin_error_log_if_plugin_fails_and_notify_has_a_build in_sandbox do |sandbox| @project.path = sandbox.root mock_build = Object.new mock_build.stubs(:artifacts_directory).returns(sandbox.root) listener = Object.new listener.expects(:build_finished).with(mock_build).raises(StandardError.new("Listener failed")) @project.add_plugin listener assert_raises('Error in plugin Object: Listener failed') { @project.notify(:build_finished, mock_build) } assert_match /^Listener failed at/, File.read("#{mock_build.artifacts_directory}/plugin_errors.log") end end def test_notify_should_not_fail_if_plugin_fails_and_notify_has_no_build_or_no_arguments_at_all in_sandbox do |sandbox| @project.path = sandbox.root listener = Object.new listener.expects(:sleeping).raises(StandardError.new("Listener failed")) listener.expects(:doing_something).with(:foo).raises(StandardError.new("Listener failed with :foo")) @project.add_plugin listener assert_raises('Error in plugin Object: Listener failed') { @project.notify(:sleeping) } assert_raises('Error in plugin Object: Listener failed with :foo') { @project.notify(:doing_something, :foo) } end end def test_build_should_generate_event_when_build_is_fixed in_sandbox do |sandbox| @project.path = sandbox.root failing_build = stub_build(1) failing_build.stubs(:successful?).returns(false) failing_build.stubs(:failed?).returns(true) new_build = new_mock_build('2') new_build.stubs(:successful?).returns(true) new_build.stubs(:failed?).returns(false) new_build.expects(:run) @project.expects(:last_build).returns(failing_build) @project.stubs(:builds).returns([failing_build]) @project.stubs(:log_changeset) @svn.stubs(:update) # event expectations listener = Object.new listener.expects(:build_started) listener.expects(:build_finished) listener.expects(:build_fixed) @project.add_plugin listener @project.build([new_revision(2)]) end end def test_should_build_when_logs_are_not_current in_sandbox do |sandbox| @project.path = sandbox.root @project.stubs(:builds).returns([Build.new(@project, 1)]) @project.stubs(:config_modified?).returns(false) revision = new_revision(2) build = new_mock_build('2') @project.stubs(:last_build).returns(nil) build.stubs(:artifacts_directory).returns(sandbox.root) @svn.expects(:revisions_since).with(@project, 1).returns([revision]) @svn.expects(:update).with(@project, revision) build.expects(:run) @project.build_if_necessary end end def test_should_not_build_when_logs_are_current in_sandbox do |sandbox| @project.path = sandbox.root @project.stubs(:config_modified?).returns(false) @project.stubs(:builds).returns([Build.new(@project, 2)]) @svn.stubs(:revisions_since).with(@project, 2).returns([]) @project.expects(:build).never @project.build_if_necessary end end def test_either_rake_task_or_build_command_can_be_set_but_not_both @project.rake_task = 'foo' assert_raises("Cannot set build_command when rake_task is already defined") do @project.build_command = 'foo' end @project.rake_task = nil @project.build_command = 'foo' assert_raises("Cannot set rake_task when build_command is already defined") do @project.rake_task = 'foo' end end def test_notify_should_handle_plugin_error plugin = Object.new @project.plugins << plugin plugin.expects(:hey_you).raises("Plugin talking") assert_raises("Error in plugin Object: Plugin talking") { @project.notify(:hey_you) } end def test_notify_should_handle_multiple_plugin_errors plugin1 = Object.new plugin2 = Object.new @project.plugins << plugin1 << plugin2 plugin1.expects(:hey_you).raises("Plugin 1 talking") plugin2.expects(:hey_you).raises("Plugin 2 talking") assert_raises("Errors in plugins:\n Object: Plugin 1 talking\n Object: Plugin 2 talking") { @project.notify(:hey_you) } end def test_request_build_should_start_builder_if_builder_was_down in_sandbox do |sandbox| @project.path = sandbox.root @project.expects(:builder_state_and_activity).times(2).returns('builder_down', 'sleeping') BuilderStarter.expects(:begin_builder).with(@project.name) @project.request_build end end def test_request_build_should_generate_build_requested_file_and_notify_listeners @project.stubs(:builder_state_and_activity).returns('sleeping') in_sandbox do |sandbox| @project.path = sandbox.root listener = Object.new listener.expects(:build_requested) @project.add_plugin listener @project.request_build assert File.file?(@project.build_requested_flag_file) end end def test_request_build_should_not_notify_listeners_when_a_build_requested_flag_is_already_set @project.stubs(:builder_state_and_activity).returns('building') in_sandbox do |sandbox| @project.path = sandbox.root sandbox.new :file => 'build_requested' listener = Object.new listener.expects(:build_requested).never @project.expects(:build_requested?).returns(true) @project.expects(:create_build_requested_flag_file).never @project.request_build end end def test_build_if_requested_should_build_if_build_requested_file_exists in_sandbox do |sandbox| @project.path = sandbox.root sandbox.new :file => 'build_requested' @project.expects(:remove_build_requested_flag_file) @project.expects(:build) @project.build_if_requested end end def test_build_requested @project.stubs(:path).returns("a_path") File.expects(:file?).with(@project.build_requested_flag_file).returns(true) assert @project.build_requested? end def test_build_should_generate_new_label_if_same_name_label_exists existing_build1 = stub_build('20') existing_build2 = stub_build('20.1') new_build = stub_build('20.2') new_build_with_interesting_number = stub_build('2') project = Project.new('project1', @svn) @svn.stubs(:update) project.stubs(:log_changeset) project.stubs(:builds).returns([existing_build1, existing_build2]) project.stubs(:last_build).returns(nil) Build.expects(:new).with(project, '20.2').returns(new_build) project.build([new_revision(20)]) Build.expects(:new).with(project, '2').returns(new_build) project.build([new_revision(2)]) end def test_should_load_configuration_from_work_directory_and_then_root_directory in_sandbox do |sandbox| @project.path = sandbox.root begin sandbox.new :file => 'work/cruise_config.rb', :with_contents => '$foobar=42; $barfoo = 12345' sandbox.new :file => 'cruise_config.rb', :with_contents => '$barfoo = 54321' @project.load_config assert_equal 42, $foobar assert_equal 54321, $barfoo ensure $foobar = $barfoo = nil end end end def test_should_mark_config_invalid_if_exception_raised_during_load_config in_sandbox do |sandbox| invalid_ruby_code = 'class Invalid' @project.path = sandbox.root sandbox.new :file => 'work/cruise_config.rb', :with_contents => invalid_ruby_code @project.load_config assert @project.settings.empty? assert_equal invalid_ruby_code, @project.config_file_content.strip assert !@project.config_valid? assert_match /Could not load project configuration:/, @project.error_message end end def test_should_remember_settings in_sandbox do |sandbox| @project.path = sandbox.root sandbox.new :file => 'work/cruise_config.rb', :with_contents => 'good = 4' sandbox.new :file => 'cruise_config.rb', :with_contents => 'time = 5' @project.load_config assert_equal "good = 4\ntime = 5\n", @project.settings assert @project.config_valid? assert @project.error_message.empty? end end def test_last_complete_build_status_should_be_failed_if_builder_status_is_fatal builder_status = Object.new builder_status.expects(:"fatal?").returns(true) BuilderStatus.expects(:new).with(@project).returns(builder_status) assert_equal "failed", @project.last_complete_build_status end def test_should_be_able_to_get_previous_build in_sandbox do |sandbox| @project.path = sandbox.root sandbox.new :file => "build-1/build_status.success" sandbox.new :file => "build-2/build_status.failure" sandbox.new :file => "build-3/build_status.incomplete" build = @project.find_build('2') assert_equal('1', @project.previous_build(build).label) build = @project.find_build('1') assert_equal(nil, @project.previous_build(build)) end end def test_should_be_able_to_get_next_build in_sandbox do |sandbox| @project.path = sandbox.root sandbox.new :file => "build-1/build_status.success" sandbox.new :file => "build-2/build_status.failure" sandbox.new :file => "build-3/build_status.incomplete" build = @project.find_build('1') assert_equal('2', @project.next_build(build).label) build = @project.find_build('2') assert_equal('3', @project.next_build(build).label) build = @project.find_build('3') assert_equal(nil, @project.next_build(build)) end end def test_should_be_able_to_get_last_n_builds in_sandbox do |sandbox| @project.path = sandbox.root sandbox.new :file => "build-1/build_status.success" sandbox.new :file => "build-2/build_status.failure" sandbox.new :file => "build-3/build_status.incomplete" assert_equal 2, @project.last_builds(2).length assert_equal 3, @project.last_builds(5).length end end private def stub_build(label) build = Object.new build.stubs(:label).returns(label) build.stubs(:artifacts_directory).returns("project1/build_#{label}") build.stubs(:run) build end def new_revision(number) Revision.new(number, 'alex', DateTime.new(2005, 1, 1), 'message', []) end def new_mock_build(label) build = Object.new Build.expects(:new).with(@project, label).returns(build) build.stubs(:artifacts_directory).returns("project1/build-#{label}") build.stubs(:last).returns(nil) build.stubs(:label).returns(label) build end end