Skip to content

Commit

Permalink
refactor tests to improve legibility
Browse files Browse the repository at this point in the history
  • Loading branch information
dlqqq committed Feb 2, 2024
1 parent ef13fed commit e9f061b
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 89 deletions.
112 changes: 66 additions & 46 deletions tests/test_pycrdt_yjs.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,95 @@
import pytest
from anyio import Event, create_task_group, move_on_after, sleep
from anyio import Event, move_on_after
from pycrdt import Array, Doc, Map
from websockets import connect

from pycrdt_websocket import WebsocketProvider


class YTest:
def __init__(self, ydoc: Doc, timeout: float = 1.0):
class YClient:
"""
A representation of a `pycrdt` client. The constructor accepts a YDoc
instance, defines a shared YMap type named `ymap`, and binds it to
`self.ymap`.
"""

def __init__(self, ydoc: Doc):
self.ydoc = ydoc
self.timeout = timeout
self.ydoc["_test"] = self.ytest = Map()
self.clock = -1.0
self.ymap = Map()
self.ydoc["ymap"] = self.ymap
self.ymap["clock"] = 0.0

@property
def clock(self):
return self.ymap["clock"]

def run_clock(self):
self.clock = max(self.clock, 0.0)
self.ytest["clock"] = self.clock
def inc_clock(self):
"""
Increments `ymap["clock"]`.
"""
self.ymap["clock"] = self.clock + 1

async def clock_run(self):
async def wait_for_reply(self):
"""
Wait for the JS client to reply in response to a previous `inc_clock()`
call by also incrementing `ymap["clock"]`.
"""
change = Event()

def callback(event):
if "clock" in event.keys:
clk = event.keys["clock"]["newValue"]
if clk > self.clock:
self.clock = clk + 1.0
change.set()
change.set()

subscription_id = self.ytest.observe(callback)
async with create_task_group():
with move_on_after(self.timeout):
await change.wait()

self.ytest.unobserve(subscription_id)
subscription_id = self.ymap.observe(callback)
# wait up to 1000ms for a reply
with move_on_after(1.0):
await change.wait()
self.ymap.unobserve(subscription_id)


@pytest.mark.anyio
@pytest.mark.parametrize("yjs_client", "0", indirect=True)
async def test_pycrdt_yjs_0(yws_server, yjs_client):
"""
The JS client (`yjs_client_0.js`) should set `ymap["out"] := ymap["in"] + 1`
whenever the clock is incremented via `yclient.inc_clock()`.
"""
MSG_COUNT = 10
ydoc = Doc()
ytest = YTest(ydoc)
yclient = YClient(ydoc)
ymap = yclient.ymap

async with connect("ws://127.0.0.1:1234/my-roomname") as websocket, WebsocketProvider(
ydoc, websocket
):
ydoc["map"] = ymap = Map()
# set a value in "in"
for v_in in range(10):
ymap["in"] = float(v_in)
ytest.run_clock()
await ytest.clock_run()
v_out = ymap["out"]
assert v_out == v_in + 1.0
for msg_num in range(1, MSG_COUNT + 1):
# set `ymap["in"]`
ymap["in"] = float(msg_num)
yclient.inc_clock()
await yclient.wait_for_reply()

# assert JS client increments `ymap["out"]` in response
assert ymap["out"] == ymap["in"] + 1.0

assert yclient.clock == MSG_COUNT * 2


@pytest.mark.anyio
@pytest.mark.parametrize("yjs_client", "1", indirect=True)
async def test_pycrdt_yjs_1(yws_server, yjs_client):
# wait for the JS client to connect
tt, dt = 0, 0.1
while True:
await sleep(dt)
if "/my-roomname" in yws_server.rooms:
break
tt += dt
if tt >= 1:
raise RuntimeError("Timeout waiting for client to connect")
ydoc = yws_server.rooms["/my-roomname"].ydoc
ytest = YTest(ydoc)
ytest.run_clock()
await ytest.clock_run()
ydoc["cells"] = ycells = Array()
ydoc["state"] = ystate = Map()
assert ycells.to_py() == [{"metadata": {"foo": "bar"}, "source": "1 + 2"}]
assert ystate.to_py() == {"state": {"dirty": False}}
"""
The JS client (`yjs_client_1.js`) should set `ydoc["cells"]
whenever the clock is incremented via `yclient.inc_clock()`.
"""
yroom = await yws_server.get_room("/my-roomname")
ydoc = yroom.ydoc
yclient = YClient(ydoc)

