├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── actions.yml ├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── assets └── failure.png ├── bin └── mtest ├── gemfiles ├── 520.gemfile ├── 520.gemfile.lock ├── 521.gemfile ├── 521.gemfile.lock ├── 522.gemfile ├── 522.gemfile.lock ├── 523.gemfile ├── 523.gemfile.lock ├── 524.gemfile ├── 524.gemfile.lock ├── 525.gemfile └── 525.gemfile.lock ├── lib ├── maxitest.rb ├── maxitest │ ├── autorun.rb │ ├── global_must.rb │ ├── helpers.rb │ ├── hook_all.rb │ ├── implicit_subject.rb │ ├── interrupt.rb │ ├── let_all.rb │ ├── let_bang.rb │ ├── pending.rb │ ├── shorted_backtrace.rb │ ├── static_class_order.rb │ ├── threads.rb │ ├── timeout.rb │ ├── vendor │ │ ├── around.rb │ │ ├── line.rb │ │ ├── line_describe.rb │ │ ├── rg.rb │ │ └── testrbl.rb │ ├── verbose_backtrace.rb │ ├── version.rb │ └── xit.rb └── minitest │ └── maxitest_plugin.rb ├── maxitest.gemspec └── spec ├── cases ├── around.rb ├── catch_interrupt.rb ├── cltr_c.rb ├── context.rb ├── error_and_failure.rb ├── global_must.rb ├── helper.rb ├── helpers.rb ├── hook_all.rb ├── implicit_subject.rb ├── let_all.rb ├── let_bang.rb ├── line.rb ├── mtest │ ├── a_test.rb │ ├── b_test.rb │ └── c.rb ├── order_dependent.rb ├── parallel.rb ├── pending.rb ├── plain.rb ├── raise.rb ├── raise_interrupt.rb ├── static_class_order.rb ├── threads.rb ├── timeout.rb └── xit.rb ├── maxitest_spec.rb └── spec_helper.rb /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for your contribution! 2 | 3 | ## Checklist 4 | - [ ] Added tests 5 | - [ ] Updated README.md (if user facing behavior changed) 6 | - [ ] Added CHANGELOG.md entry under `# NEXT` (if user facing behavior changed) 7 | -------------------------------------------------------------------------------- /.github/workflows/actions.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [master] 5 | pull_request: 6 | branches: [master] 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | # keep in sync with maxitest.gemspec 14 | ruby: [ '3.2', '3.3', '3.4'] 15 | gemfile: [ '520', '523', '524', '525'] 16 | exclude: 17 | - ruby: '3.4' # object_id warning breaks stuff 18 | gemfile: '520' 19 | - ruby: '3.4' # object_id warning breaks stuff 20 | gemfile: '521' 21 | - ruby: '3.4' # object_id warning breaks stuff 22 | gemfile: '522' 23 | - ruby: '3.4' # object_id warning breaks stuff 24 | gemfile: '523' 25 | - ruby: '3.4' # object_id warning breaks stuff 26 | gemfile: '524' 27 | env: 28 | BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: ruby/setup-ruby@v1 32 | with: 33 | ruby-version: ${{ matrix.ruby }} 34 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 35 | - run: bundle exec rake 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ruby-version 2 | .tool-versions 3 | .idea 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Next 2 | 3 | # v6.0.0 4 | - drop support for Ruby <=3.1 and minitest <5.20, test on ruby 3.4 5 | 6 | # v5.8.0 7 | - bin/rails test line bug 8 | 9 | # v5.7.1 10 | - support disabling interrupt handling to avoid minitest-reporters bug with MAXITEST_NO_INTERRUPT=true 11 | 12 | # v5.7.0 13 | - support minitest 5.25 14 | 15 | # v5.6.0 16 | - support minitest 5.24 17 | 18 | # v5.5.0 19 | - support minitest 5.23 20 | 21 | # v5.4.0 22 | - support minitest 5.20 23 | 24 | # v5.3.1 25 | - remove MiniTest usage to make Zeitwerk happy 26 | 27 | # v5.3.0 28 | - `pending "broken", if: ENV["CI"] do` 29 | - `with_env` 30 | - `capture_stderr` + `capture_stdout` 31 | - hide maxitest backtraces by default 32 | 33 | # v5.2.0 34 | - support minitest 5.19 35 | 36 | # v5.1.0 37 | - support `before :all` 38 | - block invalid `after :all` and `around :all` 39 | 40 | # v5.0.0 41 | - add minitest 5.14 support 42 | - drop EOL rubies (<=2.7) and minitest versions 43 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec 3 | 4 | gem "debug" 5 | gem "bump" 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | maxitest (6.0.0) 5 | minitest (>= 5.20.0, < 5.26.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | bump (0.10.0) 11 | date (3.4.1) 12 | debug (1.10.0) 13 | irb (~> 1.10) 14 | reline (>= 0.3.8) 15 | diff-lcs (1.6.1) 16 | io-console (0.8.0) 17 | irb (1.15.1) 18 | pp (>= 0.6.0) 19 | rdoc (>= 4.0.0) 20 | reline (>= 0.4.2) 21 | minitest (5.25.5) 22 | pp (0.6.2) 23 | prettyprint 24 | prettyprint (0.2.0) 25 | psych (5.2.3) 26 | date 27 | stringio 28 | rake (13.2.1) 29 | rdoc (6.13.1) 30 | psych (>= 4.0.0) 31 | reline (0.6.0) 32 | io-console (~> 0.5) 33 | rspec (3.13.0) 34 | rspec-core (~> 3.13.0) 35 | rspec-expectations (~> 3.13.0) 36 | rspec-mocks (~> 3.13.0) 37 | rspec-core (3.13.3) 38 | rspec-support (~> 3.13.0) 39 | rspec-expectations (3.13.3) 40 | diff-lcs (>= 1.2.0, < 2.0) 41 | rspec-support (~> 3.13.0) 42 | rspec-mocks (3.13.2) 43 | diff-lcs (>= 1.2.0, < 2.0) 44 | rspec-support (~> 3.13.0) 45 | rspec-support (3.13.2) 46 | stringio (3.1.6) 47 | 48 | PLATFORMS 49 | arm64-darwin-23 50 | ruby 51 | 52 | DEPENDENCIES 53 | bump 54 | debug 55 | maxitest! 56 | rake 57 | rspec 58 | 59 | BUNDLED WITH 60 | 2.6.6 61 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Michael Grosser 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Minitest + all the features you always wanted. 2 | [![CI](https://github.com/grosser/maxitest/actions/workflows/actions.yml/badge.svg?branch=master)](https://github.com/grosser/maxitest/actions/workflows/actions.yml?query=branch%3Amaster) 3 | [![Gem Version](https://badge.fury.io/rb/maxitest.svg)](https://badge.fury.io/rb/maxitest) 4 | 5 | ![Failure](assets/failure.png?raw=true) 6 | 7 | Features 8 | ======== 9 | - **Ctrl+c** stops tests and prints failures 10 | - **pastable rerun snippet** for failures (disabled/integrated on rails 5) 11 | - multiple before & after blocks 12 | - `before :all` blocks 13 | - **around** blocks `around { |t| Dir.chdir(...) { t.call } }` 14 | - **red-green** output (disabled/integrated on rails 5) 15 | - `mtest` executable to **run by line number** and by folder (disabled/integrated on rails 5) 16 | - full backtrace for errors and assertions with verbose (`-v`) 17 | - `let!` 18 | - `let_all` execute once for all tests in a class and it's subclasses 19 | - `order_dependent!` to make your tests run in given order 20 | - `Maxitest.static_class_order = true` no longer sort tests class/sub-classes in random order 21 | - `context` for more expression 22 | - `pending { assert false }` is skip when it fails, but fails when it passes 23 | - implicit subject via `require 'maxitest/implicit_subject'` 24 | - `xit` to skip test (also does not call setup or teardown) 25 | - `with_env` to change environment variables during test run 26 | - `capture_stdout` and `capture_stderr` to capture stdout or stderr but not both (like `capture_io` does) 27 | - `require 'maxitest/timeout'` to make hanging tests fail after `Maxitest.timeout` seconds 28 | - `require 'maxitest/threads'` fail tests that leave extra threads running 29 | - `require 'maxitest/global_must'` (before autorun) disable deprecation on global `must_*` or [global_expectations](https://github.com/jeremyevans/minitest-global_expectations) gem 30 | 31 | Install 32 | ======= 33 | 34 | ```Bash 35 | gem install maxitest 36 | ``` 37 | 38 | Usage 39 | ===== 40 | 41 | ```Ruby 42 | require "maxitest/autorun" 43 | 44 | # ... normal minitest tests ... 45 | describe MyClass do 46 | describe "#my_method" do 47 | it "passes" do 48 | _(MyClass.new.my_method).must_equal 1 49 | end 50 | end 51 | end 52 | ``` 53 | 54 | ### pending 55 | 56 | - `pending "need to fix" do` to show why something is pending 57 | - `pending "need to fix", if: ENV["CI"] do` to only skip on CI (if something is supposed to work locally) 58 | 59 | ### with_env 60 | 61 | Use during test: `with_env FOO: "bar do ...` 62 | Use as `around` block: `with_env FOO: "bar"` 63 | 64 | ### context 65 | 66 | Use as alias for `describe` 67 | 68 | ```ruby 69 | describe "#my_method" do 70 | context "with bad state" do 71 | before { errors += 1 } 72 | it "fails" # ... 73 | end 74 | end 75 | ``` 76 | 77 | ### capture_stdout / capture_stderr 78 | 79 | ```ruby 80 | output = capture_stdout { puts 1 } 81 | _(output).must_equal "1\n" 82 | ``` 83 | 84 | ### minitest-reporters 85 | 86 | If [PR](https://github.com/minitest-reporters/minitest-reporters/pull/357) is not resolved, 87 | disable Interrupt handling with `ENV["MAXITEST_NO_INTERRUPT"] = "true"` to avoid "stack level too deep" errors. 88 | 89 | Development 90 | =========== 91 | - everything vendored into 1 gem to avoid dependency madness 92 | - tested via rspec to avoid messing up our own tests by accident 93 | - fixes should go back to the original libraries 94 | - restrictive minitest dependency so nothing breaks by accident 95 | - ruby >=3.0 96 | - `rake bundle` to update all vendored gems 97 | 98 | Author 99 | ====== 100 | - running by line number from [minitest-line](https://github.com/judofyr/minitest-line) 101 | - around from [minitest-around](https://github.com/splattael/minitest-around) 102 | - mtest from [testrbl](https://github.com/grosser/testrbl) 103 | - red-green from [minitest-rg](https://github.com/blowmage/minitest-rg) 104 | 105 | [Michael Grosser](http://grosser.it)
106 | michael@grosser.it
107 | License: MIT 108 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | require "bundler/gem_tasks" 3 | require 'rspec/core/rake_task' 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | begin 7 | require "bump/tasks" 8 | Bump.replace_in_default = Dir["gemfiles/**.lock"] 9 | rescue LoadError # not available in gemfiles/ 10 | end 11 | 12 | module VendorUpdate 13 | class << self 14 | def all 15 | require "open-uri" 16 | Dir["lib/maxitest/vendor/*"].each { |f| update_file f } 17 | end 18 | 19 | private 20 | 21 | def update_file(file) 22 | # a file has multiple parts, and each part starts with the url where it was copied from 23 | do_not_modify = "generated by rake update, do not modify" 24 | start = "# BEGIN #{do_not_modify}\n" 25 | finish = "# END #{do_not_modify}\n" 26 | urls = File.read(file).scan(/#{Regexp.escape start}# (\S+)\n.*?#{Regexp.escape(finish)}/m).flatten(1) 27 | 28 | urls.map! do |url| 29 | code = URI.open(url).read 30 | code = modify_code code, url 31 | "#{start}# #{url}\n#{code}\n#{finish}" 32 | end 33 | 34 | File.write(file, urls.reject(&:empty?).join("\n")) 35 | end 36 | 37 | def modify_code(code, url) 38 | code = code.dup 39 | code.gsub!(/require .*?\n/, "") # we inline everything 40 | code.strip! 41 | code = "=begin\n#{code}\n=end" if url.include?("LICENSE") 42 | 43 | if url.end_with?("/testrbl.rb") 44 | # nest under Maxitest to avoid collision, more modifications are done in bin/mtest 45 | code = "module Maxitest\n#{code.gsub(/^/, " ").gsub(/^\s+$/, "")}\nend" 46 | elsif url.end_with?("/line_plugin.rb") 47 | # add rails detector 48 | raise unless code.sub!(%{pwd = Pathname.new(Dir.pwd)}, %{pwd = Pathname.new(Dir.pwd)\n bin_rails = File.exist?("bin/rails")}) 49 | # replace ruby with `mtest` or `bin/rails test` 50 | # to work around https://github.com/minitest/minitest-rails/issues/256 51 | raise unless code.sub!(%{output = "ruby \#{file} -l \#{line}"}, %{output = "\#{bin_rails ? "bin/rails test" : "mtest"} \#{file}:\#{line}"}) 52 | elsif url.end_with?('/around/spec.rb') 53 | # do not fail with resume for nil class when before was never called 54 | # for example when putting <% raise %> into a fixture file 55 | raise unless code.sub!(%{fib.resume unless fib == :failed}, %{fib.resume if fib && fib != :failed}) 56 | 57 | # make `after :all` blow up to avoid confusion 58 | raise unless code.sub!(%{fib = nil}, %{raise ArgumentError, "only :each or no argument is supported" if args != [] && args != [:each]\n fib = nil}) 59 | elsif url.end_with?('/rg_plugin.rb') 60 | # support disabling/enabling colors 61 | # https://github.com/blowmage/minitest-rg/pull/15 62 | raise unless code.sub!( 63 | %(opts.on "--rg", "Add red/green to test output." do\n RG.rg!), 64 | %(opts.on "--[no-]rg", "Add red/green to test output." do |bool|\n RG.rg! bool), 65 | ) 66 | raise unless code.sub!( 67 | %( def self.rg!\n @rg = true), 68 | %( def self.rg!(bool = true)\n @rg = bool), 69 | ) 70 | raise unless code.sub!( 71 | "reporter.reporters.grep(Minitest::Reporter).each do |rep|\n rep.io = io if rep.io.tty?", 72 | "reporter.reporters.grep(Minitest::Reporter).each do |rep|\n rep.io = io" 73 | ) 74 | raise unless code.sub!( 75 | "MiniTest", 76 | "Minitest", 77 | ) 78 | end 79 | code 80 | end 81 | end 82 | end 83 | 84 | desc "Run all tests" 85 | task default: :spec 86 | 87 | desc "Update all dependencies" 88 | task :update do 89 | VendorUpdate.all 90 | end 91 | 92 | desc "bundle all gemfiles/ EXTRA=" 93 | task :bundle do 94 | extra = ENV["EXTRA"] 95 | Bundler.with_original_env do 96 | Dir["gemfiles/*.gemfile"].reverse.each { |gemfile| sh "BUNDLE_GEMFILE=#{gemfile} bundle #{extra}" } 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /assets/failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grosser/maxitest/297fc065c25814d685dd71e54deac48a197c7596/assets/failure.png -------------------------------------------------------------------------------- /bin/mtest: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # parse raw -v but not verbose 4 | if ARGV == ["-v"] || ARGV == ["--version"] 5 | puts Maxitest::VERSION 6 | exit 0 7 | end 8 | 9 | # parse raw -h 10 | if ARGV == ["-h"] || ARGV == ["--help"] 11 | puts <<~EOF 12 | test given file, folder or file:line 13 | Usage: mtest foo_test.rb 14 | 15 | -v to run without bracktrace cleaner 16 | --changed to run all all tests in current `git diff` or `git show` 17 | EOF 18 | exit 0 19 | end 20 | 21 | # send rest to testrbl to get --changed support, but do not guess at lines but just convert number to -l 22 | # so minitest-line picks it up 23 | require 'optparse' 24 | 25 | $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib') 26 | require 'maxitest/vendor/testrbl' 27 | 28 | class << Maxitest::Testrbl 29 | def line_pattern_option(file, line) 30 | [file, "-l", line] 31 | end 32 | end 33 | 34 | Maxitest::Testrbl.run_from_cli(ARGV) 35 | -------------------------------------------------------------------------------- /gemfiles/520.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec path: "../" 3 | gem "minitest", "~> 5.20.0" 4 | gem "mutex_m" # for ruby 3.4 5 | -------------------------------------------------------------------------------- /gemfiles/520.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | maxitest (6.0.0) 5 | minitest (>= 5.20.0, < 5.26.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | diff-lcs (1.6.1) 11 | minitest (5.20.0) 12 | mutex_m (0.3.0) 13 | rake (13.2.1) 14 | rspec (3.13.0) 15 | rspec-core (~> 3.13.0) 16 | rspec-expectations (~> 3.13.0) 17 | rspec-mocks (~> 3.13.0) 18 | rspec-core (3.13.3) 19 | rspec-support (~> 3.13.0) 20 | rspec-expectations (3.13.3) 21 | diff-lcs (>= 1.2.0, < 2.0) 22 | rspec-support (~> 3.13.0) 23 | rspec-mocks (3.13.2) 24 | diff-lcs (>= 1.2.0, < 2.0) 25 | rspec-support (~> 3.13.0) 26 | rspec-support (3.13.2) 27 | 28 | PLATFORMS 29 | ruby 30 | 31 | DEPENDENCIES 32 | maxitest! 33 | minitest (~> 5.20.0) 34 | mutex_m 35 | rake 36 | rspec 37 | 38 | BUNDLED WITH 39 | 2.6.6 40 | -------------------------------------------------------------------------------- /gemfiles/521.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec path: "../" 3 | gem "minitest", "~> 5.21.0" 4 | -------------------------------------------------------------------------------- /gemfiles/521.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | maxitest (6.0.0) 5 | minitest (>= 5.20.0, < 5.26.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | diff-lcs (1.6.1) 11 | minitest (5.21.2) 12 | rake (13.2.1) 13 | rspec (3.13.0) 14 | rspec-core (~> 3.13.0) 15 | rspec-expectations (~> 3.13.0) 16 | rspec-mocks (~> 3.13.0) 17 | rspec-core (3.13.3) 18 | rspec-support (~> 3.13.0) 19 | rspec-expectations (3.13.3) 20 | diff-lcs (>= 1.2.0, < 2.0) 21 | rspec-support (~> 3.13.0) 22 | rspec-mocks (3.13.2) 23 | diff-lcs (>= 1.2.0, < 2.0) 24 | rspec-support (~> 3.13.0) 25 | rspec-support (3.13.2) 26 | 27 | PLATFORMS 28 | arm64-darwin-23 29 | ruby 30 | 31 | DEPENDENCIES 32 | maxitest! 33 | minitest (~> 5.21.0) 34 | rake 35 | rspec 36 | 37 | BUNDLED WITH 38 | 2.6.6 39 | -------------------------------------------------------------------------------- /gemfiles/522.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec path: "../" 3 | gem "minitest", "~> 5.22.0" 4 | -------------------------------------------------------------------------------- /gemfiles/522.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | maxitest (6.0.0) 5 | minitest (>= 5.20.0, < 5.26.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | diff-lcs (1.6.1) 11 | minitest (5.22.3) 12 | rake (13.2.1) 13 | rspec (3.13.0) 14 | rspec-core (~> 3.13.0) 15 | rspec-expectations (~> 3.13.0) 16 | rspec-mocks (~> 3.13.0) 17 | rspec-core (3.13.3) 18 | rspec-support (~> 3.13.0) 19 | rspec-expectations (3.13.3) 20 | diff-lcs (>= 1.2.0, < 2.0) 21 | rspec-support (~> 3.13.0) 22 | rspec-mocks (3.13.2) 23 | diff-lcs (>= 1.2.0, < 2.0) 24 | rspec-support (~> 3.13.0) 25 | rspec-support (3.13.2) 26 | 27 | PLATFORMS 28 | arm64-darwin-23 29 | ruby 30 | 31 | DEPENDENCIES 32 | maxitest! 33 | minitest (~> 5.22.0) 34 | rake 35 | rspec 36 | 37 | BUNDLED WITH 38 | 2.6.6 39 | -------------------------------------------------------------------------------- /gemfiles/523.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec path: "../" 3 | gem "minitest", "~> 5.23.0" 4 | -------------------------------------------------------------------------------- /gemfiles/523.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | maxitest (6.0.0) 5 | minitest (>= 5.20.0, < 5.26.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | diff-lcs (1.6.1) 11 | minitest (5.23.1) 12 | rake (13.2.1) 13 | rspec (3.13.0) 14 | rspec-core (~> 3.13.0) 15 | rspec-expectations (~> 3.13.0) 16 | rspec-mocks (~> 3.13.0) 17 | rspec-core (3.13.3) 18 | rspec-support (~> 3.13.0) 19 | rspec-expectations (3.13.3) 20 | diff-lcs (>= 1.2.0, < 2.0) 21 | rspec-support (~> 3.13.0) 22 | rspec-mocks (3.13.2) 23 | diff-lcs (>= 1.2.0, < 2.0) 24 | rspec-support (~> 3.13.0) 25 | rspec-support (3.13.2) 26 | 27 | PLATFORMS 28 | arm64-darwin-23 29 | ruby 30 | 31 | DEPENDENCIES 32 | maxitest! 33 | minitest (~> 5.23.0) 34 | rake 35 | rspec 36 | 37 | BUNDLED WITH 38 | 2.6.6 39 | -------------------------------------------------------------------------------- /gemfiles/524.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec path: "../" 3 | gem "minitest", "~> 5.24.0" 4 | -------------------------------------------------------------------------------- /gemfiles/524.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | maxitest (6.0.0) 5 | minitest (>= 5.20.0, < 5.26.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | diff-lcs (1.6.1) 11 | minitest (5.24.1) 12 | rake (13.2.1) 13 | rspec (3.13.0) 14 | rspec-core (~> 3.13.0) 15 | rspec-expectations (~> 3.13.0) 16 | rspec-mocks (~> 3.13.0) 17 | rspec-core (3.13.3) 18 | rspec-support (~> 3.13.0) 19 | rspec-expectations (3.13.3) 20 | diff-lcs (>= 1.2.0, < 2.0) 21 | rspec-support (~> 3.13.0) 22 | rspec-mocks (3.13.2) 23 | diff-lcs (>= 1.2.0, < 2.0) 24 | rspec-support (~> 3.13.0) 25 | rspec-support (3.13.2) 26 | 27 | PLATFORMS 28 | arm64-darwin-23 29 | ruby 30 | 31 | DEPENDENCIES 32 | maxitest! 33 | minitest (~> 5.24.0) 34 | rake 35 | rspec 36 | 37 | BUNDLED WITH 38 | 2.6.6 39 | -------------------------------------------------------------------------------- /gemfiles/525.gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec path: "../" 3 | gem "minitest", "~> 5.25.0" 4 | -------------------------------------------------------------------------------- /gemfiles/525.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | maxitest (6.0.0) 5 | minitest (>= 5.20.0, < 5.26.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | diff-lcs (1.6.1) 11 | minitest (5.25.5) 12 | rake (13.2.1) 13 | rspec (3.13.0) 14 | rspec-core (~> 3.13.0) 15 | rspec-expectations (~> 3.13.0) 16 | rspec-mocks (~> 3.13.0) 17 | rspec-core (3.13.3) 18 | rspec-support (~> 3.13.0) 19 | rspec-expectations (3.13.3) 20 | diff-lcs (>= 1.2.0, < 2.0) 21 | rspec-support (~> 3.13.0) 22 | rspec-mocks (3.13.2) 23 | diff-lcs (>= 1.2.0, < 2.0) 24 | rspec-support (~> 3.13.0) 25 | rspec-support (3.13.2) 26 | 27 | PLATFORMS 28 | arm64-darwin-23 29 | ruby 30 | 31 | DEPENDENCIES 32 | maxitest! 33 | minitest (~> 5.25.0) 34 | rake 35 | rspec 36 | 37 | BUNDLED WITH 38 | 2.6.6 39 | -------------------------------------------------------------------------------- /lib/maxitest.rb: -------------------------------------------------------------------------------- 1 | module Maxitest 2 | end 3 | -------------------------------------------------------------------------------- /lib/maxitest/autorun.rb: -------------------------------------------------------------------------------- 1 | require "minitest" 2 | require "minitest/autorun" 3 | require "maxitest/vendor/around" 4 | require "maxitest/interrupt" unless ENV["MAXITEST_NO_INTERRUPT"] 5 | require "maxitest/let_bang" 6 | require "maxitest/let_all" 7 | require "maxitest/hook_all" 8 | require "maxitest/pending" 9 | require "maxitest/helpers" 10 | require "maxitest/xit" 11 | require "maxitest/static_class_order" 12 | require "maxitest/shorted_backtrace" 13 | require "maxitest/vendor/line_describe" # not a plugin 14 | 15 | module Maxitest 16 | ENABLE_PLUGINS = true 17 | end 18 | 19 | Minitest::Spec::DSL.send(:alias_method, :context, :describe) 20 | 21 | class << Minitest::Test 22 | alias_method :order_dependent!, :i_suck_and_my_tests_are_order_dependent! 23 | end 24 | 25 | # do not show maxitest as causing errors, but the last line in the users code 26 | old = Minitest::BacktraceFilter::MT_RE 27 | Minitest::BacktraceFilter.send(:remove_const, :MT_RE) 28 | Minitest::BacktraceFilter::MT_RE = Regexp.union(old, %r%lib/maxitest%) 29 | -------------------------------------------------------------------------------- /lib/maxitest/global_must.rb: -------------------------------------------------------------------------------- 1 | # Allow global must_* assertion style without deprecations 2 | # 3 | # Must be required before maxitest/autorun 4 | Module.prepend(Module.new do 5 | def infect_an_assertion(_, new_name, *) 6 | super # define with deprecation 7 | 8 | # remove old to avoid warnings from re-defining 9 | remove_method new_name 10 | 11 | # re-define without deprecation 12 | class_eval <<-EOM, __FILE__, __LINE__ + 1 13 | def #{new_name} *args 14 | Minitest::Expectation.new(self, Minitest::Spec.current).#{new_name}(*args) 15 | end 16 | EOM 17 | end 18 | end) 19 | -------------------------------------------------------------------------------- /lib/maxitest/helpers.rb: -------------------------------------------------------------------------------- 1 | module Maxitest 2 | module Helpers 3 | module InstanceMethods 4 | def with_env(env) 5 | _synchronize do 6 | old = ENV.to_h 7 | env.each { |k, v| ENV[k.to_s] = v } 8 | yield 9 | ensure 10 | ENV.replace old 11 | end 12 | end 13 | 14 | # stripped down version of capture_io 15 | def capture_stdout 16 | _synchronize do 17 | begin 18 | captured_stdout = StringIO.new 19 | orig_stdout = $stdout 20 | $stdout = captured_stdout 21 | yield 22 | return captured_stdout.string 23 | ensure 24 | $stdout = orig_stdout 25 | end 26 | end 27 | end 28 | 29 | # stripped down version of capture_io 30 | def capture_stderr 31 | _synchronize do 32 | begin 33 | captured_stderr = StringIO.new 34 | orig_stderr = $stderr 35 | $stderr = captured_stderr 36 | yield 37 | return captured_stderr.string 38 | ensure 39 | $stderr = orig_stderr 40 | end 41 | end 42 | end 43 | end 44 | 45 | module ClassMethods 46 | def with_env(env) 47 | around { |t| with_env(env, &t) } 48 | end 49 | end 50 | end 51 | end 52 | 53 | Minitest::Test.send(:include, Maxitest::Helpers::InstanceMethods) 54 | Minitest::Test.send(:extend, Maxitest::Helpers::ClassMethods) 55 | -------------------------------------------------------------------------------- /lib/maxitest/hook_all.rb: -------------------------------------------------------------------------------- 1 | module Maxitest 2 | class << self 3 | attr_accessor :hook_all_counter 4 | end 5 | end 6 | Maxitest.hook_all_counter = 0 7 | 8 | module Maxitest 9 | module HookAll 10 | [:before, :after].each do |hook| 11 | # minitest discards the type argument, so we are not sending it along 12 | define_method(hook) do |type = :each, &block| 13 | case type 14 | when :each then super(&block) 15 | when :all 16 | raise ArgumentError, ":all is not supported in after" if hook == :after 17 | c = (Maxitest.hook_all_counter += 1) 18 | callback = :"maxitest_hook_all_#{c}" 19 | let_all(callback, &block) 20 | super() { send callback } 21 | else 22 | raise ArgumentError, "only :each and :all are supported" 23 | end 24 | end 25 | end 26 | end 27 | end 28 | 29 | Minitest::Spec::DSL.prepend(Maxitest::HookAll) 30 | -------------------------------------------------------------------------------- /lib/maxitest/implicit_subject.rb: -------------------------------------------------------------------------------- 1 | # this is a bit hacky / overwrites describe, so not included by default ... 2 | module Maxitest 3 | module ImplicitSubject 4 | def describe(*args, &block) 5 | klass = super 6 | if args.first.is_a?(Class) && !klass.instance_methods(false).include?(:subject) 7 | klass.let(:subject) { args.first.new } 8 | end 9 | klass 10 | end 11 | end 12 | end 13 | 14 | Object.send(:include, Maxitest::ImplicitSubject) # Minitest hacks Kernel -> we need to use alias method or go into Object 15 | -------------------------------------------------------------------------------- /lib/maxitest/interrupt.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # - show current backtrace when interrupting a stuck test with Ctrl+c 4 | # - skip remaining tests 5 | 6 | module Maxitest 7 | Interrupted = Class.new(StandardError) 8 | class << self 9 | attr_accessor :interrupted 10 | end 11 | 12 | module InterruptHandler 13 | # capture interrupt and treat it as a regular error so we get a backtrace 14 | def capture_exceptions(&block) 15 | super(&block) 16 | rescue Interrupt => e 17 | Maxitest.interrupted = true 18 | failures << Minitest::UnexpectedError.new(e) 19 | end 20 | 21 | # skip remaining tests if we were interrupted 22 | def run 23 | if Maxitest.interrupted 24 | # produce a real error so we do not crash in -v mode 25 | failures << 26 | begin 27 | raise Minitest::Skip, 'Maxitest::Interrupted' 28 | rescue Minitest::Skip 29 | $! 30 | end 31 | result = Minitest::Result.from(self) 32 | result.time = 0 33 | result 34 | else 35 | super() 36 | end 37 | end 38 | end 39 | end 40 | 41 | Minitest::Test.prepend(Maxitest::InterruptHandler) 42 | -------------------------------------------------------------------------------- /lib/maxitest/let_all.rb: -------------------------------------------------------------------------------- 1 | module Maxitest 2 | module LetAll 3 | def let_all(name, &block) 4 | cache = [] 5 | define_method(name) do 6 | if cache.empty? 7 | cache << instance_eval(&block) 8 | end 9 | cache.first 10 | end 11 | end 12 | 13 | def self.included(base) 14 | base.extend(self) 15 | end 16 | end 17 | end 18 | 19 | Minitest::Spec::DSL.include(Maxitest::LetAll) 20 | -------------------------------------------------------------------------------- /lib/maxitest/let_bang.rb: -------------------------------------------------------------------------------- 1 | Minitest::Spec::DSL.class_eval do 2 | def let!(name, &block) 3 | let(name, &block) 4 | before { send(name) } 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/maxitest/pending.rb: -------------------------------------------------------------------------------- 1 | module Maxitest 2 | module Pending 3 | def pending(reason=nil, **kwargs) 4 | raise ArgumentError, "Need a block to execute" unless block_given? 5 | raise ArgumentError, "Only :if option is supported" if (kwargs.keys | [:if] != [:if]) 6 | return yield if kwargs.fetch(:if, true) == false # allow user to for example mark test only pending on CI with `if: ENV["CI"]` 7 | 8 | begin 9 | yield # execute test 10 | rescue StandardError, Minitest::Assertion 11 | skip reason # test failed as expected 12 | else 13 | flunk "Test is fixed, remove `pending`" 14 | end 15 | end 16 | end 17 | end 18 | 19 | Minitest::Test.send(:include, Maxitest::Pending) 20 | -------------------------------------------------------------------------------- /lib/maxitest/shorted_backtrace.rb: -------------------------------------------------------------------------------- 1 | Minitest::BacktraceFilter.send(:prepend, Module.new do 2 | def filter(*) 3 | backtrace = super 4 | pwd = "#{Dir.pwd}/" 5 | section = pwd.size..-1 6 | backtrace.map { |b| b.start_with?(pwd) ? b[section] : b } 7 | end 8 | end) 9 | -------------------------------------------------------------------------------- /lib/maxitest/static_class_order.rb: -------------------------------------------------------------------------------- 1 | module Maxitest 2 | class << self 3 | attr_accessor :static_class_order 4 | end 5 | end 6 | 7 | class << Minitest::Runnable 8 | alias_method :runnables_without_static_order, :runnables 9 | 10 | def runnables 11 | return runnables_without_static_order unless Maxitest.static_class_order 12 | 13 | # Minitest.__run uses Runnable.runnables.shuffle -> hack it 14 | runnables = runnables_without_static_order 15 | def runnables.shuffle 16 | self 17 | end 18 | 19 | # ugly hack to fight minitest 5.10 https://github.com/seattlerb/minitest/commit/478e3f9cfeb0a2f8cc4b029bbcbe7bb16648dd96 20 | def runnables.reject(*args, &block) 21 | reject!(*args, &block) 22 | end 23 | 24 | runnables 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/maxitest/threads.rb: -------------------------------------------------------------------------------- 1 | # tests that leave extra threads running can break other tests in weird ways ... prevent that from happening 2 | 3 | module Maxitest 4 | module Threads 5 | def setup 6 | @maxitest_threads_before = Thread.list 7 | super 8 | end 9 | 10 | def teardown 11 | super 12 | found = maxitest_extra_threads 13 | raise "Test left #{found.size} extra threads (#{found})" if found.any? 14 | ensure 15 | maxitest_kill_extra_threads 16 | end 17 | 18 | # also a helper methods for users 19 | def maxitest_wait_for_extra_threads 20 | sleep 0.01 while maxitest_extra_threads.any? 21 | end 22 | 23 | # also a helper methods for users 24 | def maxitest_kill_extra_threads 25 | maxitest_extra_threads.map(&:kill).map(&:join) 26 | end 27 | 28 | # if setup crashes we do not return anything to make the initial error visible 29 | def maxitest_extra_threads 30 | @maxitest_threads_before ? Thread.list - @maxitest_threads_before : [] 31 | end 32 | end 33 | end 34 | 35 | # not using prepend since that would clash with webmock 36 | # include works because original setup lives in also included Minitest::LifecycleHooks 37 | Minitest::Test.send :include, Maxitest::Threads 38 | -------------------------------------------------------------------------------- /lib/maxitest/timeout.rb: -------------------------------------------------------------------------------- 1 | # tests sometimes hang locally or on ci and with this we can actually debug the cause instead of just hanging forever 2 | require 'timeout' 3 | 4 | module Maxitest 5 | class << self 6 | attr_accessor :timeout 7 | end 8 | 9 | module Timeout 10 | class TestCaseTimeout < StandardError 11 | def message 12 | "Test took too long to finish, aborting. To use a debugger, def maxitest_timeout;false;end in the test file." 13 | end 14 | end 15 | 16 | def run(*, &block) 17 | # NOTE: having a default def maxitest_timeout would break using let(:maxitest_timeout) 18 | timeout = (defined?(maxitest_timeout) ? maxitest_timeout : Maxitest.timeout || 5) 19 | if timeout == false 20 | super 21 | else 22 | begin 23 | ::Timeout.timeout(timeout, TestCaseTimeout) { super } 24 | rescue TestCaseTimeout => e 25 | failures << UnexpectedError.new(e) 26 | end 27 | end 28 | end 29 | end 30 | end 31 | 32 | Minitest::Test.send :prepend, Maxitest::Timeout 33 | 34 | 35 | -------------------------------------------------------------------------------- /lib/maxitest/vendor/around.rb: -------------------------------------------------------------------------------- 1 | # BEGIN generated by rake update, do not modify 2 | # https://raw.githubusercontent.com/splattael/minitest-around/master/LICENSE 3 | =begin 4 | Copyright (c) 2012 Peter Suschlik 5 | Copyright (c) 2018 Peter Leitzen (Suschlik) 6 | 7 | MIT License 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining 10 | a copy of this software and associated documentation files (the 11 | "Software"), to deal in the Software without restriction, including 12 | without limitation the rights to use, copy, modify, merge, publish, 13 | distribute, sublicense, and/or sell copies of the Software, and to 14 | permit persons to whom the Software is furnished to do so, subject to 15 | the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be 18 | included in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | =end 28 | # END generated by rake update, do not modify 29 | 30 | # BEGIN generated by rake update, do not modify 31 | # https://raw.githubusercontent.com/splattael/minitest-around/master/lib/minitest/around/version.rb 32 | module MinitestAround 33 | VERSION = '0.5.0' 34 | end 35 | # END generated by rake update, do not modify 36 | 37 | # BEGIN generated by rake update, do not modify 38 | # https://raw.githubusercontent.com/splattael/minitest-around/master/lib/minitest/around/unit.rb 39 | Minitest::Test.class_eval do 40 | alias_method :run_without_around, :run 41 | def run(*args) 42 | if defined?(around) 43 | result = nil 44 | around { result = run_without_around(*args) } 45 | result 46 | else 47 | run_without_around(*args) 48 | end 49 | end 50 | end 51 | # END generated by rake update, do not modify 52 | 53 | # BEGIN generated by rake update, do not modify 54 | # https://raw.githubusercontent.com/splattael/minitest-around/master/lib/minitest/around/spec.rb 55 | Minitest::Spec::DSL.class_eval do 56 | # - resume to call first part 57 | # - execute test 58 | # - resume fiber to execute last part 59 | def around(*args, &block) 60 | raise ArgumentError, "only :each or no argument is supported" if args != [] && args != [:each] 61 | fib = nil 62 | before do 63 | fib = Fiber.new do |context, resume| 64 | begin 65 | context.instance_exec(resume, &block) 66 | rescue Object 67 | fib = :failed 68 | raise 69 | end 70 | end 71 | fib.resume(self, lambda { Fiber.yield }) 72 | end 73 | after { fib.resume if fib && fib != :failed } 74 | end 75 | 76 | # Minitest does not support multiple before/after blocks 77 | remove_method :before 78 | def before(type=nil, &block) 79 | include Module.new { define_method(:setup) { super(); instance_exec(&block) } } 80 | end 81 | 82 | remove_method :after 83 | def after(type=nil, &block) 84 | include(Module.new do 85 | define_method(:teardown) do 86 | begin 87 | instance_exec(&block) 88 | ensure 89 | super() 90 | end 91 | end 92 | end) 93 | end 94 | end 95 | # END generated by rake update, do not modify 96 | -------------------------------------------------------------------------------- /lib/maxitest/vendor/line.rb: -------------------------------------------------------------------------------- 1 | # BEGIN generated by rake update, do not modify 2 | # https://raw.githubusercontent.com/judofyr/minitest-line/master/MIT-LICENSE.txt 3 | =begin 4 | Copyright (c) 2014 Magnus Holm 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | =end 25 | # END generated by rake update, do not modify 26 | 27 | # BEGIN generated by rake update, do not modify 28 | # https://raw.githubusercontent.com/judofyr/minitest-line/master/lib/minitest/line_plugin.rb 29 | module Minitest 30 | module Line 31 | class << self 32 | def tests_with_lines 33 | target_file = $0 34 | methods_with_lines(target_file).concat describes_with_lines(target_file) 35 | end 36 | 37 | private 38 | 39 | def methods_with_lines(target_file) 40 | runnables.flat_map do |runnable| 41 | rname = runnable.name 42 | runnable.runnable_methods.map do |name| 43 | file, line = runnable.instance_method(name).source_location 44 | next unless file == target_file 45 | test_name = (rname ? "#{rname}##{name}" : name) 46 | [test_name, line] 47 | end 48 | end.uniq.compact 49 | end 50 | 51 | def describes_with_lines(target_file) 52 | runnables.map do |runnable| 53 | next unless caller = runnable.instance_variable_defined?(:@minitest_line_caller) && runnable.instance_variable_get(:@minitest_line_caller) 54 | next unless line = caller.detect { |l| l.include?(target_file) } 55 | ["/#{Regexp.escape(runnable.name)}/", line[/:(\d+):in/, 1].to_i] 56 | end.compact 57 | end 58 | 59 | def runnables 60 | Minitest::Runnable.runnables 61 | end 62 | end 63 | end 64 | 65 | def self.plugin_line_options(opts, options) 66 | opts.on '-l', '--line N', Integer, "Run test at line number" do |lineno| 67 | options[:line] = lineno 68 | end 69 | end 70 | 71 | def self.plugin_line_init(options) 72 | unless exp_line = options[:line] 73 | reporter.reporters << LineReporter.new 74 | return 75 | end 76 | 77 | tests = Minitest::Line.tests_with_lines 78 | 79 | filter, _ = tests.sort_by { |n, l| -l }.detect { |n, l| exp_line >= l } 80 | 81 | raise "Could not find test method before line #{exp_line}" unless filter 82 | 83 | options[:filter] = filter 84 | end 85 | 86 | class LineReporter < Reporter 87 | def initialize(*) 88 | super 89 | @failures = [] 90 | end 91 | 92 | def record(result) 93 | if !result.skipped? && !result.passed? 94 | @failures << result 95 | end 96 | end 97 | 98 | def report 99 | return unless @failures.any? 100 | io.puts 101 | io.puts "Focus on failing tests:" 102 | pwd = Pathname.new(Dir.pwd) 103 | bin_rails = File.exist?("bin/rails") 104 | @failures.each do |res| 105 | result = (res.respond_to?(:source_location) ? res : res.method(res.name)) 106 | file, line = result.source_location 107 | 108 | if file 109 | file = Pathname.new(file) 110 | file = file.relative_path_from(pwd) if file.absolute? 111 | output = "#{bin_rails ? "bin/rails test" : "mtest"} #{file}:#{line}" 112 | output = "\e[31m#{output}\e[0m" if $stdout.tty? 113 | io.puts output 114 | end 115 | end 116 | end 117 | end 118 | 119 | def self.plugin_line_inject_reporter 120 | end 121 | end 122 | # END generated by rake update, do not modify 123 | -------------------------------------------------------------------------------- /lib/maxitest/vendor/line_describe.rb: -------------------------------------------------------------------------------- 1 | # BEGIN generated by rake update, do not modify 2 | # https://raw.githubusercontent.com/judofyr/minitest-line/master/lib/minitest/line/describe_track.rb 3 | module Minitest 4 | module Line 5 | module DescribeTrack 6 | def describe(*args, &block) 7 | klass = super 8 | klass.instance_variable_set(:@minitest_line_caller, caller(0..5)) 9 | klass 10 | end 11 | end 12 | end 13 | end 14 | 15 | Object.send(:include, Minitest::Line::DescribeTrack) 16 | # END generated by rake update, do not modify 17 | -------------------------------------------------------------------------------- /lib/maxitest/vendor/rg.rb: -------------------------------------------------------------------------------- 1 | # BEGIN generated by rake update, do not modify 2 | # https://raw.githubusercontent.com/blowmage/minitest-rg/master/LICENSE 3 | =begin 4 | Copyright (c) 2012 Mike Moore 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | =end 25 | # END generated by rake update, do not modify 26 | 27 | # BEGIN generated by rake update, do not modify 28 | # https://raw.githubusercontent.com/blowmage/minitest-rg/master/lib/minitest/rg_plugin.rb 29 | module Minitest 30 | def self.plugin_rg_options opts, _options # :nodoc: 31 | opts.on "--[no-]rg", "Add red/green to test output." do |bool| 32 | RG.rg! bool 33 | end 34 | end 35 | 36 | def self.plugin_rg_init options # :nodoc: 37 | if RG.rg? 38 | io = RG.new options[:io] 39 | 40 | reporter.reporters.grep(Minitest::Reporter).each do |rep| 41 | rep.io = io 42 | end 43 | end 44 | end 45 | 46 | class RG 47 | VERSION = "5.2.0".freeze 48 | 49 | COLORS = { 50 | "." => "\e[32m.\e[0m", 51 | "E" => "\e[33mE\e[0m", 52 | "F" => "\e[31mF\e[0m", 53 | "S" => "\e[36mS\e[0m" 54 | }.freeze 55 | 56 | attr_reader :io, :colors 57 | 58 | def self.rg!(bool = true) 59 | @rg = bool 60 | end 61 | 62 | def self.rg? 63 | @rg ||= false 64 | end 65 | 66 | def initialize io, colors = COLORS 67 | @io = io 68 | @colors = colors 69 | end 70 | 71 | def print o 72 | io.print(colors[o] || o) 73 | end 74 | 75 | def puts o = nil 76 | return io.puts if o.nil? 77 | if o =~ /(\d+) failures, (\d+) errors/ 78 | if Regexp.last_match[1] != "0" || Regexp.last_match[2] != "0" 79 | io.puts "\e[31m#{o}\e[0m" 80 | else 81 | io.puts "\e[32m#{o}\e[0m" 82 | end 83 | else 84 | io.puts o 85 | end 86 | end 87 | 88 | def method_missing msg, *args 89 | return super unless io.respond_to? msg 90 | io.send(msg, *args) 91 | end 92 | end 93 | end 94 | # END generated by rake update, do not modify 95 | -------------------------------------------------------------------------------- /lib/maxitest/vendor/testrbl.rb: -------------------------------------------------------------------------------- 1 | # BEGIN generated by rake update, do not modify 2 | # https://raw.githubusercontent.com/grosser/testrbl/master/lib/testrbl.rb 3 | module Maxitest 4 | module Testrbl 5 | PATTERNS = [ 6 | /^(\s+)(should|test|it)(\s+|\s*\(\s*)['"](.*)['"](\s*\))?\s+do\s*(?:#.*)?$/, 7 | /^(\s+)(context|describe)(\s+|\s*\(\s*)['"]?(.*?)['"]?(\s*\))?\s+do\s*(?:#.*)?$/, 8 | /^(\s+)def(\s+)(test_)([a-z_\d]+)\s*(?:#.*)?$/ 9 | ] 10 | 11 | OPTION_WITH_ARGUMENT = ["-I", "-r", "-n", "--name", "-e", "--exclude", "-s", "--seed"] 12 | INTERPOLATION = /\\\#\\\{.*?\\\}/ 13 | 14 | class << self 15 | def run_from_cli(argv) 16 | files, options = partition_argv(argv) 17 | files.concat(changed_files) if options.delete("--changed") 18 | files = ["test"] if files.empty? 19 | files = files.map { |f| localize(f) } 20 | load_options, options = partition_options(options) 21 | 22 | if files.size == 1 and files.first =~ /^(\S+):(\d+)$/ 23 | file = $1 24 | line = $2 25 | run(ruby + load_options + line_pattern_option(file, line) + options) 26 | else 27 | if files.size == 1 and File.file?(files.first) 28 | run(ruby + load_options + files + options) 29 | elsif options.none? { |arg| arg =~ /^-n/ } 30 | seed = if seed = options.index("--seed") 31 | ["--"] + options.slice!(seed, 2) 32 | else 33 | [] 34 | end 35 | files = files.map { |f| File.directory?(f) ? all_test_files_in(f) : f }.flatten 36 | run(ruby + load_options + files.map { |f| "-r#{f}" } + options + ["-e", ""] + seed) 37 | else # pass though 38 | # no bundle exec: projects with mini and unit-test do not run well via bundle exec testrb 39 | run ["testrb"] + argv 40 | end 41 | end 42 | end 43 | 44 | # overwritten by maxitest to just return line 45 | def line_pattern_option(file, line) 46 | [file, "-n", "/#{pattern_from_file(File.readlines(file), line)}/"] 47 | end 48 | 49 | # usable via external tools like zeus 50 | def pattern_from_file(lines, line) 51 | possible_lines = lines[0..(line.to_i-1)].reverse 52 | 53 | found = possible_lines.map { |line| test_pattern_from_line(line) || block_start_from_line(line) }.compact 54 | 55 | # pattern and the groups it is nested under (like describe - describe - it) 56 | last_spaces = " " * 100 57 | patterns = found.select do |spaces, name| 58 | last_spaces = spaces if spaces.size < last_spaces.size 59 | end.map(&:last).compact 60 | 61 | return filter_duplicate_final(patterns).reverse.join(".*") if found.size > 0 62 | 63 | raise "no test found before line #{line}" 64 | end 65 | 66 | # only keep 1 pattern that stops matching via $ 67 | def filter_duplicate_final(patterns) 68 | found_final = 0 69 | patterns.reject { |p| p.end_with?("$") and (found_final += 1) > 1 } 70 | end 71 | 72 | private 73 | 74 | def all_test_files_in(folder) 75 | Dir[File.join(folder, "{**/,}*_{test,spec}.rb")].uniq 76 | end 77 | 78 | def partition_options(options) 79 | next_is_before = false 80 | options.partition do |option| 81 | if next_is_before 82 | next_is_before = false 83 | true 84 | else 85 | if option =~ /^-(r|I)/ 86 | next_is_before = (option.size == 2) 87 | true 88 | else 89 | false 90 | end 91 | end 92 | end 93 | end 94 | 95 | # fix 1.9 not being able to load local files 96 | def localize(file) 97 | file =~ /^[-a-z\d_]/ ? "./#{file}" : file 98 | end 99 | 100 | def partition_argv(argv) 101 | next_is_option = false 102 | argv.partition do |arg| 103 | if next_is_option 104 | next_is_option = false 105 | else 106 | if arg =~ /^-.$/ or arg =~ /^--/ # single letter option followed by argument like -I test or long options like --verbose 107 | next_is_option = true if OPTION_WITH_ARGUMENT.include?(arg) 108 | false 109 | elsif arg =~ /^-/ # multi letter option like -Itest 110 | false 111 | else 112 | true 113 | end 114 | end 115 | end 116 | end 117 | 118 | def changed_files 119 | changed_files = sh("git status -s").split("\n").map { |l| l.strip.split(/\s+/, 2)[1] } 120 | 121 | if changed_files.empty? 122 | # user wants to test last commit and not current diff 123 | changed_files = sh("git show --name-only").split("\n\n").last.split("\n") 124 | end 125 | 126 | # we only want test files that were added or changed (not deleted) 127 | changed_files.select { |f| f =~ /_(test|spec)\.rb$/ && File.exist?(f) } 128 | end 129 | 130 | def sh(command) 131 | result = `#{command}` 132 | raise "Failed: #{command} -> #{result}" unless $?.success? 133 | result 134 | end 135 | 136 | def ruby 137 | if File.file?("Gemfile") 138 | ["ruby", "-rbundler/setup"] # faster then bundle exec ruby 139 | else 140 | ["ruby"] 141 | end 142 | end 143 | 144 | def run(command) 145 | puts command.join(" ") 146 | STDOUT.flush # if exec fails horribly we at least see some output 147 | Kernel.exec *command 148 | end 149 | 150 | def block_start_from_line(line) 151 | if line =~ /^(\s*).* do( \|.*\|)?$/ 152 | [$1, nil] 153 | end 154 | end 155 | 156 | def test_pattern_from_line(line) 157 | PATTERNS.each do |r| 158 | next unless line =~ r 159 | whitespace, method, test_name = $1, $2, $4 160 | return [whitespace, test_pattern_from_match(method, test_name)] 161 | end 162 | nil 163 | end 164 | 165 | def test_pattern_from_match(method, test_name) 166 | regex = Regexp.escape(test_name).gsub("\\ "," ").gsub(INTERPOLATION, ".*") 167 | 168 | regex = if method == "test" 169 | # test "xxx -_ yyy" 170 | # test-unit: "test: xxx -_ yyy" 171 | # activesupport: "test_xxx_-__yyy" 172 | "^test(: |_)#{regex.gsub(" ", ".")}$" 173 | elsif method == "describe" || (method == "context" && !via_shoulda?) 174 | "#{regex}(::)?" 175 | elsif method == "should" && via_shoulda? 176 | optional_test_name = "(?:\(.*\))?" 177 | "#{method} #{regex}\. #{optional_test_name}$" 178 | elsif ["it", "should"].include?(method) # minitest aliases for shoulda 179 | "#test_\\d+_#{regex}$" 180 | else 181 | regex 182 | end 183 | 184 | regex.gsub("'", ".") 185 | end 186 | 187 | def via_shoulda? 188 | return @via_shoulda if defined?(@via_shoulda) 189 | @via_shoulda = !File.exist?("Gemfile.lock") || File.read("Gemfile.lock").include?(" shoulda-context ") 190 | end 191 | end 192 | end 193 | end 194 | # END generated by rake update, do not modify 195 | -------------------------------------------------------------------------------- /lib/maxitest/verbose_backtrace.rb: -------------------------------------------------------------------------------- 1 | module Maxitest 2 | module VerboseBacktrace 3 | class NullFilter 4 | def self.filter(backtrace) 5 | backtrace 6 | end 7 | end 8 | 9 | class << self 10 | attr_accessor :verbose 11 | end 12 | 13 | def plugin_maxitest_verbose_backtrace_init(options) 14 | return unless options[:verbose] 15 | Maxitest::VerboseBacktrace.verbose = true 16 | Minitest.backtrace_filter = Maxitest::VerboseBacktrace::NullFilter 17 | Rails.backtrace_cleaner.remove_silencers! if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) 18 | end 19 | end 20 | end 21 | 22 | Minitest.extensions << 'maxitest_verbose_backtrace' 23 | Minitest.extend Maxitest::VerboseBacktrace 24 | 25 | module Maxitest::VerboseAssertion 26 | def message 27 | if Maxitest::VerboseBacktrace.verbose 28 | "#{self.class}: #{super}\n #{backtrace.join "\n "}" 29 | else 30 | super 31 | end 32 | end 33 | end 34 | 35 | Minitest::Assertion.send(:include, Maxitest::VerboseAssertion) 36 | -------------------------------------------------------------------------------- /lib/maxitest/version.rb: -------------------------------------------------------------------------------- 1 | module Maxitest 2 | VERSION = "6.0.0" 3 | end 4 | -------------------------------------------------------------------------------- /lib/maxitest/xit.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Maxitest 4 | module XitMethod 5 | def xit(*args) 6 | describe 'skip' do 7 | define_method(:setup) {} 8 | define_method(:teardown) {} 9 | it(*args) 10 | end 11 | end 12 | 13 | def self.included(base) 14 | base.extend(self) 15 | end 16 | end 17 | end 18 | 19 | Minitest::Spec::DSL.include(Maxitest::XitMethod) 20 | -------------------------------------------------------------------------------- /lib/minitest/maxitest_plugin.rb: -------------------------------------------------------------------------------- 1 | # we are not enabling our extensions unless maxitest/autorun was loaded 2 | # minitest plugin system auto-loads all files in the load path and that would 3 | # always enable all our plugins even if they were not wanted 4 | if defined?(Maxitest::ENABLE_PLUGINS) && Maxitest::ENABLE_PLUGINS 5 | disabled_for_rails = begin 6 | require 'rails/version' 7 | Rails::VERSION::MAJOR >= 5 8 | rescue LoadError 9 | false 10 | end 11 | 12 | # rails has --backtrace which disables rails own backtrace cleaner, but not minitests 13 | require "maxitest/verbose_backtrace" 14 | 15 | unless disabled_for_rails # rails 5 adds default red/green output 16 | require "maxitest/vendor/rg" 17 | Minitest.extensions << "rg" 18 | Minitest::RG.rg! $stdout.tty? 19 | end 20 | 21 | # rails 5.2+ has it's own line support 22 | # - it breaks `mtest file:line` format 23 | # - we still want our format with the nice summary at the end, even if the `bin/rails test` output is already inline 24 | require "maxitest/vendor/line" 25 | Minitest.extensions << "line" 26 | end 27 | -------------------------------------------------------------------------------- /maxitest.gemspec: -------------------------------------------------------------------------------- 1 | require_relative "lib/maxitest/version" 2 | 3 | Gem::Specification.new "maxitest", Maxitest::VERSION do |s| 4 | s.summary = "Minitest + all the features you always wanted" 5 | s.authors = ["Michael Grosser"] 6 | s.email = "michael@grosser.it" 7 | s.homepage = "https://github.com/grosser/maxitest" 8 | s.files = Dir["{bin,lib}/**/*", "MIT-LICENSE", "README.md", "CHANGELOG.md"] 9 | s.license = "MIT" 10 | s.executables = ["mtest"] 11 | 12 | # keep in sync with .github/workflows/actions.yml 13 | s.add_runtime_dependency "minitest", [">= 5.20.0", "< 5.26.0"] 14 | s.required_ruby_version = '>= 3.2', '< 3.5' 15 | 16 | s.add_development_dependency "rake" 17 | s.add_development_dependency "rspec" 18 | end 19 | -------------------------------------------------------------------------------- /spec/cases/around.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe "2" do 4 | let(:calls) { [] } 5 | 6 | before { calls << 1 } 7 | around((ENV["HOOK_TYPE"] || "each").to_sym) { |test| calls << 2; test.call } 8 | around { |test| calls << 3; test.call } 9 | after { calls.must_equal [1,2,3,4] } 10 | after { calls << 4 } 11 | 12 | it "is ordered" do 13 | calls.must_equal [1,2,3] 14 | end 15 | end 16 | 17 | class AroundTest < Minitest::Test 18 | def around 19 | yield 20 | end 21 | 22 | def test_things 23 | assert 1 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/cases/catch_interrupt.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 2 do 4 | it "xx" do 5 | x = 2 6 | begin 7 | raise Interrupt 8 | rescue Interrupt 9 | x = 1 10 | else 11 | x = 3 12 | end 13 | x.must_equal 1 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/cases/cltr_c.rb: -------------------------------------------------------------------------------- 1 | ENV["GLOBAL_MUST"] = "true" 2 | require "./spec/cases/helper" 3 | 4 | describe 2 do 5 | i_suck_and_my_tests_are_order_dependent! 6 | 7 | before { puts "BEFORE" } 8 | after { puts "AFTER" } 9 | 10 | it "is even" do 11 | false.must_equal true 12 | end 13 | 14 | it "is not odd" do 15 | puts "hit Cltr+c" 16 | sleep 10 17 | end 18 | 19 | it "stops after" do 20 | puts "should not get here ..." 21 | sleep 10 22 | end 23 | 24 | it "really does ..." do 25 | puts "should not get here ..." 26 | sleep 10 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/cases/context.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 2 do 4 | context "methods" do 5 | it "is even" do 6 | 2.even?.must_equal true 7 | end 8 | 9 | it "is not odd" do 10 | 2.odd?.must_equal false 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/cases/error_and_failure.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 2 do 4 | it 'errors' do 5 | raise 6 | end 7 | 8 | it 'fails' do 9 | assert false 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/cases/global_must.rb: -------------------------------------------------------------------------------- 1 | ENV["GLOBAL_MUST"] = "true" 2 | require "./spec/cases/helper" 3 | 4 | describe "threads" do 5 | def assert_it 6 | 1.must_equal 1 7 | end 8 | 9 | it "can assert normal" do 10 | assert_it 11 | end 12 | 13 | it "can assert in threads" do 14 | result = "not called" 15 | Thread.new do 16 | begin 17 | assert_it 18 | rescue NoMethodError, RuntimeError, NameError # different errors depending on minitest and ruby version 19 | result = "error" 20 | end 21 | end.join 22 | result.must_equal "error" 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/cases/helper.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | $VERBOSE = true 3 | 4 | if ENV['SIMULATE_TTY'] 5 | def $stdout.tty? 6 | true 7 | end 8 | end 9 | 10 | if ENV['GLOBAL_MUST'] 11 | require 'maxitest/global_must' 12 | end 13 | 14 | require 'maxitest/autorun' 15 | -------------------------------------------------------------------------------- /spec/cases/helpers.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe "helpers" do 4 | describe "#with_env" do 5 | it "changes env" do 6 | with_env A: "b" do 7 | _(ENV["A"]).must_equal "b" 8 | end 9 | end 10 | 11 | it "restores env" do 12 | ENV["A"] = "c" 13 | with_env A: "b" do 14 | ENV["B"] = "a" 15 | _(ENV["A"]).must_equal "b" 16 | end 17 | _(ENV["A"]).must_equal "c" 18 | _(ENV["B"]).must_be_nil 19 | end 20 | end 21 | 22 | describe ".with_env" do 23 | with_env A: "a" 24 | it "sets env" do 25 | _(ENV["A"]).must_equal "a" 26 | end 27 | end 28 | 29 | describe "#capture_stdout" do 30 | it "keeps stdout" do 31 | _(capture_stdout { puts "X" }).must_equal "X\n" 32 | end 33 | 34 | it "lets stderr through" do 35 | out, err = capture_io { capture_stdout { warn "X" } } 36 | _(out).must_equal "" 37 | _(err).must_equal "X\n" 38 | end 39 | end 40 | 41 | describe "#capture_stderr" do 42 | it "keeps stderr" do 43 | _(capture_stderr { warn "X" }).must_equal "X\n" 44 | end 45 | 46 | it "lets stdout through" do 47 | out, err = capture_io { capture_stderr { puts "X" } } 48 | _(out).must_equal "X\n" 49 | _(err).must_equal "" 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/cases/hook_all.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | hook_method = (ENV["HOOK_METHOD"] || "before").to_sym 4 | hook_type = (ENV["HOOK_TYPE"] || "all").to_sym 5 | 6 | # need this globally or classes don't sort 7 | r = Minitest::Runnable.runnables 8 | def r.shuffle 9 | self 10 | end 11 | 12 | # needed or minitest <=5.15 13 | def r.reject 14 | replace super 15 | end 16 | 17 | describe 2 do 18 | order_dependent! 19 | 20 | send hook_method, hook_type do 21 | puts "ALL" 22 | end 23 | 24 | it "works" do 25 | puts "T1" 26 | end 27 | 28 | describe "subclass" do 29 | order_dependent! 30 | 31 | send hook_method, hook_type do 32 | puts "ALL-SUB" 33 | end 34 | 35 | it "still works" do 36 | puts "TS1" 37 | end 38 | 39 | it "yes it does" do 40 | puts "TS2" 41 | end 42 | end 43 | 44 | it "works after subclass" do 45 | puts "T2" 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/cases/implicit_subject.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | require "maxitest/implicit_subject" 3 | 4 | class DoNotCallMe 5 | def initialize 6 | raise "WRONG" 7 | end 8 | end 9 | 10 | describe String do 11 | it "has implicit subject" do 12 | subject.must_equal "" 13 | end 14 | 15 | describe Array do 16 | def other_method 17 | true 18 | end 19 | 20 | it "has nested implicit subject" do 21 | subject.must_equal [] 22 | other_method.must_equal true 23 | end 24 | end 25 | 26 | describe Hash do 27 | it "has other nested implicit subject" do 28 | subject.must_equal({}) 29 | end 30 | 31 | describe "strings" do 32 | it "does not overwrite subject" do 33 | subject.must_equal({}) 34 | end 35 | end 36 | 37 | describe :symbols do 38 | it "does not overwrite subject" do 39 | subject.must_equal({}) 40 | end 41 | end 42 | end 43 | 44 | describe DoNotCallMe do 45 | let(:subject) { 1 } 46 | 47 | it "can overwrite" do 48 | subject.must_equal 1 49 | end 50 | end 51 | end 52 | 53 | describe "without" do 54 | it "raises as expected" do 55 | assert_raises(NameError) { subject } 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/cases/let_all.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | # executing sub-classes first makes the tests fail 4 | Maxitest.static_class_order = true 5 | 6 | describe "A" do 7 | order_dependent! 8 | 9 | let(:calls) { [] } 10 | let_all(:foo) { calls << 1; nil } 11 | 12 | describe "subclass gets randomly executed first" do 13 | it "is called when used" do 14 | calls.must_equal [] 15 | foo.must_equal nil 16 | calls.must_equal [1] 17 | end 18 | end 19 | 20 | describe "then another subclass" do 21 | it "is not called multiple times" do 22 | calls.must_equal [] 23 | foo.must_equal nil 24 | calls.must_equal [] 25 | end 26 | end 27 | 28 | describe "overwrite" do 29 | let_all(:foo) { calls << 2; true } 30 | 31 | it "can overwrite" do 32 | calls.must_equal [] 33 | foo.must_equal true 34 | calls.must_equal [2] 35 | end 36 | end 37 | 38 | describe "nested" do 39 | it "is not called multiple times from child classes" do 40 | calls.must_equal [] 41 | foo.must_equal nil 42 | calls.must_equal [] 43 | end 44 | end 45 | end 46 | 47 | describe "B" do 48 | let_all(:foo) { :foo } 49 | 50 | it "does not go across class boundaries" do 51 | foo.must_equal :foo 52 | end 53 | end 54 | 55 | -------------------------------------------------------------------------------- /spec/cases/let_bang.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 2 do 4 | let(:calls) { [] } 5 | let!(:foo) { calls << 1 } 6 | 7 | before { calls << 2 } 8 | 9 | it "is called before" do 10 | calls.must_equal [1, 2] 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/cases/line.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 2 do 4 | it "is even" do 5 | 2.even?.must_equal true 6 | end 7 | 8 | it "is not odd" do 9 | 2.odd?.must_equal true 10 | end 11 | 12 | describe "block" do 13 | it "is even" do 14 | 2.even?.must_equal true 15 | end 16 | 17 | it "is still even" do 18 | 2.even?.must_equal true 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/cases/mtest/a_test.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 1 do 4 | it "is even" do 5 | 2.even?.must_equal true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/cases/mtest/b_test.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 2 do 4 | it "is even" do 5 | 2.even?.must_equal true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/cases/mtest/c.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 3 do 4 | it "is even" do 5 | 2.even?.must_equal true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/cases/order_dependent.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | $ordered_calls = [] 4 | 5 | describe 2 do 6 | order_dependent! 7 | 8 | it "a" do 9 | $ordered_calls << 1 10 | end 11 | 12 | it "c" do 13 | $ordered_calls << 2 14 | end 15 | 16 | it "b" do 17 | $ordered_calls << 3 18 | end 19 | 20 | it "d" do 21 | $ordered_calls << 4 22 | end 23 | 24 | it "z" do 25 | $ordered_calls.must_equal [1,2,3,4] 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/cases/parallel.rb: -------------------------------------------------------------------------------- 1 | ENV["N"] = ENV["MT_CPU"] = "3" # make this test hardware independent, need N for old minitest versions 2 | require "./spec/cases/helper" 3 | 4 | describe String do 5 | parallelize_me! 6 | 7 | it "1" do 8 | sleep 0.1 9 | end 10 | 11 | it "2" do 12 | sleep 0.1 13 | end 14 | 15 | it "3" do 16 | sleep 0.1 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/cases/pending.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 2 do 4 | it "runs regular tests" do 5 | _(2.even?).must_equal true 6 | end 7 | 8 | it "fails when pending is fixed" do 9 | e = assert_raises Minitest::Assertion do 10 | pending "fail" do 11 | _(2).must_equal 2 12 | end 13 | end 14 | _(e.message).must_include "fixed" 15 | end 16 | 17 | it "skips when pending still needed" do 18 | e = assert_raises Minitest::Skip do 19 | pending "Skipping with reason" do 20 | _(2).must_equal 3, "This should not fail" 21 | end 22 | end 23 | _(e.message).must_equal "Skipping with reason" 24 | end 25 | 26 | it "skips exceptions" do 27 | e = assert_raises Minitest::Skip do 28 | pending "Skipping with exception" do 29 | raise "Oh noes" 30 | end 31 | end 32 | _(e.message).must_equal "Skipping with exception" 33 | end 34 | 35 | it "skips without reason" do 36 | pending do 37 | _(2).must_equal 3 38 | end 39 | end 40 | 41 | it "fails without block" do 42 | e = assert_raises(ArgumentError) do 43 | pending "success" 44 | end 45 | _(e.message).must_equal "Need a block to execute" 46 | end 47 | 48 | it "can disable pending" do 49 | pending "Not skipping", if: false do 50 | _(2).must_equal 2 51 | end 52 | end 53 | 54 | it "can enable pending" do 55 | pending "skipping conditionally", if: true do 56 | raise "Oh noes" 57 | end 58 | end 59 | 60 | it "fails when given unknown kwargs" do 61 | assert_raises(ArgumentError) do 62 | pending "skipping conditionally", unless: true do 63 | raise "Oh noes" 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /spec/cases/plain.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 2 do 4 | it "is even" do 5 | 2.even?.must_equal true 6 | end 7 | 8 | it "is not odd" do 9 | 2.odd?.must_equal false 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/cases/raise.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | # cause a raise in a required file 4 | module Maxitest 5 | class Timeout 6 | end 7 | end 8 | 9 | describe "explode" do 10 | it "explodes" do 11 | require 'maxitest/timeout' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/cases/raise_interrupt.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 2 do 4 | i_suck_and_my_tests_are_order_dependent! 5 | 6 | it "xx" do 7 | raise Interrupt 8 | end 9 | 10 | it "yyy" do 11 | assert false 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/cases/static_class_order.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | Maxitest.static_class_order = true 3 | $calls = [] 4 | 5 | 5.times do |i| 6 | describe rand do 7 | it { puts "#{i}" } 8 | 9 | describe rand do 10 | it { puts "#{i}n" } 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/cases/threads.rb: -------------------------------------------------------------------------------- 1 | ENV["MT_CPU"] = "2" # so we get 3 threads total and not variation based on machines cpus 2 | require "maxitest/global_must" 3 | require "./spec/cases/helper" 4 | require "maxitest/threads" 5 | 6 | describe "threads" do 7 | order_dependent! 8 | 9 | def assert_correct_threads 10 | Thread.list.count.must_equal 3, Thread.list 11 | end 12 | 13 | def create_thread 14 | Thread.new { sleep 0.1 } 15 | end 16 | 17 | it "is fine without extra threads" do 18 | assert_correct_threads 19 | end 20 | 21 | it "fails on extra threads" do 22 | assert_correct_threads 23 | create_thread 24 | Thread.list.must_equal 4 25 | end 26 | 27 | it "can kill extra threads" do 28 | assert_correct_threads 29 | create_thread 30 | maxitest_kill_extra_threads 31 | end 32 | 33 | it "can wait for extra threads" do 34 | assert_correct_threads 35 | maxitest_wait_for_extra_threads 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/cases/timeout.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | require "maxitest/timeout" 3 | 4 | Maxitest.timeout = (ENV['DISABLE'] || ENV['CUSTOM'] ? false : 0.1) 5 | 6 | describe 2 do 7 | if ENV['CUSTOM'] 8 | let(:maxitest_timeout) { 0.1 } 9 | end 10 | 11 | it "x" do 12 | 1 13 | end 14 | 15 | it "times out" do 16 | sleep 1 17 | puts "DID NOT TIME OUT" 18 | end 19 | 20 | it "y" do 21 | 1 22 | end 23 | 24 | describe "sum" do 25 | before { sleep 0.04 } 26 | after do 27 | sleep 0.04 28 | puts "DID NOT TIME OUT" 29 | end 30 | 31 | it "fails" do 32 | sleep 0.04 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/cases/xit.rb: -------------------------------------------------------------------------------- 1 | require "./spec/cases/helper" 2 | 3 | describe 2 do 4 | it 'should include the xit module' do 5 | assert_includes Minitest::Spec::DSL.included_modules, Maxitest::XitMethod 6 | end 7 | 8 | xit "is even" do 9 | 2.even?.must_equal true 10 | end 11 | 12 | it "is odd" do 13 | 2.odd?.must_equal false 14 | end 15 | 16 | describe "with evil setup/teardown" do 17 | before { raise "called before" } 18 | after { raise "called after" } 19 | xit "should not be called" do 20 | raise 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/maxitest_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require 'open3' 3 | 4 | describe Maxitest do 5 | it "has a VERSION" do 6 | Maxitest::VERSION.should =~ /^[\.\da-z]+$/ 7 | end 8 | 9 | it "does not add extra output" do 10 | result = with_global_must do 11 | run_cmd("ruby spec/cases/plain.rb") 12 | end 13 | result.sub!(/seed \d+/, 'seed X') 14 | result.gsub!(/\d+\.\d+/, 'X') 15 | result.should == "Run options: --seed X\n\n# Running:\n\n..\n\nFinished in Xs, X runs/s, X assertions/s.\n\n2 runs, 2 assertions, 0 failures, 0 errors, 0 skips" 16 | end 17 | 18 | it "runs via ruby" do 19 | run_cmd("ruby spec/cases/plain.rb").should include "\n2 runs, 2 assertions" 20 | end 21 | 22 | it "supports context" do 23 | run_cmd("ruby spec/cases/context.rb").should include "\n2 runs, 2 assertions" 24 | end 25 | 26 | it "supports let!" do 27 | run_cmd("ruby spec/cases/let_bang.rb").should include "\n1 runs, 1 assertions" 28 | end 29 | 30 | it "supports let_all" do 31 | run_cmd("ruby spec/cases/let_all.rb") 32 | end 33 | 34 | it "can use static_class_order" do 35 | run_cmd("ruby spec/cases/static_class_order.rb").should include("\n0\n.0n\n.1\n.1n\n.2\n.2n\n.3\n.3n\n.4\n.4n\n.\n") 36 | end 37 | 38 | it "supports order_dependent" do 39 | run_cmd("ruby spec/cases/order_dependent.rb").should include "5 runs, 1 assertions, 0 failures, 0 errors, 0 skips" 40 | end 41 | 42 | it "has pending" do 43 | run_cmd("ruby spec/cases/pending.rb") 44 | end 45 | 46 | it "does not call xit specs" do 47 | result = run_cmd("ruby spec/cases/xit.rb -v") 48 | result.should include "(no tests defined)" 49 | result.should include "4 runs, 3 assertions, 0 failures, 0 errors, 2 skips" 50 | end 51 | 52 | it "shows short backtraces" do 53 | out = run_cmd("ruby spec/cases/raise.rb", fail: true) 54 | 55 | # Ruby 3.2 has a different backtrace it add 2 lines between the lib/maxitest/timeout.rb 56 | # and the spec/cases/raise.rb 57 | out.gsub!(/\n.*previous definition of Timeout.*/, "") 58 | 59 | # unify backtraces between ruby versions 60 | output_in = out.gsub!(/:in .*/, "") 61 | 62 | output_in.should include "TypeError: Timeout is not a module" 63 | output_in.should include 'spec/cases/raise.rb:11' 64 | 65 | # Minitest 5.21.0+ backtrace is more verbose and the short backtrace feature seems to be gone 66 | # re-test by running spec/cases/raise.rb and only loading minitest/autorun and not maxitest 67 | if Minitest::VERSION <= "5.21.0" 68 | output_in.should_not include 'lib/maxitest' 69 | end 70 | end 71 | 72 | it "has helpers" do 73 | run_cmd("ruby spec/cases/helpers.rb") 74 | end 75 | 76 | it "does not use old MiniTest constant" do 77 | Dir["lib/**/*.rb"].each do |p| 78 | File.read(p).should_not include "MiniTest" 79 | end 80 | end 81 | 82 | describe "before/after/around" do 83 | it "works" do 84 | out = run_cmd("ruby spec/cases/hook_all.rb") 85 | out.should include "Running:\n\nALL\nT1\n.T2\n.ALL-SUB\nTS1\n.TS2\n.\n\nFinished" 86 | end 87 | 88 | it "fails when using unsupported type" do 89 | with_env HOOK_TYPE: "foo" do 90 | out = run_cmd("ruby spec/cases/hook_all.rb", fail: true) 91 | out.should include "only :each and :all are supported (ArgumentError)" 92 | end 93 | end 94 | 95 | it "informs user about missing after :all" do 96 | with_env HOOK_METHOD: "after" do 97 | out = run_cmd("ruby spec/cases/hook_all.rb --seed 123", fail: true) 98 | out.should include ":all is not supported in after (ArgumentError)" 99 | end 100 | end 101 | 102 | it "supports around" do 103 | run_cmd("ruby spec/cases/around.rb").should include "\n2 runs, 3 assertions" 104 | end 105 | 106 | it "informs user about missing around :all" do 107 | with_env HOOK_TYPE: "all" do 108 | out = run_cmd("ruby spec/cases/around.rb", fail: true) 109 | out.should include "only :each or no argument is supported (ArgumentError)" 110 | end 111 | end 112 | end 113 | 114 | describe "color" do 115 | it "is color-less without tty" do 116 | run_cmd("ruby spec/cases/plain.rb").should include "\n2 runs, 2 assertions" 117 | end 118 | 119 | it "is colorful on tty" do 120 | simulate_tty do 121 | run_cmd("ruby spec/cases/plain.rb").should include "\n\e[32m2 runs, 2 assertions" 122 | end 123 | end 124 | 125 | it "is colorful without tty but --rg" do 126 | run_cmd("ruby spec/cases/plain.rb --rg").should include "\n\e[32m2 runs, 2 assertions" 127 | end 128 | 129 | it "is color-less with --no-rg and tty" do 130 | simulate_tty do 131 | run_cmd("ruby spec/cases/plain.rb --no-rg").should include "\n2 runs, 2 assertions" 132 | end 133 | end 134 | end 135 | 136 | describe "timeout" do 137 | it "times out long running tests" do 138 | result = run_cmd("ruby spec/cases/timeout.rb -v", fail: true) 139 | 140 | # 1 test takes too long and fails with a nice error message 141 | result.should include "Maxitest::Timeout::TestCaseTimeout: Test took too long to finish, aborting" 142 | 143 | # results look normal 144 | result.should include ", 2 errors," 145 | end 146 | 147 | it "times out after custom interval" do 148 | result = run_cmd("CUSTOM=1 ruby spec/cases/timeout.rb -v", fail: true) 149 | 150 | # 1 test takes too long and fails with a nice error message 151 | result.should include "Maxitest::Timeout::TestCaseTimeout: Test took too long to finish, aborting" 152 | 153 | # results look normal 154 | result.should include ", 2 errors," 155 | end 156 | 157 | it "does not time out when disabled" do 158 | result = run_cmd("DISABLE=1 ruby spec/cases/timeout.rb -v") 159 | 160 | # 1 test takes too long and fails with a nice error message 161 | result.should include "DID NOT TIME OUT" 162 | 163 | # results look normal 164 | result.should include "4 runs" 165 | end 166 | end 167 | 168 | describe "global_must" do 169 | let(:deprecated) { "DEPRECATED" } 170 | 171 | it "complain when not used" do 172 | run_cmd("ruby spec/cases/plain.rb").should include deprecated 173 | end 174 | 175 | it "does not complain when used" do 176 | with_global_must do 177 | run_cmd("ruby spec/cases/plain.rb").should_not include deprecated 178 | end 179 | end 180 | 181 | it "fails when used in threads" do 182 | with_global_must do 183 | run_cmd("ruby spec/cases/global_must.rb") 184 | end 185 | end 186 | end 187 | 188 | describe "extra threads" do 189 | it "fails on extra and passes on regular" do 190 | result = with_global_must do 191 | run_cmd("ruby spec/cases/threads.rb -v", fail: true) 192 | end 193 | result.gsub(/\d\.\d+/, "0.0").should include <<-OUT.gsub(/^\s+/, "") 194 | threads#test_0001_is fine without extra threads = 0.0 s = . 195 | threads#test_0002_fails on extra threads = 0.0 s = F 196 | threads#test_0003_can kill extra threads = 0.0 s = . 197 | threads#test_0004_can wait for extra threads = 0.0 s = . 198 | OUT 199 | end 200 | end 201 | 202 | describe "line" do 203 | let(:focus) { "Focus on failing tests:" } 204 | let(:expected_command) { "mtest spec/cases/line.rb:8" } 205 | 206 | it "prints line numbers on failed" do 207 | run_cmd("ruby spec/cases/line.rb", fail: true).should include "#{focus}\n#{expected_command}" 208 | end 209 | 210 | it "can run with line numbers" do 211 | result = run_cmd(expected_command, fail: true) 212 | result.should include("1 runs, 1 assertions, 1 failures, 0 errors, 0 skips") 213 | result.should_not include(focus) # ran 1 line, no need to reprint 214 | end 215 | 216 | it "can describe with line numbers" do 217 | result = run_cmd("mtest spec/cases/line.rb:12") 218 | result.should include("2 runs, 2 assertions, 0 failures, 0 errors, 0 skips") 219 | end 220 | 221 | it "can run with -l line numbers" do 222 | result = run_cmd("ruby spec/cases/line.rb -l 8", fail: true) 223 | result.should include("1 runs, 1 assertions, 1 failures, 0 errors, 0 skips") 224 | result.should_not include(focus) # ran 1 line, no need to reprint 225 | end 226 | 227 | it "uses colors on tty" do 228 | simulate_tty do 229 | run_cmd("ruby spec/cases/line.rb", fail: true).should include "\e[31m#{expected_command}\e[0m" 230 | end 231 | end 232 | end 233 | 234 | describe "Interrupts" do 235 | it "stops on ctrl+c and prints errors" do 236 | t = Thread.new { run_cmd("ruby spec/cases/cltr_c.rb", fail: true) } 237 | sleep 1 # let thread start 238 | kill_process_with_name("spec/cases/cltr_c.rb") 239 | output = t.value 240 | output.should include "4 runs, 1 assertions, 1 failures, 1 errors, 2 skips" # failed, error from interrupt (so you see a backtrace), rest skipped 241 | output.gsub(/:\d+:in/, ":D:in").should match /cltr_c\.rb:D:in (`|'Kernel#)sleep'/ # let you know where it happened 242 | output.should include "Interrupt:" # let you know what happened 243 | output.should include "Expected: true\n Actual: false" # not hide other errors 244 | output.scan(/BEFORE/).size.should == 2 # before calls avoided when skipping 245 | output.scan(/AFTER/).size.should == 2 # after calls avoided when skipping 246 | end 247 | 248 | it "allows Interrupts to be caught normally" do 249 | output = run_cmd("ruby spec/cases/catch_interrupt.rb") 250 | output.should include "1 runs, 1 assertions, 0 failures, 0 errors, 0 skips" 251 | end 252 | 253 | it "catches directly raised Interrupt" do 254 | output = run_cmd("ruby spec/cases/raise_interrupt.rb", fail: true) 255 | output.should include "runs, " 256 | output.should include "Interrupt: Interrupt" 257 | end 258 | 259 | it "shows backtraces when in verbose mode" do 260 | t = Thread.new { run_cmd("ruby spec/cases/cltr_c.rb -v", fail: true) } 261 | sleep 1 # let thread start 262 | kill_process_with_name("spec/cases/cltr_c.rb") 263 | output = t.value 264 | output.gsub(/:\d+:in/, ":D:in").should match /cltr_c.rb:D:in (`|'Kernel#)sleep'/ # let you know where it happened 265 | end 266 | end 267 | 268 | describe "mtest" do 269 | it "shows version" do 270 | run_cmd("mtest -v").should == Maxitest::VERSION 271 | run_cmd("mtest --version").should == Maxitest::VERSION 272 | end 273 | 274 | it "shows help" do 275 | run_cmd("mtest -h").should include "Usage:" 276 | run_cmd("mtest --help").should include "Usage:" 277 | end 278 | 279 | it "runs a single file" do 280 | run_cmd("mtest spec/cases/mtest/a_test.rb").should include "1 runs, 1 assertions, 0 failures, 0 errors, 0 skips" 281 | end 282 | 283 | it "runs a folder" do 284 | run_cmd("mtest spec/cases/mtest").should include "2 runs, 2 assertions, 0 failures, 0 errors, 0 skips" 285 | end 286 | 287 | it "runs multiple files" do 288 | run_cmd("mtest spec/cases/mtest/a_test.rb spec/cases/mtest/c.rb").should include "2 runs, 2 assertions, 0 failures, 0 errors, 0 skips" 289 | end 290 | end 291 | 292 | describe "backtraces" do 293 | it "shows no backtrace without verbose" do 294 | result = run_cmd("ruby spec/cases/error_and_failure.rb", fail: true) 295 | result.should include "error_and_failure.rb:5" 296 | result.should include "error_and_failure.rb:9" 297 | result.should_not include "minitest.rb" 298 | end 299 | 300 | it "shows backtrace for errors with verbose" do 301 | result = run_cmd("ruby spec/cases/error_and_failure.rb -n '/errors/' -v", fail: true) 302 | result.should include "1 run" 303 | result.should include "error_and_failure.rb:5" 304 | result.should include "minitest.rb" 305 | end 306 | 307 | it "shows backtrace for failures with verbose" do 308 | result = run_cmd("ruby spec/cases/error_and_failure.rb -n '/fails/' -v", fail: true) 309 | result.should include "1 run" 310 | result.should include "error_and_failure.rb:9" 311 | result.should include "minitest.rb" 312 | end 313 | end 314 | 315 | describe "parallel" do 316 | it "can run in parallel" do 317 | result = run_cmd("MT_CPU=3 ruby spec/cases/parallel.rb -v") 318 | result.should include "\n3 runs" 319 | result.should include "Finished in 0.1" 320 | end 321 | end 322 | 323 | private 324 | 325 | def simulate_tty(&block) 326 | with_env SIMULATE_TTY: 'true', &block 327 | end 328 | 329 | def with_global_must(&block) 330 | with_env GLOBAL_MUST: 'true', &block 331 | end 332 | 333 | def with_env(h) 334 | old = ENV.to_h 335 | h.each { |k, v| ENV[k.to_s] = v} 336 | yield 337 | ensure 338 | ENV.replace old 339 | end 340 | 341 | def run_cmd(command, options = {}) 342 | stdout, stderr, status = Open3.capture3(command) 343 | 344 | unless options[:keep_output] 345 | stdout += "\n" + stderr 346 | end 347 | 348 | raise "#{options[:fail] ? "SUCCESS" : "FAIL"} #{command}\n#{stdout}" if status.success? == !!options[:fail] 349 | 350 | stdout.strip 351 | end 352 | 353 | # copied from https://github.com/grosser/parallel/blob/master/spec/parallel_spec.rb#L10-L15 354 | def kill_process_with_name(file, signal='INT') 355 | running_processes = `ps -f`.split("\n").map{ |line| line.split(/\s+/) } 356 | pid_index = running_processes.detect { |p| p.include?("UID") }.index("UID") + 1 357 | parent = running_processes.detect { |p| p.include?(file) and not p.include?("sh") } 358 | raise "Unable to find parent in #{running_processes} with #{file}" unless parent 359 | `kill -s #{signal} #{parent.fetch(pid_index)}` 360 | end 361 | end 362 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "maxitest/version" 2 | require "maxitest" 3 | require "minitest" 4 | 5 | RSpec.configure do |config| 6 | config.expect_with(:rspec) { |c| c.syntax = :should } 7 | config.mock_with(:rspec) { |c| c.syntax = :should } 8 | end 9 | --------------------------------------------------------------------------------