tldr 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/README.md +142 -83
- data/lib/tldr/argv_parser.rb +4 -8
- data/lib/tldr/parallel_controls.rb +47 -0
- data/lib/tldr/parallelizer.rb +26 -14
- data/lib/tldr/path_util.rb +41 -0
- data/lib/tldr/planner.rb +6 -44
- data/lib/tldr/rake.rb +40 -3
- data/lib/tldr/reporters/default.rb +7 -2
- data/lib/tldr/reporters/icon_provider.rb +8 -0
- data/lib/tldr/runner.rb +11 -2
- data/lib/tldr/strategizer.rb +38 -5
- data/lib/tldr/value/config.rb +54 -31
- data/lib/tldr/version.rb +1 -1
- data/lib/tldr.rb +2 -1
- metadata +4 -3
- data/lib/tldr/run_these_together.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32f7b9dbcf9e09433eef762eaafe19804643aa543ac2c0a9599303ea2e960f25
|
4
|
+
data.tar.gz: 789c07fb2db1fad39b234976999b575a1bea27c4796a4d062a373df11d3716e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7975ec24ae3f080ed564666c5319928964c970794a740a21253551be1be9b945374394b2f860cdd8737151f8e21fdb0d592c126895dd02b91dd21c899824226a
|
7
|
+
data.tar.gz: b11f3c52624b63a7aa44eadd4f17aa662d134648d61eb55eb7124e7956d0b28f20a04545552f9af8710330ffa98c94e85cec87dc80715a532522ccdeb15a8b42
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
-
## [
|
1
|
+
## [0.5.0]
|
2
|
+
|
3
|
+
* Define your own Rake tasks with `TLDR::Task` and pass in a custom configuration
|
4
|
+
* Any tests with `--prepend` AND marked thread-unsafe with `dont_run_these_in_parallel`
|
5
|
+
will be run BEFORE the parallel tests start running. This way if you're working
|
6
|
+
on a non-parallelizable test, running `tldr` will automatically run it first
|
7
|
+
thing
|
8
|
+
* Stop printing `--seed` in run commands, since it can be confusing to discover
|
9
|
+
that will disable `--parallel`. Instead, print the seed option beneath
|
10
|
+
|
11
|
+
## [0.4.0]
|
12
|
+
|
13
|
+
* Add `TLDR.dont_run_these_in_parallel!` method to allow tests to indicate that they
|
14
|
+
must be run in isolation and not concurrently with any other tests
|
15
|
+
* Add support for `around` hooks (similar to [minitest-around](https://github.com/splattael/minitest-around))
|
2
16
|
|
3
17
|
## [0.3.0]
|
4
18
|
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# TLDR - for people who don't have time for slow tests
|
2
2
|
|
3
|
-
|
3
|
+
Okay, you might need to sit down for this:
|
4
|
+
|
5
|
+
**tl;dr, TLDR is a Ruby test framework that stops running your tests after 1.8 seconds.**
|
4
6
|
|
5
7
|
We initially meant this as a joke [while
|
6
8
|
pairin'](https://www.youtube.com/live/bmi-SWeH4MA?si=p5g1j1FQZrbYEOCg&t=63), but
|
@@ -9,16 +11,28 @@ in addition to being funny, it was also a pretty good idea. So we fleshed out
|
|
9
11
|
[Minitest-compatible](#minitest-compatibility), and downright pleasant test
|
10
12
|
framework for Ruby.
|
11
13
|
|
14
|
+
The "big idea" here is TLDR is designed for users to run the `tldr` command
|
15
|
+
repeatedly as they work—as opposed to only running the tests for whatever is
|
16
|
+
being worked on. Even if the suite run over the 1.8 second time limit. Because
|
17
|
+
TLDR shuffles and runs in parallel and is guaranteed to take less than two
|
18
|
+
seconds,
|
19
|
+
**you'll actually wind up running _all_ of your tests quite often as you work**,
|
20
|
+
catching any problems much earlier than if you had waited until the end of the
|
21
|
+
day to push your work and let a continuous integration server run the full
|
22
|
+
suite.
|
23
|
+
|
12
24
|
Some stuff you might like:
|
13
25
|
|
14
26
|
* A CLI that can run tests by line number(s) (e.g. `foo.rb:5 bar.rb:3:10`) and
|
15
27
|
by names or patterns (e.g. `--name test_fail,test_error --name "/_\d/"`)
|
16
|
-
* Everything is **parallel by default**, and seems pretty fast
|
17
|
-
|
28
|
+
* Everything is **parallel by default**, and seems pretty darn fast; TLDR
|
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
|
+
* Surprisingly delightful color diff output when two things fail to equal one
|
31
|
+
another, care of [@mcmire's super_diff gem](https://github.com/mcmire/super_diff)
|
18
32
|
* By default, the CLI will prepend your most-recently-edited test file to the
|
19
|
-
front of your suite so its tests will run first. The
|
20
|
-
the most likely
|
21
|
-
`--prepend` option)
|
33
|
+
front of your suite so its tests will run first. The test you worked on most recently
|
34
|
+
is the one you most likely want to ensure runs, so TLDR runs it first (see the
|
35
|
+
`--prepend` option for how to control this behavior)
|
22
36
|
* And, of course, our signature feature: your test suite will never grow into
|
23
37
|
a glacially slow, soul-sucking albatross around your neck, because **after 1.8
|
24
38
|
seconds, it stops running your tests**, with a report on what it _was_ able to
|
@@ -50,7 +64,7 @@ end
|
|
50
64
|
```
|
51
65
|
|
52
66
|
A TLDR subclass defines its tests with instance methods that begin with
|
53
|
-
`
|
67
|
+
`test_`. They can define `setup` and/or `teardown` methods which will run before
|
54
68
|
and after each test, respectively.
|
55
69
|
|
56
70
|
If you place your tests in `test/**/*_test.rb` (and/or `test/**/test_*.rb`)
|
@@ -82,68 +96,9 @@ flags:
|
|
82
96
|
$ tldr --name FooTest#test_foo -n test_bar,test_baz -n /_qux/
|
83
97
|
```
|
84
98
|
|
85
|
-
(The above will translate to this array of name
|
99
|
+
(The above will translate to this array of name filters internally:
|
86
100
|
`["FooTest#test_foo", "test_bar", "test_baz", "/_qux/"]`.)
|
87
101
|
|
88
|
-
### Running tests without the CLI
|
89
|
-
|
90
|
-
If you'd rather use TLDR by running Ruby files instead of the `tldr` CLI
|
91
|
-
(similar to `require "minitest/autorun"`), here's how to do it!
|
92
|
-
|
93
|
-
Given a file `test/some_test.rb`:
|
94
|
-
|
95
|
-
```ruby
|
96
|
-
require "tldr"
|
97
|
-
TLDR::Run.at_exit! TLDR::Config.new(no_emoji: true)
|
98
|
-
|
99
|
-
class SomeTest < TLDR
|
100
|
-
def test_truth
|
101
|
-
assert true
|
102
|
-
end
|
103
|
-
end
|
104
|
-
```
|
105
|
-
|
106
|
-
You could run the test with:
|
107
|
-
|
108
|
-
```
|
109
|
-
$ ruby test/some_test.rb
|
110
|
-
```
|
111
|
-
|
112
|
-
To maximize control and to avoid running code accidentally (and _unlike_ the
|
113
|
-
`tldr` CLI), running `at_exit!` will not set default values to the `paths`,
|
114
|
-
`helper`, `load_paths`, and `prepend_tests` config properties. You'll have to
|
115
|
-
pass any values you want to set on a [Config object](/lib/tldr/value/config.rb)
|
116
|
-
and pass it to `at_exit!`.
|
117
|
-
|
118
|
-
To avoid running multiple suites accidentally, if `TLDR::Run.at_exit!` is
|
119
|
-
encountered multiple times, only the first hook will be registered. If the
|
120
|
-
`tldr` CLI is running and encounters a call to `at_exit!`, it will be ignored.
|
121
|
-
|
122
|
-
#### Setting up the load path
|
123
|
-
|
124
|
-
When running TLDR from a Ruby script, one thing the framework can't help you with
|
125
|
-
is setting up load paths for you.
|
126
|
-
|
127
|
-
If you want to require code in `test/` or `lib/` without using
|
128
|
-
`require_relative`, you'll need to add those directories to the load path. You
|
129
|
-
can do this programmatically by prepending the path to `$LOAD_PATH`, like
|
130
|
-
this:
|
131
|
-
|
132
|
-
```ruby
|
133
|
-
$LOAD_PATH.unshift "test"
|
134
|
-
|
135
|
-
require "tldr"
|
136
|
-
TLDR::Run.at_exit! TLDR::Config.new(no_emoji: true)
|
137
|
-
|
138
|
-
require "helper"
|
139
|
-
```
|
140
|
-
|
141
|
-
Or by using Ruby's `-I` flag to include it:
|
142
|
-
|
143
|
-
```
|
144
|
-
$ ruby -Itest test/some_test.rb
|
145
|
-
```
|
146
|
-
|
147
102
|
### Options
|
148
103
|
|
149
104
|
Here is the full list of CLI options:
|
@@ -192,7 +147,8 @@ well as some [internal tests](/tests/dotfile_test.rb) demonstrating its behavior
|
|
192
147
|
Tests you write with tldr are designed to be mostly-compatible with
|
193
148
|
[Minitest](https://github.com/minitest/minitest) tests. Some notes:
|
194
149
|
|
195
|
-
* `setup` and `teardown` hook methods should work as you expect
|
150
|
+
* `setup` and `teardown` hook methods should work as you expect. (We even threw
|
151
|
+
in [an `around` hook](https://github.com/splattael/minitest-around) as a bonus!)
|
196
152
|
* All of Minitest's assertions (e.g. `assert`, `assert_equals`) are provided,
|
197
153
|
with these caveats:
|
198
154
|
* To retain the `expected, actual` argument ordering, `tldr` defines
|
@@ -201,7 +157,7 @@ with these caveats:
|
|
201
157
|
* If you want to maximize compatibility and mix in `assert_includes` and the
|
202
158
|
deprecated `assert_send`, just `include
|
203
159
|
TLDR::Assertions::MinitestCompatibility` into the `TLDR` base class or
|
204
|
-
individual test
|
160
|
+
individual test classesJust set it
|
205
161
|
|
206
162
|
### Running TLDR with Rake
|
207
163
|
|
@@ -225,7 +181,92 @@ You could then run the task with:
|
|
225
181
|
$ TLDR_OPTS="--no-parallel" bundle exec rake tldr
|
226
182
|
```
|
227
183
|
|
228
|
-
|
184
|
+
One reason you'd want to invoke TLDR with Rake is because you have multiple
|
185
|
+
test suites that you want to be able to conveniently run separately ([this
|
186
|
+
talk](https://blog.testdouble.com/talks/2014-05-25-breaking-up-with-your-test-suite/)
|
187
|
+
discussed a few reasons why this can be useful).
|
188
|
+
|
189
|
+
To create a custom TLDR Rake test, just instantiate `TLDR::Task` like this:
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
require "tldr/rake"
|
193
|
+
|
194
|
+
TLDR::Task.new(name: :safe_tests, config: TLDR::Config.new(
|
195
|
+
paths: FileList["safe/**/*_test.rb"],
|
196
|
+
helper: "safe/helper.rb",
|
197
|
+
load_paths: ["lib", "safe"]
|
198
|
+
))
|
199
|
+
```
|
200
|
+
|
201
|
+
The above will create a second Rake task named `safe_tests` running a different
|
202
|
+
set of tests than the default `tldr` task. Here's [an
|
203
|
+
example](/example/b/Rakefile).
|
204
|
+
|
205
|
+
### Running tests without the CLI
|
206
|
+
|
207
|
+
If you'd rather use TLDR by running Ruby files instead of the `tldr` CLI
|
208
|
+
(similar to `require "minitest/autorun"`), here's how to do it!
|
209
|
+
|
210
|
+
Given a file `test/some_test.rb`:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
require "tldr"
|
214
|
+
TLDR::Run.at_exit! TLDR::Config.new(no_emoji: true)
|
215
|
+
|
216
|
+
class SomeTest < TLDR
|
217
|
+
def test_truth
|
218
|
+
assert true
|
219
|
+
end
|
220
|
+
end
|
221
|
+
```
|
222
|
+
|
223
|
+
You could run the test with:
|
224
|
+
|
225
|
+
```
|
226
|
+
$ ruby test/some_test.rb
|
227
|
+
```
|
228
|
+
|
229
|
+
To maximize control and to avoid running code accidentally (and _unlike_ the
|
230
|
+
`tldr` CLI), running `at_exit!` will not set default values to the `paths`,
|
231
|
+
`helper`, `load_paths`, and `prepend_paths` config properties. You'll have to
|
232
|
+
pass any values you want to set on a [Config object](/lib/tldr/value/config.rb)
|
233
|
+
and pass it to `at_exit!`.
|
234
|
+
|
235
|
+
To avoid running multiple suites accidentally, if `TLDR::Run.at_exit!` is
|
236
|
+
encountered multiple times, only the first hook will be registered. If the
|
237
|
+
`tldr` CLI is running and encounters a call to `at_exit!`, it will be ignored.
|
238
|
+
|
239
|
+
#### Setting up the load path
|
240
|
+
|
241
|
+
When running TLDR from a Ruby script, one thing the framework can't help you with
|
242
|
+
is setting up load paths for you.
|
243
|
+
|
244
|
+
If you want to require code in `test/` or `lib/` without using
|
245
|
+
`require_relative`, you'll need to add those directories to the load path. You
|
246
|
+
can do this programmatically by prepending the path to `$LOAD_PATH`, like
|
247
|
+
this:
|
248
|
+
|
249
|
+
```ruby
|
250
|
+
$LOAD_PATH.unshift "test"
|
251
|
+
|
252
|
+
require "tldr"
|
253
|
+
TLDR::Run.at_exit! TLDR::Config.new(no_emoji: true)
|
254
|
+
|
255
|
+
require "helper"
|
256
|
+
```
|
257
|
+
|
258
|
+
Or by using Ruby's `-I` flag to include it:
|
259
|
+
|
260
|
+
```
|
261
|
+
$ ruby -Itest test/some_test.rb
|
262
|
+
```
|
263
|
+
|
264
|
+
## Questions you might be asking
|
265
|
+
|
266
|
+
TLDR is very similar to Minitest in API, but different in enough ways that you
|
267
|
+
probably have some questions.
|
268
|
+
|
269
|
+
### Parallel-by-default is nice in theory but half my tests are failing. Wat?
|
229
270
|
|
230
271
|
**Read this before you add `--no-parallel` because some tests are failing when
|
231
272
|
you run `tldr`.**
|
@@ -234,17 +275,17 @@ The vast majority of test suites in the wild are not parallelized and the vast
|
|
234
275
|
majority of _those_ will only parallelize by forking processes as opposed to
|
235
276
|
using a thread pool. We wanted to encourage more people to save time (after all,
|
236
277
|
you only get 1.8 seconds here) by making your test suite run as fast as it can,
|
237
|
-
so your tests run in parallel by default.
|
278
|
+
so your tests run in parallel threads by default.
|
238
279
|
|
239
280
|
If you're writing new code and tests with TLDR and dutifully running `tldr`
|
240
281
|
constantly for fast feedback, odds are that this will help you catch thread
|
241
|
-
safety issues early—this is a good thing, because it gives you a chance to
|
242
|
-
them! But maybe you're porting an
|
243
|
-
parallel for the first time, or maybe
|
244
|
-
_can't_ be exercised in a thread-safe
|
245
|
-
give you some tools to prevent you from
|
246
|
-
your entire test suite and **slowing
|
247
|
-
tests**.
|
282
|
+
safety issues early—this is a good thing, because it gives you a chance to
|
283
|
+
address them before they're too hard to fix! But maybe you're porting an
|
284
|
+
existing test suite to TLDR and running in parallel for the first time, or maybe
|
285
|
+
you need to test something that simply _can't_ be exercised in a thread-safe
|
286
|
+
way. For those cases, TLDR's goal is to give you some tools to prevent you from
|
287
|
+
giving up and adding `--no-parallel` to your entire test suite and **slowing
|
288
|
+
everything down for the sake of a few tests**.
|
248
289
|
|
249
290
|
So, when you see a test that is failing when run in parallel with the rest of your
|
250
291
|
suite, here is what we recommend doing, in priority order:
|
@@ -254,11 +295,17 @@ thread-safe. Modern versions of Ruby provide a number of tools to make this
|
|
254
295
|
easier than it used to be, and it may be as simple as making an instance
|
255
296
|
variable thread-local
|
256
297
|
2. If the problem is that a subset of your tests depend on the same resource,
|
257
|
-
try using [TLDR.run_these_together!](
|
298
|
+
try using [TLDR.run_these_together!](lib/tldr/parallel_controls.rb) class to
|
258
299
|
group the tests together. This will ensure that those tests run in the same
|
259
|
-
thread in sequence (here's a
|
300
|
+
thread in sequence (here's a [simple
|
260
301
|
example](/tests/fixture/run_these_together.rb))
|
261
|
-
3.
|
302
|
+
3. For tests that affect process-wide resources like setting the system clock or
|
303
|
+
changing the process's working directory (i.e. `Dir.chdir`), you can sequester
|
304
|
+
them to run sequentially _after_ all parallel tests in your suite have run with
|
305
|
+
[TLDR.dont_run_these_in_parallel!](lib/tldr/parallel_controls.rb), which takes
|
306
|
+
the same arguments as `run_these_together!`
|
307
|
+
([example](/tests/fixture/dont_run_these_in_parallel.rb))
|
308
|
+
4. Give up and make the whole suite `--no-parallel`. If you find that you need
|
262
309
|
to resort to this, you might save some keystrokes by adding `parallel: false` in
|
263
310
|
a [.tldr.yml](#setting-defaults-in-tldryml) file
|
264
311
|
|
@@ -267,11 +314,23 @@ your suite without slowing down the rest of your tests, so stay tuned!
|
|
267
314
|
|
268
315
|
### How will I run all my tests in CI without the time bomb going off?
|
269
316
|
|
270
|
-
TLDR will run all your tests in CI without the time bomb going off.
|
317
|
+
TLDR will run all your tests in CI without the time bomb going off. If
|
318
|
+
`tldr` is run in a non-interactive shell and a `CI` environment variable is set
|
319
|
+
(as it is on virtually every CI service), then the bomb will be defused.
|
320
|
+
|
321
|
+
### Is there a plugin system?
|
322
|
+
|
323
|
+
There is not.
|
324
|
+
|
325
|
+
Currently, the only pluggable aspect of TLDR are reporters, which can be set
|
326
|
+
with the `--reporter` command line option. It can be set to any fully-qualified
|
327
|
+
class name that extends from
|
328
|
+
[TLDR::Reporters::Base](/lib/tldr/reporters/base.rb).
|
271
329
|
|
272
330
|
### What about mocking?
|
273
331
|
|
274
|
-
TLDR is laser-focused on running tests
|
332
|
+
TLDR is laser-focused on running tests, so it doesn't provide a built-in mocking
|
333
|
+
facility. Might we interest you in a refreshing
|
275
334
|
[mocktail](https://github.com/testdouble/mocktail), instead?
|
276
335
|
|
277
336
|
## Acknowledgements
|
data/lib/tldr/argv_parser.rb
CHANGED
@@ -4,11 +4,7 @@ class TLDR
|
|
4
4
|
class ArgvParser
|
5
5
|
PATTERN_FRIENDLY_SPLITTER = /,(?=(?:[^\/]*\/[^\/]*\/)*[^\/]*$)/
|
6
6
|
|
7
|
-
def parse(args)
|
8
|
-
options = {
|
9
|
-
cli_mode: true
|
10
|
-
}
|
11
|
-
|
7
|
+
def parse(args, options = {cli_defaults: true})
|
12
8
|
OptionParser.new do |opts|
|
13
9
|
opts.banner = "Usage: tldr [options] some_tests/**/*.rb some/path.rb:13 ..."
|
14
10
|
|
@@ -47,9 +43,9 @@ class TLDR
|
|
47
43
|
options[:no_helper] = true
|
48
44
|
end
|
49
45
|
|
50
|
-
opts.on "#{CONFLAGS[:
|
51
|
-
options[:
|
52
|
-
options[:
|
46
|
+
opts.on "#{CONFLAGS[:prepend_paths]} PATH", Array, "Prepend one or more paths to run before the rest (Default: most recently modified test)" do |prepend|
|
47
|
+
options[:prepend_paths] ||= []
|
48
|
+
options[:prepend_paths] += prepend
|
53
49
|
end
|
54
50
|
|
55
51
|
opts.on CONFLAGS[:no_prepend], "Don't prepend any tests before the rest of the suite" do
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class TLDR
|
2
|
+
# If it's not safe to run a set of tests in parallel, you can force them to
|
3
|
+
# run in a group together (in a single worker) with `run_these_together!` in
|
4
|
+
# your test.
|
5
|
+
#
|
6
|
+
# This method takes an array of tuples, where the first element is the class
|
7
|
+
# (or its name as a string, if the class is not yet defined in the current
|
8
|
+
# file) and the second element is the method name. If the second element is
|
9
|
+
# nil, then all the tests on the class will be run together.
|
10
|
+
#
|
11
|
+
# Examples:
|
12
|
+
# - `run_these_together!` will run all the tests defined on the current
|
13
|
+
# class to be run in a group
|
14
|
+
# - `run_these_together!([[ClassA, nil], ["ClassB", :test_1], [ClassB, :test_2]])`
|
15
|
+
# will run all the tests defined on ClassA, and test_1 and test_2 from ClassB
|
16
|
+
#
|
17
|
+
GROUPED_TESTS = Concurrent::Array.new
|
18
|
+
def self.run_these_together! klass_method_tuples = [[self, nil]]
|
19
|
+
GROUPED_TESTS << TestGroup.new(klass_method_tuples)
|
20
|
+
end
|
21
|
+
|
22
|
+
# This is a similar API to run_these_together! but its effect is more drastic
|
23
|
+
# Rather than running the provided (class, method) tuples in a group within a
|
24
|
+
# thread as part of a parallel run, it will reserve all tests specified by
|
25
|
+
# all calls to `dont_run_these_in_parallel!` to be run after all parallel tests have
|
26
|
+
# finished.
|
27
|
+
#
|
28
|
+
# This has an important implication! If your test suite is over TLDR's time
|
29
|
+
# limit, it means that these tests will never be run outside of CI unless you
|
30
|
+
# run them manually.
|
31
|
+
#
|
32
|
+
# Like `run_these_together!`, `dont_run_these_in_parallel!` takes an array of
|
33
|
+
# tuples, where the first element is the class (or its fully-qualified name as
|
34
|
+
# a string) and the second element is `nil` (matching all the class's test
|
35
|
+
# methods) or else one of the methods on the class.
|
36
|
+
#
|
37
|
+
# Examples:
|
38
|
+
# - `dont_run_these_in_parallel!` will run all the tests defined on the current
|
39
|
+
# class after all parallel tests have finished
|
40
|
+
# - `dont_run_these_in_parallel!([[ClassA, nil], ["ClassB", :test_1], [ClassB, :test_2]])`
|
41
|
+
# will run all the tests defined on ClassA, and test_1 and test_2 from ClassB
|
42
|
+
#
|
43
|
+
THREAD_UNSAFE_TESTS = Concurrent::Array.new
|
44
|
+
def self.dont_run_these_in_parallel! klass_method_tuples = [[self, nil]]
|
45
|
+
THREAD_UNSAFE_TESTS << TestGroup.new(klass_method_tuples)
|
46
|
+
end
|
47
|
+
end
|
data/lib/tldr/parallelizer.rb
CHANGED
@@ -2,35 +2,47 @@ class TLDR
|
|
2
2
|
class Parallelizer
|
3
3
|
def initialize
|
4
4
|
@strategizer = Strategizer.new
|
5
|
-
|
6
|
-
|
7
|
-
def parallelize tests, parallel, &blk
|
8
|
-
return tests.map(&blk) if tests.size < 2 || !parallel
|
9
|
-
tldr_pool = Concurrent::ThreadPoolExecutor.new(
|
5
|
+
@thread_pool = Concurrent::ThreadPoolExecutor.new(
|
10
6
|
name: "tldr",
|
11
7
|
auto_terminate: true
|
12
8
|
)
|
9
|
+
end
|
10
|
+
|
11
|
+
def parallelize all_tests, config, &blk
|
12
|
+
return run_in_sequence(all_tests, &blk) if all_tests.size < 2 || !config.parallel
|
13
|
+
|
14
|
+
strategy = @strategizer.strategize(
|
15
|
+
all_tests,
|
16
|
+
GROUPED_TESTS,
|
17
|
+
THREAD_UNSAFE_TESTS,
|
18
|
+
(config.no_prepend ? [] : config.prepend_paths)
|
19
|
+
)
|
13
20
|
|
14
|
-
strategy
|
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)
|
24
|
+
end
|
15
25
|
|
16
|
-
|
26
|
+
private
|
27
|
+
|
28
|
+
def run_in_sequence tests, &blk
|
29
|
+
tests.map(&blk)
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_in_parallel tests_and_groups, &blk
|
33
|
+
tests_and_groups.map { |test_or_group|
|
17
34
|
tests_to_run = if test_or_group.group?
|
18
|
-
test_or_group.tests
|
35
|
+
test_or_group.tests
|
19
36
|
else
|
20
37
|
[test_or_group]
|
21
38
|
end
|
22
39
|
|
23
40
|
unless tests_to_run.empty?
|
24
|
-
Concurrent::Promises.future_on(
|
41
|
+
Concurrent::Promises.future_on(@thread_pool) {
|
25
42
|
tests_to_run.map(&blk)
|
26
43
|
}
|
27
44
|
end
|
28
45
|
}.compact.flat_map(&:value)
|
29
46
|
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def substitute_tests_grouped_by_run_these_together! tests
|
34
|
-
end
|
35
47
|
end
|
36
48
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class TLDR
|
2
|
+
module PathUtil
|
3
|
+
def self.expand_search_locations path_strings
|
4
|
+
path_strings.flat_map { |path_string|
|
5
|
+
File.directory?(path_string) ? Dir["#{path_string}/**/*.rb"] : path_string
|
6
|
+
}.flat_map { |path_string|
|
7
|
+
absolute_path = File.expand_path(path_string.gsub(/:.*$/, ""), Dir.pwd)
|
8
|
+
line_numbers = path_string.scan(/:(\d+)/).flatten.map(&:to_i)
|
9
|
+
|
10
|
+
if line_numbers.any?
|
11
|
+
line_numbers.map { |line_number| Location.new absolute_path, line_number }
|
12
|
+
else
|
13
|
+
[Location.new(absolute_path, nil)]
|
14
|
+
end
|
15
|
+
}.uniq
|
16
|
+
end
|
17
|
+
|
18
|
+
# Because search paths to TLDR can include line numbers (e.g. a.rb:4), we
|
19
|
+
# can't just pass everything to Dir.glob. Instead, we have to check whether
|
20
|
+
# a user-provided search path looks like a glob, and if so, expand it
|
21
|
+
#
|
22
|
+
# Globby characters specified here:
|
23
|
+
# https://ruby-doc.org/3.2.2/Dir.html#method-c-glob
|
24
|
+
def self.expand_globs search_paths
|
25
|
+
search_paths.flat_map { |search_path|
|
26
|
+
if search_path.match?(/[*?\[\]{}]/)
|
27
|
+
raise Error, "Can't combine globs and line numbers in: #{search_path}" if search_path.match?(/:(\d+)$/)
|
28
|
+
Dir[search_path]
|
29
|
+
else
|
30
|
+
search_path
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.locations_include_test? locations, test
|
36
|
+
locations.any? { |location|
|
37
|
+
location.file == test.file && (location.line.nil? || test.covers_line?(location.line))
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/tldr/planner.rb
CHANGED
@@ -3,7 +3,7 @@ require "pathname"
|
|
3
3
|
class TLDR
|
4
4
|
class Planner
|
5
5
|
def plan config
|
6
|
-
search_locations = expand_search_locations config.paths
|
6
|
+
search_locations = PathUtil.expand_search_locations config.paths
|
7
7
|
|
8
8
|
prepend_load_paths config
|
9
9
|
require_test_helper config
|
@@ -32,21 +32,6 @@ class TLDR
|
|
32
32
|
|
33
33
|
private
|
34
34
|
|
35
|
-
def expand_search_locations path_strings
|
36
|
-
path_strings.flat_map { |path_string|
|
37
|
-
File.directory?(path_string) ? Dir["#{path_string}/**/*.rb"] : path_string
|
38
|
-
}.flat_map { |path_string|
|
39
|
-
absolute_path = File.expand_path(path_string.gsub(/:.*$/, ""), Dir.pwd)
|
40
|
-
line_numbers = path_string.scan(/:(\d+)/).flatten.map(&:to_i)
|
41
|
-
|
42
|
-
if line_numbers.any?
|
43
|
-
line_numbers.map { |line_number| Location.new absolute_path, line_number }
|
44
|
-
else
|
45
|
-
[Location.new(absolute_path, nil)]
|
46
|
-
end
|
47
|
-
}.uniq
|
48
|
-
end
|
49
|
-
|
50
35
|
def gather_tests
|
51
36
|
gather_descendants(TLDR).flat_map { |subklass|
|
52
37
|
subklass.instance_methods.grep(/^test_/).sort.map { |method|
|
@@ -57,9 +42,9 @@ class TLDR
|
|
57
42
|
|
58
43
|
def prepend tests, config
|
59
44
|
return tests if config.no_prepend
|
60
|
-
prepended_locations = expand_search_locations expand_globs config.
|
45
|
+
prepended_locations = PathUtil.expand_search_locations PathUtil.expand_globs config.prepend_paths
|
61
46
|
prepended, rest = tests.partition { |test|
|
62
|
-
locations_include_test? prepended_locations, test
|
47
|
+
PathUtil.locations_include_test? prepended_locations, test
|
63
48
|
}
|
64
49
|
prepended + rest
|
65
50
|
end
|
@@ -69,11 +54,11 @@ class TLDR
|
|
69
54
|
end
|
70
55
|
|
71
56
|
def exclude_by_path tests, exclude_paths
|
72
|
-
excluded_locations = expand_search_locations expand_globs exclude_paths
|
57
|
+
excluded_locations = PathUtil.expand_search_locations PathUtil.expand_globs exclude_paths
|
73
58
|
return tests if excluded_locations.empty?
|
74
59
|
|
75
60
|
tests.reject { |test|
|
76
|
-
locations_include_test? excluded_locations, test
|
61
|
+
PathUtil.locations_include_test? excluded_locations, test
|
77
62
|
}
|
78
63
|
end
|
79
64
|
|
@@ -94,7 +79,7 @@ class TLDR
|
|
94
79
|
return tests if line_specific_locations.empty?
|
95
80
|
|
96
81
|
tests.select { |test|
|
97
|
-
locations_include_test? line_specific_locations, test
|
82
|
+
PathUtil.locations_include_test? line_specific_locations, test
|
98
83
|
}
|
99
84
|
end
|
100
85
|
|
@@ -133,29 +118,6 @@ class TLDR
|
|
133
118
|
}
|
134
119
|
end
|
135
120
|
|
136
|
-
def locations_include_test? locations, test
|
137
|
-
locations.any? { |location|
|
138
|
-
location.file == test.file && (location.line.nil? || test.covers_line?(location.line))
|
139
|
-
}
|
140
|
-
end
|
141
|
-
|
142
|
-
# Because search paths to TLDR can include line numbers (e.g. a.rb:4), we
|
143
|
-
# can't just pass everything to Dir.glob. Instead, we have to check whether
|
144
|
-
# a user-provided search path looks like a glob, and if so, expand it
|
145
|
-
#
|
146
|
-
# Globby characters specified here:
|
147
|
-
# https://ruby-doc.org/3.2.2/Dir.html#method-c-glob
|
148
|
-
def expand_globs search_paths
|
149
|
-
search_paths.flat_map { |search_path|
|
150
|
-
if search_path.match?(/[*?\[\]{}]/)
|
151
|
-
raise Error, "Can't combine globs and line numbers in: #{search_path}" if search_path.match?(/:(\d+)$/)
|
152
|
-
Dir[search_path]
|
153
|
-
else
|
154
|
-
search_path
|
155
|
-
end
|
156
|
-
}
|
157
|
-
end
|
158
|
-
|
159
121
|
def expand_names_with_patterns names
|
160
122
|
names.map { |name|
|
161
123
|
if name.is_a?(String) && name =~ /^\/(.*)\/$/
|
data/lib/tldr/rake.rb
CHANGED
@@ -1,4 +1,41 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require "rake"
|
2
|
+
require "shellwords"
|
3
|
+
|
4
|
+
require "tldr"
|
5
|
+
|
6
|
+
class TLDR
|
7
|
+
class Task
|
8
|
+
include Rake::DSL
|
9
|
+
|
10
|
+
def initialize(name: "tldr", config: Config.new)
|
11
|
+
define name, config
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def define name, task_config
|
17
|
+
desc "Run #{name} tests (use TLDR_OPTS or .tldr.yml to configure)"
|
18
|
+
task name do
|
19
|
+
cli_args = build_cli_args(task_config)
|
20
|
+
fail unless system "#{"bundle exec " if defined?(Bundler)}tldr #{cli_args}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_cli_args task_config
|
25
|
+
config = if ENV["TLDR_OPTS"]
|
26
|
+
env_argv = Shellwords.shellwords(ENV["TLDR_OPTS"])
|
27
|
+
opts_config = ArgvParser.new.parse(env_argv, {
|
28
|
+
config_intended_for_merge_only: true
|
29
|
+
})
|
30
|
+
task_config.merge(opts_config)
|
31
|
+
else
|
32
|
+
task_config
|
33
|
+
end
|
34
|
+
|
35
|
+
config.to_full_args
|
36
|
+
end
|
37
|
+
end
|
4
38
|
end
|
39
|
+
|
40
|
+
# Create the default tldr task for users
|
41
|
+
TLDR::Task.new
|
@@ -9,7 +9,7 @@ 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}#{"\n#{@icons.seed} #{CONFLAGS[:seed]} #{@config.seed}" unless @config.seed_set_intentionally}
|
13
13
|
|
14
14
|
#{@icons.run} Running:
|
15
15
|
|
@@ -17,12 +17,17 @@ class TLDR
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def after_test result
|
20
|
-
|
20
|
+
output = case result.type
|
21
21
|
when :success then @icons.success
|
22
22
|
when :skip then @icons.skip
|
23
23
|
when :failure then @icons.failure
|
24
24
|
when :error then @icons.error
|
25
25
|
end
|
26
|
+
if @config.verbose
|
27
|
+
@out.puts "#{output} #{result.type.capitalize} - #{describe(result.test, result.relevant_location)}"
|
28
|
+
else
|
29
|
+
@out.print output
|
30
|
+
end
|
26
31
|
end
|
27
32
|
|
28
33
|
def time_diff start, stop = Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
|
data/lib/tldr/runner.rb
CHANGED
@@ -34,7 +34,7 @@ class TLDR
|
|
34
34
|
end
|
35
35
|
}
|
36
36
|
|
37
|
-
results = @parallelizer.parallelize(plan.tests, config
|
37
|
+
results = @parallelizer.parallelize(plan.tests, config) { |test|
|
38
38
|
e = nil
|
39
39
|
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
|
40
40
|
wip_test = WIPTest.new test, start_time
|
@@ -42,7 +42,16 @@ class TLDR
|
|
42
42
|
runtime = time_it(start_time) do
|
43
43
|
instance = test.klass.new
|
44
44
|
instance.setup if instance.respond_to? :setup
|
45
|
-
instance.
|
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
|
46
55
|
instance.teardown if instance.respond_to? :teardown
|
47
56
|
rescue Skip, Failure, StandardError => e
|
48
57
|
end
|
data/lib/tldr/strategizer.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class TLDR
|
2
2
|
class Strategizer
|
3
|
-
Strategy = Struct.new :
|
3
|
+
Strategy = Struct.new :prepend_thread_unsafe_tests, :parallel_tests_and_groups, :thread_unsafe_tests
|
4
4
|
|
5
5
|
# Combine all discovered test methods with any methods grouped by run_these_together!
|
6
6
|
#
|
@@ -8,10 +8,13 @@ class TLDR
|
|
8
8
|
# - Map over tests to build out groups in order to retain shuffle order
|
9
9
|
# (group will run in position of first test in the group)
|
10
10
|
# - If a test is in multiple groups, only run it once
|
11
|
-
def strategize
|
12
|
-
|
11
|
+
def strategize all_tests, run_these_together_groups, thread_unsafe_test_groups, prepend_paths
|
12
|
+
thread_unsafe_tests, thread_safe_tests = partition_unsafe(all_tests, thread_unsafe_test_groups)
|
13
|
+
prepend_thread_unsafe_tests, thread_unsafe_tests = partition_prepend(thread_unsafe_tests, prepend_paths)
|
13
14
|
|
14
|
-
|
15
|
+
grouped_tests = prepare_run_together_groups run_these_together_groups, thread_safe_tests, thread_unsafe_tests
|
16
|
+
already_included_groups = []
|
17
|
+
Strategy.new prepend_thread_unsafe_tests, thread_safe_tests.map { |test|
|
15
18
|
if (group = grouped_tests.find { |group| group.tests.include? test })
|
16
19
|
if already_included_groups.include? group
|
17
20
|
next
|
@@ -25,7 +28,37 @@ class TLDR
|
|
25
28
|
else
|
26
29
|
test
|
27
30
|
end
|
28
|
-
}.compact
|
31
|
+
}.compact, thread_unsafe_tests
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def partition_unsafe tests, thread_unsafe_test_groups
|
37
|
+
tests.partition { |test|
|
38
|
+
thread_unsafe_test_groups.any? { |group| group.tests.include? test }
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sadly duplicative with Planner.rb, necessitating the extraction of PathUtil
|
43
|
+
# Suboptimal, but we do indeed need to do this work in two places ¯\_(ツ)_/¯
|
44
|
+
def partition_prepend thread_unsafe_tests, prepend_paths
|
45
|
+
locations = PathUtil.expand_search_locations PathUtil.expand_globs prepend_paths
|
46
|
+
|
47
|
+
thread_unsafe_tests.partition { |test|
|
48
|
+
PathUtil.locations_include_test? locations, test
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def prepare_run_together_groups run_these_together_groups, thread_safe_tests, thread_unsafe_tests
|
53
|
+
grouped_tests = run_these_together_groups.map(&:dup)
|
54
|
+
|
55
|
+
grouped_tests.each do |group|
|
56
|
+
group.tests = group.tests.select { |test|
|
57
|
+
thread_safe_tests.include?(test) && !thread_unsafe_tests.include?(test)
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
grouped_tests.reject { |group| group.tests.size < 2 }
|
29
62
|
end
|
30
63
|
end
|
31
64
|
end
|
data/lib/tldr/value/config.rb
CHANGED
@@ -10,7 +10,7 @@ class TLDR
|
|
10
10
|
names: "--name",
|
11
11
|
fail_fast: "--fail-fast",
|
12
12
|
no_emoji: "--no-emoji",
|
13
|
-
|
13
|
+
prepend_paths: "--prepend",
|
14
14
|
no_prepend: "--no-prepend",
|
15
15
|
exclude_paths: "--exclude-path",
|
16
16
|
exclude_names: "--exclude-name",
|
@@ -19,25 +19,35 @@ class TLDR
|
|
19
19
|
paths: nil
|
20
20
|
}.freeze
|
21
21
|
|
22
|
-
PATH_FLAGS = [:paths, :helper, :load_paths, :
|
22
|
+
PATH_FLAGS = [:paths, :helper, :load_paths, :prepend_paths, :exclude_paths].freeze
|
23
23
|
MOST_RECENTLY_MODIFIED_TAG = "MOST_RECENTLY_MODIFIED".freeze
|
24
24
|
|
25
25
|
Config = Struct.new :paths, :seed, :no_helper, :verbose, :reporter,
|
26
26
|
:helper, :load_paths, :parallel, :names, :fail_fast, :no_emoji,
|
27
|
-
:
|
27
|
+
:prepend_paths, :no_prepend, :exclude_paths, :exclude_names, :base_path,
|
28
28
|
:no_dotfile,
|
29
|
-
|
29
|
+
# Internal properties
|
30
|
+
:config_intended_for_merge_only, :seed_set_intentionally, :cli_defaults,
|
31
|
+
keyword_init: true do
|
30
32
|
def initialize(**args)
|
31
|
-
|
32
|
-
|
33
|
+
unless args[:config_intended_for_merge_only]
|
34
|
+
change_working_directory_because_i_am_bad_and_i_should_feel_bad!(args[:base_path])
|
35
|
+
args = merge_dotfile_args(args) unless args[:no_dotfile]
|
36
|
+
end
|
37
|
+
args = undefault_parallel_if_seed_set(args)
|
38
|
+
unless args[:config_intended_for_merge_only]
|
39
|
+
args = merge_defaults(args)
|
40
|
+
end
|
33
41
|
|
34
|
-
super(**
|
42
|
+
super(**args)
|
35
43
|
end
|
36
44
|
|
37
|
-
#
|
38
|
-
undef_method :
|
45
|
+
# These are for internal tracking and resolved at initialization-time
|
46
|
+
undef_method :config_intended_for_merge_only=, :seed_set_intentionally=,
|
47
|
+
# These must be set when the Config is first initialized
|
48
|
+
:cli_defaults=, :no_dotfile=, :base_path=
|
39
49
|
|
40
|
-
def self.build_defaults
|
50
|
+
def self.build_defaults cli_defaults: true
|
41
51
|
common = {
|
42
52
|
seed: rand(10_000),
|
43
53
|
no_helper: false,
|
@@ -53,36 +63,36 @@ class TLDR
|
|
53
63
|
base_path: nil
|
54
64
|
}
|
55
65
|
|
56
|
-
if
|
66
|
+
if cli_defaults
|
57
67
|
common.merge(
|
58
68
|
paths: Dir["test/**/*_test.rb", "test/**/test_*.rb"],
|
59
69
|
helper: "test/helper.rb",
|
60
70
|
load_paths: ["test"],
|
61
|
-
|
71
|
+
prepend_paths: [MOST_RECENTLY_MODIFIED_TAG]
|
62
72
|
)
|
63
73
|
else
|
64
74
|
common.merge(
|
65
75
|
paths: [],
|
66
76
|
helper: nil,
|
67
77
|
load_paths: [],
|
68
|
-
|
78
|
+
prepend_paths: []
|
69
79
|
)
|
70
80
|
end
|
71
81
|
end
|
72
82
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
83
|
+
def undefault_parallel_if_seed_set args
|
84
|
+
args.merge(
|
85
|
+
seed_set_intentionally: !args[:seed].nil?,
|
86
|
+
parallel: (args[:parallel].nil? ? args[:seed].nil? : args[:parallel])
|
87
|
+
)
|
88
|
+
end
|
77
89
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
merged_args[:parallel] = merged_args[:seed].nil?
|
82
|
-
end
|
90
|
+
def merge_defaults user_args
|
91
|
+
merged_args = user_args.dup
|
92
|
+
defaults = Config.build_defaults(cli_defaults: merged_args[:cli_defaults])
|
83
93
|
|
84
94
|
# Arrays
|
85
|
-
[:paths, :load_paths, :names, :
|
95
|
+
[:paths, :load_paths, :names, :prepend_paths, :exclude_paths, :exclude_names].each do |key|
|
86
96
|
merged_args[key] = defaults[key] if merged_args[key].nil? || merged_args[key].empty?
|
87
97
|
end
|
88
98
|
|
@@ -99,13 +109,21 @@ class TLDR
|
|
99
109
|
merged_args
|
100
110
|
end
|
101
111
|
|
112
|
+
def merge other
|
113
|
+
this_config = to_h
|
114
|
+
kwargs = this_config.merge(
|
115
|
+
other.to_h.compact.except(:config_intended_for_merge_only)
|
116
|
+
)
|
117
|
+
Config.new(**kwargs)
|
118
|
+
end
|
119
|
+
|
102
120
|
# We needed this hook (to be called by the planner), because we can't know
|
103
121
|
# the default prepend location until we have all the resolved test paths,
|
104
122
|
# so we have to mutate it after the fact.
|
105
123
|
def update_after_gathering_tests! tests
|
106
|
-
return unless
|
124
|
+
return unless prepend_paths.include?(MOST_RECENTLY_MODIFIED_TAG)
|
107
125
|
|
108
|
-
self.
|
126
|
+
self.prepend_paths = prepend_paths.map { |path|
|
109
127
|
if path == MOST_RECENTLY_MODIFIED_TAG
|
110
128
|
most_recently_modified_test_file tests
|
111
129
|
else
|
@@ -115,12 +133,17 @@ class TLDR
|
|
115
133
|
end
|
116
134
|
|
117
135
|
def to_full_args(exclude: [])
|
118
|
-
to_cli_argv(
|
136
|
+
to_cli_argv(
|
137
|
+
CONFLAGS.keys -
|
138
|
+
exclude - [
|
139
|
+
(:seed unless seed_set_intentionally)
|
140
|
+
]
|
141
|
+
).join(" ")
|
119
142
|
end
|
120
143
|
|
121
144
|
def to_single_path_args(path)
|
122
145
|
argv = to_cli_argv(CONFLAGS.keys - [
|
123
|
-
:seed, :parallel, :names, :fail_fast, :paths, :
|
146
|
+
:seed, :parallel, :names, :fail_fast, :paths, :prepend_paths,
|
124
147
|
:no_prepend, :exclude_paths
|
125
148
|
])
|
126
149
|
|
@@ -129,14 +152,14 @@ class TLDR
|
|
129
152
|
|
130
153
|
private
|
131
154
|
|
132
|
-
def to_cli_argv
|
133
|
-
defaults = Config.build_defaults(
|
155
|
+
def to_cli_argv options = CONFLAGS.keys
|
156
|
+
defaults = Config.build_defaults(cli_defaults: true)
|
134
157
|
options.map { |key|
|
135
158
|
flag = CONFLAGS[key]
|
136
159
|
|
137
160
|
# Special cases
|
138
|
-
if key == :
|
139
|
-
if
|
161
|
+
if key == :prepend_paths
|
162
|
+
if prepend_paths.map { |s| stringify(key, s) }.sort == paths.map { |s| stringify(:paths, s) }.sort
|
140
163
|
# Don't print prepended tests if they're the same as the test paths
|
141
164
|
next
|
142
165
|
elsif no_prepend
|
data/lib/tldr/version.rb
CHANGED
data/lib/tldr.rb
CHANGED
@@ -4,10 +4,11 @@ require_relative "tldr/argv_parser"
|
|
4
4
|
require_relative "tldr/assertions"
|
5
5
|
require_relative "tldr/backtrace_filter"
|
6
6
|
require_relative "tldr/error"
|
7
|
+
require_relative "tldr/parallel_controls"
|
7
8
|
require_relative "tldr/parallelizer"
|
9
|
+
require_relative "tldr/path_util"
|
8
10
|
require_relative "tldr/planner"
|
9
11
|
require_relative "tldr/reporters"
|
10
|
-
require_relative "tldr/run_these_together"
|
11
12
|
require_relative "tldr/runner"
|
12
13
|
require_relative "tldr/skippable"
|
13
14
|
require_relative "tldr/sorbet_compatibility"
|
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.5.0
|
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-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: super_diff
|
@@ -60,14 +60,15 @@ files:
|
|
60
60
|
- lib/tldr/assertions/minitest_compatibility.rb
|
61
61
|
- lib/tldr/backtrace_filter.rb
|
62
62
|
- lib/tldr/error.rb
|
63
|
+
- lib/tldr/parallel_controls.rb
|
63
64
|
- lib/tldr/parallelizer.rb
|
65
|
+
- lib/tldr/path_util.rb
|
64
66
|
- lib/tldr/planner.rb
|
65
67
|
- lib/tldr/rake.rb
|
66
68
|
- lib/tldr/reporters.rb
|
67
69
|
- lib/tldr/reporters/base.rb
|
68
70
|
- lib/tldr/reporters/default.rb
|
69
71
|
- lib/tldr/reporters/icon_provider.rb
|
70
|
-
- lib/tldr/run_these_together.rb
|
71
72
|
- lib/tldr/runner.rb
|
72
73
|
- lib/tldr/skippable.rb
|
73
74
|
- lib/tldr/sorbet_compatibility.rb
|
@@ -1,23 +0,0 @@
|
|
1
|
-
class TLDR
|
2
|
-
GROUPED_TESTS = Concurrent::Array.new
|
3
|
-
|
4
|
-
# If it's not safe to run a set of tests in parallel, you can force them to
|
5
|
-
# run in a group together (in a single worker) with `run_these_together!` in
|
6
|
-
# your test.
|
7
|
-
#
|
8
|
-
# This method takes an array of tuples, where the first element is the class
|
9
|
-
# (or its name as a string, if the class is not yet defined in the current
|
10
|
-
# file) and the second element is the method name. If the second element is
|
11
|
-
# nil, then all the tests on the class will be run together.
|
12
|
-
#
|
13
|
-
# Examples:
|
14
|
-
# - `run_these_together!` will run all the tests defined on the current
|
15
|
-
# class to be run in a group
|
16
|
-
# - `run_these_together!([[ClassA, nil], ["ClassB", :test_1], [ClassB, :test_2]])`
|
17
|
-
# will run all the tests defined on ClassA, and test_1 and test_2 from ClassB
|
18
|
-
# (nil in the second position means "all the tests on this class")
|
19
|
-
#
|
20
|
-
def self.run_these_together! klass_method_tuples = [[self, nil]]
|
21
|
-
GROUPED_TESTS << TestGroup.new(klass_method_tuples)
|
22
|
-
end
|
23
|
-
end
|