smartest 0.2.0.alpha5 → 0.2.1.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exe/smartest +4 -1
- data/lib/smartest/cli_arguments.rb +37 -4
- data/lib/smartest/reporter.rb +34 -1
- data/lib/smartest/version.rb +1 -1
- data/smartest/smartest_test.rb +132 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbe5852f8932b55c528baa05ddcdaad39071557e43707e03ab99b5acc9e13ede
|
|
4
|
+
data.tar.gz: 8c0d1165cab6932fe337623d9429b18d201b97f25b5f5dcf55724179e4036271
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8610801f162fca9e54e542db7141ba0b69269d1e02339dd212690dcc89b768cc3acd72a0fdc2b4dd8471a680db7f135d3829131a085277c3fcdbd79eda202000
|
|
7
|
+
data.tar.gz: ab6ce118ab140fc1bfe8eb2d2667ed19d5b9d56c657ebf53166217e721e20ddcdab3018dd22e05cb0cfc5403cce92c817d7f311a549879bbb9fe9239a748463a
|
data/exe/smartest
CHANGED
|
@@ -9,11 +9,13 @@ usage = <<~USAGE
|
|
|
9
9
|
Usage:
|
|
10
10
|
smartest [paths...]
|
|
11
11
|
smartest path/to/test_file.rb:line[-line]
|
|
12
|
+
smartest --profile [N]
|
|
12
13
|
smartest --init
|
|
13
14
|
smartest --version
|
|
14
15
|
smartest --help
|
|
15
16
|
|
|
16
17
|
When no paths are given, Smartest loads smartest/**/*_test.rb.
|
|
18
|
+
--profile prints the slowest tests after the run (default 5).
|
|
17
19
|
USAGE
|
|
18
20
|
|
|
19
21
|
command = :run
|
|
@@ -45,7 +47,8 @@ begin
|
|
|
45
47
|
load File.expand_path(file)
|
|
46
48
|
end
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
reporter = Smartest::Reporter.new($stdout, profile_count: arguments.profile_count)
|
|
51
|
+
exit Smartest::Runner.new(tests: arguments.select_tests(Smartest.suite.tests), reporter: reporter).run
|
|
49
52
|
rescue Exception => error
|
|
50
53
|
raise if Smartest.fatal_exception?(error)
|
|
51
54
|
|
|
@@ -4,14 +4,18 @@ require "set"
|
|
|
4
4
|
|
|
5
5
|
module Smartest
|
|
6
6
|
class CLIArguments
|
|
7
|
-
|
|
7
|
+
DEFAULT_PROFILE_COUNT = 5
|
|
8
|
+
|
|
9
|
+
attr_reader :files, :line_filters, :profile_count
|
|
8
10
|
|
|
9
11
|
def initialize(argv)
|
|
10
12
|
@files = []
|
|
11
13
|
@whole_files = Set.new
|
|
12
14
|
@line_filters = Hash.new { |hash, key| hash[key] = Set.new }
|
|
15
|
+
@profile_count = nil
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
paths = extract_options(argv)
|
|
18
|
+
parse_paths(paths.empty? ? ["smartest/**/*_test.rb"] : paths)
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
def filter_tests?
|
|
@@ -32,8 +36,37 @@ module Smartest
|
|
|
32
36
|
|
|
33
37
|
private
|
|
34
38
|
|
|
35
|
-
def
|
|
36
|
-
|
|
39
|
+
def extract_options(argv)
|
|
40
|
+
paths = []
|
|
41
|
+
index = 0
|
|
42
|
+
|
|
43
|
+
while index < argv.length
|
|
44
|
+
argument = argv[index]
|
|
45
|
+
|
|
46
|
+
case argument
|
|
47
|
+
when "--profile"
|
|
48
|
+
next_argument = argv[index + 1]
|
|
49
|
+
if next_argument && next_argument.match?(/\A\d+\z/)
|
|
50
|
+
@profile_count = next_argument.to_i
|
|
51
|
+
index += 2
|
|
52
|
+
else
|
|
53
|
+
@profile_count = DEFAULT_PROFILE_COUNT
|
|
54
|
+
index += 1
|
|
55
|
+
end
|
|
56
|
+
when /\A--profile=(\d+)\z/
|
|
57
|
+
@profile_count = Regexp.last_match(1).to_i
|
|
58
|
+
index += 1
|
|
59
|
+
else
|
|
60
|
+
paths << argument
|
|
61
|
+
index += 1
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
paths
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def parse_paths(paths)
|
|
69
|
+
paths.each do |argument|
|
|
37
70
|
pattern, line_filter = split_line_filter(argument)
|
|
38
71
|
matches = Dir[pattern]
|
|
39
72
|
files = matches.empty? ? [pattern] : matches
|
data/lib/smartest/reporter.rb
CHANGED
|
@@ -7,8 +7,9 @@ module Smartest
|
|
|
7
7
|
SKIP_MARK = "-"
|
|
8
8
|
PENDING_MARK = "*"
|
|
9
9
|
|
|
10
|
-
def initialize(io = $stdout)
|
|
10
|
+
def initialize(io = $stdout, profile_count: nil)
|
|
11
11
|
@io = io
|
|
12
|
+
@profile_count = profile_count
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def start(count)
|
|
@@ -28,6 +29,7 @@ module Smartest
|
|
|
28
29
|
report_failures(failures) if failures.any?
|
|
29
30
|
report_suite_errors(suite_errors) if suite_errors.any?
|
|
30
31
|
report_suite_cleanup_errors(suite_cleanup_errors) if suite_cleanup_errors.any?
|
|
32
|
+
report_profile(results) if @profile_count && @profile_count.positive?
|
|
31
33
|
|
|
32
34
|
@io.puts
|
|
33
35
|
summary = "#{results.count} #{results.count == 1 ? 'test' : 'tests'}, #{results.count(&:passed?)} passed, #{failures.count} failed"
|
|
@@ -99,6 +101,37 @@ module Smartest
|
|
|
99
101
|
end
|
|
100
102
|
end
|
|
101
103
|
|
|
104
|
+
def report_profile(results)
|
|
105
|
+
timed = results.select { |result| result.duration }
|
|
106
|
+
return if timed.empty?
|
|
107
|
+
|
|
108
|
+
count = [@profile_count, timed.length].min
|
|
109
|
+
slowest = timed.sort_by { |result| -result.duration }.first(count)
|
|
110
|
+
total_duration = timed.sum(&:duration)
|
|
111
|
+
slowest_duration = slowest.sum(&:duration)
|
|
112
|
+
percent = total_duration.positive? ? (slowest_duration / total_duration) * 100 : 0.0
|
|
113
|
+
|
|
114
|
+
@io.puts
|
|
115
|
+
@io.puts "Top #{count} slowest #{count == 1 ? 'test' : 'tests'} " \
|
|
116
|
+
"(#{format_duration(slowest_duration)} seconds, #{format('%.1f', percent)}% of total time):"
|
|
117
|
+
|
|
118
|
+
slowest.each do |result|
|
|
119
|
+
@io.puts " #{result.test_case.name}"
|
|
120
|
+
location_suffix = profile_location_suffix(result.test_case.location)
|
|
121
|
+
@io.puts " #{format_duration(result.duration)} seconds#{location_suffix}"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def profile_location_suffix(location)
|
|
126
|
+
return "" unless location
|
|
127
|
+
|
|
128
|
+
" #{location.path}:#{location.lineno}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def format_duration(seconds)
|
|
132
|
+
format("%.5f", seconds)
|
|
133
|
+
end
|
|
134
|
+
|
|
102
135
|
def report_location(location)
|
|
103
136
|
return unless location
|
|
104
137
|
|
data/lib/smartest/version.rb
CHANGED
data/smartest/smartest_test.rb
CHANGED
|
@@ -1569,9 +1569,141 @@ test("cli prints help") do
|
|
|
1569
1569
|
expect(stdout).to include("smartest [paths...]")
|
|
1570
1570
|
expect(stdout).to include("smartest path/to/test_file.rb:line[-line]")
|
|
1571
1571
|
expect(stdout).to include("smartest --init")
|
|
1572
|
+
expect(stdout).to include("smartest --profile [N]")
|
|
1572
1573
|
expect(stdout).to include("smartest/**/*_test.rb")
|
|
1573
1574
|
end
|
|
1574
1575
|
|
|
1576
|
+
test("--profile prints the slowest tests with default count of 5") do
|
|
1577
|
+
output = StringIO.new
|
|
1578
|
+
reporter = Smartest::Reporter.new(output, profile_count: 5)
|
|
1579
|
+
|
|
1580
|
+
results = [
|
|
1581
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("alpha", proc {}), duration: 0.10),
|
|
1582
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("bravo", proc {}), duration: 0.50),
|
|
1583
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("charlie", proc {}), duration: 0.30),
|
|
1584
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("delta", proc {}), duration: 0.20),
|
|
1585
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("echo", proc {}), duration: 0.40),
|
|
1586
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("foxtrot", proc {}), duration: 0.05)
|
|
1587
|
+
]
|
|
1588
|
+
|
|
1589
|
+
reporter.finish(results)
|
|
1590
|
+
text = output.string
|
|
1591
|
+
|
|
1592
|
+
expect(text).to include("Top 5 slowest tests")
|
|
1593
|
+
expect(text).to include("bravo")
|
|
1594
|
+
expect(text).to include("echo")
|
|
1595
|
+
expect(text).to include("charlie")
|
|
1596
|
+
expect(text).to include("delta")
|
|
1597
|
+
expect(text).to include("alpha")
|
|
1598
|
+
expect(text).not_to include("foxtrot\n 0")
|
|
1599
|
+
|
|
1600
|
+
bravo_index = text.index("bravo")
|
|
1601
|
+
echo_index = text.index("echo")
|
|
1602
|
+
charlie_index = text.index("charlie")
|
|
1603
|
+
delta_index = text.index("delta")
|
|
1604
|
+
alpha_index = text.index("alpha")
|
|
1605
|
+
expect(bravo_index < echo_index).to eq(true)
|
|
1606
|
+
expect(echo_index < charlie_index).to eq(true)
|
|
1607
|
+
expect(charlie_index < delta_index).to eq(true)
|
|
1608
|
+
expect(delta_index < alpha_index).to eq(true)
|
|
1609
|
+
end
|
|
1610
|
+
|
|
1611
|
+
test("--profile N shows top N slowest tests") do
|
|
1612
|
+
output = StringIO.new
|
|
1613
|
+
reporter = Smartest::Reporter.new(output, profile_count: 2)
|
|
1614
|
+
|
|
1615
|
+
results = [
|
|
1616
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("alpha", proc {}), duration: 0.10),
|
|
1617
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("bravo", proc {}), duration: 0.50),
|
|
1618
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("charlie", proc {}), duration: 0.30)
|
|
1619
|
+
]
|
|
1620
|
+
|
|
1621
|
+
reporter.finish(results)
|
|
1622
|
+
text = output.string
|
|
1623
|
+
|
|
1624
|
+
expect(text).to include("Top 2 slowest tests")
|
|
1625
|
+
expect(text).to include("bravo")
|
|
1626
|
+
expect(text).to include("charlie")
|
|
1627
|
+
expect(text).not_to include(" alpha\n")
|
|
1628
|
+
end
|
|
1629
|
+
|
|
1630
|
+
test("--profile does not error when fewer tests than the requested count") do
|
|
1631
|
+
output = StringIO.new
|
|
1632
|
+
reporter = Smartest::Reporter.new(output, profile_count: 5)
|
|
1633
|
+
|
|
1634
|
+
results = [
|
|
1635
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("solo", proc {}), duration: 0.01),
|
|
1636
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("duo", proc {}), duration: 0.02)
|
|
1637
|
+
]
|
|
1638
|
+
|
|
1639
|
+
reporter.finish(results)
|
|
1640
|
+
text = output.string
|
|
1641
|
+
|
|
1642
|
+
expect(text).to include("Top 2 slowest tests")
|
|
1643
|
+
expect(text).to include("solo")
|
|
1644
|
+
expect(text).to include("duo")
|
|
1645
|
+
end
|
|
1646
|
+
|
|
1647
|
+
test("--profile is not printed when profile_count is nil") do
|
|
1648
|
+
output = StringIO.new
|
|
1649
|
+
reporter = Smartest::Reporter.new(output)
|
|
1650
|
+
|
|
1651
|
+
reporter.finish([
|
|
1652
|
+
Smartest::TestResult.passed(test_case: SmartestSelfTest.test_case("alpha", proc {}), duration: 0.10)
|
|
1653
|
+
])
|
|
1654
|
+
|
|
1655
|
+
expect(output.string).not_to include("slowest")
|
|
1656
|
+
end
|
|
1657
|
+
|
|
1658
|
+
test("--profile is not printed when there are no results") do
|
|
1659
|
+
output = StringIO.new
|
|
1660
|
+
reporter = Smartest::Reporter.new(output, profile_count: 5)
|
|
1661
|
+
|
|
1662
|
+
reporter.finish([])
|
|
1663
|
+
|
|
1664
|
+
expect(output.string).not_to include("slowest")
|
|
1665
|
+
end
|
|
1666
|
+
|
|
1667
|
+
test("CLIArguments parses --profile in various forms") do
|
|
1668
|
+
expect(Smartest::CLIArguments.new(["--profile"]).profile_count).to eq(5)
|
|
1669
|
+
expect(Smartest::CLIArguments.new(["--profile", "3"]).profile_count).to eq(3)
|
|
1670
|
+
expect(Smartest::CLIArguments.new(["--profile=7"]).profile_count).to eq(7)
|
|
1671
|
+
expect(Smartest::CLIArguments.new([]).profile_count).to eq(nil)
|
|
1672
|
+
expect(Smartest::CLIArguments.new(["--profile", "smartest/foo_test.rb"]).profile_count).to eq(5)
|
|
1673
|
+
end
|
|
1674
|
+
|
|
1675
|
+
test("cli runs with --profile and prints slowest tests") do
|
|
1676
|
+
Dir.mktmpdir do |dir|
|
|
1677
|
+
smartest_dir = File.join(dir, "smartest")
|
|
1678
|
+
FileUtils.mkdir_p(smartest_dir)
|
|
1679
|
+
File.write(File.join(smartest_dir, "sample_test.rb"), <<~RUBY)
|
|
1680
|
+
require "smartest/autorun"
|
|
1681
|
+
|
|
1682
|
+
test("first") do
|
|
1683
|
+
expect(1).to eq(1)
|
|
1684
|
+
end
|
|
1685
|
+
|
|
1686
|
+
test("second") do
|
|
1687
|
+
expect(1).to eq(1)
|
|
1688
|
+
end
|
|
1689
|
+
RUBY
|
|
1690
|
+
|
|
1691
|
+
stdout, stderr, status = Open3.capture3(
|
|
1692
|
+
{ "RUBYLIB" => File.expand_path("../lib", __dir__) },
|
|
1693
|
+
"ruby",
|
|
1694
|
+
File.expand_path("../exe/smartest", __dir__),
|
|
1695
|
+
"--profile",
|
|
1696
|
+
"1",
|
|
1697
|
+
chdir: dir
|
|
1698
|
+
)
|
|
1699
|
+
|
|
1700
|
+
expect(status.success?).to eq(true)
|
|
1701
|
+
expect(stderr).to eq("")
|
|
1702
|
+
expect(stdout).to include("Top 1 slowest test")
|
|
1703
|
+
expect(stdout).to include("2 tests, 2 passed, 0 failed")
|
|
1704
|
+
end
|
|
1705
|
+
end
|
|
1706
|
+
|
|
1575
1707
|
test("cli initializes a runnable test scaffold") do
|
|
1576
1708
|
Dir.mktmpdir do |dir|
|
|
1577
1709
|
stdout, stderr, status = Open3.capture3(
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smartest
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1.alpha1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yusuke Iwaki
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|