├── .gemtest ├── .gitignore ├── Gemfile ├── lib └── minitest │ ├── rg.rb │ └── rg_plugin.rb ├── .autotest ├── Manifest.txt ├── scripts ├── run_skip ├── run_error ├── run_fail ├── run_pass ├── run_color └── run_no_color ├── .rubocop.yml ├── CHANGELOG.rdoc ├── LICENSE ├── .github └── workflows │ └── ci.yml ├── Rakefile ├── minitest-rg.gemspec ├── README.rdoc └── test └── test_rg.rb /.gemtest: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | doc 2 | pkg 3 | vendor 4 | Gemfile.lock 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | gemspec 6 | -------------------------------------------------------------------------------- /lib/minitest/rg.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "minitest" 4 | 5 | Minitest.load_plugins 6 | Minitest::RG.rg! color: $stdout.tty? 7 | -------------------------------------------------------------------------------- /.autotest: -------------------------------------------------------------------------------- 1 | # -*- ruby -*- 2 | 3 | require "autotest/restart" 4 | 5 | Autotest.add_hook :initialize do |at| 6 | at.testlib = "minitest/autorun" 7 | at.find_directories = ARGV unless ARGV.empty? 8 | end 9 | -------------------------------------------------------------------------------- /Manifest.txt: -------------------------------------------------------------------------------- 1 | CHANGELOG.rdoc 2 | LICENSE 3 | Manifest.txt 4 | README.rdoc 5 | Rakefile 6 | lib/minitest/rg.rb 7 | lib/minitest/rg_plugin.rb 8 | minitest-rg.gemspec 9 | scripts/run_color 10 | scripts/run_error 11 | scripts/run_fail 12 | scripts/run_no_color 13 | scripts/run_pass 14 | scripts/run_skip 15 | test/test_rg.rb 16 | -------------------------------------------------------------------------------- /scripts/run_skip: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | gem "minitest" 5 | require "minitest/autorun" 6 | 7 | $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" 8 | require "minitest/rg" 9 | 10 | class TestPass < Minitest::Test 11 | def test_skip 12 | skip "Skip should be CYAN" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /scripts/run_error: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | gem "minitest" 5 | require "minitest/autorun" 6 | 7 | $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" 8 | require "minitest/rg" 9 | 10 | class TestPass < Minitest::Test 11 | def test_error 12 | raise "Error should be YELLOW" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /scripts/run_fail: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | gem "minitest" 5 | require "minitest/autorun" 6 | 7 | $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" 8 | require "minitest/rg" 9 | 10 | class TestPass < Minitest::Test 11 | def test_fail 12 | assert_equal 1, 2, "Failure should be RED" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /scripts/run_pass: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | gem "minitest" 5 | require "minitest/autorun" 6 | 7 | $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" 8 | require "minitest/rg" 9 | 10 | class TestPass < Minitest::Test 11 | def test_pass 12 | assert_equal 1, 1, "Pass should be GREEN" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /scripts/run_color: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | gem "minitest" 5 | 6 | ARGV << "--rg" 7 | 8 | require "minitest/autorun" 9 | 10 | $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" 11 | require "minitest/rg" 12 | 13 | class TestPass < Minitest::Test 14 | def test_pass 15 | assert_equal 1, 1, "Pass should be GREEN" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /scripts/run_no_color: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | gem "minitest" 5 | 6 | ARGV << "--no-rg" 7 | 8 | require "minitest/autorun" 9 | 10 | $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" 11 | require "minitest/rg" 12 | 13 | class TestPass < Minitest::Test 14 | def test_pass 15 | assert_equal 1, 1, "Pass should be GREEN" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | NewCops: enable 3 | SuggestExtensions: false 4 | TargetRubyVersion: 2.7 5 | Exclude: 6 | - "doc/**/*" 7 | - "pkg/**/*" 8 | - "vendor/**/*" 9 | - "minitest-rg.gemspec" 10 | - "Rakefile" 11 | - "Gemfile" 12 | 13 | 14 | Layout/SpaceAroundOperators: 15 | Enabled: false 16 | Metrics/AbcSize: 17 | Max: 25 18 | Metrics/CyclomaticComplexity: 19 | Max: 10 20 | Metrics/MethodLength: 21 | Max: 15 22 | Style/Documentation: 23 | Enabled: false 24 | Metrics/PerceivedComplexity: 25 | Max: 10 26 | Style/MethodDefParentheses: 27 | EnforcedStyle: require_no_parentheses 28 | Style/StringLiterals: 29 | EnforcedStyle: double_quotes 30 | -------------------------------------------------------------------------------- /CHANGELOG.rdoc: -------------------------------------------------------------------------------- 1 | === 5.3.0 / 2023-10-31 2 | 3 | Add +--no-rg+ flag. (grosser) 4 | 5 | Fix loading issue on newer Minitest versions. (zenspider/dazuma) 6 | 7 | === 5.2.0 / 2015-08-14 8 | 9 | Only output red/green colors when TTY is supported. (cbougher) 10 | Add +--rg+ flag. 11 | 12 | === 5.1.0 / 2014-04-16 13 | 14 | Activate plugin only when required, and fix circular loading issue. (grosser) 15 | 16 | === 5.0.0 / 2013-11-13 17 | 18 | Updated for Minitest 5.0 19 | 20 | === 1.1.1 / 2013-04-23 21 | 22 | Set minitest dependency. 23 | 24 | === 1.1.0 / 2012-09-20 25 | 26 | Color summary output. 27 | 28 | === 1.0.0 / 2012-07-28 29 | 30 | New release! Thanks to previous maintainer Enrico Thierbach for allowing the project to continue without him. 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Mike Moore 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 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ruby CI 3 | 4 | on: 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | 10 | jobs: 11 | test: 12 | name: test 13 | 14 | runs-on: ubuntu-latest 15 | if: | 16 | !( contains(github.event.pull_request.title, '[ci skip]') 17 | || contains(github.event.pull_request.title, '[skip ci]')) 18 | 19 | strategy: 20 | matrix: 21 | ruby-version: ['3.2', '3.1', '3.0', '2.7'] 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Set up Ruby ${{ matrix.ruby-version }} 27 | uses: ruby/setup-ruby@v1 28 | with: 29 | ruby-version: ${{ matrix.ruby-version }} 30 | bundler-cache: true 31 | 32 | - name: Run tests 33 | run: bundle exec rake test 34 | 35 | rubocop: 36 | name: rubocop 37 | 38 | runs-on: ubuntu-latest 39 | if: | 40 | !( contains(github.event.pull_request.title, '[ci skip]') 41 | || contains(github.event.pull_request.title, '[skip ci]')) 42 | 43 | steps: 44 | - uses: actions/checkout@v4 45 | 46 | - uses: ruby/setup-ruby@v1 47 | with: 48 | ruby-version: '2.7' 49 | bundler-cache: true 50 | 51 | - name: Run Rubocop 52 | run: bundle exec rubocop 53 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # -*- ruby -*- 4 | 5 | require "rubygems" 6 | require "hoe" 7 | 8 | Hoe.plugin :gemspec # `gem install hoe-gemspec` 9 | Hoe.plugin :git # `gem install hoe-git` 10 | Hoe.plugin :minitest # `gem install hoe-minitest` 11 | 12 | Hoe.spec "minitest-rg" do 13 | developer "Mike Moore", "mike@blowmage.com" 14 | 15 | self.summary = "Red/Green for Minitest" 16 | self.description = "Colored red/green output for Minitest" 17 | license "MIT" 18 | 19 | self.readme_file = "README.rdoc" 20 | self.history_file = "CHANGELOG.rdoc" 21 | 22 | dependency "minitest", "~> 5.0" 23 | dependency "rubocop", "~> 1.57.0", :dev 24 | end 25 | 26 | # vim: syntax=ruby 27 | 28 | desc "Run all test type scripts" 29 | task :sanity do 30 | puts "="*72 31 | puts "Running a \e[32mpassing\e[0m test:" 32 | puts "="*72 33 | puts 34 | puts capture_output("pass") 35 | puts 36 | 37 | puts "="*72 38 | puts "Running a \e[31mfailing\e[0m test:" 39 | puts "="*72 40 | puts 41 | puts capture_output("fail") 42 | puts 43 | 44 | puts "="*72 45 | puts "Running a \e[33merroring\e[0m test:" 46 | puts "="*72 47 | puts 48 | puts capture_output("error") 49 | puts 50 | 51 | puts "="*72 52 | puts "Running a \e[36mskipped\e[0m test:" 53 | puts "="*72 54 | puts 55 | puts capture_output("skip") 56 | puts 57 | end 58 | 59 | def capture_output command 60 | os = `uname -s`.chomp 61 | if os.include?("BSD") || os.include?("Darwin") 62 | `script -q /dev/null ./scripts/run_#{command}` 63 | else 64 | `script -q -c ./scripts/run_#{command} /dev/null` 65 | end 66 | end 67 | 68 | require "rubocop/rake_task" 69 | 70 | RuboCop::RakeTask.new 71 | -------------------------------------------------------------------------------- /minitest-rg.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # stub: minitest-rg 5.3.0.20231031004948 ruby lib 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "minitest-rg".freeze 6 | s.version = "5.3.0.20231031004948" 7 | 8 | s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= 9 | s.metadata = { "bug_tracker_uri" => "https://github.com/minitest/minitest-rg/issues", "homepage_uri" => "https://github.com/minitest/minitest-rg" } if s.respond_to? :metadata= 10 | s.require_paths = ["lib".freeze] 11 | s.authors = ["Mike Moore".freeze] 12 | s.date = "2023-10-31" 13 | s.description = "Colored red/green output for Minitest".freeze 14 | s.email = ["mike@blowmage.com".freeze] 15 | s.extra_rdoc_files = ["CHANGELOG.rdoc".freeze, "Manifest.txt".freeze, "README.rdoc".freeze] 16 | s.files = [".autotest".freeze, ".gemtest".freeze, ".rubocop.yml".freeze, "CHANGELOG.rdoc".freeze, "Gemfile".freeze, "LICENSE".freeze, "Manifest.txt".freeze, "README.rdoc".freeze, "Rakefile".freeze, "lib/minitest/rg.rb".freeze, "lib/minitest/rg_plugin.rb".freeze, "minitest-rg.gemspec".freeze, "scripts/run_error".freeze, "scripts/run_fail".freeze, "scripts/run_pass".freeze, "scripts/run_skip".freeze, "test/test_rg.rb".freeze] 17 | s.homepage = "https://github.com/minitest/minitest-rg".freeze 18 | s.licenses = ["MIT".freeze] 19 | s.rdoc_options = ["--main".freeze, "README.rdoc".freeze] 20 | s.rubygems_version = "3.4.12".freeze 21 | s.summary = "Red/Green for Minitest".freeze 22 | 23 | s.specification_version = 4 24 | 25 | s.add_runtime_dependency(%q.freeze, ["~> 5.0"]) 26 | s.add_development_dependency(%q.freeze, ["~> 1.57.0"]) 27 | s.add_development_dependency(%q.freeze, [">= 4.0", "< 7"]) 28 | s.add_development_dependency(%q.freeze, ["~> 4.0"]) 29 | end 30 | -------------------------------------------------------------------------------- /lib/minitest/rg_plugin.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "minitest" 4 | 5 | module Minitest 6 | def self.plugin_rg_options opts, _options # :nodoc: 7 | opts.on "--[no-]rg", "Add red/green to test output." do |bool| 8 | RG.rg! color: bool 9 | end 10 | end 11 | 12 | def self.plugin_rg_init options # :nodoc: 13 | return unless RG.rg? 14 | 15 | io = RG.new options[:io] 16 | 17 | reporter.reporters.grep(Minitest::Reporter).each { |rep| rep.io = io } 18 | end 19 | 20 | class RG 21 | VERSION = "5.3.0" 22 | 23 | COLORS = { 24 | "." => "\e[32m.\e[0m", 25 | "E" => "\e[33mE\e[0m", 26 | "F" => "\e[31mF\e[0m", 27 | "S" => "\e[36mS\e[0m" 28 | }.freeze 29 | 30 | attr_reader :io, :colors 31 | 32 | def self.rg! color: true 33 | @rg = color 34 | end 35 | 36 | def self.rg? 37 | @rg ||= false 38 | end 39 | 40 | def initialize io, colors = COLORS 41 | @io = io 42 | @colors = colors 43 | end 44 | 45 | def print output 46 | io.print(colors[output] || output) 47 | end 48 | 49 | def puts output = nil 50 | return io.puts if output.nil? 51 | 52 | if output =~ /(\d+) failures, (\d+) errors/ 53 | if Regexp.last_match[1] != "0" || Regexp.last_match[2] != "0" 54 | io.puts "\e[31m#{output}\e[0m" 55 | else 56 | io.puts "\e[32m#{output}\e[0m" 57 | end 58 | else 59 | io.puts output 60 | end 61 | end 62 | 63 | def method_missing msg, *args 64 | return io.send(msg, *args) if io.respond_to? msg 65 | 66 | super 67 | end 68 | 69 | def respond_to_missing? method_name, include_all = false 70 | return true if io.respond_to? method_name, include_all 71 | 72 | super 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = minitest-rg 2 | 3 | home :: https://github.com/minitest/minitest-rg 4 | bugs :: https://github.com/minitest/minitest-rg/issues 5 | 6 | {rdoc-image:https://github.com/minitest/minitest-rg/actions/workflows/ci.yml/badge.svg}[https://github.com/minitest/minitest-rg/actions/workflows/ci.yml] 7 | {rdoc-image:https://github.com/minitest/minitest-rg/actions/workflows/github-code-scanning/codeql/badge.svg}[https://github.com/minitest/minitest-rg/actions/workflows/github-code-scanning/codeql] 8 | 9 | Adds red/green color to your Minitest output. 10 | 11 | == Install 12 | 13 | gem install minitest-rg 14 | 15 | == Configure 16 | 17 | Add the following to your test helper: 18 | 19 | require "minitest/rg" 20 | 21 | Or you can run your tests with the `--rg` flag: 22 | 23 | $ ruby test/test_the_thing.rb --rg 24 | 25 | Or if you are using a rake task to run your tests you can set the `--rg` flag in the `TESTOPTS` environment variable. 26 | 27 | $ rake test TESTOPTS="--rg" 28 | 29 | == Get Involved 30 | 31 | Join the Github discussions to get help or offer suggestions: 32 | 33 | https://github.com/minitest/minitest-rg/discussions 34 | 35 | Report bugs by opening an issue: 36 | 37 | https://github.com/minitest/minitest-rg/issues 38 | 39 | Contribute changes by creating a pull request: 40 | 41 | https://github.com/minitest/minitest-rg/pulls 42 | 43 | == License 44 | 45 | Copyright (c) 2023 Mike Moore 46 | 47 | Permission is hereby granted, free of charge, to any person obtaining 48 | a copy of this software and associated documentation files (the 49 | "Software"), to deal in the Software without restriction, including 50 | without limitation the rights to use, copy, modify, merge, publish, 51 | distribute, sublicense, and/or sell copies of the Software, and to 52 | permit persons to whom the Software is furnished to do so, subject to 53 | the following conditions: 54 | 55 | The above copyright notice and this permission notice shall be 56 | included in all copies or substantial portions of the Software. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 59 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 60 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 61 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 62 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 63 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 64 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 65 | -------------------------------------------------------------------------------- /test/test_rg.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/setup" 4 | 5 | $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib" 6 | 7 | require "minitest/autorun" 8 | require "minitest/rg" 9 | 10 | class TestRG < Minitest::Test 11 | def test_pass_is_green 12 | output = capture_output "pass" 13 | 14 | exp_dot = "\e[32m.\e[0m" 15 | exp_sum = "\e[32m1 runs, 1 assertions, 0 failures, 0 errors, 0 skips\e[0m" 16 | 17 | assert_match exp_dot, output, "Passing tests are GREEN" 18 | assert_match exp_sum, output, "Passing summary is GREEN" 19 | end 20 | 21 | def test_fail_is_red 22 | output = capture_output "fail" 23 | 24 | exp_dot = "\e[31mF\e[0m" 25 | exp_sum = "\e[31m1 runs, 1 assertions, 1 failures, 0 errors, 0 skips\e[0m" 26 | 27 | assert_match exp_dot, output, "Failing tests are RED" 28 | assert_match exp_sum, output, "Failing summary is RED" 29 | end 30 | 31 | def test_error_is_yellow 32 | output = capture_output "error" 33 | 34 | exp_dot = "\e[33mE\e[0m" 35 | exp_sum = "\e[31m1 runs, 0 assertions, 0 failures, 1 errors, 0 skips\e[0m" 36 | 37 | assert_match exp_dot, output, "Erroring tests are YELLOW" 38 | assert_match exp_sum, output, "Erroring summary is YELLOW" 39 | end 40 | 41 | def test_skip_is_cyan 42 | output = capture_output "skip" 43 | 44 | exp_dot = "\e[36mS\e[0m" 45 | exp_sum = "\e[32m1 runs, 0 assertions, 0 failures, 0 errors, 1 skips\r\n" \ 46 | "\r\nYou have skipped tests. Run with --verbose for details.\e[0m" 47 | 48 | assert_match exp_dot, output, "Skipped tests are CYAN" 49 | assert_match exp_sum, output, "Skipped summary is CYAN" 50 | end 51 | 52 | def test_no_colors_via_option 53 | output = capture_output "no_color" 54 | assert_includes output, "\n1 runs" 55 | end 56 | 57 | def test_force_colors_via_option 58 | output = capture_output "color", tty: false 59 | assert_includes output, "\n\e[32m1 runs" 60 | end 61 | 62 | def test_no_color_without_tty 63 | output = capture_output "pass", tty: false 64 | assert_includes output, "\n1 runs" 65 | end 66 | 67 | def capture_output command, tty: true 68 | if tty 69 | os = `uname -s`.chomp 70 | if os.include?("BSD") || os.include?("Darwin") 71 | `script -q /dev/null ./scripts/run_#{command}` 72 | else 73 | `script -q -c ./scripts/run_#{command} /dev/null` 74 | end 75 | else 76 | `scripts/run_#{command}