Skip to content

Commit

Permalink
Merge pull request #2 from DataDog/lloeki/add-rake-test-task
Browse files Browse the repository at this point in the history
Add rake test task
  • Loading branch information
TonyCTHsu authored Jul 9, 2024
2 parents d7f9a0e + 5774ec2 commit 495db06
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 2 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:
docker run --platform linux/x86_64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} ruby -e 'puts RUBY_DESCRIPTION'
docker run --platform linux/x86_64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} gem --version
docker run --platform linux/x86_64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} bundle --version
docker run --platform linux/x86_64 --rm -v "${PWD}":"${PWD}" -w "${PWD}" ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} /bin/sh -c 'bundle install && ruby test/engines/test_encoding.rb'
docker run --platform linux/x86_64 --rm -v "${PWD}":"${PWD}" -w "${PWD}" ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} /bin/sh -c 'bundle install && bundle exec rake test'
# Then, build image for aarch64 which, being emulated under qemu, is slower
#
Expand All @@ -108,7 +108,7 @@ jobs:
docker run --platform linux/aarch64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} ruby -e 'puts RUBY_DESCRIPTION'
docker run --platform linux/aarch64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} gem --version
docker run --platform linux/aarch64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} bundle --version
docker run --platform linux/aarch64 --rm -v "${PWD}":"${PWD}" -w "${PWD}" ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} /bin/sh -c 'bundle install && ruby test/engines/test_encoding.rb'
docker run --platform linux/aarch64 --rm -v "${PWD}":"${PWD}" -w "${PWD}" ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} /bin/sh -c 'bundle install && bundle exec rake test'
# Finally, assemble multi-arch image for a combined push to the registry
#
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/.envrc
/vendor/bundle
/Gemfile.lock
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
source 'https://rubygems.org'

gem 'minitest'
gem 'rake'
6 changes: 6 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

# @type self: Rake::TaskLib

# load rake tasks from tasks directory
Dir.glob(File.join(__dir__ || Dir.pwd, "tasks", "**", "*.rake")) { |f| import f }
15 changes: 15 additions & 0 deletions tasks/test.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

# @type self: Rake::DSL

begin
require "minitest/test_task"
rescue LoadError
# Backport "minitest/test_task" for minitest 5.15.0
require_relative "../vendor/minitest/test_task"
end

Minitest::TestTask.create(:test) do |t|
t.warning = false
t.test_globs = ["test/**/test_*.rb"]
end
301 changes: 301 additions & 0 deletions vendor/minitest/test_task.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
require "shellwords"
require "rbconfig"
require "rake/tasklib"

module Minitest # :nodoc:

##
# Minitest::TestTask is a rake helper that generates several rake
# tasks under the main test task's name-space.
#
# task <name> :: the main test task
# task <name>:cmd :: prints the command to use
# task <name>:deps :: runs each test file by itself to find dependency errors
# task <name>:slow :: runs the tests and reports the slowest 25 tests.
#
# Examples:
#
# Minitest::TestTask.create
#
# The most basic and default setup.
#
# Minitest::TestTask.create :my_tests
#
# The most basic/default setup, but with a custom name
#
# Minitest::TestTask.create :unit do |t|
# t.test_globs = ["test/unit/**/*_test.rb"]
# t.warning = false
# end
#
# Customize the name and only run unit tests.
#
# NOTE: To hook this task up to the default, make it a dependency:
#
# task default: :unit

class TestTask < Rake::TaskLib
WINDOWS = RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ # :nodoc:

##
# Create several test-oriented tasks under +name+. Takes an
# optional block to customize variables.

def self.create name = :test, &block
task = new name
task.instance_eval(&block) if block
task.process_env
task.define
task
end

##
# Extra arguments to pass to the tests. Defaults empty but gets
# populated by a number of enviroment variables:
#
# N (-n flag) :: a string or regexp of tests to run.
# X (-e flag) :: a string or regexp of tests to exclude.
# A (arg) :: quick way to inject an arbitrary argument (eg A=--help).
#
# See #process_env

attr_accessor :extra_args

##
# The code to load the framework. Defaults to requiring
# minitest/autorun...
#
# Why do I have this as an option?

attr_accessor :framework

##
# Extra library directories to include. Defaults to %w[lib test
# .]. Also uses $MT_LIB_EXTRAS allowing you to dynamically
# override/inject directories for custom runs.

attr_accessor :libs

##
# The name of the task and base name for the other tasks generated.

attr_accessor :name

##
# File globs to find test files. Defaults to something sensible to
# find test files under the test directory.

attr_accessor :test_globs

##
# Turn on ruby warnings (-w flag). Defaults to true.

attr_accessor :warning

##
# Optional: Additional ruby to run before the test framework is loaded.

attr_accessor :test_prelude

##
# Print out commands as they run. Defaults to Rake's +trace+ (-t
# flag) option.

attr_accessor :verbose

##
# Use TestTask.create instead.

