minitest-perf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .minitest-perf*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in minitest-perf.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Albert Llop
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # MiniTest::Perf
2
+
3
+ `minitest-perf` stores information about your minitest runs so that you can later analyze and see where
4
+ the pain points are. It's in pretty early beta status, so I'm more than glad to receive bug reportsand
5
+ feature suggestions.
6
+
7
+ For now it should work with later versions of MiniTest 4, since the biggest test suite I have available
8
+ is with this version. MiniTest 5 probably doesn't work yet.
9
+
10
+ ## How it works
11
+
12
+ Every test that is executed is stored in a sqlite database for later query. Just require `minitet/perf`
13
+ somewhere in your test_helper, and you're good to go. Internally, `minitest/perf` includes itself as
14
+ just another module and starts feeding the sqlite database.
15
+
16
+ The sqlite database is stored by default as `.minitest-perf.db`.
17
+
18
+ An executable is provided that does prints a very basic analysis of the information it already has,
19
+ but the sqlite database is perfectly normal, so you can do your own queries.
20
+
21
+ This is an example:
22
+
23
+ ```
24
+ $ be minitest-perf
25
+ Slowest individual tests
26
+
27
+ 190.98ms | JobsCategoriesControllerLoggedOutTest#test_show_action_should_render_show_page_without_contact_distance_call_for_logged_out_users
28
+ 161.12ms | APostingFrozenNotificationActiveRecordTestCasTest#test_should_log_the_exception_and_any_additional_data_when_a_PerlBackend::Error_is_thrown
29
+ 147.39ms | VersionsTest#test_rubygems_version
30
+ 132.52ms | JobsCategoriesControllerLoggedInTest#test_show_action_should_show__all__postings_of_a_category_for_logged_in_users
31
+ 124.21ms | JobsCategoriesControllerLoggedInTest#test_should_show_all_postings_of_selected_subcategories
32
+ 122.99ms | OrderReachedAboutToRunOutOfPostingsBeforeLast24HoursTest#test_should_return_false_if_at_amount_left_is_15_percent_of_amount,_and_last_posting_created_within_24_hours
33
+ 121.98ms | JobsCategoriesControllerLoggedOutTest#test_show_action_should_show_all_public_postings_for_logged_out_users
34
+ 116.97ms | JobsCategoriesControllerUserDependantLanguageTest#test_should_show_postings_in_categories_user's_browser_language_and_default_language_en_logged-out
35
+ 110.12ms | JobsCategoriesControllerUserDependantLanguageTest#test_caching_of_cities_is_dependant_on_the_user's_language_logged-in
36
+ 109.20ms | PostingsControllerNewActionTest#test_should_take_the_posters_business_country_as_default_country_for_new_postings_on_GET
37
+
38
+
39
+ Slowest test suites
40
+
41
+ 116.07ms | 3 | JobsCategoriesControllerLoggedOutTest
42
+ 113.54ms | 2 | JobsCategoriesControllerUserDependantLanguageTest
43
+ 98.19ms | 1 | PublishToCompanyPostingControllerTest
44
+ 97.81ms | 1 | ShowActionPagingForPostingSearchTest
45
+ 79.66ms | 1 | PostingMostViewedNamedScopeTest
46
+ 78.92ms | 3 | OrderReachedAboutToRunOutOfPostingsBeforeLast24HoursTest
47
+ 68.92ms | 1 | Jobs::ProductAvailabilityTimePeriodOverlapTest
48
+ 68.53ms | 1 | TheMotherOfAllBillingCycleTests
49
+ 66.77ms | 4 | CategoriesTest
50
+ 62.60ms | 2 | RecommendationsActivityBoxFallbackTest
51
+ ```
52
+
53
+ ## TODO
54
+
55
+ These are nice to haves I'd like to implement in the future:
56
+
57
+ * Customizable db file
58
+ * Store also in mysql
59
+ * Web interface
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.pattern = "test/**/*_test.rb"
7
+ t.verbose = true
8
+ end
9
+ Rake::Task['test'].comment = "Run all tests"
10
+
11
+ task :default => :test
data/bin/minitest-perf ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'minitest/perf'
4
+
5
+ Minitest::Perf::Cli.new.run
@@ -0,0 +1,21 @@
1
+ module MiniTest
2
+ module Perf
3
+ class Cli
4
+ def run
5
+ puts "Slowest individual tests"
6
+ puts
7
+ Statistics.slowest_tests.each do |suite, test_name, time|
8
+ printf "% 12.2fms | %s#%s\n", time * 1000, suite, test_name
9
+ end
10
+
11
+ puts
12
+ puts
13
+ puts "Slowest test suites"
14
+ puts
15
+ Statistics.slowest_suites.each do |suite_name, tests_count, avg_test_time|
16
+ printf "% 12.2fms | % 4i | %s\n", avg_test_time * 1000, tests_count, suite_name
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ require 'sqlite3'
2
+
3
+ module MiniTest
4
+ module Perf
5
+ module Persistence
6
+ class << self
7
+ def write(test)
8
+ db.execute <<-SQL, [test.run.to_s, test.suite, test.name, test.total]
9
+ INSERT INTO tests (run, suite, name, total)
10
+ VALUES (?, ?, ?, ?)
11
+ SQL
12
+ end
13
+
14
+ def read_tests
15
+ db.execute("SELECT * FROM TESTS").map do |run, suite, name, total|
16
+ Test.new(run, suite, name, total)
17
+ end
18
+ end
19
+
20
+ def sql(query)
21
+ db.execute query
22
+ end
23
+
24
+ private
25
+
26
+ def db
27
+ @@db ||= begin
28
+ db = SQLite3::Database.new ".minitest-perf.db"
29
+ db.execute <<-SQL
30
+ create table if not exists tests (
31
+ run varchar(255),
32
+ suite varchar(255),
33
+ name varchar(255),
34
+ total float
35
+ );
36
+ SQL
37
+ db
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,19 @@
1
+ module MiniTest
2
+ module Perf
3
+ module Plugin
4
+ def before_setup
5
+ current_perf_run.start(self.class.name, __name__)
6
+ super
7
+ end
8
+
9
+ def after_teardown
10
+ super
11
+ current_perf_run.finish(self.class.name, __name__)
12
+ end
13
+
14
+ def current_perf_run
15
+ @@current_perf_run ||= MiniTest::Perf::Run.new
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ module MiniTest
2
+ module Perf
3
+ class Run
4
+ attr_reader :tests
5
+
6
+ def initialize(started_at = Time.now)
7
+ @tests = []
8
+ @started_at = started_at
9
+ end
10
+
11
+ def start(suite, name, now = Time.now)
12
+ @test_start = now
13
+ end
14
+
15
+ def finish(suite, name, now = Time.now)
16
+ test_total = now - @test_start
17
+
18
+ add_test Test.new(
19
+ @started_at, suite, name, test_total
20
+ ).tap(&:persist)
21
+ end
22
+
23
+ def add_test(test)
24
+ @tests << test
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ module MiniTest
2
+ module Perf
3
+ module Statistics
4
+ class << self
5
+ def slowest_tests
6
+ Persistence.sql(<<-SQL)
7
+ SELECT suite, name, avg(total) as avg_total
8
+ FROM tests
9
+ GROUP BY suite, name
10
+ ORDER BY avg_total DESC
11
+ LIMIT 10
12
+ SQL
13
+ end
14
+
15
+ def slowest_suites
16
+ Persistence.sql(<<-SQL)
17
+ SELECT suite, AVG(test_count), AVG(avg_total_per_test_run) as avg_total
18
+ FROM (
19
+ SELECT run, suite, COUNT(name) AS test_count, AVG(total) as avg_total_per_test_run
20
+ FROM tests
21
+ GROUP BY run, suite
22
+ ) as temp
23
+ GROUP BY suite
24
+ ORDER BY avg_total desc
25
+ LIMIT 10
26
+ SQL
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ module MiniTest
2
+ module Perf
3
+ class Test
4
+ attr_reader :run, :suite, :name, :total
5
+
6
+ def initialize(run, suite, name, total)
7
+ @run = run
8
+ @suite = suite
9
+ @name = name
10
+ @total = total
11
+ end
12
+
13
+ def persist
14
+ Persistence.write(self)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ module MiniTest
2
+ module Perf
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require 'minitest/perf/version'
2
+ require 'minitest/unit'
3
+
4
+ module MiniTest::Perf
5
+ autoload :Run, 'minitest/perf/run'
6
+ autoload :Test, 'minitest/perf/test'
7
+ autoload :Suite, 'minitest/perf/suite'
8
+ autoload :Cli, 'minitest/perf/cli'
9
+ autoload :Plugin, 'minitest/perf/plugin'
10
+ autoload :Persistence, 'minitest/perf/persistence'
11
+ autoload :Statistics, 'minitest/perf/statistics'
12
+ end
13
+
14
+ class MiniTest::Unit::TestCase
15
+ include MiniTest::Perf::Plugin
16
+ end
@@ -0,0 +1 @@
1
+ require 'minitest/perf'
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'minitest/perf/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "minitest-perf"
8
+ spec.version = MiniTest::Perf::VERSION
9
+ spec.authors = ["Albert Llop"]
10
+ spec.email = ["mrsimo@gmail.com"]
11
+ spec.description = %q{Save test run data to find slow tests and other interesting information}
12
+ spec.summary = <<-SUMMARY
13
+ Require minitest/perf when running your tests, and a handful of data will be recorded
14
+ for later query and study. Find particularly slow tests, and understand your tests
15
+ even more.
16
+ SUMMARY
17
+ spec.homepage = "https://github.com/mrsimo/minitest-perf"
18
+ spec.license = "MIT"
19
+
20
+ spec.files = `git ls-files`.split($/)
21
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "mocha"
28
+ spec.add_development_dependency "debugger"
29
+ spec.add_dependency "minitest", "~> 4.0"
30
+ spec.add_dependency "sqlite3"
31
+ end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+ require 'sqlite3'
3
+
4
+ module MiniTest::Perf
5
+ class PersistenceTest < MiniTest::Unit::TestCase
6
+ def setup
7
+ @test = Test.new('run', 'suite', 'name', 10)
8
+ Persistence.sql "delete from tests"
9
+ end
10
+
11
+ def test_exposes_a_sql_interface
12
+ assert_instance_of Array, Persistence.sql('select * from tests')
13
+ end
14
+
15
+ def test_stores_the_test_as_a_new_row
16
+ Persistence.write(@test)
17
+
18
+ result = Persistence.sql "SELECT * FROM tests"
19
+
20
+ assert_equal 1, result.size
21
+
22
+ run, suite, name, total = result.first
23
+
24
+ assert_equal 'run', run
25
+ assert_equal 'suite', suite
26
+ assert_equal 'name', name
27
+ assert_equal 10, total
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class PluginTest < MiniTest::Unit::TestCase
4
+ def test_minitest_has_the_plugin_included
5
+ assert_includes MiniTest::Unit::TestCase.included_modules, MiniTest::Perf::Plugin
6
+ end
7
+ end
@@ -0,0 +1,37 @@
1
+ require 'test_helper'
2
+
3
+ module MiniTest::Perf
4
+ class RunTest < MiniTest::Unit::TestCase
5
+ def test_a_new_run_has_no_tests
6
+ run = Run.new
7
+
8
+ assert_empty run.tests
9
+ end
10
+
11
+ def test_creates_a_new_test_calling_start_and_finish
12
+ run = Run.new(Time.at(0))
13
+ run.start('SuiteName', 'test_something', Time.at(1))
14
+
15
+ assert_empty run.tests
16
+
17
+ run.finish('SuiteName', 'test_something', Time.at(8))
18
+
19
+ assert_equal 1, run.tests.size
20
+
21
+ test = run.tests.last
22
+
23
+ assert_equal 'SuiteName', test.suite
24
+ assert_equal 'test_something', test.name
25
+ assert_equal Time.at(0), test.run
26
+ assert_equal 7, test.total
27
+ end
28
+
29
+ def test_persists_the_test
30
+ run = Run.new
31
+ run.start('SuiteName', 'test_something')
32
+
33
+ Test.any_instance.expects(:persist)
34
+ run.finish('SuiteName', 'test_something')
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ module MiniTest::Perf
4
+ class TestTest < MiniTest::Unit::TestCase
5
+ def test_creation
6
+ test = Test.new('run', 'suite', 'name', 10)
7
+
8
+ assert_equal 'run', test.run
9
+ assert_equal 'suite', test.suite
10
+ assert_equal 'name', test.name
11
+ assert_equal 10, test.total
12
+ end
13
+
14
+ def test_persists_calls_persistence
15
+ test = Test.new('run', 'suite', 'name', 10)
16
+
17
+ Persistence.expects(:write).with(test)
18
+
19
+ test.persist
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'minitest/autorun'
4
+ require 'minitest/pride'
5
+ require 'mocha/setup'
6
+ require 'debugger'
7
+
8
+ require 'minitest/perf'
9
+
10
+ ##
11
+ # We disable the real plugin or stuff gets all confusing
12
+ # in the tests when mocking and creating expectations.
13
+ class FakeRun
14
+ def start(*pars); end
15
+ def finish(*pars); end
16
+ end
17
+
18
+ MiniTest::Perf::Plugin.class_eval do
19
+ def current_perf_run
20
+ @current_perf_run ||= FakeRun.new
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: minitest-perf
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Albert Llop
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ type: :development
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '1.3'
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ type: :development
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: mocha
48
+ type: :development
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: debugger
64
+ type: :development
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: minitest
80
+ type: :runtime
81
+ requirement: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ~>
85
+ - !ruby/object:Gem::Version
86
+ version: '4.0'
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '4.0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: sqlite3
96
+ type: :runtime
97
+ requirement: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Save test run data to find slow tests and other interesting information
111
+ email:
112
+ - mrsimo@gmail.com
113
+ executables:
114
+ - minitest-perf
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - .gitignore
119
+ - Gemfile
120
+ - LICENSE.txt
121
+ - README.md
122
+ - Rakefile
123
+ - bin/minitest-perf
124
+ - lib/minitest-perf.rb
125
+ - lib/minitest/perf.rb
126
+ - lib/minitest/perf/cli.rb
127
+ - lib/minitest/perf/persistence.rb
128
+ - lib/minitest/perf/plugin.rb
129
+ - lib/minitest/perf/run.rb
130
+ - lib/minitest/perf/statistics.rb
131
+ - lib/minitest/perf/test.rb
132
+ - lib/minitest/perf/version.rb
133
+ - minitest-perf.gemspec
134
+ - test/perf/persistence_test.rb
135
+ - test/perf/plugin_test.rb
136
+ - test/perf/run_test.rb
137
+ - test/perf/test_test.rb
138
+ - test/test_helper.rb
139
+ homepage: https://github.com/mrsimo/minitest-perf
140
+ licenses:
141
+ - MIT
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 1.8.23
161
+ signing_key:
162
+ specification_version: 3
163
+ summary: Require minitest/perf when running your tests, and a handful of data will
164
+ be recorded for later query and study. Find particularly slow tests, and understand
165
+ your tests even more.
166
+ test_files:
167
+ - test/perf/persistence_test.rb
168
+ - test/perf/plugin_test.rb
169
+ - test/perf/run_test.rb
170
+ - test/perf/test_test.rb
171
+ - test/test_helper.rb