commit 5e36e4e8c9554310e3bf4e196ba4ffc798c7293c Author: John Bintz Date: Thu Jan 19 15:59:05 2012 -0500 initial commit, starting work on features diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d87d4be --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.gem +*.rbc +.bundle +.config +.yardoc +Gemfile.lock +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..22f8332 --- /dev/null +++ b/Gemfile @@ -0,0 +1,8 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in flowerbox-delivery.gemspec +gemspec + +gem 'guard' +gem 'guard-rspec' +gem 'guard-cucumber' diff --git a/Guardfile b/Guardfile new file mode 100644 index 0000000..bab3f01 --- /dev/null +++ b/Guardfile @@ -0,0 +1,25 @@ +# A sample Guardfile +# More info at https://github.com/guard/guard#readme + +guard 'rspec', :version => 2 do + watch(%r{^spec/.+_spec\.rb$}) + watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } + watch('spec/spec_helper.rb') { "spec" } + + # Rails example + watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } + watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } + watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } + watch(%r{^spec/support/(.+)\.rb$}) { "spec" } + watch('config/routes.rb') { "spec/routing" } + watch('app/controllers/application_controller.rb') { "spec/controllers" } + # Capybara request specs + watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" } +end + + +guard 'cucumber', :cli => '-f pretty' do + watch(%r{^features/.+\.feature$}) + watch(%r{^features/support/.+$}) { 'features' } + watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' } +end diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..87b491e --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012 John Bintz + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5fb425d --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# Flowerbox Delivery + +Fresh cut code testing flowers, delivered straight to your door! + +This component of Flowerbox uses Sprockets to compile your test suite's code into a nice package, +then delivers it to a runner in two ways: + +* Straight on the filesystem, the absolute fastest way to run your tests +* Through a Rack server, so you can teest things that need a real HTTP url, like history and WebWorkers + +## Installation + +Add this line to your application's Gemfile: + + gem 'flowerbox-delivery' + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install flowerbox-delivery + +## Usage + +TODO: Write usage instructions here + +## Contributing + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Added some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..f57ae68 --- /dev/null +++ b/Rakefile @@ -0,0 +1,2 @@ +#!/usr/bin/env rake +require "bundler/gem_tasks" diff --git a/config/cucumber.yml b/config/cucumber.yml new file mode 100644 index 0000000..a061551 --- /dev/null +++ b/config/cucumber.yml @@ -0,0 +1,2 @@ +default: -r features + diff --git a/features/start_server.feature b/features/start_server.feature new file mode 100644 index 0000000..225d043 --- /dev/null +++ b/features/start_server.feature @@ -0,0 +1,27 @@ +Feature: Start Server + Scenario: With no conflicting port, start a random port server + Given I have a server with the default configuration + And the server will start on the random port "12345" + When I start the server + Then the server should have started on port "12345", interface "127.0.0.1" + + Scenario: With a conflicting port, start a random port server + Given I have a server with the default configuration + And the server will start on the random ports "12345,23456" + And I have an existing service running on port "12345" + When I start the server + Then the server should have started on port "23456", interface "127.0.0.1" + + Scenario: Specify a port for starting the server + Given I have a server with the configuration: + | port | 12345 | + When I start the server + Then the server should have started on port "12345", interface "127.0.0.1" + + Scenario: Specify an adapter and port for starting + Given I have a server with the configuration: + | port | 12345 | + | interface | 0.0.0.0 | + When I start the server + Then the server should have started on port "12345", interface "127.0.0.1" + diff --git a/features/step_definitions/given/i_have_existing_service.rb b/features/step_definitions/given/i_have_existing_service.rb new file mode 100644 index 0000000..937362c --- /dev/null +++ b/features/step_definitions/given/i_have_existing_service.rb @@ -0,0 +1,15 @@ +require 'thread' +require 'rack' + +Given /^I have an existing service running on port "([^"]*)"$/ do |port| + @running_server = Thread.new do + Rack::Handler::WEBrick.run(lambda { |env| [ 200, {}, [] ] }, :Port => port) { |server| + Thread.current[:server] = server + } + end + + while !@running_server[:server] + sleep 0.1 + end +end + diff --git a/features/step_definitions/given/i_have_server_with_config.rb b/features/step_definitions/given/i_have_server_with_config.rb new file mode 100644 index 0000000..a620bdb --- /dev/null +++ b/features/step_definitions/given/i_have_server_with_config.rb @@ -0,0 +1,9 @@ +Given /^I have a server with the configuration:$/ do |table| + options = {} + + table.rows_hash.each do |key, value| + options[key.to_sym] = value + end + + @server = Flowerbox::Delivery::Server.new(options) +end diff --git a/features/step_definitions/given/i_have_server_with_default_config.rb b/features/step_definitions/given/i_have_server_with_default_config.rb new file mode 100644 index 0000000..f126654 --- /dev/null +++ b/features/step_definitions/given/i_have_server_with_default_config.rb @@ -0,0 +1,3 @@ +Given /^I have a server with the default configuration$/ do + @server = Flowerbox::Delivery::Server.new +end diff --git a/features/step_definitions/given/the_server_will_start_on_random_port.rb b/features/step_definitions/given/the_server_will_start_on_random_port.rb new file mode 100644 index 0000000..bdc12a3 --- /dev/null +++ b/features/step_definitions/given/the_server_will_start_on_random_port.rb @@ -0,0 +1,6 @@ +Given /^the server will start on the random ports? "([^"]*)"$/ do |ports| + ports = ports.split(',').collect(&:to_i) + + @server.stubs(:random_port).returns(*ports) +end + diff --git a/features/step_definitions/then/server_should_be_running.rb b/features/step_definitions/then/server_should_be_running.rb new file mode 100644 index 0000000..1d32a13 --- /dev/null +++ b/features/step_definitions/then/server_should_be_running.rb @@ -0,0 +1,7 @@ +require 'net/http' + +Then /^the server should have started on port "([^"]*)", interface "([^"]*)"$/ do |port, interface| + socket = TCPSocket.new(interface, port.to_i) + socket.close +end + diff --git a/features/step_definitions/when/i_start_the_server.rb b/features/step_definitions/when/i_start_the_server.rb new file mode 100644 index 0000000..1171e54 --- /dev/null +++ b/features/step_definitions/when/i_start_the_server.rb @@ -0,0 +1,4 @@ +When /^I start the server$/ do + @server.start +end + diff --git a/features/support/env.rb b/features/support/env.rb new file mode 100644 index 0000000..06cbad9 --- /dev/null +++ b/features/support/env.rb @@ -0,0 +1,26 @@ +require 'flowerbox-delivery' + +require 'mocha' + +World(Mocha::API) + +Before do + mocha_setup +end + +After do + begin + mocha_verify + ensure + mocha_teardown + end + + if @running_server + @running_server[:server].shutdown + @running_server = nil + + sleep 0.1 + end + + @server.stop if @server +end diff --git a/flowerbox-delivery.gemspec b/flowerbox-delivery.gemspec new file mode 100644 index 0000000..2e01a64 --- /dev/null +++ b/flowerbox-delivery.gemspec @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../lib/flowerbox/delivery/version', __FILE__) + +Gem::Specification.new do |gem| + gem.authors = ["John Bintz"] + gem.email = ["john@coswellproductions.com"] + gem.description = %q{TODO: Write a gem description} + gem.summary = %q{TODO: Write a gem summary} + gem.homepage = "" + + gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } + gem.files = `git ls-files`.split("\n") + gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + gem.name = "flowerbox-delivery" + gem.require_paths = ["lib"] + gem.version = Flowerbox::Delivery::VERSION + + gem.add_development_dependency 'cucumber' + gem.add_development_dependency 'rspec' + gem.add_development_dependency 'jquery-rails', '~> 1.0.0' + gem.add_development_dependency 'mocha' + + gem.add_runtime_dependency 'rack' + gem.add_runtime_dependency 'sprockets' + gem.add_runtime_dependency 'coffee-script' +end diff --git a/lib/flowerbox-delivery.rb b/lib/flowerbox-delivery.rb new file mode 100644 index 0000000..830bacb --- /dev/null +++ b/lib/flowerbox-delivery.rb @@ -0,0 +1,2 @@ +require "flowerbox/delivery" + diff --git a/lib/flowerbox-delivery/version.rb b/lib/flowerbox-delivery/version.rb new file mode 100644 index 0000000..385aa8c --- /dev/null +++ b/lib/flowerbox-delivery/version.rb @@ -0,0 +1,5 @@ +module Flowerbox + module Delivery + VERSION = "0.0.1" + end +end diff --git a/lib/flowerbox/delivery.rb b/lib/flowerbox/delivery.rb new file mode 100644 index 0000000..bc2e398 --- /dev/null +++ b/lib/flowerbox/delivery.rb @@ -0,0 +1,6 @@ +module Flowerbox + module Delivery + autoload :Server, 'flowerbox/delivery/server' + end +end + diff --git a/lib/flowerbox/delivery/server.rb b/lib/flowerbox/delivery/server.rb new file mode 100644 index 0000000..b0b85b1 --- /dev/null +++ b/lib/flowerbox/delivery/server.rb @@ -0,0 +1,97 @@ +require 'rack' +require 'net/http' +require 'socket' + +module Flowerbox + module Delivery + class Server + attr_reader :options + + def initialize(options = {}) + @options = options || {} + end + + def start + @server_thread = Thread.new do + Rack::Handler::WEBrick.run(lambda { |env| [ 200, {}, [] ] }, :Port => port, :Host => interface) { |server| + Thread.current[:server] = server + } + end + + while !@server_thread[:server] + sleep 0.1 + end + end + + def stop + if @server_thread + @server_thread[:server].shutdown + + wait_for_server_to_stop + end + end + + def interface + options[:interface] || '0.0.0.0' + end + + def port + return @port if @port ||= options[:port] + + attempts = 100 + + begin + attempts -= 1 + + current_port = random_port + + begin + socket = TCPSocket.new(interface, current_port) + socket.close + rescue Errno::ECONNREFUSED => e + @port = current_port + end + end while !@port and attempts > 0 + + raise StandardError.new("can't start server") if attempts == 0 + + @port + end + + private + def wait_for_server_to_start + while true do + begin + connect_interface = '127.0.0.1' if interface == '0.0.0.0' + + TCPSocket.new(connect_interface, port) + break + rescue Errno::ECONNREFUSED => e + end + + sleep 0.1 + end + end + + def wait_for_server_to_stop + while true do + begin + connect_interface = '127.0.0.1' if interface == '0.0.0.0' + + socket = TCPSocket.new(connect_interface, port) + socket.close + rescue Errno::ECONNREFUSED => e + return + end + + sleep 0.1 + end + end + + def random_port + 25000 + Kernel.rand(1000) + end + end + end +end + diff --git a/lib/flowerbox/delivery/version.rb b/lib/flowerbox/delivery/version.rb new file mode 100644 index 0000000..572e687 --- /dev/null +++ b/lib/flowerbox/delivery/version.rb @@ -0,0 +1,6 @@ +module Flowerbox + module Delivery + VERSION = "0.0.1" + end +end + diff --git a/spec/flowerbox/delivery/server_spec.rb b/spec/flowerbox/delivery/server_spec.rb new file mode 100644 index 0000000..1333c93 --- /dev/null +++ b/spec/flowerbox/delivery/server_spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' +require 'socket' +require 'thread' + +describe Flowerbox::Delivery::Server do + let(:server) { described_class.new(options) } + let(:options) { nil } + + subject { server } + + describe '#initialize' do + let(:options) { { :port => port, :interface => interface } } + let(:port) { 'port' } + let(:interface) { 'interface' } + + its(:port) { should == port } + its(:interface) { should == interface } + end + + describe '#start' do + let(:port) { 12345 } + let(:interface) { '127.0.0.1' } + + before do + server.stubs(:port).returns(port) + server.stubs(:interface).returns(interface) + end + + it 'should start a Rack server' do + server.start + + TCPSocket.new(server.interface, server.port) + end + end + + describe '#interface' do + subject { server.interface } + + it { should == '0.0.0.0' } + end + + describe '#port' do + let(:interface) { '127.0.0.1' } + + before do + server.stubs(:interface).returns(interface) + end + + subject { server.port } + + context 'no running service' do + before do + Kernel.stubs(:rand).returns(0) + end + + it { should == 25000 } + end + + context 'running service' do + before do + @server = Thread.new do + TCPServer.new(interface, 25000) + end + + server.stubs(:random_port).returns(25000, 25001) + end + + it { should == 25001 } + end + end +end + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..6942209 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,6 @@ +require 'flowerbox-delivery' +require 'mocha' + +RSpec.configure do |c| + c.mock_with :mocha +end