Skip to content

Commit f25bbbc

Browse files
committed
docs+test: round-2 review polish — session qualifier, helper reuse, regression test
- frozen_timestamp docstring and TestChangesetDateStamping class docstring now carry the same per-session qualifier as every other statement of the never-share-a-second guarantee (round 1 missed these two spots). - test_nested_frozen_timestamp_reuses_outer_stamp builds its editor via the file's _make_manager helper instead of a raw DocxXMLEditor construction. - New test_clock_regression_keeps_dates_monotonic drives the settable clock strictly backwards, pinning the documented NTP-step-back behavior (dates never regress; previous+1 until the clock catches up) — previously only the equality branch was exercised.
1 parent 6d3cf9e commit f25bbbc

2 files changed

Lines changed: 22 additions & 7 deletions

File tree

‎docx_editor/xml_editor.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,8 +1313,9 @@ def frozen_timestamp(self) -> Iterator[None]:
13131313
one edit as several groups.
13141314
13151315
One changeset = one outermost ``frozen_timestamp()`` scope, stamped
1316-
via ``_get_next_changeset_timestamp`` (collision-bumped so two changesets
1317-
never share a second). Reentrant by reuse: a nested entry joins the
1316+
via ``_get_next_changeset_timestamp`` (collision-bumped so two
1317+
changesets by the same author never share a second within one open
1318+
session). Reentrant by reuse: a nested entry joins the
13181319
enclosing changeset — ``batch_edit``/``batch_rewrite`` open the scope
13191320
once for the whole call and the per-op ``_grouped()`` freezes join
13201321
it. Only the outermost entry allocates a stamp and clears it on exit.

‎tests/test_revision_groups.py‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -890,10 +890,10 @@ class TestChangesetDateStamping:
890890
"""Collision-bumped w:date stamping at changeset granularity (ISSUES.md #53).
891891
892892
One changeset = one single edit call or one whole batch_edit/batch_rewrite
893-
call. Distinct changesets by the same author never share a second (the
894-
stamp is bumped past the previous one on collision), so parse-time
895-
reconstruction (#46) never merges them; all ops of one batch call share
896-
one date by design.
893+
call. Within one open session, distinct changesets by the same author
894+
never share a second (the stamp is bumped past the previous one on
895+
collision), so parse-time reconstruction (#46) never merges them; all ops
896+
of one batch call share one date by design.
897897
"""
898898

899899
def test_batch_ops_share_one_date(self, temp_docx, ticking_clock):
@@ -977,6 +977,20 @@ def test_drift_bounded_by_changesets_per_second(self, temp_docx, settable_clock)
977977
datetime(2025, 6, 1, 12, 0, 10, tzinfo=timezone.utc),
978978
}
979979

980+
def test_clock_regression_keeps_dates_monotonic(self, temp_docx, settable_clock):
981+
# A clock step back (e.g. NTP) is treated as a collision: dates
982+
# never go backwards, pinned at previous+1 per changeset until
983+
# the clock catches up.
984+
with Document.open(temp_docx) as doc:
985+
ref = find_ref(doc, "quick brown fox")
986+
first = doc.replace("quick", "speedy", paragraph=ref)
987+
settable_clock.current = datetime(2025, 6, 1, 11, 59, 30)
988+
doc.replace("lazy", "sleepy", paragraph=first)
989+
assert {rev.date for rev in doc.list_revisions()} == {
990+
datetime(2025, 6, 1, 12, 0, 0, tzinfo=timezone.utc),
991+
datetime(2025, 6, 1, 12, 0, 1, tzinfo=timezone.utc),
992+
}
993+
980994
def test_two_batches_same_second_get_distinct_dates(self, temp_docx, frozen_clock):
981995
# Two batch_edit calls are two changesets even inside one real
982996
# second — distinct dates, two inferred groups after reopen.
@@ -997,7 +1011,7 @@ def test_nested_frozen_timestamp_reuses_outer_stamp(self, temp_xml):
9971011
# frozen_timestamp is reentrant-by-reuse: an inner scope joins the
9981012
# enclosing changeset (batch wrapper + per-op _grouped). Only the
9991013
# outermost entry allocates a stamp; only the outermost exit clears.
1000-
editor = DocxXMLEditor(temp_xml("<w:p><w:r><w:t>x</w:t></w:r></w:p>"), rsid="00000000", author=AUTHOR_B)
1014+
editor = _make_manager(temp_xml("<w:p><w:r><w:t>x</w:t></w:r></w:p>")).editor
10011015
with editor.frozen_timestamp():
10021016
outer = editor._frozen_timestamp
10031017
assert outer is not None

0 commit comments

Comments
 (0)