Skip to content

Commit 8106847

Browse files
committed
📈 Add benchmark rake task to compare gem versions
To compare against the latest release: ```console $ rake benchmarks:compare ``` To compare against several different versions: ```console $ rake benchmarks:compare[0.1.1,0.2.2,0.3.7,0.4.2] ``` To filter or apply any other `benchmark-driver` arguments: ```console $ rake benchmarks:compare BENCHMARK_ARGS="--filter 'fetch|search'" ```
1 parent 095d396 commit 8106847

File tree

7 files changed

+119
-53
lines changed

7 files changed

+119
-53
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
/spec/reports/
99
/tmp/
1010
/Gemfile.lock
11+
benchmarks/Gemfile*

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ gem "rake"
88
gem "rdoc"
99
gem "test-unit"
1010
gem "test-unit-ruby-core", git: "https://github.com/ruby/test-unit-ruby-core"
11+
12+
gem "benchmark-driver"

benchmarks/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Gemfile-*.lock

benchmarks/generate_parser_benchmarks

Lines changed: 0 additions & 52 deletions
This file was deleted.

benchmarks/parser.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ benchmark:
8181
response = load_response("../test/net/imap/fixtures/response_parser/body_structure_responses.yml",
8282
"test_bodystructure_bug8167_delivery_status_with_extra_data")
8383
script: parser.parse(response)
84+
- name: bodystructure_extension_fields
85+
prelude: |2
86+
response = load_response("../test/net/imap/fixtures/response_parser/body_structure_responses.yml",
87+
"test_bodystructure_extension_fields")
88+
script: parser.parse(response)
8489
- name: bodystructure_mixed_boundary
8590
prelude: |2
8691
response = load_response("../test/net/imap/fixtures/response_parser/body_structure_responses.yml",
@@ -156,6 +161,16 @@ benchmark:
156161
response = load_response("../test/net/imap/fixtures/response_parser/quirky_behaviors.yml",
157162
"test_invalid_noop_response_is_ignored")
158163
script: parser.parse(response)
164+
- name: invalid_noop_response_with_numeric_prefix
165+
prelude: |2
166+
response = load_response("../test/net/imap/fixtures/response_parser/quirky_behaviors.yml",
167+
"test_invalid_noop_response_with_numeric_prefix")
168+
script: parser.parse(response)
169+
- name: invalid_noop_response_with_unparseable_data
170+
prelude: |2
171+
response = load_response("../test/net/imap/fixtures/response_parser/quirky_behaviors.yml",
172+
"test_invalid_noop_response_with_unparseable_data")
173+
script: parser.parse(response)
159174
- name: invalid_search_response_multiple_result_with_trailing_space
160175
prelude: |2
161176
response = load_response("../test/net/imap/fixtures/response_parser/search_responses.yml",

net-imap.gemspec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ Gem::Specification.new do |spec|
2525
# Specify which files should be added to the gem when it is released.
2626
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
2727
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
28-
`git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(bin|test|spec|features|rfcs)/}) }
28+
`git ls-files -z 2>/dev/null`.split("\x0")
29+
.reject {|f| f.match(%r{^(bin|test|spec|benchmarks|features|rfcs)/}) }
2930
end
3031
spec.bindir = "exe"
3132
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }

rakelib/benchmarks.rake

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# frozen_string_literal: true
2+
3+
PARSER_TEST_FIXTURES = FileList.new "test/net/imap/fixtures/response_parser/*.yml"
4+
CLOBBER.include "benchmarks/parser.yml"
5+
CLEAN.include "benchmarks/Gemfile-*"
6+
7+
BENCHMARK_INIT = <<RUBY
8+
require "yaml"
9+
require "net/imap"
10+
11+
def load_response(file, name)
12+
YAML.unsafe_load_file(file).dig(:tests, name, :response)
13+
.force_encoding "ASCII-8BIT" \\
14+
or abort "ERRORO: missing %p fixture data in %p" % [name, file]
15+
end
16+
17+
parser = Net::IMAP::ResponseParser.new
18+
RUBY
19+
20+
PER_BENCHMARK_PRELUDE = <<RUBY
21+
response = load_response(%p,
22+
%p)
23+
RUBY
24+
25+
file "benchmarks/parser.yml" => PARSER_TEST_FIXTURES do |t|
26+
require "yaml"
27+
require "pathname"
28+
require "net/imap"
29+
30+
path = Pathname.new(__dir__) / "../test/net/imap/fixtures/response_parser"
31+
files = path.glob("*.yml")
32+
tests = files.flat_map {|file|
33+
file.read
34+
.gsub(%r{([-:]) !ruby/struct:\S+}) { $1 }
35+
.then {
36+
YAML.safe_load(_1, filename: file,
37+
permitted_classes: [Symbol, Regexp], aliases: true)
38+
}
39+
.fetch(:tests)
40+
.select {|test_name, test|
41+
:parser_assert_equal == test.fetch(:test_type) {
42+
test.key?(:expected) ? :parser_assert_equal : :parser_pending
43+
}
44+
}
45+
.map {|test_name, _|
46+
[file.relative_path_from(__dir__).to_s, test_name.to_s]
47+
}
48+
}
49+
50+
benchmarks = tests.map {|file, fixture_name|
51+
{"name" => fixture_name.delete_prefix("test_"),
52+
"prelude" => PER_BENCHMARK_PRELUDE % [file, fixture_name],
53+
"script" => "parser.parse(response)"}
54+
}
55+
.sort_by { _1["name"] }
56+
57+
YAML.dump({"prelude" => BENCHMARK_INIT, "benchmark" => benchmarks})
58+
.then { File.write t.name, _1 }
59+
end
60+
61+
namespace :benchmarks do
62+
desc "Generate benchmarks from fixture data"
63+
task :generate => "benchmarks/parser.yml"
64+
65+
desc "run the parser benchmarks comparing multiple gem versions"
66+
task :compare => :generate do |task, args|
67+
cd Pathname.new(__dir__) + ".."
68+
current = `git describe --tags --dirty`.chomp
69+
current = "dev" if current.empty?
70+
versions = args.to_a
71+
if versions.empty?
72+
latest = %x{git describe --tags --abbrev=0 --match 'v*.*.*'}.chomp
73+
versions = latest.empty? ? [] : [latest.delete_prefix("v")]
74+
end
75+
versions = versions.to_h { [_1, "Gemfile-v#{_1}"] }
76+
cd "benchmarks" do
77+
versions.each do |version, gemfile|
78+
File.write gemfile, <<~RUBY
79+
# frozen_string_literal: true
80+
source "https://rubygems.org"
81+
gem "net-imap", #{version.dump}
82+
RUBY
83+
end
84+
versions = {current => "../Gemfile" , **versions}.map {
85+
"%s::/usr/bin/env BUNDLE_GEMFILE=%s ruby" % _1
86+
}.join(";")
87+
88+
extra = ENV.fetch("BENCHMARK_ARGS", "").shellsplit
89+
90+
sh("benchmark-driver",
91+
"--bundler",
92+
"-e", versions,
93+
"parser.yml",
94+
*extra)
95+
end
96+
end
97+
98+
end

0 commit comments

Comments
 (0)