tldr 1.0.0 → 1.1.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 +5 -0
- data/README.md +47 -16
- data/lib/tldr/argv_parser.rb +8 -0
- data/lib/tldr/runner.rb +10 -4
- data/lib/tldr/value/config.rb +7 -3
- data/lib/tldr/value/test.rb +1 -1
- data/lib/tldr/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d60d27a492477aa1034acf117993630307f595690f97d356da763fb352f4837e
|
4
|
+
data.tar.gz: 240ee628207bf5b79568fff7ef93b5e6174825b8a53748e2b453076d2d00db7a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e513372aedda6a808ee61e426fc3c738e1f7837fe3ccb55ec658153c15998b4eab9da976680f062d936c638e6fcdcbdcdfabec181137acc48ce6187b5d4aa69
|
7
|
+
data.tar.gz: 4fa415b7f6858e8a26766232a251542f28e7485ead112dd514d72fb55364ae54520ff1d3585dada05e8e56861295dfe458922f4038b1cdb67ee7ec1c4883f382
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
## unreleased
|
2
2
|
|
3
|
+
## [1.1.0]
|
4
|
+
|
5
|
+
* Add `--exit-0-on-timeout` to allow the suite timeout to be used more as a budget constraint (e.g. "I know my full suite is going to take 30 seconds to run but I want to constantly run as many as I can in 500ms to get fast feedback")
|
6
|
+
* Add `--exit-2-on-failure` flag to exit with status 2 for test failures (not just errors), useful for Claude Code hooks which only block on exit code 2
|
7
|
+
|
3
8
|
## [1.0.0]
|
4
9
|
|
5
10
|
* **BREAKING** you know how the whole point of TLDR is that it aborts your test
|
data/README.md
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
# TLDR -
|
1
|
+
# TLDR - Testing for rubyists who want fast feedback
|
2
2
|
|
3
3
|
TLDR is a suspiciously-delightful testing framework for Ruby.
|
4
4
|
|
5
5
|
As a test library, TLDR is largely [API-compatible with Minitest](#minitest-compatibility). As a test runner, TLDR boasts a few features RSpec's CLI still doesn't have.
|
6
6
|
|
7
|
-
The library, command line interface, and every decision in-between was prioritized to maximize productivity
|
7
|
+
The library, command line interface, and every decision in-between was prioritized to maximize productivity to promote fast feedback loops. Some highlights:
|
8
8
|
|
9
|
-
* Numerous ways to run specific tests:
|
9
|
+
* Numerous ways to run specific tests: path (`foo_test.rb`), line number (`foo_test.rb:13`), name (`--name test_foo`), or regex pattern (`-n /_foo/`)
|
10
10
|
* Parallel test execution by default, as well as [controls for serial execution of thread-unsafe tests](#parallel-by-default-is-nice-in-theory-but-half-my-tests-are-failing-wat)
|
11
11
|
* Continuously run [after every file change](#running-tests-continuously-with---watch) with `--watch`
|
12
12
|
* An optional timer to [enforce your tests never get slow](#enforcing-a-testing---timeout) with `--timeout`
|
13
|
-
* A `--fail-fast` flag that aborts the run as soon as a failure is encountered
|
13
|
+
* A `--fail-fast` flag that aborts the run [as soon as a failure is encountered](#failing-fast-and-first)
|
14
14
|
* Running your most-recently-edited test before all the others (see `--prepend`)
|
15
15
|
* Delightful diffs when assertions fail, care of [super_diff](https://github.com/splitwise/super_diff)
|
16
16
|
|
@@ -18,9 +18,9 @@ We hope you'll give it a try!
|
|
18
18
|
|
19
19
|
## Getting started
|
20
20
|
|
21
|
-
You can either read the rest of this README and learn about TLDR passively, or you can just clone this [TLDR demo repo](https://github/searls/tldr_demo) and work through its README as you play with its tests and run them with various options.
|
21
|
+
You can either read the rest of this README and learn about TLDR passively, or you can just clone this [TLDR demo repo](https://github.com/searls/tldr_demo) and work through its README as you play with its tests and run them with various options.
|
22
22
|
|
23
|
-
**JUST IN CASE YOU'RE ALREADY SKIMMING THIS**, I said stop reading and [clone this interactive repo](https://github/searls/tldr_demo) if you're a hands-on learner.
|
23
|
+
**JUST IN CASE YOU'RE ALREADY SKIMMING THIS**, I said stop reading and [clone this interactive repo](https://github.com/searls/tldr_demo) if you're a hands-on learner.
|
24
24
|
|
25
25
|
### Install
|
26
26
|
|
@@ -61,15 +61,17 @@ Usage: tldr [options] some_tests/**/*.rb some/path.rb:13 ...
|
|
61
61
|
--[no-]warnings Print Ruby warnings (Default: true)
|
62
62
|
-v, --verbose Print stack traces for errors
|
63
63
|
--yes-i-know Suppress TLDR report when suite runs beyond any configured --timeout
|
64
|
+
--exit-0-on-timeout Exit with status code 0 when suite times out instead of 3
|
65
|
+
--exit-2-on-failure Exit with status code 2 (normally for errors) for both failures and errors
|
64
66
|
--print-interrupted-test-backtraces
|
65
67
|
Print stack traces of tests interrupted after a timeout
|
66
68
|
```
|
67
69
|
|
68
70
|
### Setting defaults in .tldr.yml
|
69
71
|
|
70
|
-
The `tldr` CLI will look for a `.tldr.yml` that can set the same
|
72
|
+
The `tldr` CLI will look for a `.tldr.yml` file in the root of your project that can set all the same options supported by the CLI. You can specify a custom YAML location with `--config some/path.yml` if you want it to live someplace else.
|
71
73
|
|
72
|
-
Any options found in the dotfile will override TLDR's defaults, but can still be overridden by the `tldr` CLI or a `TLDR::Config` object
|
74
|
+
Any options found in the dotfile will override TLDR's defaults, but can still be overridden by the `tldr` CLI or a `TLDR::Config` object when running tests programmatically.
|
73
75
|
|
74
76
|
Here's an [example project](/example/c) that specifies a `.tldr.yml` file as well as some [internal tests](/tests/dotfile_test.rb) demonstrating its behavior.
|
75
77
|
|
@@ -77,7 +79,7 @@ Here's an [example project](/example/c) that specifies a `.tldr.yml` file as wel
|
|
77
79
|
|
78
80
|
If you've ever seen a [Minitest](https://github.com/minitest/minitest?tab=readme-ov-file#synopsis-) test, then you already know how to write TLDR tests. Rather than document how to write tests, this section just highlights the ways TLDR tests differ from Minitest tests.
|
79
81
|
|
80
|
-
First, instead of inheriting from `Minitest::Test`,
|
82
|
+
First, instead of inheriting from `Minitest::Test`, TLDR test classes should descend from (wait for it) the `TLDR` base class:
|
81
83
|
|
82
84
|
```ruby
|
83
85
|
class MyTest < TLDR
|
@@ -87,7 +89,7 @@ class MyTest < TLDR
|
|
87
89
|
end
|
88
90
|
```
|
89
91
|
|
90
|
-
Second, if your tests depend on a test helper,
|
92
|
+
Second, if your tests depend on a test helper, it will be automatically loaded by TLDR _if_ you name it `test/helper.rb`. That means you don't need to add `require "helper"` to the top of every test. If you want to name the helper something else, you can do so with the `--helper` option:
|
91
93
|
|
92
94
|
```
|
93
95
|
tldr --helper test/test_helper.rb
|
@@ -98,13 +100,13 @@ Third, TLDR offers fewer features:
|
|
98
100
|
* No built-in mock library ([use mocktail](https://justin.searls.co/posts/a-real-world-mocktail-example-test/), maybe!)
|
99
101
|
* No "spec" API
|
100
102
|
* No benchmark tool
|
101
|
-
* No
|
103
|
+
* No bisect script
|
102
104
|
|
103
|
-
And that's it! You
|
105
|
+
And that's it! You officially know how to write TLDR tests.
|
104
106
|
|
105
107
|
## Running your tests
|
106
108
|
|
107
|
-
Because TLDR ships with a CLI, it offers a
|
109
|
+
Because TLDR ships with a CLI, it offers a veritable _plethora_ of ways to run your tests.
|
108
110
|
|
109
111
|
### Running your tests
|
110
112
|
|
@@ -120,7 +122,7 @@ This assumes your tests are stored in `test/`. It will also add `lib/` to Ruby's
|
|
120
122
|
|
121
123
|
TLDR ships with a minimal [rake task](lib/tldr/rake.rb) that simply shells out to the `tldr` CLI by default. If you want to run TLDR with Rake, you can configure the task by setting flags on an env var named `TLDR_OPTS` or in a [.tldr.yml file](#setting-defaults-in-tldryml).
|
122
124
|
|
123
|
-
All your Rakefile needs is `require "tldr/rake` and you can run the task individually like this:
|
125
|
+
All your Rakefile needs is `require "tldr/rake"` and you can run the task individually like this:
|
124
126
|
|
125
127
|
```
|
126
128
|
$ rake tldr
|
@@ -138,9 +140,9 @@ require "tldr/rake"
|
|
138
140
|
task default: ["tldr", "standard:fix"]
|
139
141
|
```
|
140
142
|
|
141
|
-
One
|
143
|
+
One situation where you'd want to invoke TLDR with Rake is when you have multiple test suites that you want to be able to easily run separately ([this talk](https://blog.testdouble.com/talks/2014-05-25-breaking-up-with-your-test-suite/) discussed a few reasons why this can be useful).
|
142
144
|
|
143
|
-
To create a custom TLDR Rake task, you can instantiate `TLDR::Task` like this, which allows you to
|
145
|
+
To create a custom TLDR Rake task, you can instantiate `TLDR::Task` like this, which allows you to define its [TLDR::Config](/lib/tldr/value/config.rb) configuration in code:
|
144
146
|
|
145
147
|
```ruby
|
146
148
|
require "tldr/rake"
|
@@ -258,6 +260,35 @@ timeout: 0.01
|
|
258
260
|
|
259
261
|
And if you're running with the timeout enabled this way, you can still disable it for any given test run by adding the `--no-timeout` flag.
|
260
262
|
|
263
|
+
#### Consider timeouts as a success with exit code 0
|
264
|
+
|
265
|
+
By default, when TLDR times out it exits with status code 3 to indicate the test suite was aborted. However, if you know that your full test suite is slower than whatever duration you consider to be "fast enough for fast feedback", you can use the `--exit-0-on-timeout` flag and simply use the timeout to enforce whatever feedback loop budget you set with `--timeout`.
|
266
|
+
|
267
|
+
For example:
|
268
|
+
|
269
|
+
```
|
270
|
+
# Get feedback within 400ms but don't consider a timeout a failure
|
271
|
+
tldr --timeout 0.4 --exit-0-on-timeout
|
272
|
+
```
|
273
|
+
|
274
|
+
This is particularly useful for:
|
275
|
+
- [Claude Code hooks](https://docs.anthropic.com/en/docs/claude-code/hooks) where you want fast feedback without failing the command
|
276
|
+
- [Git pre-commit hooks](https://git-scm.com/docs/githooks#_pre_commit) where you want quick test results without blocking commits
|
277
|
+
- Development workflows where timeouts are informational rather than failures
|
278
|
+
- Any situation where you want a time budget without hard failures
|
279
|
+
|
280
|
+
## Using TLDR as a Claude Code hook
|
281
|
+
|
282
|
+
In AI agent-based coding workflows, it's common to [configure hooks](https://docs.anthropic.com/en/docs/claude-code/hooks) that will block the agent from proceeding whenever tests or linters fail. With Claude Code specifically, a hook will only block if it exits with status code 2. As a result, you can use `--exit-2-on-failure` so that assertion failures will block the agent from continuing.
|
283
|
+
|
284
|
+
So you might configure a Claude Code hook with a 250ms budget by setting timeouts to exit code 0 and failures to exit code 2:
|
285
|
+
|
286
|
+
```
|
287
|
+
# Get feedback within 250ms, don't block the agent on timeouts, but do block it on assertion failures
|
288
|
+
tldr --timeout 0.25 --exit-0-on-timeout --exit-2-on-failure
|
289
|
+
```
|
290
|
+
|
291
|
+
|
261
292
|
## Questions you might be asking
|
262
293
|
|
263
294
|
TLDR is very similar to Minitest in API, but different in enough ways that you
|
data/lib/tldr/argv_parser.rb
CHANGED
@@ -106,6 +106,14 @@ class TLDR
|
|
106
106
|
options[:yes_i_know] = true
|
107
107
|
end
|
108
108
|
|
109
|
+
opts.on CONFLAGS[:exit_0_on_timeout], "Exit with status code 0 when suite times out instead of 3" do
|
110
|
+
options[:exit_0_on_timeout] = true
|
111
|
+
end
|
112
|
+
|
113
|
+
opts.on CONFLAGS[:exit_2_on_failure], "Exit with status code 2 (normally for errors) for both failures and errors" do
|
114
|
+
options[:exit_2_on_failure] = true
|
115
|
+
end
|
116
|
+
|
109
117
|
opts.on CONFLAGS[:print_interrupted_test_backtraces], "Print stack traces of tests interrupted after a timeout" do |print_interrupted_test_backtraces|
|
110
118
|
options[:print_interrupted_test_backtraces] = print_interrupted_test_backtraces
|
111
119
|
end
|
data/lib/tldr/runner.rb
CHANGED
@@ -40,7 +40,13 @@ class TLDR
|
|
40
40
|
@run_aborted.make_true
|
41
41
|
@wip.each(&:capture_backtrace_at_exit)
|
42
42
|
reporter.after_tldr(plan.tests, @wip.dup, @results.dup) if reporter.respond_to?(:after_tldr)
|
43
|
-
|
43
|
+
|
44
|
+
# If there are failures/errors, use their exit code regardless of exit_0_on_timeout
|
45
|
+
if @results.any? { |result| result.error? || result.failure? }
|
46
|
+
exit!(exit_code(@results, config))
|
47
|
+
else
|
48
|
+
exit!(config.exit_0_on_timeout ? 0 : 3)
|
49
|
+
end
|
44
50
|
end
|
45
51
|
|
46
52
|
sleep(config.timeout)
|
@@ -62,7 +68,7 @@ class TLDR
|
|
62
68
|
|
63
69
|
unless @run_aborted.true?
|
64
70
|
reporter.after_suite(results) if reporter.respond_to?(:after_suite)
|
65
|
-
exit(exit_code(results))
|
71
|
+
exit(exit_code(results, config))
|
66
72
|
end
|
67
73
|
end
|
68
74
|
|
@@ -119,11 +125,11 @@ class TLDR
|
|
119
125
|
((Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond) - start) / 1000.0).round
|
120
126
|
end
|
121
127
|
|
122
|
-
def exit_code results
|
128
|
+
def exit_code results, config
|
123
129
|
if results.any? { |result| result.error? }
|
124
130
|
2
|
125
131
|
elsif results.any? { |result| result.failure? }
|
126
|
-
1
|
132
|
+
config.exit_2_on_failure ? 2 : 1
|
127
133
|
else
|
128
134
|
0
|
129
135
|
end
|
data/lib/tldr/value/config.rb
CHANGED
@@ -22,6 +22,8 @@ class TLDR
|
|
22
22
|
yes_i_know: "--yes-i-know",
|
23
23
|
print_interrupted_test_backtraces: "--print-interrupted-test-backtraces",
|
24
24
|
i_am_being_watched: "--i-am-being-watched",
|
25
|
+
exit_0_on_timeout: "--exit-0-on-timeout",
|
26
|
+
exit_2_on_failure: "--exit-2-on-failure",
|
25
27
|
paths: nil
|
26
28
|
}.freeze
|
27
29
|
|
@@ -32,7 +34,7 @@ class TLDR
|
|
32
34
|
:exclude_paths, :helper_paths, :no_helper, :prepend_paths, :no_prepend,
|
33
35
|
:load_paths, :base_path, :config_path, :reporter, :emoji, :warnings,
|
34
36
|
:verbose, :yes_i_know, :print_interrupted_test_backtraces,
|
35
|
-
:i_am_being_watched, :paths,
|
37
|
+
:i_am_being_watched, :exit_0_on_timeout, :exit_2_on_failure, :paths,
|
36
38
|
# Internal properties
|
37
39
|
:config_intended_for_merge_only, :seed_set_intentionally, :cli_defaults
|
38
40
|
].freeze
|
@@ -79,7 +81,9 @@ class TLDR
|
|
79
81
|
verbose: false,
|
80
82
|
yes_i_know: false,
|
81
83
|
print_interrupted_test_backtraces: false,
|
82
|
-
i_am_being_watched: false
|
84
|
+
i_am_being_watched: false,
|
85
|
+
exit_0_on_timeout: false,
|
86
|
+
exit_2_on_failure: false
|
83
87
|
}
|
84
88
|
|
85
89
|
if cli_defaults
|
@@ -118,7 +122,7 @@ class TLDR
|
|
118
122
|
end
|
119
123
|
|
120
124
|
# Booleans
|
121
|
-
[:watch, :fail_fast, :parallel, :no_helper, :no_prepend, :emoji, :warnings, :verbose, :yes_i_know, :print_interrupted_test_backtraces, :i_am_being_watched].each do |key|
|
125
|
+
[:watch, :fail_fast, :parallel, :no_helper, :no_prepend, :emoji, :warnings, :verbose, :yes_i_know, :print_interrupted_test_backtraces, :i_am_being_watched, :exit_0_on_timeout, :exit_2_on_failure].each do |key|
|
122
126
|
merged_args[key] = defaults[key] if merged_args[key].nil?
|
123
127
|
end
|
124
128
|
|
data/lib/tldr/value/test.rb
CHANGED
data/lib/tldr/version.rb
CHANGED
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: 1.
|
4
|
+
version: 1.1.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: 2025-
|
12
|
+
date: 2025-07-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: super_diff
|
@@ -130,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '0'
|
132
132
|
requirements: []
|
133
|
-
rubygems_version: 3.3.
|
133
|
+
rubygems_version: 3.3.26
|
134
134
|
signing_key:
|
135
135
|
specification_version: 4
|
136
136
|
summary: TLDR will run your tests, but only for 1.8 seconds.
|