From 2ed1fe8d44f2007bcade24ff613ff20933af0c40 Mon Sep 17 00:00:00 2001 From: brainopia Date: Sun, 22 Jan 2012 08:54:42 +0400 Subject: [PATCH 1/6] Add specs for initialization of Foreman::Process --- spec/foreman/process_spec.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spec/foreman/process_spec.rb b/spec/foreman/process_spec.rb index 2fb5841..9e32200 100644 --- a/spec/foreman/process_spec.rb +++ b/spec/foreman/process_spec.rb @@ -1,2 +1,19 @@ require "spec_helper" require "foreman/process" +require 'ostruct' + +describe Foreman::Process do + subject { described_class.new entry, number, port } + + let(:number) { 1 } + let(:port) { 777 } + let(:command) { :script } + let(:name) { :foobar } + let(:entry) { OpenStruct.new :name => name, :command => command } + + its(:entry) { entry } + its(:num) { number } + its(:port) { port } + its(:name) { "#{name}.#{port}" } + its(:pid) { nil } +end From 158c184f8cb5e2c43f04459ada4f1f0a74f15bdd Mon Sep 17 00:00:00 2001 From: brainopia Date: Sun, 22 Jan 2012 08:55:45 +0400 Subject: [PATCH 2/6] Add specs for options of Foreman::Process#run --- spec/foreman/process_spec.rb | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spec/foreman/process_spec.rb b/spec/foreman/process_spec.rb index 9e32200..e64e92c 100644 --- a/spec/foreman/process_spec.rb +++ b/spec/foreman/process_spec.rb @@ -16,4 +16,39 @@ describe Foreman::Process do its(:port) { port } its(:name) { "#{name}.#{port}" } its(:pid) { nil } + + describe '#run' do + let(:pipe) { :pipe } + let(:basedir) { Dir.mktmpdir } + let(:env) {{ 'foo' => 'bar' }} + + let(:run) do + subject.run pipe, basedir, env + end + + it 'should change to basedir' do + mock(Dir).chdir basedir + run + end + + it 'should set PORT for environment' do + mock(subject).run_process(command, pipe) do + ENV['PORT'].should == port.to_s + end + run + end + + it 'should set custom variables for environment' do + mock(subject).run_process(command, pipe) do + ENV['foo'].should == 'bar' + end + run + end + + it 'should restore environment afterwards' do + mock(subject).run_process command, pipe + run + ENV.should_not include('PORT', 'foo') + end + end end From df043e60d8f1480845f6c10db21dcfa3b6088030 Mon Sep 17 00:00:00 2001 From: brainopia Date: Sun, 22 Jan 2012 08:57:09 +0400 Subject: [PATCH 3/6] Simplify Foreman::Process#with_environment --- lib/foreman/process.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/foreman/process.rb b/lib/foreman/process.rb index ac929fa..e37bc17 100644 --- a/lib/foreman/process.rb +++ b/lib/foreman/process.rb @@ -59,12 +59,11 @@ private end def with_environment(environment) - old_env = ENV.each_pair.inject({}) { |h,(k,v)| h.update(k => v) } - environment.each { |k,v| ENV[k] = v } - ret = yield - ENV.clear - old_env.each { |k,v| ENV[k] = v} - ret + original = ENV.to_hash + ENV.update environment + yield + ensure + ENV.replace original end end From 047f106d4857ed51d300826fe7e810af34f787f1 Mon Sep 17 00:00:00 2001 From: brainopia Date: Sun, 22 Jan 2012 15:00:43 +0400 Subject: [PATCH 4/6] - Use explicit fakefs tag in specs - Clean up trailing whitespace --- spec/foreman/cli_spec.rb | 20 ++++++++++---------- spec/foreman/engine_spec.rb | 2 +- spec/foreman/export/bluepill_spec.rb | 6 +++--- spec/foreman/export/runit_spec.rb | 20 ++++++++++---------- spec/foreman/export/upstart_spec.rb | 2 +- spec/foreman_spec.rb | 9 ++------- spec/helper_spec.rb | 6 +++--- spec/spec_helper.rb | 5 +++-- 8 files changed, 33 insertions(+), 37 deletions(-) diff --git a/spec/foreman/cli_spec.rb b/spec/foreman/cli_spec.rb index 8b9285f..4170af9 100644 --- a/spec/foreman/cli_spec.rb +++ b/spec/foreman/cli_spec.rb @@ -1,7 +1,7 @@ require "spec_helper" require "foreman/cli" -describe "Foreman::CLI" do +describe "Foreman::CLI", :fakefs do subject { Foreman::CLI.new } describe "start" do @@ -89,47 +89,47 @@ describe "Foreman::CLI" do end end end - + describe "run" do describe "with a valid Procfile" do before { write_procfile } describe "and a command" do let(:command) { ["ls", "-l"] } - + before(:each) do stub(subject).exec end - + it "should load the environment file" do write_env preserving_env do subject.run *command ENV["FOO"].should == "bar" end - + ENV["FOO"].should be_nil end - + it "should runute the command as a string" do mock(subject).exec(command.join(" ")) subject.run *command end end - + describe "and a non-existent command" do let(:command) { "iuhtngrglhulhdfg" } - + it "should print an error" do mock_error(subject, "command not found: #{command}") do subject.run command end end end - + describe "and a non-executable command" do let(:command) { __FILE__ } - + it "should print an error" do mock_error(subject, "not executable: #{command}") do subject.run command diff --git a/spec/foreman/engine_spec.rb b/spec/foreman/engine_spec.rb index d7d42ee..3b416b5 100644 --- a/spec/foreman/engine_spec.rb +++ b/spec/foreman/engine_spec.rb @@ -1,7 +1,7 @@ require "spec_helper" require "foreman/engine" -describe "Foreman::Engine" do +describe "Foreman::Engine", :fakefs do subject { Foreman::Engine.new("Procfile", {}) } describe "initialize" do diff --git a/spec/foreman/export/bluepill_spec.rb b/spec/foreman/export/bluepill_spec.rb index 89c0071..5d2eefa 100644 --- a/spec/foreman/export/bluepill_spec.rb +++ b/spec/foreman/export/bluepill_spec.rb @@ -3,7 +3,7 @@ require "foreman/engine" require "foreman/export/bluepill" require "tmpdir" -describe Foreman::Export::Bluepill do +describe Foreman::Export::Bluepill, :fakefs do let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") } let(:engine) { Foreman::Engine.new(procfile) } let(:bluepill) { Foreman::Export::Bluepill.new(engine) } @@ -18,7 +18,7 @@ describe Foreman::Export::Bluepill do it "exports to the filesystem with concurrency" do bluepill.export("/tmp/init", :concurrency => "alpha=2") - + normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app-concurrency.pill")) end -end \ No newline at end of file +end diff --git a/spec/foreman/export/runit_spec.rb b/spec/foreman/export/runit_spec.rb index d42fb5c..3b5f48f 100644 --- a/spec/foreman/export/runit_spec.rb +++ b/spec/foreman/export/runit_spec.rb @@ -3,34 +3,34 @@ require "foreman/engine" require "foreman/export/runit" require "tmpdir" -describe Foreman::Export::Runit do +describe Foreman::Export::Runit, :fakefs do let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", 'bar=baz') } let(:engine) { Foreman::Engine.new(procfile) } let(:runit) { Foreman::Export::Runit.new(engine) } - + before(:each) { load_export_templates_into_fakefs("runit") } before(:each) { stub(runit).say } before(:each) { stub(FakeFS::FileUtils).chmod } - + it "exports to the filesystem" do FileUtils.mkdir_p('/tmp/init') runit.export('/tmp/init', :concurrency => "alpha=2,bravo=1") - + File.read("/tmp/init/app-alpha-1/run").should == example_export_file('runit/app-alpha-1-run') - File.read("/tmp/init/app-alpha-1/log/run").should == + File.read("/tmp/init/app-alpha-1/log/run").should == example_export_file('runit/app-alpha-1-log-run') File.read("/tmp/init/app-alpha-1/env/PORT").should == "5000\n" File.read("/tmp/init/app-alpha-1/env/BAR").should == "baz\n" - + File.read("/tmp/init/app-alpha-2/run").should == example_export_file('runit/app-alpha-2-run') - File.read("/tmp/init/app-alpha-2/log/run").should == + File.read("/tmp/init/app-alpha-2/log/run").should == example_export_file('runit/app-alpha-2-log-run') File.read("/tmp/init/app-alpha-2/env/PORT").should == "5001\n" File.read("/tmp/init/app-alpha-2/env/BAR").should == "baz\n" - + File.read("/tmp/init/app-bravo-1/run").should == example_export_file('runit/app-bravo-1-run') - File.read("/tmp/init/app-bravo-1/log/run").should == + File.read("/tmp/init/app-bravo-1/log/run").should == example_export_file('runit/app-bravo-1-log-run') File.read("/tmp/init/app-bravo-1/env/PORT").should == "5100\n" end -end \ No newline at end of file +end diff --git a/spec/foreman/export/upstart_spec.rb b/spec/foreman/export/upstart_spec.rb index bc582b4..39881ca 100644 --- a/spec/foreman/export/upstart_spec.rb +++ b/spec/foreman/export/upstart_spec.rb @@ -3,7 +3,7 @@ require "foreman/engine" require "foreman/export/upstart" require "tmpdir" -describe Foreman::Export::Upstart do +describe Foreman::Export::Upstart, :fakefs do let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") } let(:engine) { Foreman::Engine.new(procfile) } let(:upstart) { Foreman::Export::Upstart.new(engine) } diff --git a/spec/foreman_spec.rb b/spec/foreman_spec.rb index ef1bbb3..dfd0fbc 100644 --- a/spec/foreman_spec.rb +++ b/spec/foreman_spec.rb @@ -8,13 +8,8 @@ describe Foreman do it { should be_a String } end - describe "::load_env!(env_file)" do - before do - FakeFS.activate! - end - + describe "::load_env!(env_file)", :fakefs do after do - FakeFS.deactivate! ENV['FOO'] = nil end @@ -22,7 +17,7 @@ describe Foreman do File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") } Foreman.load_env!("/tmp/env1") ENV['FOO'].should == 'bar' - end + end it "should assume env_file in ./.env" do File.open("./.env", "w") { |f| f.puts("FOO=bar") } diff --git a/spec/helper_spec.rb b/spec/helper_spec.rb index 6fd5efe..a6aaece 100644 --- a/spec/helper_spec.rb +++ b/spec/helper_spec.rb @@ -3,16 +3,16 @@ require "spec_helper" describe "spec helpers" do describe "#preserving_env" do after { ENV.delete "FOO" } - + it "should remove added environment vars" do preserving_env { ENV["FOO"] = "baz" } ENV["FOO"].should == nil end - + it "should reset modified environment vars" do ENV["FOO"] = "bar" preserving_env { ENV["FOO"] = "baz"} ENV["FOO"].should == "bar" end end -end \ No newline at end of file +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1803f4a..1f2f637 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -76,10 +76,11 @@ end def normalize_space(s) s.gsub(/\n[\n\s]*/, "\n") end - + RSpec.configure do |config| + config.treat_symbols_as_metadata_keys_with_true_values = true config.color_enabled = true config.order = 'rand' - config.include FakeFS::SpecHelpers + config.include FakeFS::SpecHelpers, :fakefs config.mock_with :rr end From 241b91a0d5271b472c412b6d898bc2e7d53c1cc5 Mon Sep 17 00:00:00 2001 From: brainopia Date: Sun, 22 Jan 2012 22:07:01 +0400 Subject: [PATCH 5/6] Implement Foreman::Process#kill,detach,alive?,dead? --- lib/foreman/engine.rb | 2 +- lib/foreman/process.rb | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/foreman/engine.rb b/lib/foreman/engine.rb index 94ac725..fbe9d38 100644 --- a/lib/foreman/engine.rb +++ b/lib/foreman/engine.rb @@ -74,7 +74,7 @@ private ###################################################################### def kill_all(signal="SIGTERM") running_processes.each do |pid, process| info "sending #{signal} to pid #{pid}" - Process.kill(signal, pid) rescue Errno::ESRCH + process.kill signal end end diff --git a/lib/foreman/process.rb b/lib/foreman/process.rb index e37bc17..bbabc80 100644 --- a/lib/foreman/process.rb +++ b/lib/foreman/process.rb @@ -25,6 +25,24 @@ class Foreman::Process "%s.%s" % [ entry.name, num ] end + def kill(signal) + pid && Process.kill(signal, pid) + rescue Errno::ESRCH + false + end + + def detach + pid && Process.detach(pid) + end + + def alive? + kill(0) + end + + def dead? + !alive? + end + private def fork_with_io(command) @@ -65,5 +83,4 @@ private ensure ENV.replace original end - end From ade0005a9270787877c98def893b03984e75328c Mon Sep 17 00:00:00 2001 From: brainopia Date: Sun, 22 Jan 2012 22:07:53 +0400 Subject: [PATCH 6/6] Add specs for Foreman::Process#run --- spec/foreman/process_spec.rb | 124 ++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 22 deletions(-) diff --git a/spec/foreman/process_spec.rb b/spec/foreman/process_spec.rb index e64e92c..bbf5349 100644 --- a/spec/foreman/process_spec.rb +++ b/spec/foreman/process_spec.rb @@ -1,6 +1,8 @@ -require "spec_helper" -require "foreman/process" +require 'spec_helper' +require 'foreman/process' require 'ostruct' +require 'timeout' +require 'tmpdir' describe Foreman::Process do subject { described_class.new entry, number, port } @@ -18,37 +20,115 @@ describe Foreman::Process do its(:pid) { nil } describe '#run' do - let(:pipe) { :pipe } - let(:basedir) { Dir.mktmpdir } - let(:env) {{ 'foo' => 'bar' }} + let(:pipe) { :pipe } + let(:basedir) { Dir.mktmpdir } + let(:env) {{ 'foo' => 'bar' }} + let(:init_delta) { 0.1 } - let(:run) do + after { FileUtils.remove_entry_secure basedir } + + def run(cmd=command) + entry.command = cmd subject.run pipe, basedir, env + subject.detach && sleep(init_delta) end - it 'should change to basedir' do - mock(Dir).chdir basedir - run + def run_file(executable, code) + file = File.open("#{basedir}/script.rb", 'w+') {|it| it << code } + run "#{executable} #{file.path}" end - it 'should set PORT for environment' do - mock(subject).run_process(command, pipe) do - ENV['PORT'].should == port.to_s + context 'options' do + it 'should change to basedir' do + mock(Dir).chdir basedir + run end - run - end - it 'should set custom variables for environment' do - mock(subject).run_process(command, pipe) do - ENV['foo'].should == 'bar' + it 'should set PORT for environment' do + mock(subject).run_process(command, pipe) do + ENV['PORT'].should == port.to_s + end + run + end + + it 'should set custom variables for environment' do + mock(subject).run_process(command, pipe) do + ENV['foo'].should == 'bar' + end + run + end + + it 'should restore environment afterwards' do + mock(subject).run_process command, pipe + run + ENV.should_not include('PORT', 'foo') end - run end - it 'should restore environment afterwards' do - mock(subject).run_process command, pipe - run - ENV.should_not include('PORT', 'foo') + context 'process' do + around do |spec| + IO.pipe do |reader, writer| + @reader, @writer = reader, writer + spec.run + end + end + + let(:pipe) { @writer } + let(:output) { @reader.read_nonblock 1024 } + + it 'should not block' do + expect { + Timeout.timeout(2*init_delta) { run 'sleep 2' } + }.should_not raise_exception + end + + it 'should be alive' do + run 'sleep 1' + subject.should be_alive + end + + it 'should be dead' do + run 'exit' + subject.should be_dead + end + + it 'should be killable' do + run 'sleep 1' + subject.kill 'TERM' + subject.should be_dead + end + + it 'should send different signals' do + run_file 'ruby', <<-CODE + trap 'TERM', 'IGNORE' + loop { sleep 1 } + CODE + subject.kill 'TERM' + subject.should be_alive + subject.kill 'KILL' + subject.should be_dead + end + + it 'should redirect stdout' do + run 'echo hey' + output.should include('hey') + end + + it 'should redirect stderr' do + run 'echo hey >2' + output.should include('hey') + end + + it 'should handle variables' do + run 'echo $PORT' + output.should include('777') + end + + it 'should handle arguments' do + pending + run %{ sh -c "trap '' TERM; sleep 10" } + subject.should be_alive + end end end end