Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance minitest runner #410

Merged
merged 11 commits into from Jan 3, 2018
2 changes: 1 addition & 1 deletion .circleci/config.yml
Expand Up @@ -12,7 +12,7 @@ steps: &steps

- run:
name: Run CI checks
command: ./script/ci.sh
command: bin/ci.sh

- run:
name: Save coverage
Expand Down
5 changes: 5 additions & 0 deletions .simplecov
@@ -0,0 +1,5 @@
# frozen_string_literal: true

SimpleCov.command_name ENV['MINITEST_RUNNER_TEST'] || 'MiniTest'
SimpleCov.add_filter '.bundle'
SimpleCov.start
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -39,7 +39,7 @@ before_script:
- git config --local user.name 'Travis CI'

script:
- script/ci.sh
- bin/ci.sh

matrix:
fast_finish: true
Expand Down
13 changes: 5 additions & 8 deletions CONTRIBUTING.md
Expand Up @@ -32,17 +32,14 @@ abide by its terms.
task is composed of 3 subtasks: `bundle exec rake compile` &&
`bundle exec rake test` && `bundle exec rake lint`.
* If you want to run specific tests, use the provided test runner, like so:
* Specific test files. For example,
`script/minitest_runner.rb test/commands/break_test.rb`
* Specific test classes. For example,
`script/minitest_runner.rb Byebug::BreakAtLinesTestCase`
* Specific test files. For example, `bin/minitest test/commands/break_test.rb`
* Specific test classes. For example, `bin/minitest BreakAtLinesTest`
* Specific tests. For example,
`script/minitest_runner.rb test_catch_removes_specific_catchpoint`
`bin/minitest test_catch_removes_specific_catchpoint`
* Specific fully qualified tests. For example,
`script/minitest_runner.rb
BreakAtLinesTest#test_setting_breakpoint_sets_correct_fields`
`bin/minitest BreakAtLinesTest#test_setting_breakpoint_sets_correct_fields`
* You can combine any of them and you will get the union of all filters. For
example: `script/minitest_runner.rb Byebug::BreakAtLinesTestCase
example: `bin/minitest BreakAtLinesTest
test_catch_removes_specific_catchpoint`

## Code style
Expand Down
4 changes: 2 additions & 2 deletions Rakefile
Expand Up @@ -31,9 +31,9 @@ Rake::ExtensionTask.new("byebug", spec) { |ext| ext.lib_dir = "lib/byebug" }

desc "Runs the test suite"
task :test do
require_relative "script/minitest_runner"
require_relative "test/minitest_runner"

exit 1 unless MinitestRunner.new.run
exit 1 unless Byebug::MinitestRunner.new.run
end

namespace :lint do
Expand Down
5 changes: 2 additions & 3 deletions appveyor.yml
Expand Up @@ -20,11 +20,10 @@ install:

- gem install bundler -v 1.16.1 --no-document --force --conservative
- bundle config force_ruby_platform true
- bundle install --jobs 3 --retry 3 --path .bundle/gems/%ruby_version%
- bundle install --jobs 3 --retry 3 --path .bundle/gems

build_script:
- bundle exec rake clobber
- bundle exec rake compile
- bundle exec rake clobber compile

test_script:
- bundle exec rake test
Expand Down
File renamed without changes.
8 changes: 8 additions & 0 deletions bin/minitest
@@ -0,0 +1,8 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require "bundler/setup"

require_relative "../test/minitest_runner"

Byebug::MinitestRunner.new.run
File renamed without changes.
2 changes: 1 addition & 1 deletion byebug.gemspec
Expand Up @@ -19,7 +19,7 @@ Gem::Specification.new do |s|
s.required_ruby_version = ">= 2.2.0"

s.files = Dir["lib/**/*.rb", "lib/**/*.yml", "ext/**/*.[ch]", "LICENSE"]
s.bindir = "bin"
s.bindir = "exe"
s.executables = ["byebug"]
s.extra_rdoc_files = %w[CHANGELOG.md CONTRIBUTING.md README.md GUIDE.md]
s.extensions = ["ext/byebug/extconf.rb"]
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion lib/byebug/helpers/path.rb
Expand Up @@ -7,7 +7,7 @@ module Helpers
#
module PathHelper
def bin_file
@bin_file ||= File.join(root_path, "bin", "byebug")
@bin_file ||= File.join(root_path, "exe", "byebug")
end

def root_path
Expand Down
94 changes: 0 additions & 94 deletions script/minitest_runner.rb

This file was deleted.

2 changes: 1 addition & 1 deletion tasks/linter.rb
Expand Up @@ -70,7 +70,7 @@ def applicable_files
end

def clean?(file)
in_exec_folder = !(%r{\A(script|bin)/} =~ file).nil?
in_exec_folder = !(%r{\A(exe|bin)/} =~ file).nil?
executable = File.executable?(file)

(in_exec_folder && executable) || (!in_exec_folder && !executable)
Expand Down
98 changes: 98 additions & 0 deletions test/minitest_runner.rb
@@ -0,0 +1,98 @@
# frozen_string_literal: true

$LOAD_PATH.unshift(File.expand_path(File.join("..", "lib"), __dir__))
$LOAD_PATH.unshift(__dir__)

require "minitest"
require "English"
require "shellwords"
require "timeout"

module Byebug
#
# Helper class to aid running minitest
#
class MinitestRunner
def initialize
@test_suites = extract_from_argv { |cmd_arg| test_suite?(cmd_arg) }
end

def run
test_suites.each { |f| require File.expand_path(f) }

flags = ["--name=/#{filtered_methods.join('|')}/"]

run_with_timeout(flags)
end

private

def run_with_timeout(flags)
Timeout.timeout(180) { Minitest.run(flags + $ARGV) }
rescue Timeout::Error
false
end

def runnables
Minitest::Runnable.runnables
end

def test_suite?(str)
all_test_suites.include?(str)
end

def test_suites
return all_test_suites if @test_suites.empty?

@test_suites
end

def test_methods(str)
if str =~ /test_.*/
filter_runnables_by_method(str)
else
filter_runnables_by_class(str)
end
end

def filter_runnables_by_method(str)
filter_runnables do |runnable|
"#{runnable}##{str}" if runnable.runnable_methods.include?(str)
end
end

def filter_runnables_by_class(str)
filter_runnables do |runnable|
runnable.runnable_methods if runnable.name == "Byebug::#{str}"
end
end

def filter_runnables
selected = runnables.flat_map do |runnable|
yield(runnable)
end.compact

return unless selected.any?

selected
end

def filtered_methods
@filtered_methods ||= extract_from_argv do |cmd_arg|
test_methods(cmd_arg)
end
end

def all_test_suites
Dir.glob("test/**/*_test.rb")
end

def extract_from_argv
matching, non_matching = $ARGV.partition { |arg| yield(arg) }

$ARGV.replace(non_matching)

matching
end
end
end
95 changes: 95 additions & 0 deletions test/minitest_runner_test.rb
@@ -0,0 +1,95 @@
# frozen_string_literal: true

require "test_helper"

module Byebug
class MinitestRunnerTest < Minitest::Test
def test_runs
output = run_minitest_runner(
{ "MINITEST_RUNNER_TEST" => __method__.to_s },
"test/debugger_alias_test.rb"
)

assert_includes output, "\n.\n"
end

def test_per_test_class
output = run_minitest_runner(
{ "MINITEST_RUNNER_TEST" => __method__.to_s },
"DebuggerAliasTest"
)

assert_includes output, "\n.\n"
end

def test_per_test
output = run_minitest_runner(
{ "MINITEST_RUNNER_TEST" => __method__.to_s },
"test_aliases_debugger_to_byebug"
)

assert_includes output, "\n.\n"
end

def test_combinations
output = run_minitest_runner(
{ "MINITEST_RUNNER_TEST" => __method__.to_s },
"DebuggerAliasTest",
"test_script_processor_clears_history"
)

assert_includes output, "\n..\n"
end

def test_with_verbose_option
output = run_minitest_runner(
{ "MINITEST_RUNNER_TEST" => __method__.to_s },
"DebuggerAliasTest",
"--verbose"
)

assert_includes \
output,
"Byebug::DebuggerAliasTest#test_aliases_debugger_to_byebug = 0.00 s = ."

assert_includes \
output,
"Run options: --name=/DebuggerAliasTest/ --verbose"
end

def test_with_seed_option
output = run_minitest_runner(
{ "MINITEST_RUNNER_TEST" => __method__.to_s },
"DebuggerAliasTest",
"--seed=37"
)

assert_includes output, "\n.\n"

assert_includes \
output,
"Run options: --name=/DebuggerAliasTest/ --seed=37"
end

private

def run_minitest_runner(env, *args)
out, = capture_subprocess_io do
assert_equal true, system(
env.merge("RUBYOPT" => "-rsimplecov"),
*binstub,
*args
)
end

out
end

def binstub
cmd = "bin/minitest"
return [cmd] unless windows?

%W[#{RbConfig.ruby} #{cmd}]
end
end
end