├── .gemtest ├── .gitignore ├── .yardopts ├── CHANGELOG ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── examples ├── example_bacon.rb ├── example_minitest.rb └── example_rspec.rb ├── lib ├── plymouth.rb └── plymouth │ ├── commands.rb │ └── version.rb ├── plymouth.gemspec └── test └── test.rb /.gemtest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/banister/plymouth/565d75b0a3d55c7a291bdd39f9321b51c5373f18/.gemtest -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | *.so 3 | *.o 4 | *.def 5 | doc/ 6 | pkg/ 7 | .yardoc/ 8 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --markup markdown 2 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/banister/plymouth/565d75b0a3d55c7a291bdd39f9321b51c5373f18/CHANGELOG -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | gemspec 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | License 2 | ------- 3 | 4 | (The MIT License) 5 | 6 | Copyright (c) 2012 John Mair (banisterfiend) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | 'Software'), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | plymouth 2 | =========== 3 | 4 | (C) John Mair (banisterfiend) 2012 5 | 6 | _Start Pry in the context of a failed test_ 7 | 8 | **Warning BETA software: Please file an [issue](https://github.com/banister/plymouth/issues) if you have any problems** 9 | 10 | `plymouth` is a gem to automatically start a [Pry](http://pry.github.com) session when a test fails, putting you in the context of the failure. 11 | It currently supports [Bacon](https://github.com/chneukirchen/bacon), [Minitest](https://github.com/seattlerb/minitest), and [RSpec](https://github.com/rspec/rspec). 12 | Support for other testing libraries is (usually) trivial to add. 13 | 14 | plymouth utilizes the powerful [pry-exception_explorer](https://github.com/pry/pry-exception_explorer) gem. 15 | 16 | plymouth currently only runs on **MRI 1.9.2+ (including 1.9.3)** 17 | 18 | **How to use:** 19 | 20 | After installing the [gem](https://rubygems.org/gems/plymouth), simply add the following line to your test suite: 21 | 22 | `require 'plymouth'` 23 | 24 | plymouth should auto-detect which testing library you're using, and 'just work' :) 25 | 26 | 27 | Example: Intercept a failing test in RSpec 28 | -------- 29 | 30 | Inside the test file: 31 | 32 | ```ruby 33 | require 'plymouth' 34 | 35 | describe Array do 36 | before do 37 | @array = [1] 38 | end 39 | 40 | it 'should be empty' do 41 | @array.empty?.should == true 42 | end 43 | end 44 | ``` 45 | 46 | And here is the result of running the above test with the `rspec` executable: 47 | 48 | ```ruby 49 | Test failure: expected: true 50 | got: false (using ==) 51 | 52 | Frame number: 0/14 53 | Frame type: block 54 | 55 | From: /Users/john/ruby/play/rspec_intercept.rb @ line 9: 56 | 57 | 4: before do 58 | 5: @array = [1] 59 | 6: end 60 | 7: 61 | 8: it 'should be empty' do 62 | => 9: @array.empty?.should == true 63 | 10: end 64 | 11: end 65 | 66 | [1] (pry) #: 0> @array.size 67 | => 1 68 | [2] (pry) #: 0> edit --current 69 | Waiting for Emacs... 70 | [3] (pry) #: 0> ^D 71 | F 72 | 73 | Failures: 74 | 75 | 1) Array should be empty 76 | Failure/Error: @array.empty?.should == true 77 | expected: true 78 | got: false (using ==) 79 | # ./rspec_intercept.rb:9:in `block (2 levels) in ' 80 | 81 | Finished in 7.74 seconds 82 | 1 example, 1 failure 83 | 84 | Failed examples: 85 | 86 | rspec ./rspec_intercept.rb:8 # Array should be empty 87 | ``` 88 | 89 | Edit the failing test code in an editor 90 | --- 91 | 92 | Notice the line `edit --current` from the example above. Entering this command will take you to the line in the test file 93 | where the failing test was defined. Once here you can modify the test code as you please. Note that the file will not be 94 | reloaded after editing is complete, instead any modifications you make will only take effect the next time you run the test suite. 95 | 96 | Turning plymouth off mid-test 97 | --- 98 | 99 | If you are inside an interactive session and do not want plymouth to intercept further failing tests you 100 | can enter the command `plymouth-off`. This will disable plymouth for the remainder of the test suite. 101 | 102 | The 'USE_PLYMOUTH' Environment variable 103 | ------- 104 | 105 | To make it easier to run your test suite normally (without plymouth's intervention) a `USE_PLYMOUTH` environment variable 106 | can be defined. If the `USE_PLYMOUTH` environment variable is set to `"false"`, `"0"`, or `"no"` plymouth will not intercept test failures. 107 | If `USE_PLYMOUTH` is not defined at all, plymouth will be used by default. 108 | 109 | Travis Compatibility 110 | --- 111 | 112 | To prevent plymouth messing up testing on [travis](http://travis-ci.org/), add the following line to your `.travis.yml` file: 113 | 114 | ``` 115 | env: USE_PLYMOUTH="no" 116 | ``` 117 | 118 | Limitations 119 | ------------------------- 120 | 121 | * Occasional (very rare) segfault on 1.9.3 (seems to work fine on 1.9.2). Please [report](https://github.com/banister/plymouth/issues) all segfaults with a full backtrace! 122 | * Only supports MRI. 123 | * Currently limited to just Bacon, RSpec and Minitest. Support for more testing libraries will be added in the future. 124 | * Only intercepts test **failures**, does not yet (generally) intercept test **errors**. Support for this will be added soon. 125 | * Does not work with Bacon's `should.raise` and `should.not.raise` constructions. 126 | 127 | Contact 128 | ------- 129 | 130 | Problems or questions contact me at [github](http://github.com/banister) 131 | 132 | 133 | License 134 | ------- 135 | 136 | (The MIT License) 137 | 138 | Copyright (c) 2012 John Mair (banisterfiend) 139 | 140 | Permission is hereby granted, free of charge, to any person obtaining 141 | a copy of this software and associated documentation files (the 142 | 'Software'), to deal in the Software without restriction, including 143 | without limitation the rights to use, copy, modify, merge, publish, 144 | distribute, sublicense, and/or sell copies of the Software, and to 145 | permit persons to whom the Software is furnished to do so, subject to 146 | the following conditions: 147 | 148 | The above copyright notice and this permission notice shall be 149 | included in all copies or substantial portions of the Software. 150 | 151 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 152 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 153 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 154 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 155 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 156 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 157 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 158 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | $:.unshift 'lib' 2 | 3 | dlext = Config::CONFIG['DLEXT'] 4 | direc = File.dirname(__FILE__) 5 | 6 | PROJECT_NAME = "plymouth" 7 | 8 | require "#{PROJECT_NAME}/version" 9 | 10 | CLASS_NAME = Plymouth 11 | 12 | require 'rake/clean' 13 | require 'rake/gempackagetask' 14 | 15 | CLOBBER.include("**/*~", "**/*#*", "**/*.log", "**/*.o") 16 | CLEAN.include("ext/**/*.log", "ext/**/*.o", 17 | "ext/**/*~", "ext/**/*#*", "ext/**/*.obj", "**/*#*", "**/*#*.*", 18 | "ext/**/*.def", "ext/**/*.pdb", "**/*_flymake*.*", "**/*_flymake") 19 | 20 | def apply_spec_defaults(s) 21 | s.name = PROJECT_NAME 22 | s.summary = "Start an interactive session when a test fails" 23 | s.version = CLASS_NAME::VERSION 24 | s.date = Time.now.strftime '%Y-%m-%d' 25 | s.author = "John Mair (banisterfiend)" 26 | s.email = 'jrmair@gmail.com' 27 | s.description = s.summary 28 | s.require_path = 'lib' 29 | s.homepage = "http://github.com/banister/#{PROJECT_NAME}" 30 | s.has_rdoc = 'yard' 31 | s.add_dependency('pry-exception_explorer', '~>0.1.7') 32 | s.add_development_dependency("bacon","~>1.1.0") 33 | s.add_development_dependency('rspec') 34 | s.required_ruby_version = '>= 1.9.2' 35 | s.files = `git ls-files`.split("\n") 36 | s.test_files = `git ls-files -- test/*`.split("\n") 37 | end 38 | 39 | desc "Run tests" 40 | task :test do 41 | sh "bacon -Itest -rubygems test.rb -q" 42 | end 43 | 44 | desc "Run bacon example" 45 | task :example_bacon do 46 | sh "bacon -I#{direc}/lib/ #{direc}/examples/example_bacon.rb " 47 | end 48 | 49 | desc "Run rspec example" 50 | task :example_rspec do 51 | sh "rspec -I#{direc}/lib/ #{direc}/examples/example_rspec.rb " 52 | end 53 | 54 | desc "Run minitest example" 55 | task :example_minitest do 56 | sh "ruby -I#{direc}/lib/ #{direc}/examples/example_minitest.rb " 57 | end 58 | 59 | desc "generate gemspec" 60 | task :gemspec => "ruby:gemspec" 61 | 62 | namespace :ruby do 63 | spec = Gem::Specification.new do |s| 64 | apply_spec_defaults(s) 65 | s.platform = Gem::Platform::RUBY 66 | end 67 | 68 | Rake::GemPackageTask.new(spec) do |pkg| 69 | pkg.need_zip = false 70 | pkg.need_tar = false 71 | end 72 | 73 | desc "Generate gemspec file" 74 | task :gemspec do 75 | File.open("#{spec.name}.gemspec", "w") do |f| 76 | f << spec.to_ruby 77 | end 78 | end 79 | end 80 | 81 | desc "Show version" 82 | task :version do 83 | puts "Plymouth version: #{Plymouth::VERSION}" 84 | end 85 | 86 | desc "Generate gemspec file" 87 | task :gemspec => "ruby:gemspec" 88 | 89 | desc "build all platform gems at once" 90 | task :gems => [:clean, :rmgems, "ruby:gem"] 91 | 92 | task :gem => [:gems] 93 | 94 | desc "remove all platform gems" 95 | task :rmgems => ["ruby:clobber_package"] 96 | 97 | desc "build and push latest gems" 98 | task :pushgems => :gems do 99 | chdir("./pkg") do 100 | Dir["*.gem"].each do |gemfile| 101 | sh "gem push #{gemfile}" 102 | end 103 | end 104 | end 105 | 106 | -------------------------------------------------------------------------------- /examples/example_bacon.rb: -------------------------------------------------------------------------------- 1 | unless Object.const_defined? :Plymouth 2 | $:.unshift File.expand_path '../../lib', __FILE__ 3 | end 4 | 5 | require 'plymouth' 6 | 7 | describe Array do 8 | before do 9 | @array = [1] 10 | end 11 | 12 | it 'should be empty' do 13 | @array.empty?.should == true 14 | end 15 | 16 | it 'should have 3 items' do 17 | [1, 2, 3].size.should == 3 18 | end 19 | 20 | it 'should contain only numbers' do 21 | [1, "2", 3].all? { |v| v.is_a?(Fixnum).should == true } 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /examples/example_minitest.rb: -------------------------------------------------------------------------------- 1 | unless Object.const_defined? :Plymouth 2 | $:.unshift File.expand_path '../../lib', __FILE__ 3 | end 4 | 5 | require 'minitest/autorun' 6 | require 'minitest/spec' 7 | require 'plymouth' 8 | 9 | describe Array do 10 | before do 11 | @array = [1] 12 | end 13 | 14 | it 'should be empty' do 15 | @array.empty?.must_equal true 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /examples/example_rspec.rb: -------------------------------------------------------------------------------- 1 | unless Object.const_defined? :Plymouth 2 | $:.unshift File.expand_path '../../lib', __FILE__ 3 | end 4 | 5 | require 'plymouth' 6 | 7 | describe Array do 8 | before do 9 | @array = [1] 10 | end 11 | 12 | it 'should be empty' do 13 | @array.empty?.should == true 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/plymouth.rb: -------------------------------------------------------------------------------- 1 | # plymouth.rb 2 | # (C) 2012 John Mair (banisterfiend); MIT license 3 | 4 | require 'pry-exception_explorer' 5 | require "plymouth/version" 6 | require 'plymouth/commands' 7 | 8 | module Plymouth 9 | 10 | # Enable plymouth. 11 | # @return [Boolean] 12 | def self.enable! 13 | ::EE.enabled = true 14 | end 15 | 16 | # Disable plymouth. 17 | # @return [Boolean] 18 | def self.disable! 19 | ::EE.enabled = false 20 | end 21 | 22 | # @return [Boolean] Whether Plymouth is enabled. 23 | def self.enabled? 24 | ::EE.enabled? 25 | end 26 | end 27 | 28 | message = nil 29 | 30 | # Ensure auto-reloading is off, so that `edit --current` does not try 31 | # to reload the test file 32 | Pry.config.disable_auto_reload = true 33 | 34 | # Decorate `whereami` command to include test failure information 35 | Pry.config.commands.before_command("whereami") do 36 | output.puts 37 | output.puts "#{Pry::Helpers::Text.bold("Test failure:")} #{message}" 38 | end 39 | 40 | if defined?(Bacon) 41 | 42 | EE.intercept do |frame, ex| 43 | 44 | if ex.is_a?(Bacon::Error) && frame.method_name != :run_requirement 45 | message = ex.message 46 | true 47 | else 48 | false 49 | end 50 | 51 | end.skip_until { |frame| frame.klass == Bacon::Context } 52 | 53 | elsif defined?(RSpec) 54 | 55 | EE.intercept do |frame, ex| 56 | 57 | if ex.class.name =~ /RSpec::Expectations::ExpectationNotMetError/ 58 | message = ex.message 59 | true 60 | else 61 | false 62 | end 63 | 64 | end.skip_until do |frame| 65 | frame.klass.name =~ /RSpec::Core::ExampleGroup::Nested/ 66 | end 67 | 68 | elsif defined?(MiniTest) 69 | 70 | EE.intercept do |frame, ex| 71 | 72 | if ex.is_a?(MiniTest::Assertion) 73 | message = ex.message 74 | true 75 | else 76 | false 77 | end 78 | 79 | end.skip_until do |frame| 80 | frame.method_name =~ /^test_/ 81 | end 82 | end 83 | 84 | # Disable Plymouth if USE_PLYMOUTH environment variable is falsy 85 | Plymouth.enable! 86 | if ['0', 'false', 'no'].include?(ENV['USE_PLYMOUTH'].to_s.downcase) 87 | Plymouth.disable! 88 | end 89 | 90 | # Bring in plymouth commands 91 | Pry.commands.import Plymouth::Commands 92 | -------------------------------------------------------------------------------- /lib/plymouth/commands.rb: -------------------------------------------------------------------------------- 1 | module Plymouth 2 | Commands = Pry::CommandSet.new do 3 | create_command "plymouth-off", "Disable Plymouth." do 4 | banner <<-BANNER 5 | Usage: plymouth-off 6 | Exit the REPL and turn Plymouth off for the duration of the test suite. 7 | BANNER 8 | 9 | def process 10 | Plymouth.disable! 11 | 12 | # exit the REPL 13 | run "exit-all" 14 | end 15 | end 16 | 17 | create_command "plymouth-on", "Enable Plymouth." do 18 | banner <<-BANNER 19 | Usage: plymouth-on 20 | Enable Plymouth. 21 | BANNER 22 | 23 | def process 24 | Plymouth.enable! 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/plymouth/version.rb: -------------------------------------------------------------------------------- 1 | module Plymouth 2 | VERSION = "0.3.3" 3 | end 4 | -------------------------------------------------------------------------------- /plymouth.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |s| 4 | s.name = "plymouth" 5 | s.version = "0.3.3" 6 | 7 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 8 | s.authors = ["John Mair (banisterfiend)"] 9 | s.date = "2012-10-04" 10 | s.description = "Start an interactive session when a test fails" 11 | s.email = "jrmair@gmail.com" 12 | s.files = [".gemtest", ".gitignore", ".yardopts", "CHANGELOG", "Gemfile", "LICENSE", "README.md", "Rakefile", "examples/example_bacon.rb", "examples/example_minitest.rb", "examples/example_rspec.rb", "lib/plymouth.rb", "lib/plymouth/commands.rb", "lib/plymouth/version.rb", "plymouth.gemspec", "test/test.rb"] 13 | s.homepage = "http://github.com/banister/plymouth" 14 | s.require_paths = ["lib"] 15 | s.required_ruby_version = Gem::Requirement.new(">= 1.9.2") 16 | s.rubygems_version = "1.8.15" 17 | s.summary = "Start an interactive session when a test fails" 18 | s.test_files = ["test/test.rb"] 19 | 20 | if s.respond_to? :specification_version then 21 | s.specification_version = 3 22 | 23 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 24 | s.add_runtime_dependency(%q, ["~> 0.1.7"]) 25 | s.add_development_dependency(%q, ["~> 1.1.0"]) 26 | s.add_development_dependency(%q, [">= 0"]) 27 | else 28 | s.add_dependency(%q, ["~> 0.1.7"]) 29 | s.add_dependency(%q, ["~> 1.1.0"]) 30 | s.add_dependency(%q, [">= 0"]) 31 | end 32 | else 33 | s.add_dependency(%q, ["~> 0.1.7"]) 34 | s.add_dependency(%q, ["~> 1.1.0"]) 35 | s.add_dependency(%q, [">= 0"]) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/test.rb: -------------------------------------------------------------------------------- 1 | direc = File.dirname(__FILE__) 2 | 3 | require 'rubygems' 4 | require "#{direc}/../lib/plymouth" 5 | require 'bacon' 6 | 7 | puts "Testing plymouth version #{Plymouth::VERSION}..." 8 | puts "Ruby version: #{RUBY_VERSION}" 9 | 10 | describe Plymouth do 11 | end 12 | 13 | --------------------------------------------------------------------------------