Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop YTest #14

Merged
merged 2 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 36 additions & 42 deletions tests/test_pycrdt_yjs.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,69 @@
from __future__ import annotations

from functools import partial

import pytest
from anyio import Event, create_task_group, move_on_after, sleep
from anyio import Event, fail_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):
self.ydoc = ydoc
class Change:
def __init__(self, event, timeout, ydata, sid, key):
self.event = event
self.timeout = timeout
self.ydoc["_test"] = self.ytest = Map()
self.clock = -1.0
self.ydata = ydata
self.sid = sid
self.key = key

def run_clock(self):
self.clock = max(self.clock, 0.0)
self.ytest["clock"] = self.clock
async def wait(self):
with fail_after(self.timeout):
await self.event.wait()
self.ydata.unobserve(self.sid)
if self.key is None:
return
return self.ydata[self.key]

async def clock_run(self):
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()
def callback(change_event, key, event):
if key is None or key in event.keys:
change_event.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)
def watch(ydata, key: str | None = None, timeout: float = 1.0):
change_event = Event()
sid = ydata.observe(partial(callback, change_event, key))
return Change(change_event, timeout, ydata, sid, key)


@pytest.mark.anyio
@pytest.mark.parametrize("yjs_client", "0", indirect=True)
async def test_pycrdt_yjs_0(yws_server, yjs_client):
ydoc = Doc()
ytest = YTest(ydoc)
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"]
v_out = await watch(ymap, "out").wait()
assert v_out == v_in + 1.0


@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 = Doc()
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}}
ycells_change = watch(ycells)
ystate_change = watch(ystate)
async with connect("ws://127.0.0.1:1234/my-roomname") as websocket, WebsocketProvider(
ydoc, websocket
):
await ycells_change.wait()
await ystate_change.wait()
assert ycells.to_py() == [{"metadata": {"foo": "bar"}, "source": "1 + 2"}]
assert ystate.to_py() == {"state": {"dirty": False}}
34 changes: 13 additions & 21 deletions tests/yjs_client_0.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
const Y = require('yjs')
const WebsocketProvider = require('y-websocket').WebsocketProvider
const ws = require('ws')

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

function increment(resolve) {
ymap.set('out', ymap.get('in') + 1);
resolve();
}

ymap.observe(event => {
if (event.transaction.local || !event.changes.keys.has('in')) {
return
}
new Promise(increment);
})

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

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

var clock = -1

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

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 wsProvider = new WebsocketProvider(
'ws://127.0.0.1:1234', 'my-roomname',
ydoc,
{ WebSocketPolyfill: ws }
)

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

var clock = -1
wsProvider.on('sync', () => {
const ycells = ydoc.getArray('cells')
const ystate = ydoc.getMap('state')

ytest.observe(event => {
event.changes.keys.forEach((change, key) => {
if (key === 'clock') {
const clk = ytest.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'}]])]
const state = {'dirty': false}
ycells.push(cells)
ystate.set('state', state)
})
Loading