testengineer 0.1.2

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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in test-engineer.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Lookout, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # TestEngineer
2
+
3
+
4
+ TestEngineer is a simple gem which adds some code around
5
+ [foreman](https://github.com/ddollar/foreman) for integration testing.
6
+
7
+ Currently it allows you to bring up an entire stack, based on what is defined
8
+ in the Foreman `Procfile`, for the duration of a test case.
9
+
10
+ TestEngineer also allows you to arbitrarily shut off processes by name during
11
+ the runtime of a test case.
12
+
13
+
14
+
15
+ ## With Cucumber
16
+
17
+ There is an `Around` hook that you can include with
18
+ `require 'testengineer/cucumber'`. This will wrap any Scenario tagged with the
19
+ `@testengineer` tag.
20
+
21
+ Imagine a `Procfile` such as:
22
+
23
+ web: ruby -r thin app.rb
24
+ db: ./script/run-mongodb
25
+ cache: memcached
26
+
27
+ A sample Cucumber feature might look like:
28
+
29
+
30
+ @testengineer
31
+ Feature: Log in to My Site
32
+ In order to facilitate meaningful relationships between users
33
+ As a web visitor
34
+ I should be able to log into my account on My Site
35
+
36
+ Scenario: Log in
37
+ Given an account named "octocat"
38
+ When I log in to My Site
39
+ Then I should be delighted with my fabulous profile
40
+
41
+ Scenario: Log in when the site is degraded
42
+ Given an account named "octocat"
43
+ And the database is offline
44
+ When I log in to My Site
45
+ Then I should see a nice friendly fail whale.
46
+
47
+
48
+ For each scenario, TestEngineer will bring the entire stack (web, db, cache) up
49
+ and down. In the second scenario, I would have defined the step for `And the
50
+ database is offline` as:
51
+
52
+ Given /^the database is offline$/ do
53
+ TestEngineer.stop_process('db')
54
+ end
55
+
56
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+
6
+ RSpec::Core::RakeTask.new('test') do |t|
7
+ t.rspec_opts = '-c --fail-fast'
8
+ end
9
+
10
+ task :default => [:test, :build]
@@ -0,0 +1,70 @@
1
+ require "testengineer/version"
2
+ require 'timeout'
3
+ require 'foreman/engine'
4
+
5
+ module TestEngineer
6
+ def self.foreman
7
+ $foreman
8
+ end
9
+
10
+ def self.wait_for_socket(host='localhost', port=nil)
11
+ return if port.nil?
12
+
13
+ puts "Waiting for server at #{host}:#{port} to come online.."
14
+ running = false
15
+ while !running do
16
+ sleep 0.2
17
+ begin
18
+ TCPSocket.new(host, port)
19
+ rescue Errno::ECONNREFUSED => e
20
+ next
21
+ end
22
+ running = true
23
+ end
24
+ end
25
+
26
+ def self.stop_process(name)
27
+ if foreman.nil?
28
+ puts "Foreman hasn't been started, whoops"
29
+ return
30
+ end
31
+
32
+ if name.nil?
33
+ raise Exception, 'TestEngineer#stop_process cannot handle a nil process name'
34
+ end
35
+ procs = foreman.send(:running_processes)
36
+ procs.each do |pid, p|
37
+ parts = p.name.split('.')
38
+ unless parts.first.start_with? name
39
+ next
40
+ end
41
+ p.kill 'SIGTERM'
42
+ ::Timeout.timeout(5) do
43
+ begin
44
+ Process.waitpid(pid)
45
+ rescue Errno::ECHILD
46
+ end
47
+ end
48
+ end
49
+ # If we don't set @terminating to false, then the eventual invocation of
50
+ # #terminate_gracefully will return immediately
51
+ foreman.instance_variable_set(:@terminating, false)
52
+ end
53
+
54
+ def self.start_stack
55
+ procfile = File.expand_path(Dir.pwd + '/Procfile')
56
+ unless File.exists? procfile
57
+ raise StandardError, 'Procfile does not exist!'
58
+ end
59
+ $foreman = ::Foreman::Engine.new(procfile, {})
60
+
61
+ Thread.new do
62
+ foreman.start
63
+ end
64
+ end
65
+
66
+ def self.stop_stack
67
+ foreman.send(:terminate_gracefully) unless foreman.nil?
68
+ foreman = nil
69
+ end
70
+ end
@@ -0,0 +1,8 @@
1
+ Around('@testengineer') do |scenario, block|
2
+ TestEngineer.start_stack
3
+ begin
4
+ block.call
5
+ ensure
6
+ TestEngineer.stop_stack
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module TestEngineer
2
+ VERSION = "0.1.#{ENV['BUILD_NUMBER'] || 'dev'}"
3
+ end
@@ -0,0 +1,3 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib/')
2
+
3
+ require 'testengineer'
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ describe TestEngineer do
4
+ let(:foreman) { double('Foreman::Engine') }
5
+
6
+ describe '#foreman' do
7
+ it 'should return nil when $foreman is nil' do
8
+ subject.foreman.should be nil
9
+ end
10
+
11
+ it 'return the value in $foreman' do
12
+ $foreman = foreman
13
+ subject.foreman.should be $foreman
14
+ end
15
+
16
+ after :each do
17
+ $foreman = nil
18
+ end
19
+ end
20
+
21
+ describe '#wait_for_socket' do
22
+ end
23
+
24
+ describe '#stop_process' do
25
+ before :each do
26
+ subject.stub(:foreman).and_return(foreman)
27
+ end
28
+ context 'no running processes' do
29
+ before :each do
30
+ foreman.stub(:running_processes).and_return([])
31
+ end
32
+
33
+ it 'do nothing and reset Foreman::Engine.terminating' do
34
+ foreman.should_receive(:instance_variable_set).with(:@terminating, false)
35
+ subject.stop_process('foreman')
36
+ end
37
+
38
+ it 'should raise an error on a nil name argument' do
39
+ expect {
40
+ subject.stop_process(nil)
41
+ }.to raise_error
42
+ end
43
+ end
44
+ context 'running processes' do
45
+ let(:process) { double('Foreman::Process') }
46
+ before :each do
47
+ process.stub(:name).and_return('mock.1')
48
+ foreman.stub(:running_processes).and_return([[1, process]])
49
+ end
50
+
51
+ it 'should not stop unmatched names' do
52
+ process.should_not_receive(:kill)
53
+ subject.stop_process('foreman')
54
+ end
55
+
56
+ it 'should not stop partially matched names' do
57
+ process.should_not_receive(:kill)
58
+ subject.stop_process('mockery')
59
+ end
60
+
61
+ it 'should stop matching named processes' do
62
+ process.should_receive(:kill)
63
+ Process.stub(:waitpid).and_return(true)
64
+ subject.stop_process('mock')
65
+ end
66
+ end
67
+ end
68
+
69
+ describe '#start_stack' do
70
+ end
71
+
72
+ describe '#stop_stack' do
73
+ it 'should not do anything if foreman is nil' do
74
+ foreman.should_not_receive(:terminate_gracefully)
75
+ subject.stub(:foreman).and_return(nil)
76
+ subject.stop_stack
77
+ end
78
+
79
+ it 'should invoke #terminate_gracefully if foreman exists' do
80
+ foreman.should_receive(:terminate_gracefully)
81
+ subject.stub(:foreman).and_return(foreman)
82
+ subject.stop_stack
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/testengineer/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["R. Tyler Croy"]
6
+ gem.email = ["rtyler.croy@mylookout.com"]
7
+ gem.description = %q{TestEngineer}
8
+ gem.summary = %q{TestEngineer}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "testengineer"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = TestEngineer::VERSION
17
+
18
+ gem.add_dependency 'foreman'
19
+ gem.add_development_dependency 'rake'
20
+ gem.add_development_dependency 'rspec'
21
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: testengineer
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 2
10
+ version: 0.1.2
11
+ platform: ruby
12
+ authors:
13
+ - R. Tyler Croy
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-17 00:00:00 +00:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 3
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ version_requirements: *id001
32
+ name: foreman
33
+ prerelease: false
34
+ type: :runtime
35
+ - !ruby/object:Gem::Dependency
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ hash: 3
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ version_requirements: *id002
46
+ name: rake
47
+ prerelease: false
48
+ type: :development
49
+ - !ruby/object:Gem::Dependency
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ version_requirements: *id003
60
+ name: rspec
61
+ prerelease: false
62
+ type: :development
63
+ description: TestEngineer
64
+ email:
65
+ - rtyler.croy@mylookout.com
66
+ executables: []
67
+
68
+ extensions: []
69
+
70
+ extra_rdoc_files: []
71
+
72
+ files:
73
+ - .gitignore
74
+ - Gemfile
75
+ - LICENSE
76
+ - README.md
77
+ - Rakefile
78
+ - lib/testengineer.rb
79
+ - lib/testengineer/cucumber.rb
80
+ - lib/testengineer/version.rb
81
+ - spec/spec_helper.rb
82
+ - spec/testengineer/main_spec.rb
83
+ - testengineer.gemspec
84
+ has_rdoc: true
85
+ homepage: ""
86
+ licenses: []
87
+
88
+ post_install_message:
89
+ rdoc_options: []
90
+
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ hash: 3
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ requirements: []
112
+
113
+ rubyforge_project:
114
+ rubygems_version: 1.6.2
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: TestEngineer
118
+ test_files:
119
+ - spec/spec_helper.rb
120
+ - spec/testengineer/main_spec.rb