Class: RuboCop::Cop::DevDoc::Test::RequireGlibTravelBlock
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::DevDoc::Test::RequireGlibTravelBlock
- Defined in:
- lib/rubocop/cop/dev_doc/test/require_glib_travel_block.rb
Overview
glib_travel must be called with a block — the bare anchor form leaks
frozen time past the test.
Rationale
glib_travel(point) do ... end auto-returns to real time at the end of
the block, so a test cannot leak a frozen clock into the next one. The
bare anchor form glib_travel(point) (no block) freezes time until a
matching glib_travel_back, which is easy to forget in teardown and
then silently corrupts every test that runs afterward. This is the form
that has repeatedly slipped in alongside the correct block form.
What is NOT flagged
- The block form
glib_travel(point) do ... end(the correct form). - A bare DURATION argument that advances the frozen clock across
multiple requests within one test, e.g.
glib_travel(61.seconds),glib_travel(1.hour),glib_travel(-5.minutes), orglib_travel(5.minutes + 30.seconds). This is the accepted non-block form for rate-limiting / clock-tick tests; it is recognized by aNumeric#second(s)/minute(s)/hour(s)/day(s)/ week(s)/month(s)/year(s)leaf, optionally unary-negated (-5.minutes) or composed additively/subtractively (5.minutes + 30.seconds), and is paired with an explicitglib_travel_back. An absolute point expression such asTime.current,DateTime.new(...), or2.hours.from_nowis NOT a duration and is flagged. A duration reached through anything the matcher cannot see statically — a constant or variable holding a duration (glib_travel(SOME_DURATION)), a method call, or multiplication/division (glib_travel(5.minutes * 2)) — is NOT recognized; inline the literal expression or# rubocop:disablewith a reason. glib_travel_freeze(covered byDevDoc/Test/AvoidGlibTravelFreeze).
Inline disable
For the rare case where the bare anchor form is genuinely required, add
a rubocop:disable DevDoc/Test/RequireGlibTravelBlock comment with a
written reason.
Constant Summary collapse
- MSG =
'Use the block form `glib_travel(time) do ... end` — the bare anchor ' \ 'form leaks frozen time past the test unless paired with ' \ '`glib_travel_back`. Use `# rubocop:disable ' \ 'DevDoc/Test/RequireGlibTravelBlock` with a reason if the bare form ' \ 'is genuinely required.'.freeze
- RESTRICT_ON_SEND =
%i[glib_travel].freeze
Instance Method Summary collapse
-
#duration_argument?(node) ⇒ Object
A duration unit — the leaf of a duration expression.
- #on_send(node) ⇒ Object
Instance Method Details
#duration_argument?(node) ⇒ Object
A duration unit — the leaf of a duration expression. Both singular
and plural ActiveSupport forms (1.hour, 2.minutes). Receiver is
unconstrained; in practice it is always a Numeric literal.
67 68 69 |
# File 'lib/rubocop/cop/dev_doc/test/require_glib_travel_block.rb', line 67 def_node_matcher :duration_argument?, <<~PATTERN (send _ {:second :seconds :minute :minutes :hour :hours :day :days :week :weeks :month :months :year :years}) PATTERN |
#on_send(node) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/rubocop/cop/dev_doc/test/require_glib_travel_block.rb', line 71 def on_send(node) # `glib_travel` is a global test helper; a receiver-bearing call # (`obj.glib_travel`) is a different method and none of our # business — but `self.glib_travel` IS the same call and must not # become a one-token dodge of the block-form rule. return if node.receiver && !node.receiver.self_type? return if block_form?(node) return if (node) add_offense(node.loc.selector) end |