cukeq 0.0.1.dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. data/.autotest +1 -0
  2. data/.document +5 -0
  3. data/.gitignore +21 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +73 -0
  6. data/Rakefile +68 -0
  7. data/VERSION +1 -0
  8. data/bin/cukeq +34 -0
  9. data/features/cukeq.feature +15 -0
  10. data/features/example1.feature +1 -0
  11. data/features/example2.feature +1 -0
  12. data/features/step_definitions/cukeq_steps.rb +22 -0
  13. data/features/support/cukeq_helper.rb +73 -0
  14. data/features/support/env.rb +13 -0
  15. data/features/support/report_app.rb +36 -0
  16. data/lib/cukeq.rb +45 -0
  17. data/lib/cukeq/broker.rb +65 -0
  18. data/lib/cukeq/em/system3.rb +53 -0
  19. data/lib/cukeq/job_clearer.rb +19 -0
  20. data/lib/cukeq/master.rb +146 -0
  21. data/lib/cukeq/reporter.rb +22 -0
  22. data/lib/cukeq/runner.rb +63 -0
  23. data/lib/cukeq/scenario_exploder.rb +32 -0
  24. data/lib/cukeq/scenario_runner.rb +134 -0
  25. data/lib/cukeq/scm.rb +51 -0
  26. data/lib/cukeq/scm/git_bridge.rb +36 -0
  27. data/lib/cukeq/scm/shell_svn_bridge.rb +65 -0
  28. data/lib/cukeq/scm/svn_bridge.rb +77 -0
  29. data/lib/cukeq/slave.rb +114 -0
  30. data/lib/cukeq/webapp.rb +38 -0
  31. data/spec/cukeq/broker_spec.rb +78 -0
  32. data/spec/cukeq/cukeq_spec.rb +10 -0
  33. data/spec/cukeq/master_spec.rb +153 -0
  34. data/spec/cukeq/reporter_spec.rb +29 -0
  35. data/spec/cukeq/runner_spec.rb +11 -0
  36. data/spec/cukeq/scenario_exploder_spec.rb +17 -0
  37. data/spec/cukeq/scenario_runner_spec.rb +32 -0
  38. data/spec/cukeq/scm/git_bridge_spec.rb +52 -0
  39. data/spec/cukeq/scm/svn_bridge_spec.rb +5 -0
  40. data/spec/cukeq/scm_spec.rb +48 -0
  41. data/spec/cukeq/slave_spec.rb +86 -0
  42. data/spec/cukeq/webapp_spec.rb +37 -0
  43. data/spec/spec.opts +1 -0
  44. data/spec/spec_helper.rb +12 -0
  45. metadata +228 -0
