tldr 0.3.0 → 0.5.0
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 +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
|