PageRenderTime 27ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/test/unit/project_test.rb

http://github.com/benburkert/cruisecontrolrb
Ruby | 661 lines | 513 code | 144 blank | 4 comment | 0 complexity | f952f6bc48b0f26c269f83100a94e75f MD5 | raw file
Possible License(s): Apache-2.0
  1. require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
  2. require 'date'
  3. require 'ostruct'
  4. require 'email_notifier'
  5. require 'fileutils'
  6. class ProjectTest < Test::Unit::TestCase
  7. include FileSandbox
  8. def setup
  9. @svn = FakeSourceControl.new
  10. @project = Project.new("lemmings")
  11. @project.source_control = @svn
  12. end
  13. def test_default_scheduler
  14. assert_equal PollingScheduler, @project.scheduler.class
  15. end
  16. def test_builds
  17. in_sandbox do |sandbox|
  18. @project.path = sandbox.root
  19. sandbox.new :directory => "build-1-success.in1s/"
  20. sandbox.new :directory => "build-10-success.in1s/"
  21. sandbox.new :directory => "build-3-failure.in1s/"
  22. sandbox.new :directory => "build-5-success.in1s/"
  23. sandbox.new :directory => "build-5.2-success.in1s/"
  24. sandbox.new :directory => "build-5.12-success.in1s/"
  25. assert_equal("1 - success - 1, 3 - failure - 3, 5 - success - 5, 5.2 - success - 5, 5.12 - success - 5, 10 - success - 10",
  26. @project.builds.collect {|b| "#{b.label} - #{b.status} - #{b.revision}"}.join(", "))
  27. assert_equal('10', @project.last_build.label)
  28. end
  29. end
  30. def test_project_should_know_last_complete_build
  31. in_sandbox do |sandbox|
  32. @project.path = sandbox.root
  33. sandbox.new :directory => "build-1-success.in1s/"
  34. sandbox.new :directory => "build-2-failure.in1s/"
  35. sandbox.new :directory => "build-3/"
  36. assert_equal '2', @project.last_complete_build.label
  37. end
  38. end
  39. def test_last_complete_build_should_return_nil_when_there_are_no_complete_builds
  40. in_sandbox do |sandbox|
  41. sandbox.new :directory => "build-3/"
  42. assert_nil @project.last_complete_build
  43. end
  44. end
  45. def test_builds_should_return_empty_array_when_project_has_no_builds
  46. in_sandbox do |sandbox|
  47. @project.path = sandbox.root
  48. assert_equal [], @project.builds
  49. end
  50. end
  51. def test_should_build_with_no_logs
  52. in_sandbox do |sandbox|
  53. @project.path = sandbox.root
  54. revision = new_revision(5)
  55. build = new_mock_build('5')
  56. build.stubs(:artifacts_directory).returns(sandbox.root)
  57. @project.stubs(:builds).returns([])
  58. @project.stubs(:config_modified?).returns(false)
  59. @svn.stubs(:latest_revision).returns(revision)
  60. @svn.expects(:update).with(revision)
  61. build.expects(:run)
  62. @project.build_if_necessary
  63. end
  64. end
  65. def test_build_if_necessary_should_generate_events
  66. in_sandbox do |sandbox|
  67. @project.path = sandbox.root
  68. revision = new_revision(5)
  69. build = new_mock_build('5')
  70. build.stubs(:artifacts_directory).returns(sandbox.root)
  71. build.stubs(:successful?).returns(false)
  72. build.stubs(:failed?).returns(false)
  73. @project.stubs(:builds).returns([OpenStruct.new(:successful? => false, :failed? => false)])
  74. @project.stubs(:config_modified?).returns(false)
  75. @svn.stubs(:up_to_date?).with{|reasons| reasons << [revision]}.returns(false)
  76. @svn.stubs(:latest_revision).returns(revision)
  77. @svn.expects(:update).with(revision)
  78. build.expects(:run)
  79. # event expectations
  80. listener = Object.new
  81. listener.expects(:polling_source_control)
  82. listener.expects(:new_revisions_detected).with([revision])
  83. listener.expects(:build_started).with(build)
  84. listener.expects(:build_finished).with(build)
  85. listener.expects(:sleeping)
  86. @project.add_plugin listener
  87. @project.build_if_necessary
  88. end
  89. end
  90. def test_build_should_generate_event_when_build_loop_crashes
  91. in_sandbox do |sandbox|
  92. @project.path = sandbox.root
  93. @project.expects(:builds).returns([])
  94. error = StandardError.new
  95. @svn.expects(:latest_revision).raises(error)
  96. # event expectations
  97. listener = Object.new
  98. listener.expects(:build_loop_failed).with(error)
  99. @project.add_plugin listener
  100. assert_raises(error) { @project.build_if_necessary }
  101. end
  102. end
  103. def test_build_should_fail_if_subversion_error
  104. in_sandbox do
  105. @project.path = sandbox.root
  106. error = StandardError.new("something bad happened")
  107. @project.expects(:update_project_to_revision).raises(error)
  108. assert_raises(error) { @project.build(new_revision(5)) }
  109. build = @project.builds.first
  110. assert build.failed?
  111. assert_equal "something bad happened", build.error
  112. end
  113. end
  114. def test_build_should_generate_event_when_build_is_broken
  115. in_sandbox do |sandbox|
  116. @project.path = sandbox.root
  117. successful_build = stub_build(1)
  118. successful_build.stubs(:successful?).returns(true)
  119. successful_build.stubs(:failed?).returns(false)
  120. new_build = new_mock_build('2')
  121. new_build.stubs(:successful?).returns(false)
  122. new_build.stubs(:failed?).returns(true)
  123. new_build.expects(:run)
  124. @project.expects(:last_build).returns(successful_build)
  125. @project.stubs(:builds).returns([successful_build])
  126. @project.stubs(:log_changeset)
  127. @project.stubs(:new_revisions).returns(nil)
  128. @svn.stubs(:update)
  129. # event expectations
  130. listener = Object.new
  131. listener.expects(:build_started)
  132. listener.expects(:build_finished)
  133. listener.expects(:build_broken)
  134. @project.add_plugin listener
  135. @project.build(new_revision(2))
  136. end
  137. end
  138. def test_build_should_detect_config_modifications
  139. in_sandbox do |sandbox|
  140. @project.path = sandbox.root
  141. revision = new_revision(1)
  142. @svn.expects(:update).with(@project, revision) do |*args|
  143. sandbox.new :file => 'work/cruise_config.rb'
  144. true
  145. end
  146. FileUtils.mkdir_p 'build-1-success.in40s'
  147. mock_build = Object.new
  148. Build.stubs(:new).returns(mock_build)
  149. mock_build.stubs(:label).returns("1")
  150. mock_build.expects(:artifacts_directory).returns('build-1-success.in40s')
  151. mock_build.expects(:abort)
  152. @project.stubs(:new_revisions).returns(nil)
  153. listener = Object.new
  154. listener.expects(:configuration_modified)
  155. @project.add_plugin listener
  156. assert_throws(:reload_project) { @project.build(revision) }
  157. end
  158. end
  159. def test_notify_should_create_plugin_error_log_if_plugin_fails_and_notify_has_a_build
  160. in_sandbox do |sandbox|
  161. @project.path = sandbox.root
  162. mock_build = Object.new
  163. mock_build.stubs(:artifacts_directory).returns(sandbox.root)
  164. listener = Object.new
  165. listener.expects(:build_finished).with(mock_build).raises(StandardError.new("Listener failed"))
  166. @project.add_plugin listener
  167. assert_raises('Error in plugin Object: Listener failed') { @project.notify(:build_finished, mock_build) }
  168. assert_match /^Listener failed at/, File.read("#{mock_build.artifacts_directory}/plugin_errors.log")
  169. end
  170. end
  171. def test_notify_should_not_fail_if_plugin_fails_and_notify_has_no_build_or_no_arguments_at_all
  172. in_sandbox do |sandbox|
  173. @project.path = sandbox.root
  174. listener = Object.new
  175. listener.expects(:sleeping).raises(StandardError.new("Listener failed"))
  176. listener.expects(:doing_something).with(:foo).raises(StandardError.new("Listener failed with :foo"))
  177. @project.add_plugin listener
  178. assert_raises('Error in plugin Object: Listener failed') { @project.notify(:sleeping) }
  179. assert_raises('Error in plugin Object: Listener failed with :foo') { @project.notify(:doing_something, :foo) }
  180. end
  181. end
  182. def test_build_should_generate_event_when_build_is_fixed
  183. in_sandbox do |sandbox|
  184. @project.path = sandbox.root
  185. failing_build = stub_build(1)
  186. failing_build.stubs(:successful?).returns(false)
  187. failing_build.stubs(:failed?).returns(true)
  188. new_build = new_mock_build('2')
  189. new_build.stubs(:successful?).returns(true)
  190. new_build.stubs(:failed?).returns(false)
  191. new_build.expects(:run)
  192. @project.expects(:last_build).returns(failing_build)
  193. @project.stubs(:builds).returns([failing_build])
  194. @project.stubs(:log_changeset)
  195. @project.stubs(:new_revisions).returns(nil)
  196. @svn.stubs(:update)
  197. # event expectations
  198. listener = Object.new
  199. listener.expects(:build_started)
  200. listener.expects(:build_finished)
  201. listener.expects(:build_fixed)
  202. @project.add_plugin listener
  203. @project.build(new_revision(2))
  204. end
  205. end
  206. def test_should_build_when_logs_are_not_current
  207. in_sandbox do |sandbox|
  208. @project.path = sandbox.root
  209. @project.stubs(:builds).returns([Build.new(@project, 1)])
  210. @project.stubs(:config_modified?).returns(false)
  211. revision = new_revision(2)
  212. build = new_mock_build('2')
  213. @project.stubs(:last_build).returns(nil)
  214. build.stubs(:artifacts_directory).returns(sandbox.root)
  215. @svn.stubs(:up_to_date?).with([]).returns(false)
  216. @svn.expects(:update).with(revision)
  217. @svn.expects(:latest_revision).returns(revision)
  218. build.expects(:run)
  219. @project.build_if_necessary
  220. end
  221. end
  222. def test_should_not_build_when_logs_are_current
  223. in_sandbox do |sandbox|
  224. @project.path = sandbox.root
  225. @project.stubs(:config_modified?).returns(false)
  226. @project.stubs(:builds).returns([Build.new(@project, 2)])
  227. @svn.stubs(:revisions_since).with(@project, 2).returns([])
  228. @project.expects(:build).never
  229. @project.build_if_necessary
  230. end
  231. end
  232. def test_either_rake_task_or_build_command_can_be_set_but_not_both
  233. @project.rake_task = 'foo'
  234. assert_raises("Cannot set build_command when rake_task is already defined") do
  235. @project.build_command = 'foo'
  236. end
  237. @project.rake_task = nil
  238. @project.build_command = 'foo'
  239. assert_raises("Cannot set rake_task when build_command is already defined") do
  240. @project.rake_task = 'foo'
  241. end
  242. end
  243. def test_notify_should_handle_plugin_error
  244. plugin = Object.new
  245. @project.plugins << plugin
  246. plugin.expects(:hey_you).raises("Plugin talking")
  247. assert_raises("Error in plugin Object: Plugin talking") { @project.notify(:hey_you) }
  248. end
  249. def test_notify_should_handle_multiple_plugin_errors
  250. plugin1 = Object.new
  251. plugin2 = Object.new
  252. @project.plugins << plugin1 << plugin2
  253. plugin1.expects(:hey_you).raises("Plugin 1 talking")
  254. plugin2.expects(:hey_you).raises("Plugin 2 talking")
  255. assert_raises("Errors in plugins:\n Object: Plugin 1 talking\n Object: Plugin 2 talking") { @project.notify(:hey_you) }
  256. end
  257. def test_request_build_should_start_builder_if_builder_was_down
  258. in_sandbox do |sandbox|
  259. @project.path = sandbox.root
  260. @project.expects(:builder_state_and_activity).times(2).returns('builder_down', 'sleeping')
  261. BuilderStarter.expects(:begin_builder).with(@project.name)
  262. @project.request_build
  263. end
  264. end
  265. def test_request_build_should_generate_build_requested_file_and_notify_listeners
  266. @project.stubs(:builder_state_and_activity).returns('sleeping')
  267. in_sandbox do |sandbox|
  268. @project.path = sandbox.root
  269. listener = Object.new
  270. listener.expects(:build_requested)
  271. @project.add_plugin listener
  272. @project.request_build
  273. assert File.file?(@project.build_requested_flag_file)
  274. end
  275. end
  276. def test_request_build_should_not_notify_listeners_when_a_build_requested_flag_is_already_set
  277. @project.stubs(:builder_state_and_activity).returns('building')
  278. in_sandbox do |sandbox|
  279. @project.path = sandbox.root
  280. sandbox.new :file => 'build_requested'
  281. listener = Object.new
  282. listener.expects(:build_requested).never
  283. @project.expects(:build_requested?).returns(true)
  284. @project.expects(:create_build_requested_flag_file).never
  285. @project.request_build
  286. end
  287. end
  288. def test_build_if_requested_should_build_if_build_requested_file_exists
  289. in_sandbox do |sandbox|
  290. @project.path = sandbox.root
  291. sandbox.new :file => 'build_requested'
  292. @project.expects(:remove_build_requested_flag_file)
  293. @project.expects(:build)
  294. @project.build_if_requested
  295. end
  296. end
  297. def test_build_requested
  298. @project.stubs(:path).returns("a_path")
  299. File.expects(:file?).with(@project.build_requested_flag_file).returns(true)
  300. assert @project.build_requested?
  301. end
  302. def test_build_should_generate_new_label_if_same_name_label_exists
  303. existing_build1 = stub_build('20')
  304. existing_build2 = stub_build('20.1')
  305. new_build = stub_build('20.2')
  306. new_build_with_interesting_number = stub_build('2')
  307. project = Project.new('project1', @svn)
  308. @svn.stubs(:update)
  309. project.stubs(:log_changeset)
  310. project.stubs(:builds).returns([existing_build1, existing_build2])
  311. project.stubs(:last_build).returns(nil)
  312. project.stubs(:new_revisions).returns(nil)
  313. Build.expects(:new).with(project, '20.2').returns(new_build)
  314. project.build(new_revision(20))
  315. Build.expects(:new).with(project, '2').returns(new_build)
  316. project.build(new_revision(2))
  317. end
  318. def test_should_load_configuration_from_work_directory_and_then_root_directory
  319. in_sandbox do |sandbox|
  320. @project.path = sandbox.root
  321. begin
  322. sandbox.new :file => 'work/cruise_config.rb', :with_contents => '$foobar=42; $barfoo = 12345'
  323. sandbox.new :file => 'cruise_config.rb', :with_contents => '$barfoo = 54321'
  324. @project.load_config
  325. assert_equal 42, $foobar
  326. assert_equal 54321, $barfoo
  327. ensure
  328. $foobar = $barfoo = nil
  329. end
  330. end
  331. end
  332. def test_should_mark_config_invalid_if_exception_raised_during_load_config
  333. in_sandbox do |sandbox|
  334. invalid_ruby_code = 'class Invalid'
  335. @project.path = sandbox.root
  336. sandbox.new :file => 'work/cruise_config.rb', :with_contents => invalid_ruby_code
  337. @project.load_config
  338. assert @project.settings.empty?
  339. assert_equal invalid_ruby_code, @project.config_file_content.strip
  340. assert !@project.config_valid?
  341. assert_match /Could not load project configuration:/, @project.error_message
  342. end
  343. end
  344. def test_should_remember_settings
  345. in_sandbox do |sandbox|
  346. @project.path = sandbox.root
  347. sandbox.new :file => 'work/cruise_config.rb', :with_contents => 'good = 4'
  348. sandbox.new :file => 'cruise_config.rb', :with_contents => 'time = 5'
  349. @project.load_config
  350. assert_equal "good = 4\ntime = 5\n", @project.settings
  351. assert @project.config_valid?
  352. assert @project.error_message.empty?
  353. end
  354. end
  355. def test_last_complete_build_status_should_be_failed_if_builder_status_is_fatal
  356. builder_status = Object.new
  357. builder_status.expects(:"fatal?").returns(true)
  358. BuilderStatus.expects(:new).with(@project).returns(builder_status)
  359. assert_equal "failed", @project.last_complete_build_status
  360. end
  361. def test_should_be_able_to_get_previous_build
  362. in_sandbox do |sandbox|
  363. @project.path = sandbox.root
  364. sandbox.new :directory => "build-1-success/"
  365. sandbox.new :directory => "build-2-failure/"
  366. sandbox.new :directory => "build-3"
  367. build = @project.find_build('2')
  368. assert_equal('1', @project.previous_build(build).label)
  369. build = @project.find_build('1')
  370. assert_equal(nil, @project.previous_build(build))
  371. end
  372. end
  373. def test_should_be_able_to_get_next_build
  374. in_sandbox do |sandbox|
  375. @project.path = sandbox.root
  376. sandbox.new :directory => "build-1-success.in1s/"
  377. sandbox.new :directory => "build-2-failure.in1s/"
  378. sandbox.new :directory => "build-3/"
  379. build = @project.find_build('1')
  380. assert_equal('2', @project.next_build(build).label)
  381. build = @project.find_build('2')
  382. assert_equal('3', @project.next_build(build).label)
  383. build = @project.find_build('3')
  384. assert_equal(nil, @project.next_build(build))
  385. end
  386. end
  387. def test_should_be_able_to_get_last_n_builds
  388. in_sandbox do |sandbox|
  389. @project.path = sandbox.root
  390. sandbox.new :directory => "build-1-success.in1s/"
  391. sandbox.new :directory => "build-2-failure.in1s/"
  392. sandbox.new :directory => "build-3/"
  393. assert_equal 2, @project.last_builds(2).length
  394. assert_equal 3, @project.last_builds(5).length
  395. end
  396. end
  397. def test_should_do_clean_checkout_if_flag_is_set
  398. in_sandbox do |sandbox|
  399. @project.do_clean_checkout :always
  400. @project.path = sandbox.root
  401. @svn.expects(:clean_checkout).with(Revision.new(5), anything)
  402. @project.build(new_revision(5))
  403. end
  404. end
  405. def test_build_when_no_revision_yet
  406. in_sandbox do |sandbox|
  407. @project.path = sandbox.root
  408. @svn.stubs(:latest_revision).returns(nil)
  409. assert_nil @project.build
  410. end
  411. end
  412. def test_build_should_still_build_even_when_no_changes_were_made
  413. in_sandbox do |sandbox|
  414. @project.path = sandbox.root
  415. @project.stubs(:builds).returns [stub_build(1), stub_build(2)]
  416. @svn.stubs(:revisions_since).returns([])
  417. @svn.stubs(:latest_revision).returns(new_revision(2))
  418. @svn.expects(:update)
  419. assert @project.build
  420. end
  421. end
  422. def test_do_clean_checkout_every_x_hours
  423. in_sandbox do |sandbox|
  424. @project.path = sandbox.root
  425. marker = sandbox.root + '/last_clean_checkout_timestamp'
  426. now = Time.now
  427. Time.stubs(:now).returns(now)
  428. @project.do_clean_checkout :every => 1.hour
  429. assert @project.do_clean_checkout?
  430. assert !@project.do_clean_checkout?
  431. assert !@project.do_clean_checkout?
  432. now += 59.minutes
  433. Time.stubs(:now).returns(now)
  434. assert !@project.do_clean_checkout?
  435. now += 2.minutes
  436. Time.stubs(:now).returns(now)
  437. assert @project.do_clean_checkout?
  438. assert !@project.do_clean_checkout?
  439. @project.do_clean_checkout :every => 2.days
  440. now += 1.day + 23.hours
  441. Time.stubs(:now).returns(now)
  442. assert !@project.do_clean_checkout?
  443. now += 2.hours
  444. Time.stubs(:now).returns(now)
  445. assert @project.do_clean_checkout?
  446. end
  447. end
  448. def increment_time_by(seconds)
  449. now = Time.now
  450. Time.stubs(:now).returns(now + seconds)
  451. File.stubs(:mtime).returns(now + seconds)
  452. end
  453. def test_do_clean_checkout_always
  454. in_sandbox do |sandbox|
  455. @project.path = sandbox.root
  456. assert !@project.do_clean_checkout?, "by default should be off"
  457. @project.do_clean_checkout
  458. assert @project.do_clean_checkout?
  459. assert @project.do_clean_checkout?
  460. end
  461. end
  462. def test_new_project_should_have_source_control_triggers
  463. project = Project.new('foo')
  464. trigger_classes = project.triggered_by.map(&:class)
  465. assert_equal [ChangeInSourceControlTrigger], trigger_classes
  466. end
  467. def test_project_triggered_by
  468. project = Project.new('foo')
  469. project.triggered_by = []
  470. assert_equal [], project.triggered_by
  471. project.triggered_by 1
  472. assert_equal [1], project.triggered_by
  473. project.triggered_by 2, 3
  474. assert_equal [1, 2, 3], project.triggered_by
  475. end
  476. def test_project_triggered_by_should_convert_strings_and_symbols_to_successful_build_triggers
  477. project = Project.new('foo')
  478. project.triggered_by = ['foo', 123]
  479. project.triggered_by :bar
  480. project.triggered_by << :baz
  481. assert_equal [SuccessfulBuildTrigger.new(project, 'foo'), 123, SuccessfulBuildTrigger.new(project, 'bar'),
  482. SuccessfulBuildTrigger.new(project, 'baz')],
  483. project.triggered_by
  484. end
  485. def test_builds_are_serialized
  486. Configuration.stubs(:serialize_builds).returns(true)
  487. project = Project.new("test")
  488. BuildSerializer.expects(:serialize).yields
  489. project.expects(:build_without_serialization)
  490. project.build []
  491. end
  492. def test_keep_scm_and_path_in_sync
  493. assert_equal(@project.path + "/work", @svn.path)
  494. @project.path = "foo"
  495. assert_equal(File.expand_path("foo/work"), @svn.path)
  496. end
  497. private
  498. def stub_build(label)
  499. build = Object.new
  500. build.stubs(:label).returns(label)
  501. build.stubs(:artifacts_directory).returns("project1/build-#{label}")
  502. build.stubs(:run)
  503. build.stubs(:successful?).returns(true)
  504. build
  505. end
  506. def new_revision(number)
  507. Revision.new(number, 'alex', DateTime.new(2005, 1, 1), 'message', [])
  508. end
  509. def new_mock_build(label)
  510. build = Object.new
  511. Build.expects(:new).with(@project, label).returns(build)
  512. build.stubs(:artifacts_directory).returns("project1/build-#{label}")
  513. build.stubs(:last).returns(nil)
  514. build.stubs(:label).returns(label)
  515. build
  516. end
  517. end