tlb-rspec1 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ TAGS
2
+ tmp
3
+ tlb_store
4
+ tlb-all*.jar
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "tlb"]
2
+ path = tlb
3
+ url = git://github.com/test-load-balancer/tlb.git
data/README.markdown ADDED
@@ -0,0 +1,30 @@
1
+ ## Using tlb_rb:
2
+
3
+ tlb_rb uses [tlb](https://github.com/test-load-balancer/tlb "TLB") under the hood. It runs a sub-process(java process) which talks to the actual tlb-server(or equivallent) to balance and post run-feedback.
4
+ Balancer process is actually an HTTP server which needs to listen to a certain TCP port so that tlb-ruby implementation can talk to it.
5
+ This is controlled by an environment variable named *'TLB_BALANCER_PORT'*, which can be set to any port number(integer between 1024 to 65535) that is guaranteed to remain un-bound while the build runs.
6
+
7
+ In addition to this extra environment variable, the usual TLB environment variable setup is required(so the balancer knows things like what partitioning algorithm to use or what server to talk to).
8
+ Detailed documentation of TLB environment variable configuration is available at [https://github.com/test-load-balancer/tlb/wiki/Configuration-Variables](https://github.com/test-load-balancer/tlb/wiki/Configuration-Variables "Tlb config reference")
9
+
10
+ As of now, rspec is the only test framework that tlb_rb supports. We plan to add support for other ruby-testing-frameworks soon.
11
+
12
+ ## Setting it up for your project
13
+
14
+ Please refer the [sample_projects](http://github.com/test-load-balancer/sample_projects "Tlb setup examples") to see the details of how to set it up.
15
+
16
+ Usually, something equivallent of this in one of your Rake files should suffice:
17
+
18
+ PATH_TO_TLB = File.join(RAILS_ROOT, 'vendor', 'plugins', 'tlb_rb', 'lib', 'tlb')
19
+ require PATH_TO_TLB
20
+ require File.join(PATH_TO_TLB, 'spec_task')
21
+
22
+ Tlb::SpecTask.new(:balanced_specs) do |t|
23
+ t.spec_files = FileList['spec/**/*_spec.rb']
24
+ t.spec_opts << "--require #{PATH_TO_TLB},#{File.join(PATH_TO_TLB, 'spec_formatter')} --format 'Tlb::SpecFormatter:/dev/null' --format nested"
25
+ end
26
+
27
+ desc "load balanced spec"
28
+ task :run_balanced => ['tlb:start', :balanced_specs]
29
+
30
+ Where run_balanced is the task you invoke at the top-level(invoked externally).
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'spec/rake/spectask'
2
+
3
+ Spec::Rake::SpecTask.new(:spec) do |t|
4
+ t.spec_files = FileList['spec/**/*_spec.rb']
5
+ end
6
+
7
+ task :build_tlb do
8
+ Dir.glob("tlb-all*.jar").each { |jar| FileUtils.rm(jar) }
9
+ sh 'ant -f tlb/build.xml package'
10
+ Dir.glob('tlb/target/tlb-all*').each { |file| FileUtils.copy(file, ".") }
11
+ end
12
+
13
+ task :package do
14
+ `gem build tlb-rspec1.gemspec`
15
+ end
data/gem_common.rb ADDED
@@ -0,0 +1,56 @@
1
+ BASE_DIR = File.dirname(__FILE__)
2
+ LIB_TLB = File.join(BASE_DIR, "lib", "tlb")
3
+ TEST_DIR = File.join(BASE_DIR, "tests")
4
+ TAG_VERSION = `git describe --abbrev=0`.gsub(/^v/, '').gsub(/-rspec-1$/, '')
5
+ CODE_VERSION = `git describe --always`
6
+ AUTHORS = ["Janmejay Singh", "Pavan KS"]
7
+ EMAIL = "singh.janmejay@gmail.com;itspanzi@gmail.com"
8
+ HOME_PAGE = "http://github.com/test-load-balancer/tlb_rb"
9
+ SUMMARY = "#{$name}-#{CODE_VERSION}"
10
+ DESC = <<END
11
+ TLB ruby implementation base, which provides support for load balancing tests written in #{$framework}.
12
+ TLB_rb test suite is not bundled, please check http://github.com/test-load-balancer/tlb_rb for tests.
13
+ Detailed configuration documentation can be found at https://github.com/test-load-balancer/tlb/wiki.
14
+ END
15
+ POST_INSTALL_MESSAGE = <<END
16
+ -------------------------------------------------------------------------
17
+ Documentation: Detailed configuration documentation can be found at https://github.com/test-load-balancer/tlb/wiki.
18
+ -----------------------------
19
+ Example: https://github.com/test-load-balancer/sample_projects has examples projects and shell script to demonstrate a typical build(by starting a local tlb server, and executing two partitions locally). While partitions in these examples execute one after another, in an actual CI/pre-checkin build, they will actually run parallely on different machines.
20
+ Its a good idea to play with the environment variables values being used in these shell-scripts to understand how they affect TLB's behaviour. You may want to check https://github.com/test-load-balancer/tlb/wiki/Configuration-Variables, which documents each variable and its effect.
21
+ -----------------------------
22
+ Issue tracker: Please report bugs/enhancements/feature-requests at http://code.google.com/p/tlb/issues/list. Github, Rubyforge or any other issue trackers are not monitored or updated.
23
+ -------------------------------------------------------------------------
24
+ END
25
+ RUBYFORGE_PROJECT = "tlb-rb"
26
+ RUBYGEMS_VERSION = "1.3.7"
27
+
28
+ def files *exclude_dirs
29
+ files = `git ls-files`.split("\n")
30
+ files += Dir.glob(File.join(File.dirname(__FILE__), "*.jar")).map { |path| File.basename(path) }
31
+ exclude_dirs.inject(files) { |files, dir| files - `git ls-files #{dir}`.split("\n") }
32
+ end
33
+
34
+
35
+ def configure_tlb s
36
+ s.name = $name
37
+ s.version = TAG_VERSION
38
+ s.platform = Gem::Platform::RUBY
39
+ s.authors = AUTHORS
40
+ s.email = EMAIL
41
+ s.homepage = HOME_PAGE
42
+ s.summary = SUMMARY
43
+ s.description = DESC
44
+
45
+ s.rubyforge_project = RUBYFORGE_PROJECT
46
+ s.rubygems_version = RUBYGEMS_VERSION
47
+
48
+ s.post_install_message = POST_INSTALL_MESSAGE
49
+
50
+ s.extra_rdoc_files = [ "README.markdown" ]
51
+ s.rdoc_options = ["--charset=UTF-8"]
52
+ s.require_path = "lib"
53
+
54
+ s.add_runtime_dependency 'open4', '>= 1.0.1'
55
+ s.add_runtime_dependency 'rake'
56
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'tlb'))
2
+
3
+ namespace :tlb do
4
+ task :start do
5
+ Tlb.start_server
6
+ at_exit do
7
+ $stderr.write "terminating tlb server\n"
8
+ Tlb.stop_server
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,70 @@
1
+ require 'spec/runner/formatter/silent_formatter'
2
+
3
+ class Tlb::SpecFormatter < Spec::Runner::Formatter::SilentFormatter
4
+ class Suite < Struct.new(:file_name, :start_time, :end_time, :failed)
5
+ MILLS_PER_SEC = 1000
6
+
7
+ def initialize(file_name, start_time)
8
+ super(file_name, start_time, start_time, false)
9
+ end
10
+
11
+ def for_file? new_file
12
+ File.identical?(file_name, new_file)
13
+ end
14
+
15
+ def run_time
16
+ ((end_time - start_time)*MILLS_PER_SEC).to_i
17
+ end
18
+
19
+ def report_to_tlb
20
+ rel_file_name = Tlb.relative_file_path(file_name)
21
+ Tlb.suite_time(rel_file_name, run_time)
22
+ Tlb.suite_result(rel_file_name, failed)
23
+ end
24
+ end
25
+
26
+ def initialize(*args)
27
+ super(*args)
28
+ @suites = []
29
+ end
30
+
31
+ def example_group_started(example_proxy_group)
32
+ file_name = example_file_name(example_proxy_group)
33
+ unless (@suites.last && @suites.last.for_file?(file_name))
34
+ @suites << Tlb::SpecFormatter::Suite.new(file_name, Time.now)
35
+ end
36
+ end
37
+
38
+ def example_passed(example_proxy)
39
+ record_suite_data(example_proxy)
40
+ end
41
+
42
+ def example_failed(example_proxy, *ignore)
43
+ record_suite_data(example_proxy) do |suite|
44
+ suite.failed = true
45
+ end
46
+ end
47
+
48
+ def example_pending(example_proxy, *ignore)
49
+ record_suite_data(example_proxy)
50
+ end
51
+
52
+ def start_dump
53
+ @suites.each do |suite_time|
54
+ suite_time.report_to_tlb
55
+ end
56
+ end
57
+
58
+ private
59
+ def record_suite_data example_proxy
60
+ file_name = example_file_name(example_proxy)
61
+ if (suite = @suites.last) #stupid framework :: retarded fix (this is necessary since rspec creates example_proxies for every example it runs, as though its an independent spec-group)
62
+ suite.end_time = Time.now
63
+ block_given? && yield(suite)
64
+ end
65
+ end
66
+
67
+ def example_file_name example_proxy
68
+ example_proxy.location ? (@last_known_file = example_proxy.location.scan(/^(.+?):\d+(:.*)?$/).flatten.first) : @last_known_file
69
+ end
70
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec/rake/spectask'
2
+
3
+ class Tlb::SpecTask < Spec::Rake::SpecTask
4
+ attr_accessor :tlb_out
5
+
6
+ def initialize *args
7
+ path_to_tlb = File.expand_path(File.join(File.dirname(__FILE__), '..', 'tlb'))
8
+ path_to_spec_formatter = File.expand_path(File.join(File.dirname(__FILE__), 'spec_formatter'))
9
+ self.tlb_out = '/dev/null'
10
+ super do |this|
11
+ yield this if block_given?
12
+ this.spec_opts.unshift "--require #{path_to_tlb},#{path_to_spec_formatter} --format 'Tlb::SpecFormatter:#{this.tlb_out}'"
13
+ end
14
+ end
15
+
16
+ alias_method :rspec_spec_file_list, :spec_file_list
17
+
18
+ def spec_file_list
19
+ balanced_and_reordered = Tlb.balance_and_order(rspec_spec_file_list.to_a)
20
+ FileList[*balanced_and_reordered]
21
+ end
22
+ end
data/lib/tlb.rb ADDED
@@ -0,0 +1,135 @@
1
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "tlb"))
2
+ require 'rubygems'
3
+ require 'open4'
4
+ require 'net/http'
5
+
6
+ module Tlb
7
+ TLB_OUT_FILE = 'TLB_OUT_FILE'
8
+ TLB_ERR_FILE = 'TLB_ERR_FILE'
9
+ TLB_APP = 'TLB_APP'
10
+
11
+ module Balancer
12
+ TLB_BALANCER_PORT = 'TLB_BALANCER_PORT'
13
+ BALANCE_PATH = '/balance'
14
+ SUITE_TIME_REPORTING_PATH = '/suite_time'
15
+ SUITE_RESULT_REPORTING_PATH = '/suite_result'
16
+
17
+ def self.host
18
+ 'localhost'
19
+ end
20
+
21
+ def self.port
22
+ ENV[TLB_BALANCER_PORT]
23
+ end
24
+
25
+ def self.send path, data
26
+ Net::HTTP.start(host, port) do |h|
27
+ res = h.post(path, data)
28
+ res.value
29
+ res.body
30
+ end
31
+ end
32
+
33
+ def self.get path
34
+ Net::HTTP.get_response(host, path, port).body
35
+ end
36
+
37
+ def self.running?
38
+ get("/control/status") == "RUNNING"
39
+ rescue
40
+ false
41
+ end
42
+
43
+ def self.wait_for_start
44
+ loop do
45
+ begin
46
+ TCPSocket.new(host, port)
47
+ break if running?
48
+ rescue
49
+ #ignore
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ def self.relative_file_path file_name
56
+ abs_file_name = File.expand_path(file_name)
57
+ rel_file_name = abs_file_name.sub(/^#{Dir.pwd}/, '.')
58
+ end
59
+
60
+ def self.balance_and_order file_set
61
+ ensure_server_running
62
+ Balancer.send(Balancer::BALANCE_PATH,
63
+ file_set.map do |file_path|
64
+ Tlb.relative_file_path(file_path)
65
+ end.join("\n")).split("\n")
66
+ end
67
+
68
+ def self.suite_result suite_name, result
69
+ ensure_server_running
70
+ Balancer.send(Balancer::SUITE_RESULT_REPORTING_PATH, "#{suite_name}: #{result}")
71
+ end
72
+
73
+ def self.suite_time suite_name, mills
74
+ ensure_server_running
75
+ Balancer.send(Balancer::SUITE_TIME_REPORTING_PATH, "#{suite_name}: #{mills}")
76
+ end
77
+
78
+ def self.fail_as_balancer_is_not_running
79
+ raise "Balancer server must be started before tests are run."
80
+ end
81
+
82
+ def self.ensure_server_running
83
+ server_running? || fail_as_balancer_is_not_running
84
+ end
85
+
86
+ def self.server_running?
87
+ Balancer.running?
88
+ end
89
+
90
+ def self.root_dir
91
+ File.expand_path(File.join(File.dirname(__FILE__), ".."))
92
+ end
93
+
94
+ def self.tlb_jar
95
+ File.expand_path(Dir.glob(File.join(root_dir, "tlb-all*")).first)
96
+ end
97
+
98
+ def self.server_command
99
+ "java -jar #{tlb_jar}"
100
+ end
101
+
102
+ def self.write_to_file file_var, clob
103
+ File.open(ENV[file_var], 'a') do |h|
104
+ h.write(clob)
105
+ end
106
+ end
107
+
108
+ def self.start_server
109
+ ENV[TLB_APP] = 'com.github.tlb.balancer.BalancerInitializer'
110
+ @pid, input, out, err = Open4.popen4(server_command)
111
+ @out_pumper = stream_pumper_for(out, TLB_OUT_FILE)
112
+ @err_pumper = stream_pumper_for(err, TLB_ERR_FILE)
113
+ Balancer.wait_for_start
114
+ end
115
+
116
+ def self.stream_pumper_for stream, dump_file
117
+ Thread.new do
118
+ loop do
119
+ stream.eof? || write_to_file(dump_file, stream.read)
120
+ Thread.current[:stop_pumping] && break
121
+ sleep 1
122
+ end
123
+ end
124
+ end
125
+
126
+ def self.stop_server
127
+ Process.kill(Signal.list["TERM"], @pid)
128
+ @pid = nil
129
+ @out_pumper[:stop_pumping] = true
130
+ @err_pumper[:stop_pumping] = true
131
+ @out_pumper.join
132
+ @err_pumper.join
133
+ Process.wait
134
+ end
135
+ end
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+ while [[ true ]] ;
3
+ do
4
+ echo "hello out"
5
+ echo "hello err" 1>&2
6
+ sleep 1
7
+ done
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'mocha'
3
+ require 'spec'
4
+ require 'rake'
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__), "..", "lib")
6
+ require 'tlb'
7
+
8
+ Spec::Runner.configure do |config|
9
+ config.mock_with :mocha
10
+ end
11
+
12
+ def tmp_file file_name
13
+ path = File.join(Dir.tmpdir, file_name)
14
+ file = File.new(path, 'w')
15
+ File.truncate path, 0
16
+ file.close
17
+ file
18
+ end
19
+
@@ -0,0 +1,140 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
+ require 'spec_formatter'
3
+ require 'spec/example/example_proxy'
4
+ require 'spec/example/example_group_proxy'
5
+
6
+ describe Tlb::SpecFormatter do
7
+ before :all do
8
+ FileUtils.mkdir_p(@dir = Dir.pwd + "/tmp/foo/../bar/..")
9
+ end
10
+
11
+ before do
12
+ @group_1, @file_1 = stubbed_group("baz/group1")
13
+ @group_2, @file_2 = stubbed_group("group2")
14
+ @group_3, @file_3 = stubbed_group("group3")
15
+ @group_proxy_1 = Spec::Example::ExampleGroupProxy.new(@group_1)
16
+ @group_proxy_2 = Spec::Example::ExampleGroupProxy.new(@group_2)
17
+ @group_proxy_3 = Spec::Example::ExampleGroupProxy.new(@group_3)
18
+ @formatter = Tlb::SpecFormatter.new(nil, nil)
19
+ end
20
+
21
+ def stubbed_group group_name
22
+ grp = stub(group_name)
23
+ grp.expects(:description).returns("#{group_name} desc")
24
+ grp.expects(:nested_descriptions).returns("#{group_name} nested desc")
25
+ grp.expects(:example_proxies).returns("#{group_name} example proxies")
26
+ grp.expects(:options).returns({:name => group_name})
27
+
28
+ file_name = "#{@dir}/#{group_name}.rb"
29
+ FileUtils.mkdir_p(File.dirname(file_name))
30
+ File.open(file_name, 'w') do |h|
31
+ h.write("something")
32
+ end
33
+ rel_file_name = File.expand_path(file_name).sub(Dir.pwd, '.')
34
+ grp.expects(:location).times(2).returns(file_name + ":4")
35
+
36
+ [grp, rel_file_name]
37
+ end
38
+
39
+ it "should be silent formatter" do
40
+ @formatter.should be_a(Spec::Runner::Formatter::SilentFormatter)
41
+ end
42
+
43
+ it "should use last finished example's time" do
44
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 5, 10))
45
+ @formatter.example_group_started(@group_proxy_1)
46
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 5, 20))
47
+ @formatter.example_passed(Spec::Example::ExampleProxy.new("group1 spec 1", {}, "#{@dir}/baz/group1.rb:12"))
48
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 5, 22))
49
+ @formatter.example_failed(Spec::Example::ExampleProxy.new("group1 spec 2", {}, "#{@dir}/baz/group1.rb:40"), 1, "ignore")
50
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 5, 29))
51
+ @formatter.example_pending(Spec::Example::ExampleProxy.new("group1 spec 3", {}, "#{@dir}/baz/group1.rb:55"), "some reason")
52
+
53
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 6, 00))
54
+ @formatter.example_group_started(@group_proxy_2)
55
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 6, 12))
56
+ @formatter.example_pending(Spec::Example::ExampleProxy.new("group2 spec 1", {}, "#{@dir}/group2.rb:5"), "some reason")
57
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 6, 25))
58
+ @formatter.example_passed(Spec::Example::ExampleProxy.new("group2 spec 2", {}, "#{@dir}/group2.rb:38"))
59
+
60
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 7, 15))
61
+ @formatter.example_group_started(@group_proxy_3)
62
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 8, 12))
63
+ @formatter.example_pending(Spec::Example::ExampleProxy.new("group3 spec 1", {}, "#{@dir}/group3.rb:45"), "some reason")
64
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 8, 55))
65
+ @formatter.example_failed(Spec::Example::ExampleProxy.new("group3 spec 2", {}, "#{@dir}/group3.rb:80"), 3, "ignore")
66
+
67
+ Tlb.stubs(:suite_result)
68
+
69
+ Tlb.expects(:suite_time).with(@file_1, 19000)
70
+ Tlb.expects(:suite_time).with(@file_2, 25000)
71
+ Tlb.expects(:suite_time).with(@file_3, 100000)
72
+
73
+ @formatter.start_dump
74
+ end
75
+
76
+ it "should report suite result to tlb" do
77
+ @formatter.example_group_started(@group_proxy_1)
78
+ @formatter.example_passed(Spec::Example::ExampleProxy.new("group1 spec 1", {}, "#{@dir}/baz/group1.rb:12"))
79
+ @formatter.example_failed(Spec::Example::ExampleProxy.new("group1 spec 2", {}, "#{@dir}/baz/group1.rb:40"), 1, "ignore")
80
+ @formatter.example_pending(Spec::Example::ExampleProxy.new("group1 spec 3", {}, "#{@dir}/baz/group1.rb:55"), "some reason")
81
+
82
+ @formatter.example_group_started(@group_proxy_2)
83
+ @formatter.example_pending(Spec::Example::ExampleProxy.new("group2 spec 1", {}, "#{@dir}/group2.rb:5"), "some reason")
84
+ @formatter.example_passed(Spec::Example::ExampleProxy.new("group2 spec 2", {}, "#{@dir}/group2.rb:38"))
85
+
86
+ @formatter.example_group_started(@group_proxy_3)
87
+ @formatter.example_pending(Spec::Example::ExampleProxy.new("group3 spec 1", {}, "#{@dir}/group3.rb:45"), "some reason")
88
+ @formatter.example_failed(Spec::Example::ExampleProxy.new("group3 spec 2", {}, "#{@dir}/group3.rb:80"), 3, "ignore")
89
+ @formatter.example_passed(Spec::Example::ExampleProxy.new("group3 spec 3", {}, "#{@dir}/group3.rb:85"))
90
+ @formatter.example_passed(Spec::Example::ExampleProxy.new("group3 spec 4", {}, "#{@dir}/group3.rb:103"))
91
+
92
+ Tlb.stubs(:suite_time)
93
+
94
+ Tlb.expects(:suite_result).with(@file_1, true)
95
+ Tlb.expects(:suite_result).with(@file_2, false)
96
+ Tlb.expects(:suite_result).with(@file_3, true)
97
+
98
+ @formatter.start_dump
99
+ end
100
+
101
+ it "should parse example_file_name" do
102
+ @formatter.send(:example_file_name, Spec::Example::ExampleProxy.new("some method call inside spec", {}, "./spec/baz_quux.rb:9")).should == "./spec/baz_quux.rb"
103
+ @formatter.send(:example_file_name, Spec::Example::ExampleProxy.new("some method call inside spec", {}, "./spec/foo_bar_baz.rb:9:in `should_quux'")).should == "./spec/foo_bar_baz.rb"
104
+ end
105
+
106
+ it "should report suite result for last heard file name when it received example_proxy having no location" do
107
+ @formatter.example_group_started(@group_proxy_1)
108
+ @formatter.example_failed(Spec::Example::ExampleProxy.new("before(:all)"))
109
+ Tlb.stubs(:suite_time)
110
+
111
+ Tlb.expects(:suite_result).with(@file_1, true)
112
+
113
+ @formatter.start_dump
114
+ end
115
+
116
+ it "should share the same suite when example_group starts twice(this happens in nested describe blocks)" do
117
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 5, 10))
118
+ @formatter.example_group_started(@group_proxy_1)
119
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 5, 20))
120
+ @formatter.example_passed(Spec::Example::ExampleProxy.new("group1 spec 1", {}, "#{@dir}/baz/group1.rb:12"))
121
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 5, 22))
122
+ @formatter.example_failed(Spec::Example::ExampleProxy.new("group1 spec 2", {}, "#{@dir}/baz/group1.rb:40"), 1, "ignore")
123
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 5, 29))
124
+ @formatter.example_pending(Spec::Example::ExampleProxy.new("group1 spec 3", {}, "#{@dir}/baz/group1.rb:55"), "some reason")
125
+
126
+ @formatter.example_group_started(@group_proxy_1)
127
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 6, 12))
128
+ @formatter.example_failed(Spec::Example::ExampleProxy.new("group1 spec 4(nested)", {}, "#{@dir}/baz/group1.rb:100"), 1, "foo_bar")
129
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 6, 25))
130
+ @formatter.example_pending(Spec::Example::ExampleProxy.new("group1 spec 4(nested)", {}, "#{@dir}/baz/group1.rb:130"), "some pending")
131
+ Time.expects(:now).returns(Time.local( 2010, "jul", 16, 12, 7, 15))
132
+ @formatter.example_passed(Spec::Example::ExampleProxy.new("group1 spec 4(nested)", {}, "#{@dir}/baz/group1.rb:145"))
133
+
134
+ Tlb.stubs(:suite_result)
135
+
136
+ Tlb.expects(:suite_time).with(@file_1, 125000)
137
+ @formatter.start_dump
138
+ end
139
+
140
+ end
@@ -0,0 +1,40 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
2
+ require 'spec_task'
3
+
4
+ describe Tlb::SpecTask do
5
+ before(:all) do
6
+ @path_to_tlb = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib', 'tlb'))
7
+ @path_to_spec_formatter = File.expand_path(File.join(@path_to_tlb, 'spec_formatter'))
8
+ end
9
+
10
+ it "should return balanced and ordered subset" do
11
+ @task = Tlb::SpecTask.new
12
+ Tlb.stubs(:start_unless_running)
13
+ @task.expects(:rspec_spec_file_list).returns(FileList['foo.rb', 'bar.rb', 'baz.rb', 'quux.rb'])
14
+ Tlb.stubs(:balance_and_order).with(['foo.rb', 'bar.rb', 'baz.rb', 'quux.rb']).returns(['quux.rb', 'foo.rb'])
15
+ balanced_list = @task.spec_file_list
16
+ balanced_list.should be_a(Rake::FileList)
17
+ balanced_list.to_a.should == ['quux.rb', 'foo.rb']
18
+ end
19
+
20
+ it "should hookup formatter so feedback is posted" do
21
+ @task = Tlb::SpecTask.new
22
+ @task.spec_opts.should == ["--require #{@path_to_tlb},#{@path_to_spec_formatter} --format 'Tlb::SpecFormatter:/dev/null'"]
23
+ end
24
+
25
+ it "should honor user specified attributes" do
26
+ @task = Tlb::SpecTask.new(:foo) do |t|
27
+ t.spec_opts << "--require foo_bar"
28
+ t.spec_opts << "--require baz_quux"
29
+ end
30
+ @task.spec_opts.should == ["--require #{@path_to_tlb},#{@path_to_spec_formatter} --format 'Tlb::SpecFormatter:/dev/null'", "--require foo_bar", "--require baz_quux"]
31
+ @task.name.should == :foo
32
+ end
33
+
34
+ it "should use specified output file for tlb's spec_formatter" do
35
+ @task = Tlb::SpecTask.new(:foo) do |t|
36
+ t.tlb_out = "/tmp/tlb_spec_formatter_out"
37
+ end
38
+ @task.spec_opts.should == ["--require #{@path_to_tlb},#{@path_to_spec_formatter} --format 'Tlb::SpecFormatter:/tmp/tlb_spec_formatter_out'"]
39
+ end
40
+ end
@@ -0,0 +1,146 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'open4'
3
+ require 'parsedate'
4
+ require 'tmpdir'
5
+ require 'webrick'
6
+
7
+ describe Tlb do
8
+ URL = "http://localhost:7019"
9
+ JOB_NAME = "foo"
10
+ TLB_BALANCER_PORT = '9173'
11
+ before :all do
12
+ ENV['TLB_APP'] = 'com.github.tlb.server.TlbServerInitializer'
13
+ @pid, i, o, e = Open4.popen4(Tlb.server_command)
14
+ end
15
+
16
+ after :all do
17
+ Process.kill(Signal.list['KILL'], @pid)
18
+ end
19
+
20
+ it "should wait for balancer server to come up before returning from start_server" do
21
+ Tlb::Balancer.expects(:wait_for_start)
22
+ Open4.stubs(:popen4)
23
+ Tlb.start_server
24
+ end
25
+
26
+ describe "using server" do
27
+ before do
28
+ ENV[Tlb::TLB_OUT_FILE] = (@out_file = tmp_file('tlb_out_file').path)
29
+ ENV[Tlb::TLB_ERR_FILE] = (@err_file = tmp_file('tlb_err_file').path)
30
+ Tlb.server_running?.should be_false #precondition (the server must be started if not running)
31
+
32
+ ENV['TLB_BALANCER_PORT'] = TLB_BALANCER_PORT
33
+ ENV['TLB_URL'] = URL
34
+ ENV['TALK_TO_SERVICE'] = "com.github.tlb.service.TalkToTlbServer"
35
+ ENV['TLB_JOB_NAME'] = JOB_NAME
36
+ ENV['TOTAL_PARTITIONS'] = '2'
37
+ ENV['JOB_VERSION'] = '123'
38
+ ENV['TLB_CRITERIA'] = 'com.github.tlb.splitter.CountBasedTestSplitterCriteria'
39
+ end
40
+
41
+ after do
42
+ Tlb.server_running?.should be_true #api calls need not worry about killing it
43
+ Tlb.stop_server
44
+ end
45
+
46
+ it "should balance for first partition" do
47
+ ENV['PARTITION_NUMBER'] = '1'
48
+ Tlb.start_server
49
+ Tlb.balance_and_order(["foo/bar.rb", "foo/baz.rb", "bar/foo.rb", "bar/quux.rb"]).should == ["./foo/bar.rb", "./foo/baz.rb"]
50
+ end
51
+
52
+ it "should balance for second partition" do
53
+ ENV['PARTITION_NUMBER'] = '2'
54
+ Tlb.start_server
55
+ Tlb.balance_and_order(["foo/bar.rb", "foo/baz.rb", "bar/foo.rb", "bar/quux.rb"]).should == ["./bar/foo.rb", "./bar/quux.rb"]
56
+ end
57
+
58
+ it "should balance with file path names relative to working dir" do
59
+ ENV['PARTITION_NUMBER'] = '1'
60
+ Tlb.start_server
61
+ Tlb.balance_and_order(["foo/hi/../baz/quux/../hello/../../bar.rb", "foo/bar/../baz.rb", "bar/baz/quux/../../foo.rb", "bar/quux.rb"]).should == ["./foo/bar.rb", "./foo/baz.rb"]
62
+ end
63
+
64
+ describe "thats already running" do
65
+ before do
66
+ Tlb.start_server
67
+ end
68
+
69
+ it "should publish suite result" do
70
+ Tlb.suite_result("foo/bar.rb", true)
71
+ Tlb.suite_result("foo/baz.rb", false)
72
+ Tlb.suite_result("foo/quux.rb", true)
73
+ get_from_tlb_server("suite_result").should include("foo/bar.rb: true", "foo/baz.rb: false", "foo/quux.rb: true")
74
+ end
75
+
76
+ it "should publish suite time" do
77
+ Tlb.suite_time("foo/bar.rb", 102)
78
+ Tlb.suite_time("foo/baz.rb", 3599)
79
+ Tlb.suite_time("foo/quux.rb", 2010)
80
+ get_from_tlb_server("suite_time").should include("foo/bar.rb: 102", "foo/baz.rb: 3599", "foo/quux.rb: 2010")
81
+ end
82
+
83
+ def get_from_tlb_server path
84
+ body = Net::HTTP.get(URI.parse("#{URL}/#{JOB_NAME}/#{path}"))
85
+ body.split("\n")
86
+ end
87
+
88
+ it "should use send method to balance" do
89
+ Tlb::Balancer.expects(:send).with(Tlb::Balancer::BALANCE_PATH, "./foo/bar.rb\n./foo/baz.rb").returns("foo\nbar")
90
+ Tlb.balance_and_order(["foo/bar.rb", "foo/baz.rb"]).should == ["foo", "bar"]
91
+ end
92
+
93
+ it "should use send method to post results" do
94
+ Tlb::Balancer.expects(:send).with(Tlb::Balancer::SUITE_RESULT_REPORTING_PATH, "foo/bar.rb: false")
95
+ Tlb.suite_result("foo/bar.rb", false)
96
+ end
97
+
98
+ it "should use send method to post time" do
99
+ Tlb::Balancer.expects(:send).with(Tlb::Balancer::SUITE_TIME_REPORTING_PATH, "foo/bar.rb: 42")
100
+ Tlb.suite_time("foo/bar.rb", 42)
101
+ end
102
+
103
+ it "should raise exception when call to tlb fails" do
104
+ lambda do
105
+ Tlb::Balancer.send("/foo", "bar")
106
+ end.should raise_error(Net::HTTPServerException, '404 "The server has not found anything matching the request URI"')
107
+ end
108
+ end
109
+ end
110
+
111
+ describe :wait_for_server_to_start do
112
+ class CtrlStatus < WEBrick::HTTPServlet::AbstractServlet
113
+ def do_GET(request, response)
114
+ response.status = 200
115
+ response['Content-Type'] = 'text/plain'
116
+ response.body = 'RUNNING'
117
+ end
118
+ end
119
+ before do
120
+ @server = nil
121
+ ENV['TLB_BALANCER_PORT'] = TLB_BALANCER_PORT
122
+ end
123
+
124
+ after do
125
+ @server.shutdown
126
+ end
127
+
128
+ it "should wait until socket has a listener" do
129
+ @wait_completed = false
130
+ before_start = Time.now
131
+ wait_thread = Thread.new do
132
+ sleep 3
133
+ @wait_completed = true
134
+ @server = WEBrick::HTTPServer.new(:Port => TLB_BALANCER_PORT,
135
+ :Logger => WEBrick::BasicLog.new(tmp_file('tlb_webrick_log').path),
136
+ :AccessLog => WEBrick::BasicLog.new(tmp_file('tlb_webrick_access_log').path))
137
+ @server.mount '/control/status', CtrlStatus
138
+ @server.start
139
+ end
140
+ @wait_completed.should be_false
141
+ Tlb::Balancer.wait_for_start
142
+ @wait_completed.should be_true
143
+ Time.now.should > (before_start + 3)
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,89 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'open4'
3
+ require 'parsedate'
4
+ require 'tmpdir'
5
+
6
+ describe Tlb do
7
+ before do
8
+ ENV[Tlb::TLB_OUT_FILE] = (@out_file = tmp_file('tlb_out_file').path)
9
+ ENV[Tlb::TLB_ERR_FILE] = (@err_file = tmp_file('tlb_err_file').path)
10
+ Tlb::Balancer.stubs(:wait_for_start)
11
+ end
12
+
13
+ MOCK_PROCESS_ID = 33040
14
+ SIG_TERM = 15
15
+
16
+ it "should terminate process when stop called" do
17
+ Tlb.instance_variable_set('@pid', MOCK_PROCESS_ID)
18
+ Tlb.instance_variable_set('@out_pumper', Thread.new { })
19
+ Tlb.instance_variable_set('@err_pumper', Thread.new { })
20
+
21
+ Process.expects(:kill).with(SIG_TERM, MOCK_PROCESS_ID)
22
+ Process.expects(:wait).with()
23
+
24
+ Tlb.stop_server
25
+ end
26
+
27
+ def times_of_output content, stream_name
28
+ content.split("\n").map { |line| line.gsub(stream_name, '') }.map do |line|
29
+ year, month, day_of_month, hour, minute, second, timezone, day_of_week = ParseDate.parsedate(line)
30
+ Time.local(year, month, day_of_month, hour, minute, second)
31
+ end
32
+ end
33
+
34
+ it "should generate the right command to run tlb balancer server" do
35
+ tlb_jar = File.expand_path(Dir.glob(File.join(File.join(File.dirname(__FILE__), ".."), "tlb-all*")).first)
36
+ Tlb.server_command.should == "java -jar #{tlb_jar}"
37
+ end
38
+
39
+ describe :integration_test do
40
+ it "should pump both error and out to the file" do
41
+ Tlb.stubs(:wait_for_start)
42
+ Tlb.expects(:server_command).returns(File.join(File.dirname(__FILE__), "fixtures", "foo.sh"))
43
+ Tlb.start_server
44
+ sleep 2
45
+ Tlb.stop_server
46
+ File.read(@out_file).should include("hello out\n")
47
+ File.read(@err_file).should include("hello err\n")
48
+ end
49
+ end
50
+
51
+ it "should fail of server not running" do
52
+ Tlb.expects(:server_running?).returns(false)
53
+ lambda { Tlb.ensure_server_running }.should raise_error('Balancer server must be started before tests are run.')
54
+ end
55
+
56
+ it "should not start server if running" do
57
+ Tlb.expects(:server_running?).returns(true)
58
+ Tlb.expects(:start_server).never
59
+ Tlb.ensure_server_running
60
+ end
61
+
62
+ describe "env var" do
63
+ before do
64
+ module Open4
65
+ class << self
66
+ alias_method :old_popen4, :popen4
67
+ end
68
+
69
+ def self.popen4 command
70
+ ENV['TLB_APP'].should == "com.github.tlb.balancer.BalancerInitializer"
71
+ end
72
+ end
73
+ end
74
+
75
+ after do
76
+ module Open4
77
+ class << self
78
+ alias_method :popen4, :old_popen4
79
+ end
80
+ end
81
+ end
82
+
83
+ it "should set TLB_APP to point to balancer before starting the server" do
84
+ ENV['TLB_APP'] = "foo"
85
+ Tlb.stubs(:server_command).returns("foo bar")
86
+ Tlb.start_server
87
+ end
88
+ end
89
+ end
Binary file
@@ -0,0 +1,11 @@
1
+ $name="tlb-rspec1"
2
+ $framework='rspec-1.x'
3
+ require File.join(File.dirname(__FILE__), 'gem_common')
4
+
5
+ Gem::Specification.new do |s|
6
+ configure_tlb(s)
7
+
8
+ s.files = files('tests', File.join('lib', 'tlb', 'test_unit'))
9
+
10
+ s.add_development_dependency 'rspec', '>= 1.3.0'
11
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tlb-rspec1
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Janmejay Singh
14
+ - Pavan KS
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-12-22 00:00:00 +05:30
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: open4
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 21
31
+ segments:
32
+ - 1
33
+ - 0
34
+ - 1
35
+ version: 1.0.1
36
+ type: :runtime
37
+ version_requirements: *id001
38
+ - !ruby/object:Gem::Dependency
39
+ name: rake
40
+ prerelease: false
41
+ requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ hash: 3
47
+ segments:
48
+ - 0
49
+ version: "0"
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: rspec
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 27
61
+ segments:
62
+ - 1
63
+ - 3
64
+ - 0
65
+ version: 1.3.0
66
+ type: :development
67
+ version_requirements: *id003
68
+ description: |
69
+ TLB ruby implementation base, which provides support for load balancing tests written in rspec-1.x.
70
+ TLB_rb test suite is not bundled, please check http://github.com/test-load-balancer/tlb_rb for tests.
71
+ Detailed configuration documentation can be found at https://github.com/test-load-balancer/tlb/wiki.
72
+
73
+ email: singh.janmejay@gmail.com;itspanzi@gmail.com
74
+ executables: []
75
+
76
+ extensions: []
77
+
78
+ extra_rdoc_files:
79
+ - README.markdown
80
+ files:
81
+ - .gitignore
82
+ - .gitmodules
83
+ - README.markdown
84
+ - Rakefile
85
+ - gem_common.rb
86
+ - lib/tasks/tlb.rake
87
+ - lib/tlb.rb
88
+ - lib/tlb/spec_formatter.rb
89
+ - lib/tlb/spec_task.rb
90
+ - spec/fixtures/foo.sh
91
+ - spec/spec_helper.rb
92
+ - spec/tlb/spec_formatter_spec.rb
93
+ - spec/tlb/spec_task_spec.rb
94
+ - spec/tlb_http_communication_spec.rb
95
+ - spec/tlb_process_control_spec.rb
96
+ - tlb-rspec1.gemspec
97
+ - tlb-all-gv0.2-9-g1f1a4aa.jar
98
+ has_rdoc: true
99
+ homepage: http://github.com/test-load-balancer/tlb_rb
100
+ licenses: []
101
+
102
+ post_install_message: |
103
+ -------------------------------------------------------------------------
104
+ Documentation: Detailed configuration documentation can be found at https://github.com/test-load-balancer/tlb/wiki.
105
+ -----------------------------
106
+ Example: https://github.com/test-load-balancer/sample_projects has examples projects and shell script to demonstrate a typical build(by starting a local tlb server, and executing two partitions locally). While partitions in these examples execute one after another, in an actual CI/pre-checkin build, they will actually run parallely on different machines.
107
+ Its a good idea to play with the environment variables values being used in these shell-scripts to understand how they affect TLB's behaviour. You may want to check https://github.com/test-load-balancer/tlb/wiki/Configuration-Variables, which documents each variable and its effect.
108
+ -----------------------------
109
+ Issue tracker: Please report bugs/enhancements/feature-requests at http://code.google.com/p/tlb/issues/list. Github, Rubyforge or any other issue trackers are not monitored or updated.
110
+ -------------------------------------------------------------------------
111
+
112
+ rdoc_options:
113
+ - --charset=UTF-8
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ hash: 3
122
+ segments:
123
+ - 0
124
+ version: "0"
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ hash: 3
131
+ segments:
132
+ - 0
133
+ version: "0"
134
+ requirements: []
135
+
136
+ rubyforge_project: tlb-rb
137
+ rubygems_version: 1.3.7
138
+ signing_key:
139
+ specification_version: 3
140
+ summary: tlb-rspec1-0.1.0-rspec-1
141
+ test_files: []
142
+