@@ -0,0 +1,10 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe CukeQ do
4
+ it "identifies this CukeQ instance" do
5
+ Socket.stub!(:gethostname => "hostname")
6
+ Etc.stub!(:getlogin => "login")
7
+
8
+ CukeQ.identifier.should == "hostname-login"
9
+ end
10
+ end
@@ -0,0 +1,153 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe CukeQ::Master do
4
+ def mock_master
5
+ CukeQ::Master.new(
6
+ mock("CukeQ::Broker").as_null_object,
7
+ mock("CukeQ::WebApp").as_null_object,
8
+ mock("CukeQ::Scm", :working_copy => ".").as_null_object,
9
+ mock("CukeQ::Reporter").as_null_object,
10
+ mock("CukeQ::Exploder").as_null_object
11
+ )
12
+ end
13
+
14
+ def running_master
15
+ master = mock_master
16
+ master.broker.stub!(:start).and_yield
17
+ master.start
18
+
19
+ master
20
+ end
21
+
22
+ describe ".configured_instance" do
23
+ it "sets up defaults if --broker is not given" do
24
+ master = CukeQ::Master.configured_instance(
25
+ %w[-s git://example.com -r http://cukereports.com]
26
+ )
27
+
28
+ master.broker.host.should == 'localhost'
29
+ master.broker.port.should == 5672
30
+ master.broker.user.should == 'cukeq-master'
31
+ master.broker.pass.should == 'cukeq123'
32
+ master.broker.vhost.should == '/cukeq'
33
+ end
34
+
35
+ it "adds defaults if the --broker argument is incomplete" do
36
+ master = CukeQ::Master.configured_instance(
37
+ %w[--broker amqp://otherhost:9000 -s git://example.com -r http://cukereports.com]
38
+ )
39
+
40
+ master.broker.host.should == 'otherhost'
41
+ master.broker.port.should == 9000
42
+ master.broker.user.should == 'cukeq-master'
43
+ master.broker.pass.should == 'cukeq123'
44
+ master.broker.vhost.should == '/cukeq'
45
+ end
46
+
47
+ it "sets up defaults if --webapp is not given" do
48
+ master = CukeQ::Master.configured_instance(
49
+ %w[-s git://example.com -r http://cukereports.com]
50
+ )
51
+
52
+ master.webapp.uri.host.should == '0.0.0.0'
53
+ master.webapp.uri.port.should == 9292
54
+ end
55
+
56
+ it "uses the given --webapp" do
57
+ master = CukeQ::Master.configured_instance(
58
+ %w[-s git://example.com -r http://cukereports.com -w http://example.com]
59
+ )
60
+
61
+ master.webapp.uri.host.should == 'example.com'
62
+ master.webapp.uri.port.should == 80
63
+ end
64
+ end
65
+
66
+ describe ".execute" do
67
+ it "starts the configured instance" do
68
+ args = %w[some args]
69
+ master = mock_master
70
+
71
+ CukeQ::Master.should_receive(:configured_instance).with(args).and_return(master)
72
+ master.should_receive(:start)
73
+
74
+ CukeQ::Master.execute(args)
75
+ end
76
+ end
77
+
78
+ describe "#start" do
79
+ it "updates, subscribes to the results queue and runs the webapp" do
80
+ master = CukeQ::Master.new(mock("CukeQ::Broker"), mock("CukeQ::WebApp"), mock("CukeQ::Scm"), nil, nil)
81
+
82
+ master.scm.should_receive(:update).and_yield
83
+ master.broker.should_receive(:start).and_yield
84
+ master.should_receive(:subscribe)
85
+ master.webapp.should_receive(:run)
86
+
87
+ master.start
88
+ end
89
+ end
90
+
91
+ describe "#run" do
92
+ it "sends the payload to the exploder" do
93
+ data = { 'features' => ["some.feature:10", "another.feature:12"] }
94
+ master = running_master
95
+
96
+ master.exploder.should_receive(:explode).with(data['features']).and_return([])
97
+ master.run(data)
98
+ end
99
+
100
+ it "publishes the exploded scenarios on the jobs queue" do
101
+ jobs = %w[job1 job2 job3 job4]
102
+ master = running_master
103
+
104
+ master.exploder.stub!(:explode).and_yield(jobs)
105
+ master.broker.should_receive(:publish).exactly(4).times
106
+
107
+ master.run({})
108
+ end
109
+
110
+ it "adds a run_id and scm info to the job payload" do
111
+ jobs = ["job1"]
112
+ master = running_master
113
+
114
+ master.exploder.stub!(:explode).and_yield(jobs)
115
+ master.scm.stub!(:current_revision).and_return("abadbabe")
116
+ master.scm.stub!(:url).and_return("git://github.com/jarib/cukeq.git")
117
+
118
+ master.broker.should_receive(:publish).with do |queue, json|
119
+ payload = JSON.parse(json)
120
+
121
+ payload['scm']['revision'].should == "abadbabe"
122
+ payload['scm']['url'].should == "git://github.com/jarib/cukeq.git"
123
+ payload['run']['id'].should == 1
124
+ payload['run']['no_of_units'].should == 1
125
+ end
126
+
127
+ master.run({'run_id' => 1})
128
+ end
129
+ end
130
+
131
+ describe "#result" do
132
+ it "sends the result to the reporter" do
133
+ result = {:some => 'data'}
134
+ master = running_master
135
+ master.reporter.should_receive(:report).with(result)
136
+
137
+ master.result(result)
138
+ end
139
+ end
140
+
141
+ describe "#subscribe" do
142
+ it "should subscribe to the results queue and process the result" do
143
+ master = running_master
144
+ result = '{"some": "result"}'
145
+
146
+ master.broker.should_receive(:subscribe).with(:results).and_yield(result)
147
+ master.should_receive(:result).with("some" => "result")
148
+
149
+ master.subscribe
150
+ end
151
+ end
152
+
153
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe CukeQ::Reporter do
4
+
5
+ def reporter
6
+ @reporter ||= CukeQ::Reporter.new(URI.parse("http://example.com/some/path"))
7
+ end
8
+
9
+ it "POSTs results to the given URL" do
10
+ expected_params = {
11
+ :host => "example.com",
12
+ :port => 80,
13
+ :verb => 'POST',
14
+ :request => '/some/path',
15
+ :content => '{"some":"result"}'
16
+ }
17
+
18
+ EM::P::HttpClient.should_receive(:request).with(expected_params)
19
+ reporter.report("some" => "result")
20
+ end
21
+
22
+ it "catches and logs EM errors" do
23
+ reporter.should_receive(:log)
24
+ EM::P::HttpClient.stub!(:request).and_raise(RuntimeError)
25
+
26
+ reporter.report(nil)
27
+ end
28
+
29
+ end
@@ -0,0 +1,11 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe CukeQ::Runner do
4
+ it "triggers a run with the files given" do
5
+ pending
6
+ end
7
+
8
+ it "prints the report URL and run ID" do
9
+ pending
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe CukeQ::ScenarioExploder do
4
+
5
+ def exploder
6
+ CukeQ::ScenarioExploder.new
7
+ end
8
+
9
+ it "returns the parsed features" do
10
+ # our IO.popen call doesn't work well with rspec - probably a race condition since
11
+ # it only fails on RCR
12
+ pending
13
+
14
+ # units = exploder.explode("features/example1.feature")
15
+ # units.first.should == {"file" => "features/example1.feature"}
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe CukeQ::ScenarioRunner do
4
+
5
+ def runner
6
+ @runner ||= CukeQ::ScenarioRunner.new
7
+ end
8
+
9
+ it "returns an error if the job is incorrect" do
10
+ runner.run({}) do |result|
11
+ result[:success].should be_false
12
+ result[:error].should_not be_empty
13
+ result[:backtrace].should_not be_empty
14
+ end
15
+ end
16
+
17
+ it "creates a configured and updated Scm instance" do
18
+ job = {'scm' => {'url' => 'git://example.com/foo/bar', 'revision' => 'some-revision'}}
19
+
20
+ CukeQ::Scm.should_receive(:new).with(job['scm']['url']).and_return(mock_scm = mock("scm"))
21
+ mock_scm.should_receive(:current_revision).and_return 'another-revision'
22
+ mock_scm.should_receive(:update).and_yield
23
+
24
+ runner.scm_for(job).should == mock_scm
25
+ end
26
+
27
+ # important.
28
+ it "executes the given job" do
29
+ pending
30
+ end
31
+
32
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path("../../../spec_helper", __FILE__)
2
+
3
+ describe CukeQ::Scm::GitBridge do
4
+ include FileUtils
5
+
6
+ GIT_URL = "git://example.com"
7
+ WORKING_COPY = "spec-scm-repo"
8
+
9
+ after(:each) do
10
+ rm_rf WORKING_COPY
11
+ end
12
+
13
+ def mock_repo
14
+ @mock_repo ||= mock("Git::Base").as_null_object
15
+ end
16
+
17
+ def bridge
18
+ @bridge ||= CukeQ::Scm::GitBridge.new(GIT_URL, WORKING_COPY)
19
+ end
20
+
21
+ it "clones the repo of it doesn't exist" do
22
+ Git.stub!(:open)
23
+ Git.should_receive(:clone).with(GIT_URL, WORKING_COPY)
24
+ bridge.repo
25
+ end
26
+
27
+ it "does not clone the repo if it already exists" do
28
+ Git.stub!(:open)
29
+ Git.should_receive(:clone).never
30
+
31
+ mkdir WORKING_COPY
32
+ bridge.repo
33
+ end
34
+
35
+ it "fetches the current revision" do
36
+ bridge.stub!(:repo).and_return(mock_repo)
37
+ mock_repo.should_receive(:revparse).with("HEAD").and_return("rev")
38
+
39
+ bridge.current_revision.should == "rev"
40
+ end
41
+
42
+ it "updates the working copy" do
43
+ bridge.stub!(:repo).and_return(mock_repo)
44
+
45
+ mock_repo.should_receive(:reset_hard)
46
+ mock_repo.should_receive(:pull)
47
+
48
+ bridge.update {}
49
+ end
50
+
51
+
52
+ end
@@ -0,0 +1,5 @@
1
+ # require File.expand_path("../../../spec_helper", __FILE__)
2
+ #
3
+ # describe CukeQ::Scm::SvnBridge do
4
+ #
5
+ # end
@@ -0,0 +1,48 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe CukeQ::Scm do
4
+
5
+ def scm(vcs, mock_bridge = false)
6
+ scm = case vcs
7
+ when :git
8
+ CukeQ::Scm.new("git://github.com/jarib/cukeq.git")
9
+ when :svn
10
+ CukeQ::Scm.new("svn://example.com/somerepo/trunk")
11
+ else
12
+ raise "unknown vcs: #{vcs.inspect}"
13
+ end
14
+
15
+ if mock_bridge
16
+ scm.instance_variable_set("@bridge", mock("scm-bridge"))
17
+ end
18
+
19
+ scm
20
+ end
21
+
22
+ it "replaces special characters in the working copy dir" do
23
+ scm(:git).working_copy.should_not =~ /[^A-z_\/.]/
24
+ end
25
+
26
+ it "creates the correct bridge" do
27
+ scm(:git).bridge.should be_kind_of(CukeQ::Scm::GitBridge)
28
+ scm(:svn).bridge.should be_kind_of(CukeQ::Scm::SvnBridge)
29
+ end
30
+
31
+ it "understands http urls" do
32
+ CukeQ::Scm.new("http://github.com/foo/bar.git").bridge.should be_kind_of(CukeQ::Scm::GitBridge)
33
+ CukeQ::Scm.new("https://svn.example.com/foo/bar").bridge.should be_kind_of(CukeQ::Scm::SvnBridge)
34
+ end
35
+
36
+ it "forwards update() to the bridge" do
37
+ scm = scm(:git, true)
38
+ scm.bridge.should_receive(:update).and_yield
39
+ scm.update {}
40
+ end
41
+
42
+ it "forwards current_revision() to the bridge" do
43
+ scm = scm(:git, true)
44
+ scm.bridge.should_receive(:current_revision)
45
+ scm.current_revision
46
+ end
47
+
48
+ end
@@ -0,0 +1,86 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe CukeQ::Slave do
4
+
5
+ def mock_slave
6
+ CukeQ::Slave.new(
7
+ mock("CukeQ::Broker").as_null_object,
8
+ mock("CukeQ::ScenarioRunner").as_null_object
9
+ )
10
+ end
11
+
12
+ def running_slave
13
+ slave = mock_slave
14
+ slave.broker.stub!(:start).and_yield
15
+
16
+ slave
17
+ end
18
+
19
+ describe ".configured_instance" do
20
+ it "sets up defaults if --broker is not given" do
21
+ slave = CukeQ::Slave.configured_instance
22
+
23
+ slave.broker.host.should == 'localhost'
24
+ slave.broker.port.should == 5672
25
+ slave.broker.user.should == 'cukeq-slave'
26
+ slave.broker.pass.should == 'cukeq123'
27
+ slave.broker.vhost.should == '/cukeq'
28
+ end
29
+
30
+ it "adds defaults if the --broker argument is incomplete" do
31
+ slave = CukeQ::Slave.configured_instance(%w[--broker amqp://otherhost:9000])
32
+
33
+ slave.broker.host.should == 'otherhost'
34
+ slave.broker.port.should == 9000
35
+ slave.broker.user.should == 'cukeq-slave'
36
+ slave.broker.pass.should == 'cukeq123'
37
+ slave.broker.vhost.should == '/cukeq'
38
+ end
39
+ end
40
+
41
+ describe ".execute" do
42
+ it "starts the configured instance" do
43
+ args = %w[some args]
44
+ slave = mock_slave
45
+
46
+ CukeQ::Slave.should_receive(:configured_instance).with(args).and_return(slave)
47
+ slave.should_receive(:start)
48
+
49
+ CukeQ::Slave.execute(args)
50
+ end
51
+ end
52
+
53
+ describe "#start" do
54
+ it "subscribes to the jobs queue" do
55
+ slave = mock_slave
56
+ slave.broker.should_receive(:start).and_yield
57
+ slave.should_receive(:poll)
58
+
59
+ slave.start
60
+ end
61
+ end
62
+
63
+ describe "#job" do
64
+ it "runs each job with the scenario runner and publishes each result on the results queue" do
65
+ slave = running_slave
66
+ job = {:some => 'job'}
67
+ result = {:some => 'result'}
68
+
69
+ slave.scenario_runner.should_receive(:run).with(job).and_yield(result)
70
+ slave.should_receive(:publish).with(result)
71
+
72
+ slave.job(job)
73
+ end
74
+ end
75
+
76
+ describe "#publish" do
77
+ it "it publishes the message on the results queue" do
78
+ slave = running_slave
79
+ message = {:run => "some message"}
80
+
81
+ slave.broker.should_receive(:publish).with(:results, message.to_json)
82
+ slave.publish(message)
83
+ end
84
+ end
85
+
86
+ end