tldr 0.5.0 → 0.6.1
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/CHANGELOG.md +14 -0
- data/README.md +6 -5
- data/Rakefile +1 -0
- data/lib/tldr/argv_parser.rb +8 -3
- data/lib/tldr/class_util.rb +15 -0
- data/lib/tldr/{parallelizer.rb → executor.rb} +9 -15
- data/lib/tldr/path_util.rb +3 -1
- data/lib/tldr/planner.rb +28 -19
- data/lib/tldr/rake.rb +8 -6
- data/lib/tldr/reporters/default.rb +4 -3
- data/lib/tldr/runner.rb +35 -31
- data/lib/tldr/strategizer.rb +23 -8
- data/lib/tldr/value/config.rb +24 -15
- data/lib/tldr/value/plan.rb +1 -1
- data/lib/tldr/value/test.rb +3 -3
- data/lib/tldr/value/test_group.rb +2 -2
- data/lib/tldr/version.rb +1 -1
- data/lib/tldr.rb +4 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e0c6abcfc7b58fc6de05591d61ceadfcbbed4867cfb140081f62877bb80d9a6
|
4
|
+
data.tar.gz: e62acff5d7e667214527d5bcafef6ba2540abbe4a187f441da07575919a34da0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c31d1f3fba81d133e2ef570b2252504cc67150d0729e56e0690fe5dbb8e3bd0512bec39e95a178eb12a8108fa92e56365fa981c3fc528ee3c845939237c347f
|
7
|
+
data.tar.gz: 3e35999424094dc072b6f2be670259e584dc14edf5621236a37efb0282c38c9ae434d63bd33fbe44b23f8fbf6f1b79fef801fb5f9375ff924d851c8063f63ba8
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.6.1]
|
2
|
+
|
3
|
+
* Correctly report the number of test classes that run
|
4
|
+
* Finish planning the test run before starting the clock on the timer (that's
|
5
|
+
a millisecond or two in savings!)
|
6
|
+
|
7
|
+
## [0.6.0]
|
8
|
+
|
9
|
+
* When `dont_run_these_in_parallel!` and `run_these_together!` are called from a
|
10
|
+
super class, gather subclasses' methods as well when the method is `nil`
|
11
|
+
* Stop shelling out to `tldr` from our Rake task. Rescue `SystemExit` instead
|
12
|
+
* Rename `Config#helper` to `Config#helper_paths`, which YAML config keys
|
13
|
+
* Print Ruby warnings by default (disable with --no-warnings)
|
14
|
+
|
1
15
|
## [0.5.0]
|
2
16
|
|
3
17
|
* Define your own Rake tasks with `TLDR::Task` and pass in a custom configuration
|
data/README.md
CHANGED
@@ -23,8 +23,8 @@ suite.
|
|
23
23
|
|
24
24
|
Some stuff you might like:
|
25
25
|
|
26
|
-
* A CLI that can
|
27
|
-
by names or patterns (e.g. `--name test_fail,test_error --name "/_\d/"`)
|
26
|
+
* A CLI that can specify tests by line number(s) (e.g. `foo.rb:5 bar.rb:3:10`)
|
27
|
+
and by names or patterns (e.g. `--name test_fail,test_error --name "/_\d/"`)
|
28
28
|
* Everything is **parallel by default**, and seems pretty darn fast; TLDR
|
29
29
|
also provides [several escape hatches to sequester tests that aren't thread-safe](#parallel-by-default-is-nice-in-theory-but-half-my-tests-are-failing-wat)
|
30
30
|
* Surprisingly delightful color diff output when two things fail to equal one
|
@@ -112,8 +112,8 @@ Usage: tldr [options] some_tests/**/*.rb some/path.rb:13 ...
|
|
112
112
|
-n, --name PATTERN One or more names or /patterns/ of tests to run (like: foo_test, /test_foo.*/, Foo#foo_test)
|
113
113
|
--exclude-name PATTERN One or more names or /patterns/ NOT to run
|
114
114
|
--exclude-path PATH One or more paths NOT to run (like: foo.rb, "test/bar/**", baz.rb:3)
|
115
|
-
--helper
|
116
|
-
--no-helper Don't
|
115
|
+
--helper PATH One or more paths to a helper that is required before any tests (Default: "test/helper.rb")
|
116
|
+
--no-helper Don't require any test helpers
|
117
117
|
--prepend PATH Prepend one or more paths to run before the rest (Default: most recently modified test)
|
118
118
|
--no-prepend Don't prepend any tests before the rest of the suite
|
119
119
|
-l, --load-path PATH Add one or more paths to the $LOAD_PATH (Default: ["test"])
|
@@ -122,6 +122,7 @@ Usage: tldr [options] some_tests/**/*.rb some/path.rb:13 ...
|
|
122
122
|
--no-dotfile Disable loading .tldr.yml dotfile
|
123
123
|
--no-emoji Disable emoji in the output
|
124
124
|
-v, --verbose Print stack traces for errors
|
125
|
+
--[no-]warnings Print Ruby warnings (Default: true)
|
125
126
|
--comment COMMENT No-op comment, used internally for multi-line execution instructions
|
126
127
|
```
|
127
128
|
|
@@ -193,7 +194,7 @@ require "tldr/rake"
|
|
193
194
|
|
194
195
|
TLDR::Task.new(name: :safe_tests, config: TLDR::Config.new(
|
195
196
|
paths: FileList["safe/**/*_test.rb"],
|
196
|
-
|
197
|
+
helper_paths: ["safe/helper.rb"],
|
197
198
|
load_paths: ["lib", "safe"]
|
198
199
|
))
|
199
200
|
```
|
data/Rakefile
CHANGED
data/lib/tldr/argv_parser.rb
CHANGED
@@ -35,11 +35,12 @@ class TLDR
|
|
35
35
|
options[:exclude_paths] += path
|
36
36
|
end
|
37
37
|
|
38
|
-
opts.on "#{CONFLAGS[:
|
39
|
-
options[:
|
38
|
+
opts.on "#{CONFLAGS[:helper_paths]} PATH", Array, "One or more paths to a helper that is required before any tests (Default: \"test/helper.rb\")" do |path|
|
39
|
+
options[:helper_paths] ||= []
|
40
|
+
options[:helper_paths] += path
|
40
41
|
end
|
41
42
|
|
42
|
-
opts.on CONFLAGS[:no_helper], "Don't
|
43
|
+
opts.on CONFLAGS[:no_helper], "Don't require any test helpers" do
|
43
44
|
options[:no_helper] = true
|
44
45
|
end
|
45
46
|
|
@@ -77,6 +78,10 @@ class TLDR
|
|
77
78
|
options[:verbose] = verbose
|
78
79
|
end
|
79
80
|
|
81
|
+
opts.on CONFLAGS[:warnings], "Print Ruby warnings (Default: true)" do |warnings|
|
82
|
+
options[:warnings] = warnings
|
83
|
+
end
|
84
|
+
|
80
85
|
opts.on "--comment COMMENT", String, "No-op comment, used internally for multi-line execution instructions" do
|
81
86
|
# See "--comment" in lib/tldr/reporters/default.rb for an example of how this is used internally
|
82
87
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class TLDR
|
2
|
+
module ClassUtil
|
3
|
+
def self.gather_descendants root_klass
|
4
|
+
root_klass.subclasses + root_klass.subclasses.flat_map { |subklass|
|
5
|
+
gather_descendants subklass
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.gather_tests klass
|
10
|
+
klass.instance_methods.grep(/^test_/).sort.map { |method|
|
11
|
+
Test.new klass, method
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,26 +1,20 @@
|
|
1
1
|
class TLDR
|
2
|
-
class
|
2
|
+
class Executor
|
3
3
|
def initialize
|
4
|
-
@strategizer = Strategizer.new
|
5
4
|
@thread_pool = Concurrent::ThreadPoolExecutor.new(
|
6
5
|
name: "tldr",
|
7
6
|
auto_terminate: true
|
8
7
|
)
|
9
8
|
end
|
10
9
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
)
|
20
|
-
|
21
|
-
run_in_sequence(strategy.prepend_thread_unsafe_tests, &blk) +
|
22
|
-
run_in_parallel(strategy.parallel_tests_and_groups, &blk) +
|
23
|
-
run_in_sequence(strategy.thread_unsafe_tests, &blk)
|
10
|
+
def execute plan, &blk
|
11
|
+
if plan.strategy.parallel?
|
12
|
+
run_in_sequence(plan.strategy.prepend_sequential_tests, &blk) +
|
13
|
+
run_in_parallel(plan.strategy.parallel_tests_and_groups, &blk) +
|
14
|
+
run_in_sequence(plan.strategy.append_sequential_tests, &blk)
|
15
|
+
else
|
16
|
+
run_in_sequence(plan.tests, &blk)
|
17
|
+
end
|
24
18
|
end
|
25
19
|
|
26
20
|
private
|
data/lib/tldr/path_util.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
class TLDR
|
2
2
|
module PathUtil
|
3
|
-
def self.
|
3
|
+
def self.expand_paths path_strings, globs: true
|
4
|
+
path_strings = expand_globs path_strings if globs
|
5
|
+
|
4
6
|
path_strings.flat_map { |path_string|
|
5
7
|
File.directory?(path_string) ? Dir["#{path_string}/**/*.rb"] : path_string
|
6
8
|
}.flat_map { |path_string|
|
data/lib/tldr/planner.rb
CHANGED
@@ -2,8 +2,13 @@ require "pathname"
|
|
2
2
|
|
3
3
|
class TLDR
|
4
4
|
class Planner
|
5
|
+
def initialize
|
6
|
+
@strategizer = Strategizer.new
|
7
|
+
end
|
8
|
+
|
5
9
|
def plan config
|
6
|
-
|
10
|
+
$VERBOSE = config.warnings
|
11
|
+
search_locations = PathUtil.expand_paths config.paths, globs: false
|
7
12
|
|
8
13
|
prepend_load_paths config
|
9
14
|
require_test_helper config
|
@@ -11,8 +16,7 @@ class TLDR
|
|
11
16
|
|
12
17
|
tests = gather_tests
|
13
18
|
config.update_after_gathering_tests! tests
|
14
|
-
|
15
|
-
Plan.new prepend(
|
19
|
+
tests_to_run = prepend(
|
16
20
|
shuffle(
|
17
21
|
exclude_by_path(
|
18
22
|
exclude_by_name(
|
@@ -28,21 +32,28 @@ class TLDR
|
|
28
32
|
),
|
29
33
|
config
|
30
34
|
)
|
35
|
+
|
36
|
+
strategy = @strategizer.strategize(
|
37
|
+
tests_to_run,
|
38
|
+
GROUPED_TESTS,
|
39
|
+
THREAD_UNSAFE_TESTS,
|
40
|
+
config
|
41
|
+
)
|
42
|
+
|
43
|
+
Plan.new tests_to_run, strategy
|
31
44
|
end
|
32
45
|
|
33
46
|
private
|
34
47
|
|
35
48
|
def gather_tests
|
36
|
-
gather_descendants(TLDR).flat_map { |subklass|
|
37
|
-
|
38
|
-
Test.new subklass, method
|
39
|
-
}
|
49
|
+
ClassUtil.gather_descendants(TLDR).flat_map { |subklass|
|
50
|
+
ClassUtil.gather_tests(subklass)
|
40
51
|
}
|
41
52
|
end
|
42
53
|
|
43
54
|
def prepend tests, config
|
44
55
|
return tests if config.no_prepend
|
45
|
-
prepended_locations = PathUtil.
|
56
|
+
prepended_locations = PathUtil.expand_paths config.prepend_paths
|
46
57
|
prepended, rest = tests.partition { |test|
|
47
58
|
PathUtil.locations_include_test? prepended_locations, test
|
48
59
|
}
|
@@ -54,7 +65,7 @@ class TLDR
|
|
54
65
|
end
|
55
66
|
|
56
67
|
def exclude_by_path tests, exclude_paths
|
57
|
-
excluded_locations = PathUtil.
|
68
|
+
excluded_locations = PathUtil.expand_paths exclude_paths
|
58
69
|
return tests if excluded_locations.empty?
|
59
70
|
|
60
71
|
tests.reject { |test|
|
@@ -69,7 +80,7 @@ class TLDR
|
|
69
80
|
|
70
81
|
tests.reject { |test|
|
71
82
|
name_excludes.any? { |filter|
|
72
|
-
filter === test.
|
83
|
+
filter === test.method_name.to_s || filter === "#{test.test_class}##{test.method_name}"
|
73
84
|
}
|
74
85
|
}
|
75
86
|
end
|
@@ -90,7 +101,7 @@ class TLDR
|
|
90
101
|
|
91
102
|
tests.select { |test|
|
92
103
|
name_filters.any? { |filter|
|
93
|
-
filter === test.
|
104
|
+
filter === test.method_name.to_s || filter === "#{test.test_class}##{test.method_name}"
|
94
105
|
}
|
95
106
|
}
|
96
107
|
end
|
@@ -102,8 +113,12 @@ class TLDR
|
|
102
113
|
end
|
103
114
|
|
104
115
|
def require_test_helper config
|
105
|
-
return if config.no_helper || config.
|
106
|
-
|
116
|
+
return if config.no_helper || config.helper_paths.empty?
|
117
|
+
PathUtil.expand_paths(config.helper_paths).map(&:file).uniq.each do |helper_file|
|
118
|
+
next unless File.exist?(helper_file)
|
119
|
+
|
120
|
+
require helper_file
|
121
|
+
end
|
107
122
|
end
|
108
123
|
|
109
124
|
def require_tests search_locations
|
@@ -112,12 +127,6 @@ class TLDR
|
|
112
127
|
end
|
113
128
|
end
|
114
129
|
|
115
|
-
def gather_descendants root_klass
|
116
|
-
root_klass.subclasses + root_klass.subclasses.flat_map { |subklass|
|
117
|
-
gather_descendants subklass
|
118
|
-
}
|
119
|
-
end
|
120
|
-
|
121
130
|
def expand_names_with_patterns names
|
122
131
|
names.map { |name|
|
123
132
|
if name.is_a?(String) && name =~ /^\/(.*)\/$/
|
data/lib/tldr/rake.rb
CHANGED
@@ -16,13 +16,17 @@ class TLDR
|
|
16
16
|
def define name, task_config
|
17
17
|
desc "Run #{name} tests (use TLDR_OPTS or .tldr.yml to configure)"
|
18
18
|
task name do
|
19
|
-
|
20
|
-
|
19
|
+
argv = Shellwords.shellwords(merge_env_opts(task_config).to_full_args)
|
20
|
+
begin
|
21
|
+
TLDR::Run.cli(argv)
|
22
|
+
rescue SystemExit => e
|
23
|
+
fail "TLDR task #{name} failed with status #{e.status}" unless e.status == 0
|
24
|
+
end
|
21
25
|
end
|
22
26
|
end
|
23
27
|
|
24
|
-
def
|
25
|
-
|
28
|
+
def merge_env_opts task_config
|
29
|
+
if ENV["TLDR_OPTS"]
|
26
30
|
env_argv = Shellwords.shellwords(ENV["TLDR_OPTS"])
|
27
31
|
opts_config = ArgvParser.new.parse(env_argv, {
|
28
32
|
config_intended_for_merge_only: true
|
@@ -31,8 +35,6 @@ class TLDR
|
|
31
35
|
else
|
32
36
|
task_config
|
33
37
|
end
|
34
|
-
|
35
|
-
config.to_full_args
|
36
38
|
end
|
37
39
|
end
|
38
40
|
end
|
@@ -9,7 +9,8 @@ class TLDR
|
|
9
9
|
def before_suite tests
|
10
10
|
@suite_start_time = Process.clock_gettime Process::CLOCK_MONOTONIC, :microsecond
|
11
11
|
@out.print <<~MSG
|
12
|
-
Command: #{tldr_command} #{@config.to_full_args}
|
12
|
+
Command: #{tldr_command} #{@config.to_full_args}
|
13
|
+
#{@icons.seed} #{CONFLAGS[:seed]} #{@config.seed}
|
13
14
|
|
14
15
|
#{@icons.run} Running:
|
15
16
|
|
@@ -86,7 +87,7 @@ class TLDR
|
|
86
87
|
@out.print "Finished in #{duration}ms."
|
87
88
|
|
88
89
|
@out.print "\n\n"
|
89
|
-
class_count = test_results.uniq { |result| result.test.
|
90
|
+
class_count = test_results.uniq { |result| result.test.test_class }.size
|
90
91
|
test_count = test_results.size
|
91
92
|
@out.print [
|
92
93
|
plural(class_count, "test class", "test classes"),
|
@@ -126,7 +127,7 @@ class TLDR
|
|
126
127
|
end
|
127
128
|
|
128
129
|
def describe test, location = test.location
|
129
|
-
"#{test.
|
130
|
+
"#{test.test_class}##{test.method_name} [#{location.locator}]"
|
130
131
|
end
|
131
132
|
|
132
133
|
def plural count, singular, plural = "#{singular}s"
|
data/lib/tldr/runner.rb
CHANGED
@@ -3,7 +3,7 @@ require "irb"
|
|
3
3
|
class TLDR
|
4
4
|
class Runner
|
5
5
|
def initialize
|
6
|
-
@
|
6
|
+
@executor = Executor.new
|
7
7
|
@wip = Concurrent::Array.new
|
8
8
|
@results = Concurrent::Array.new
|
9
9
|
@run_aborted = Concurrent::AtomicBoolean.new false
|
@@ -12,8 +12,8 @@ class TLDR
|
|
12
12
|
def run config, plan
|
13
13
|
@wip.clear
|
14
14
|
@results.clear
|
15
|
-
reporter = config.reporter.new
|
16
|
-
reporter.before_suite
|
15
|
+
reporter = config.reporter.new(config)
|
16
|
+
reporter.before_suite(plan.tests)
|
17
17
|
|
18
18
|
time_bomb = Thread.new {
|
19
19
|
explode = proc do
|
@@ -34,34 +34,8 @@ class TLDR
|
|
34
34
|
end
|
35
35
|
}
|
36
36
|
|
37
|
-
results = @
|
38
|
-
|
39
|
-
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
|
40
|
-
wip_test = WIPTest.new test, start_time
|
41
|
-
@wip << wip_test
|
42
|
-
runtime = time_it(start_time) do
|
43
|
-
instance = test.klass.new
|
44
|
-
instance.setup if instance.respond_to? :setup
|
45
|
-
if instance.respond_to? :around
|
46
|
-
did_run = false
|
47
|
-
instance.around {
|
48
|
-
did_run = true
|
49
|
-
instance.send(test.method)
|
50
|
-
}
|
51
|
-
raise Error, "#{test.klass}#around failed to yield or call the passed test block" unless did_run
|
52
|
-
else
|
53
|
-
instance.send(test.method)
|
54
|
-
end
|
55
|
-
instance.teardown if instance.respond_to? :teardown
|
56
|
-
rescue Skip, Failure, StandardError => e
|
57
|
-
end
|
58
|
-
TestResult.new(test, e, runtime).tap do |result|
|
59
|
-
next if @run_aborted.true?
|
60
|
-
@results << result
|
61
|
-
@wip.delete wip_test
|
62
|
-
reporter.after_test result
|
63
|
-
fail_fast reporter, plan, result if result.failing? && config.fail_fast
|
64
|
-
end
|
37
|
+
results = @executor.execute(plan) { |test|
|
38
|
+
run_test(test, config, plan, reporter)
|
65
39
|
}.tap do
|
66
40
|
time_bomb.kill
|
67
41
|
end
|
@@ -74,6 +48,36 @@ class TLDR
|
|
74
48
|
|
75
49
|
private
|
76
50
|
|
51
|
+
def run_test test, config, plan, reporter
|
52
|
+
e = nil
|
53
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
|
54
|
+
wip_test = WIPTest.new test, start_time
|
55
|
+
@wip << wip_test
|
56
|
+
runtime = time_it(start_time) do
|
57
|
+
instance = test.test_class.new
|
58
|
+
instance.setup if instance.respond_to? :setup
|
59
|
+
if instance.respond_to? :around
|
60
|
+
did_run = false
|
61
|
+
instance.around {
|
62
|
+
did_run = true
|
63
|
+
instance.send(test.method_name)
|
64
|
+
}
|
65
|
+
raise Error, "#{test.test_class}#around failed to yield or call the passed test block" unless did_run
|
66
|
+
else
|
67
|
+
instance.send(test.method_name)
|
68
|
+
end
|
69
|
+
instance.teardown if instance.respond_to? :teardown
|
70
|
+
rescue Skip, Failure, StandardError => e
|
71
|
+
end
|
72
|
+
TestResult.new(test, e, runtime).tap do |result|
|
73
|
+
next if @run_aborted.true?
|
74
|
+
@results << result
|
75
|
+
@wip.delete wip_test
|
76
|
+
reporter.after_test result
|
77
|
+
fail_fast reporter, plan, result if result.failing? && config.fail_fast
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
77
81
|
def fail_fast reporter, plan, fast_failed_result
|
78
82
|
unless @run_aborted.true?
|
79
83
|
@run_aborted.make_true
|
data/lib/tldr/strategizer.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
class TLDR
|
2
2
|
class Strategizer
|
3
|
-
Strategy = Struct.new :
|
3
|
+
Strategy = Struct.new :parallel?, :prepend_sequential_tests,
|
4
|
+
:parallel_tests_and_groups, :append_sequential_tests,
|
5
|
+
keyword_init: true
|
4
6
|
|
5
7
|
# Combine all discovered test methods with any methods grouped by run_these_together!
|
6
8
|
#
|
@@ -8,13 +10,15 @@ class TLDR
|
|
8
10
|
# - Map over tests to build out groups in order to retain shuffle order
|
9
11
|
# (group will run in position of first test in the group)
|
10
12
|
# - If a test is in multiple groups, only run it once
|
11
|
-
def strategize all_tests, run_these_together_groups, thread_unsafe_test_groups,
|
13
|
+
def strategize all_tests, run_these_together_groups, thread_unsafe_test_groups, config
|
14
|
+
return Strategy.new(parallel?: false) if run_sequentially?(all_tests, config)
|
15
|
+
|
12
16
|
thread_unsafe_tests, thread_safe_tests = partition_unsafe(all_tests, thread_unsafe_test_groups)
|
13
|
-
|
17
|
+
prepend_sequential_tests, append_sequential_tests = partition_prepend(thread_unsafe_tests, config)
|
14
18
|
|
15
|
-
grouped_tests = prepare_run_together_groups run_these_together_groups, thread_safe_tests,
|
19
|
+
grouped_tests = prepare_run_together_groups run_these_together_groups, thread_safe_tests, append_sequential_tests
|
16
20
|
already_included_groups = []
|
17
|
-
|
21
|
+
parallel_tests_and_groups = thread_safe_tests.map { |test|
|
18
22
|
if (group = grouped_tests.find { |group| group.tests.include? test })
|
19
23
|
if already_included_groups.include? group
|
20
24
|
next
|
@@ -28,11 +32,21 @@ class TLDR
|
|
28
32
|
else
|
29
33
|
test
|
30
34
|
end
|
31
|
-
}.compact
|
35
|
+
}.compact
|
36
|
+
Strategy.new(
|
37
|
+
parallel?: true,
|
38
|
+
prepend_sequential_tests:,
|
39
|
+
parallel_tests_and_groups:,
|
40
|
+
append_sequential_tests:
|
41
|
+
)
|
32
42
|
end
|
33
43
|
|
34
44
|
private
|
35
45
|
|
46
|
+
def run_sequentially? all_tests, config
|
47
|
+
all_tests.size < 2 || !config.parallel
|
48
|
+
end
|
49
|
+
|
36
50
|
def partition_unsafe tests, thread_unsafe_test_groups
|
37
51
|
tests.partition { |test|
|
38
52
|
thread_unsafe_test_groups.any? { |group| group.tests.include? test }
|
@@ -41,8 +55,9 @@ class TLDR
|
|
41
55
|
|
42
56
|
# Sadly duplicative with Planner.rb, necessitating the extraction of PathUtil
|
43
57
|
# Suboptimal, but we do indeed need to do this work in two places ¯\_(ツ)_/¯
|
44
|
-
def partition_prepend thread_unsafe_tests,
|
45
|
-
|
58
|
+
def partition_prepend thread_unsafe_tests, config
|
59
|
+
prepend_paths = config.no_prepend ? [] : config.prepend_paths
|
60
|
+
locations = PathUtil.expand_paths prepend_paths
|
46
61
|
|
47
62
|
thread_unsafe_tests.partition { |test|
|
48
63
|
PathUtil.locations_include_test? locations, test
|
data/lib/tldr/value/config.rb
CHANGED
@@ -4,7 +4,7 @@ class TLDR
|
|
4
4
|
no_helper: "--no-helper",
|
5
5
|
verbose: "--verbose",
|
6
6
|
reporter: "--reporter",
|
7
|
-
|
7
|
+
helper_paths: "--helper",
|
8
8
|
load_paths: "--load-path",
|
9
9
|
parallel: "--[no-]parallel",
|
10
10
|
names: "--name",
|
@@ -16,19 +16,22 @@ class TLDR
|
|
16
16
|
exclude_names: "--exclude-name",
|
17
17
|
base_path: "--base-path",
|
18
18
|
no_dotfile: "--no-dotfile",
|
19
|
+
warnings: "--[no-]warnings",
|
19
20
|
paths: nil
|
20
21
|
}.freeze
|
21
22
|
|
22
|
-
PATH_FLAGS = [:paths, :
|
23
|
+
PATH_FLAGS = [:paths, :helper_paths, :load_paths, :prepend_paths, :exclude_paths].freeze
|
23
24
|
MOST_RECENTLY_MODIFIED_TAG = "MOST_RECENTLY_MODIFIED".freeze
|
24
|
-
|
25
|
-
|
26
|
-
:
|
25
|
+
CONFIG_ATTRIBUTES = [
|
26
|
+
:paths, :seed, :no_helper, :verbose, :reporter,
|
27
|
+
:helper_paths, :load_paths, :parallel, :names, :fail_fast, :no_emoji,
|
27
28
|
:prepend_paths, :no_prepend, :exclude_paths, :exclude_names, :base_path,
|
28
|
-
:no_dotfile,
|
29
|
+
:no_dotfile, :warnings,
|
29
30
|
# Internal properties
|
30
|
-
:config_intended_for_merge_only, :seed_set_intentionally, :cli_defaults
|
31
|
-
|
31
|
+
:config_intended_for_merge_only, :seed_set_intentionally, :cli_defaults
|
32
|
+
].freeze
|
33
|
+
|
34
|
+
Config = Struct.new(*CONFIG_ATTRIBUTES, keyword_init: true) do
|
32
35
|
def initialize(**args)
|
33
36
|
unless args[:config_intended_for_merge_only]
|
34
37
|
change_working_directory_because_i_am_bad_and_i_should_feel_bad!(args[:base_path])
|
@@ -60,20 +63,21 @@ class TLDR
|
|
60
63
|
no_prepend: false,
|
61
64
|
exclude_paths: [],
|
62
65
|
exclude_names: [],
|
63
|
-
base_path: nil
|
66
|
+
base_path: nil,
|
67
|
+
warnings: true
|
64
68
|
}
|
65
69
|
|
66
70
|
if cli_defaults
|
67
71
|
common.merge(
|
68
72
|
paths: Dir["test/**/*_test.rb", "test/**/test_*.rb"],
|
69
|
-
|
73
|
+
helper_paths: ["test/helper.rb"],
|
70
74
|
load_paths: ["test"],
|
71
75
|
prepend_paths: [MOST_RECENTLY_MODIFIED_TAG]
|
72
76
|
)
|
73
77
|
else
|
74
78
|
common.merge(
|
75
79
|
paths: [],
|
76
|
-
|
80
|
+
helper_paths: [],
|
77
81
|
load_paths: [],
|
78
82
|
prepend_paths: []
|
79
83
|
)
|
@@ -92,17 +96,17 @@ class TLDR
|
|
92
96
|
defaults = Config.build_defaults(cli_defaults: merged_args[:cli_defaults])
|
93
97
|
|
94
98
|
# Arrays
|
95
|
-
[:paths, :load_paths, :names, :prepend_paths, :exclude_paths, :exclude_names].each do |key|
|
99
|
+
[:paths, :helper_paths, :load_paths, :names, :prepend_paths, :exclude_paths, :exclude_names].each do |key|
|
96
100
|
merged_args[key] = defaults[key] if merged_args[key].nil? || merged_args[key].empty?
|
97
101
|
end
|
98
102
|
|
99
103
|
# Booleans
|
100
|
-
[:no_helper, :verbose, :fail_fast, :no_emoji, :no_prepend].each do |key|
|
104
|
+
[:no_helper, :verbose, :fail_fast, :no_emoji, :no_prepend, :warnings].each do |key|
|
101
105
|
merged_args[key] = defaults[key] if merged_args[key].nil?
|
102
106
|
end
|
103
107
|
|
104
108
|
# Values
|
105
|
-
[:seed, :reporter
|
109
|
+
[:seed, :reporter].each do |key|
|
106
110
|
merged_args[key] ||= defaults[key]
|
107
111
|
end
|
108
112
|
|
@@ -166,7 +170,7 @@ class TLDR
|
|
166
170
|
# Don't print prepended tests if they're disabled
|
167
171
|
next
|
168
172
|
end
|
169
|
-
elsif key == :
|
173
|
+
elsif key == :helper_paths && no_helper
|
170
174
|
# Don't print the helper if it's disabled
|
171
175
|
next
|
172
176
|
elsif key == :parallel
|
@@ -176,6 +180,8 @@ class TLDR
|
|
176
180
|
"--parallel"
|
177
181
|
end
|
178
182
|
next val
|
183
|
+
elsif key == :warnings && defaults[:warnings] != self[:warnings]
|
184
|
+
next warnings ? "--warnings" : "--no-warnings"
|
179
185
|
end
|
180
186
|
|
181
187
|
if defaults[key] == self[key]
|
@@ -231,6 +237,9 @@ class TLDR
|
|
231
237
|
if dotfile_args.key?(:reporter)
|
232
238
|
dotfile_args[:reporter] = Kernel.const_get(dotfile_args[:reporter])
|
233
239
|
end
|
240
|
+
if (invalid_args = dotfile_args.except(*CONFIG_ATTRIBUTES)).any?
|
241
|
+
raise Error, "Invalid keys in .tldr.yml file: #{invalid_args.keys.join(", ")}"
|
242
|
+
end
|
234
243
|
|
235
244
|
dotfile_args.merge(args)
|
236
245
|
end
|
data/lib/tldr/value/plan.rb
CHANGED
data/lib/tldr/value/test.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
class TLDR
|
2
|
-
Test = Struct.new :
|
2
|
+
Test = Struct.new :test_class, :method_name do
|
3
3
|
attr_reader :file, :line, :location
|
4
4
|
|
5
5
|
def initialize(*args)
|
6
6
|
super
|
7
|
-
@file, @line = SorbetCompatibility.unwrap_method(
|
7
|
+
@file, @line = SorbetCompatibility.unwrap_method(test_class.instance_method(method_name)).source_location
|
8
8
|
@location = Location.new file, line
|
9
9
|
end
|
10
10
|
|
11
11
|
# Memoizing at call time, because re-parsing isn't free and isn't usually necessary
|
12
12
|
def end_line
|
13
13
|
@end_line ||= begin
|
14
|
-
test_method = SorbetCompatibility.unwrap_method
|
14
|
+
test_method = SorbetCompatibility.unwrap_method test_class.instance_method(method_name)
|
15
15
|
RubyVM::AbstractSyntaxTree.of(test_method).last_lineno
|
16
16
|
end
|
17
17
|
end
|
@@ -6,8 +6,8 @@ class TLDR
|
|
6
6
|
@tests ||= configuration.flat_map { |(klass, method)|
|
7
7
|
klass = Kernel.const_get(klass) if klass.is_a? String
|
8
8
|
if method.nil?
|
9
|
-
klass.
|
10
|
-
|
9
|
+
([klass] + ClassUtil.gather_descendants(klass)).flat_map { |klass|
|
10
|
+
ClassUtil.gather_tests(klass)
|
11
11
|
}
|
12
12
|
else
|
13
13
|
Test.new klass, method
|
data/lib/tldr/version.rb
CHANGED
data/lib/tldr.rb
CHANGED
@@ -3,9 +3,10 @@ require "concurrent-ruby"
|
|
3
3
|
require_relative "tldr/argv_parser"
|
4
4
|
require_relative "tldr/assertions"
|
5
5
|
require_relative "tldr/backtrace_filter"
|
6
|
+
require_relative "tldr/class_util"
|
6
7
|
require_relative "tldr/error"
|
8
|
+
require_relative "tldr/executor"
|
7
9
|
require_relative "tldr/parallel_controls"
|
8
|
-
require_relative "tldr/parallelizer"
|
9
10
|
require_relative "tldr/path_util"
|
10
11
|
require_relative "tldr/planner"
|
11
12
|
require_relative "tldr/reporters"
|
@@ -40,6 +41,8 @@ class TLDR
|
|
40
41
|
def self.at_exit! config = Config.new
|
41
42
|
# Ignore at_exit when running tldr CLI, since that will run any tests
|
42
43
|
return if $PROGRAM_NAME.end_with? "tldr"
|
44
|
+
# Also ignore if we're running from within our rake task
|
45
|
+
return if caller.any? { |line| line.include? "lib/tldr/rake.rb" }
|
43
46
|
# Ignore at_exit when we've already registered an at_exit hook
|
44
47
|
return if @@at_exit_registered
|
45
48
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tldr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Searls
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-09-
|
12
|
+
date: 2023-09-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: super_diff
|
@@ -59,9 +59,10 @@ files:
|
|
59
59
|
- lib/tldr/assertions.rb
|
60
60
|
- lib/tldr/assertions/minitest_compatibility.rb
|
61
61
|
- lib/tldr/backtrace_filter.rb
|
62
|
+
- lib/tldr/class_util.rb
|
62
63
|
- lib/tldr/error.rb
|
64
|
+
- lib/tldr/executor.rb
|
63
65
|
- lib/tldr/parallel_controls.rb
|
64
|
-
- lib/tldr/parallelizer.rb
|
65
66
|
- lib/tldr/path_util.rb
|
66
67
|
- lib/tldr/planner.rb
|
67
68
|
- lib/tldr/rake.rb
|
@@ -106,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
107
|
- !ruby/object:Gem::Version
|
107
108
|
version: '0'
|
108
109
|
requirements: []
|
109
|
-
rubygems_version: 3.4.
|
110
|
+
rubygems_version: 3.4.17
|
110
111
|
signing_key:
|
111
112
|
specification_version: 4
|
112
113
|
summary: TLDR will run your tests, but only for 1.8 seconds.
|