Skip to content

Commit a4b662d

Browse files
committed
Add test with expectation of granular reload and append
1 parent a18f320 commit a4b662d

File tree

3 files changed

+190
-20
lines changed

3 files changed

+190
-20
lines changed

tests/test_ynotebook.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4-
from dataclasses import dataclass
5-
64
from pycrdt import ArrayEvent, Map, MapEvent, TextEvent
75
from pytest import mark
6+
from utils import ExpectedEvent
87

98
from jupyter_ydoc import YNotebook
109

@@ -119,24 +118,6 @@ def record_changes(topic, event):
119118
]
120119

121120

122-
@dataclass
123-
class ExpectedEvent:
124-
kind: type
125-
path: str | None = None
126-
127-
def __eq__(self, other):
128-
if not isinstance(other, self.kind):
129-
return False
130-
if self.path is not None and self.path != other.path:
131-
return False
132-
return True
133-
134-
def __repr__(self):
135-
if self.path is not None:
136-
return f"ExpectedEvent({self.kind.__name__}, path={self.path!r})"
137-
return f"ExpectedEvent({self.kind.__name__})"
138-
139-
140121
@mark.parametrize(
141122
"modifications, expected_events",
142123
[

tests/test_yunicode.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4+
from pycrdt import TextEvent
5+
from utils import ExpectedEvent
6+
47
from jupyter_ydoc import YUnicode
58

69

@@ -25,3 +28,163 @@ def record_changes(topic, event):
2528

2629
# No changes should be observed at all
2730
assert changes == []
31+
32+
33+
def test_set_granular_changes():
34+
text = YUnicode()
35+
36+
text.set(
37+
"\n".join(
38+
[
39+
"Mary had a little lamb,",
40+
"Its fleece was white as snow.",
41+
"And everywhere that Mary went,",
42+
"The lamb was sure to go.",
43+
]
44+
)
45+
)
46+
47+
changes = []
48+
49+
def record_changes(topic, event):
50+
changes.append((topic, event)) # pragma: nocover
51+
52+
text.observe(record_changes)
53+
54+
# Call set with the bunny version
55+
text.set(
56+
"\n".join(
57+
[
58+
"Mary had a little bunny,",
59+
"Its fur was white as snow.",
60+
"And everywhere that Mary went,",
61+
"The bunny was sure to hop.",
62+
]
63+
)
64+
)
65+
66+
assert len(changes) == 1
67+
source_events = [e for t, e in changes if t == "source"]
68+
assert source_events == [
69+
ExpectedEvent(
70+
TextEvent,
71+
delta=[
72+
# "Mary had a little <delete:lam>b"
73+
{"retain": 18},
74+
{"delete": 3},
75+
{"retain": 1},
76+
# "Mary had a little b<insert:unny>"
77+
{"insert": "unny"},
78+
# ",↵ Its f<delete:leece>"
79+
{"retain": 7},
80+
{"delete": 5},
81+
# ",↵ Its f<insert:ur>"
82+
{"insert": "ur"},
83+
# " was white as snow.↵"
84+
# "And everywhere that Mary went,↵"
85+
# "The <delete:lam>b"
86+
{"retain": 55},
87+
{"delete": 3},
88+
{"retain": 1},
89+
# "The b<insert:unny> was sure to"
90+
{"insert": "unny"},
91+
{"retain": 13},
92+
# "<delete:g><insert:h>o<insert:p>"
93+
{"delete": 1},
94+
{"insert": "h"},
95+
{"retain": 1},
96+
{"insert": "p"},
97+
],
98+
)
99+
]
100+
101+
102+
def test_set_granular_append():
103+
text = YUnicode()
104+
105+
text.set(
106+
"\n".join(
107+
[
108+
"Mary had a little lamb,",
109+
"Its fleece was white as snow.",
110+
]
111+
)
112+
)
113+
114+
changes = []
115+
116+
def record_changes(topic, event):
117+
changes.append((topic, event)) # pragma: nocover
118+
119+
text.observe(record_changes)
120+
121+
# append a line
122+
text.set(
123+
"\n".join(
124+
[
125+
"Mary had a little lamb,",
126+
"Its fleece was white as snow.",
127+
"And everywhere that Mary went,",
128+
]
129+
)
130+
)
131+
132+
# append one more line
133+
text.set(
134+
"\n".join(
135+
[
136+
"Mary had a little lamb,",
137+
"Its fleece was white as snow.",
138+
"And everywhere that Mary went,",
139+
"The lamb was sure to go.",
140+
]
141+
)
142+
)
143+
144+
assert len(changes) == 2
145+
source_events = [e for t, e in changes if t == "source"]
146+
assert source_events == [
147+
ExpectedEvent(
148+
TextEvent, delta=[{"retain": 53}, {"insert": "\nAnd everywhere that Mary went,"}]
149+
),
150+
ExpectedEvent(TextEvent, delta=[{"retain": 84}, {"insert": "\nThe lamb was sure to go."}]),
151+
]
152+
153+
154+
def test_set_hard_reload_if_very_different():
155+
text = YUnicode()
156+
157+
text.set(
158+
"\n".join(
159+
[
160+
"Mary had a little lamb,",
161+
"Its fleece was white as snow.",
162+
"And everywhere that Mary went,",
163+
"The lamb was sure to go.",
164+
]
165+
)
166+
)
167+
168+
changes = []
169+
170+
def record_changes(topic, event):
171+
changes.append((topic, event)) # pragma: nocover
172+
173+
text.observe(record_changes)
174+
175+
# Call set with a very different nursery rhyme
176+
twinkle_lyrics = "\n".join(
177+
[
178+
"Twinkle, twinkle, little star,",
179+
"How I wonder what you are!",
180+
"Up above the world so high,",
181+
"Like a diamond in the sky.",
182+
]
183+
)
184+
text.set(twinkle_lyrics)
185+
186+
assert len(changes) == 1
187+
source_events = [e for t, e in changes if t == "source"]
188+
assert source_events == [
189+
ExpectedEvent(TextEvent, delta=[{"delete": 109}, {"insert": twinkle_lyrics}])
190+
]

tests/utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4+
from dataclasses import dataclass
5+
46
from anyio import Lock, connect_tcp
57

68

@@ -41,3 +43,27 @@ async def ensure_server_running(host: str, port: int) -> None:
4143
pass
4244
else:
4345
break
46+
47+
48+
@dataclass
49+
class ExpectedEvent:
50+
kind: type
51+
path: str | None = None
52+
delta: list[dict] | None = None
53+
54+
def __eq__(self, other):
55+
if not isinstance(other, self.kind):
56+
return False
57+
if self.path is not None and self.path != other.path:
58+
return False
59+
if self.delta is not None and self.delta != other.delta:
60+
return False
61+
return True
62+
63+
def __repr__(self):
64+
fragments = [self.kind.__name__]
65+
if self.path is not None:
66+
fragments.append(f"path={self.path!r}")
67+
if self.delta is not None:
68+
fragments.append(f"delta={self.delta!r}")
69+
return f"ExpectedEvent({', '.join(fragments)})"

0 commit comments

Comments
 (0)