def initialize name = :test # :nodoc:
self.extra_args = []
self.framework = %(require "minitest/autorun")
self.libs = %w[lib test .]
self.name = name
self.test_globs = ["test/**/test_*.rb",
"test/**/*_test.rb"]
self.test_prelude = nil
self.verbose = Rake.application.options.trace
self.warning = true
end

##
# Extract variables from the environment and convert them to
# command line arguments. See #extra_args.
#
# Environment Variables:
#
# MT_LIB_EXTRAS :: Extra libs to dynamically override/inject for custom runs.
# N :: Tests to run (string or /regexp/).
# X :: Tests to exclude (string or /regexp/).
# A :: Any extra arguments. Honors shell quoting.
#
# Deprecated:
#
# TESTOPTS :: For argument passing, use +A+.
# N :: For parallel testing, use +MT_CPU+.
# FILTER :: Same as +TESTOPTS+.

def process_env
warn "TESTOPTS is deprecated in Minitest::TestTask. Use A instead" if
ENV["TESTOPTS"]
warn "FILTER is deprecated in Minitest::TestTask. Use A instead" if
ENV["FILTER"]
warn "N is deprecated in Minitest::TestTask. Use MT_CPU instead" if
ENV["N"] && ENV["N"].to_i > 0

lib_extras = (ENV["MT_LIB_EXTRAS"] || "").split File::PATH_SEPARATOR
self.libs[0,0] = lib_extras

extra_args << "-n" << ENV["N"] if ENV["N"]
extra_args << "-e" << ENV["X"] if ENV["X"]
extra_args.concat Shellwords.split(ENV["TESTOPTS"]) if ENV["TESTOPTS"]
extra_args.concat Shellwords.split(ENV["FILTER"]) if ENV["FILTER"]
extra_args.concat Shellwords.split(ENV["A"]) if ENV["A"]

ENV.delete "N" if ENV["N"]

# TODO? RUBY_DEBUG = ENV["RUBY_DEBUG"]
# TODO? ENV["RUBY_FLAGS"]

extra_args.compact!
end

def define # :nodoc:
desc "Run the test suite. Use N, X, A, and TESTOPTS to add flags/args."
task name do
ruby make_test_cmd, verbose:verbose
end

desc "Print out the test command. Good for profiling and other tools."
task "#{name}:cmd" do
puts "ruby #{make_test_cmd}"
end

desc "Show which test files fail when run in isolation."
task "#{name}:isolated" do
tests = Dir[*self.test_globs].uniq

# 3 seems to be the magic number... (tho not by that much)
bad, good, n = {}, [], (ENV.delete("K") || 3).to_i
file = ENV.delete("F")
times = {}

tt0 = Time.now

n.threads_do tests.sort do |path|
t0 = Time.now
output = `#{Gem.ruby} #{make_test_cmd path} 2>&1`
t1 = Time.now - t0

times[path] = t1

if $?.success?
$stderr.print "."
good << path
else
$stderr.print "x"
bad[path] = output
end
end

puts "done"
puts "Ran in %.2f seconds" % [ Time.now - tt0 ]

if file then
require "json"
File.open file, "w" do |io|
io.puts JSON.pretty_generate times
end
end

unless good.empty?
puts
puts "# Good tests:"
puts
good.sort.each do |path|
puts "%.2fs: %s" % [times[path], path]
end
end

unless bad.empty?
puts
puts "# Bad tests:"
puts
bad.keys.sort.each do |path|
puts "%.2fs: %s" % [times[path], path]
end
puts
puts "# Bad Test Output:"
puts
bad.sort.each do |path, output|
puts
puts "# #{path}:"
puts output
end
exit 1
end
end

task "#{name}:deps" => "#{name}:isolated" # now just an alias

desc "Show bottom 25 tests wrt time."
task "#{name}:slow" do
sh ["rake #{name} A=-v",
"egrep '#test_.* s = .'",
"sort -n -k2 -t=",
"tail -25"].join " | "
end
end

##
# Generate the test command-line.

def make_test_cmd globs = test_globs
tests = []
tests.concat Dir[*globs].sort.shuffle # TODO: SEED -> srand first?
tests.map! { |f| %(require "#{f}") }

runner = []
runner << test_prelude if test_prelude
runner << framework
runner.concat tests
runner = runner.join "; "

args = []
args << "-I#{libs.join(File::PATH_SEPARATOR)}" unless libs.empty?
args << "-w" if warning
args << '-e'
args << "'#{runner}'"
args << '--'
args << extra_args.map(&:shellescape)

args.join " "
end
end
end

class Work < Queue # :nodoc:
def initialize jobs = [] # :nodoc:
super()

jobs.each do |job|
self << job
end

close
end
end

class Integer # :nodoc:
def threads_do(jobs) # :nodoc:
q = Work.new jobs

self.times.map {
Thread.new do
while job = q.pop # go until quit value
yield job
end
end
}.each(&:join)
end
end

0 comments on commit 495db06

Please sign in to comment.