yclient.inc_clock()
await yclient.wait_for_reply()

# TODO: remove the need to set a root type before accessing it
ydoc["cells"] = Array()
ydoc["state"] = Map()
assert ydoc["cells"].to_py() == [{"metadata": {"foo": "bar"}, "source": "1 + 2"}]
assert ydoc["state"].to_py() == {"state": {"dirty": False}}
40 changes: 20 additions & 20 deletions tests/yjs_client_0.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
const Y = require('yjs')
const WebsocketProvider = require('y-websocket').WebsocketProvider
const Y = require("yjs");
const WebsocketProvider = require("y-websocket").WebsocketProvider;

const ydoc = new Y.Doc()
const ytest = ydoc.getMap('_test')
const ymap = ydoc.getMap('map')
const ws = require('ws')
const ydoc = new Y.Doc();
const ymap = ydoc.getMap("ymap");
const ws = require("ws");

const wsProvider = new WebsocketProvider(
'ws://127.0.0.1:1234', 'my-roomname',
"ws://127.0.0.1:1234",
"my-roomname",
ydoc,
{ WebSocketPolyfill: ws }
)
);

wsProvider.on('status', event => {
console.log(event.status)
})
wsProvider.on("status", (event) => {
console.log(event.status);
});

var clock = -1
var clock = -1;

ytest.observe(event => {
ymap.observe((event) => {
event.changes.keys.forEach((change, key) => {
if (key === 'clock') {
const clk = ytest.get('clock')
if (key === "clock") {
const clk = ymap.get("clock");
if (clk > clock) {
ymap.set('out', ymap.get('in') + 1)
clock = clk + 1
ytest.set('clock', clock)
ymap.set("out", ymap.get("in") + 1);
clock = clk + 1;
ymap.set("clock", clock);
}
}
})
})
});
});
52 changes: 29 additions & 23 deletions tests/yjs_client_1.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,41 @@
const Y = require('yjs')
const WebsocketProvider = require('y-websocket').WebsocketProvider
const Y = require("yjs");
const WebsocketProvider = require("y-websocket").WebsocketProvider;

const ydoc = new Y.Doc()
const ytest = ydoc.getMap('_test')
const ycells = ydoc.getArray("cells")
const ystate = ydoc.getMap("state")
const ws = require('ws')
const ydoc = new Y.Doc();
const ymap = ydoc.getMap("ymap");
const ycells = ydoc.getArray("cells");
const ystate = ydoc.getMap("state");
const ws = require("ws");

const wsProvider = new WebsocketProvider(
'ws://127.0.0.1:1234', 'my-roomname',
"ws://127.0.0.1:1234",
"my-roomname",
ydoc,
{ WebSocketPolyfill: ws }
)
);

wsProvider.on('status', event => {
console.log(event.status)
})
wsProvider.on("status", (event) => {
console.log(event.status);
});

var clock = -1
var clock = -1;

ytest.observe(event => {
ymap.observe((event) => {
event.changes.keys.forEach((change, key) => {
if (key === 'clock') {
const clk = ytest.get('clock')
if (key === "clock") {
const clk = ymap.get("clock");
if (clk > clock) {
const cells = [new Y.Map([['source', new Y.Text('1 + 2')], ['metadata', {'foo': 'bar'}]])]
ycells.push(cells)
ystate.set('state', {'dirty': false})
clock = clk + 1
ytest.set('clock', clock)
const cells = [
new Y.Map([
["source", new Y.Text("1 + 2")],
["metadata", { foo: "bar" }],
]),
];
ycells.push(cells);
ystate.set("state", { dirty: false });
clock = clk + 1;
ymap.set("clock", clock);
}
}
})
})
});
});

0 comments on commit e9f061b

Please sign in to comment.