From 62bb84709eaca0ee94ae25d9768e6235af139373 Mon Sep 17 00:00:00 2001 From: Carlos Herrero <26092748+hbcarlos@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:45:23 +0200 Subject: [PATCH 1/4] Adds general tests --- tests/conftest.py | 2 + tests/test_general.py | 111 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 tests/test_general.py diff --git a/tests/conftest.py b/tests/conftest.py index 67845d32..d87481d9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -90,6 +90,8 @@ async def _inner( # Create a notebook string and write to file. if content is None: nb = nbformat.v4.new_notebook() + notary = nbformat.sign.NotebookNotary() + notary.sign(nb) content = nbformat.writes(nb, version=4) nbpath.write_text(content) diff --git a/tests/test_general.py b/tests/test_general.py new file mode 100644 index 00000000..e4371396 --- /dev/null +++ b/tests/test_general.py @@ -0,0 +1,111 @@ +# Copyright (c) Jupyter Development Team. +# Distributed under the terms of the Modified BSD License. + +from __future__ import annotations + +from asyncio import create_task, sleep, wait + +import nbformat +from jupyter_ydoc import YNotebook, YUnicode +from ypy_websocket import WebsocketProvider + + +async def test_clients(rtc_create_file, rtc_connect_doc_client): + file_path = "test.txt" + file_format = "text" + file_type = "file" + await rtc_create_file(file_path) + + async def fn( + format: str, type: str, path: str, doc: YUnicode, content: int | None = None + ) -> None: + ydoc = doc.ydoc + test_array = ydoc.get_array("test") + + async with await rtc_connect_doc_client(format, type, path) as ws, WebsocketProvider( + ydoc, ws + ): + if content is not None: + with ydoc.begin_transaction() as txn: + test_array.extend(txn, [content]) + await sleep(0.2) + + clients = [] + n = 10 + for _ in range(n): + doc = YUnicode() + clients.append(create_task(fn(file_format, file_type, file_path, doc, 1))) + + doc = YUnicode() + test_array = doc.ydoc.get_array("test") + clients.append(create_task(fn(file_format, file_type, file_path, doc))) + + await wait(clients) + + assert sum(list(test_array)) == n + + +async def test_clients_insert_text(rtc_create_file, rtc_connect_doc_client): + file_path = "test.txt" + file_format = "text" + file_type = "file" + await rtc_create_file(file_path) + + async def fn( + format: str, type: str, path: str, doc: YUnicode, content: str | None = None + ) -> None: + async with await rtc_connect_doc_client(format, type, path) as ws, WebsocketProvider( + doc.ydoc, ws + ): + if content is not None: + with doc.ydoc.begin_transaction() as txn: + doc._ysource.extend(txn, content) + + await sleep(0.2) + + n = 10 + content = "test" + res = len(content) * n + + clients = [] + for _ in range(n): + doc = YUnicode() + clients.append(create_task(fn(file_format, file_type, file_path, doc, content))) + + doc = YUnicode() + clients.append(create_task(fn(file_format, file_type, file_path, doc))) + + await wait(clients) + + assert len(doc._ysource) == res + + +async def test_clients_insert_cell(rtc_create_notebook, rtc_connect_doc_client): + file_path = "test.ipynb" + file_format = "json" + file_type = "notebook" + await rtc_create_notebook(file_path) + + async def fn( + format: str, type: str, path: str, doc: YNotebook, content: str | None = "" + ) -> None: + async with await rtc_connect_doc_client(format, type, path) as ws, WebsocketProvider( + doc.ydoc, ws + ): + if content is not None: + doc.append_cell(nbformat.v4.new_code_cell(content)) + await sleep(0.2) + + n = 10 + clients = [] + for _ in range(n): + doc = YNotebook() + clients.append(create_task(fn(file_format, file_type, file_path, doc, "test"))) + + doc = YNotebook() + clients.append(create_task(fn(file_format, file_type, file_path, doc, None))) + + await wait(clients) + + # +1 For the initial cell :( + assert doc.cell_number == n + 1 From 0cd5da60fc89a037e6bcbd6904156b66418b4a5e Mon Sep 17 00:00:00 2001 From: Carlos Herrero <26092748+hbcarlos@users.noreply.github.com> Date: Wed, 18 Oct 2023 17:07:14 +0200 Subject: [PATCH 2/4] Sleep after closing clients --- tests/test_general.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_general.py b/tests/test_general.py index e4371396..d7834449 100644 --- a/tests/test_general.py +++ b/tests/test_general.py @@ -43,6 +43,7 @@ async def fn( await wait(clients) assert sum(list(test_array)) == n + await sleep(1) async def test_clients_insert_text(rtc_create_file, rtc_connect_doc_client): @@ -78,6 +79,7 @@ async def fn( await wait(clients) assert len(doc._ysource) == res + await sleep(1) async def test_clients_insert_cell(rtc_create_notebook, rtc_connect_doc_client): @@ -109,3 +111,4 @@ async def fn( # +1 For the initial cell :( assert doc.cell_number == n + 1 + await sleep(1) From aad974cdcc060deddaeec36d43a897eab9d7cd46 Mon Sep 17 00:00:00 2001 From: Carlos Herrero <26092748+hbcarlos@users.noreply.github.com> Date: Wed, 18 Oct 2023 17:33:35 +0200 Subject: [PATCH 3/4] Remove cleanup delay on tests --- tests/conftest.py | 1 + tests/test_app.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index d87481d9..1e2e7981 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -32,6 +32,7 @@ def jp_server_config(jp_root_dir, jp_server_config): "password": "", "disable_check_xsrf": True, }, + "YDocExtension": {"document_cleanup_delay": 0}, "SQLiteYStore": {"db_path": str(jp_root_dir.joinpath(".rtc_test.db"))}, "BaseFileIdManager": { "root_dir": str(jp_root_dir), diff --git a/tests/test_app.py b/tests/test_app.py index becc278c..37856ca5 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -11,7 +11,7 @@ def test_default_settings(jp_serverapp): assert settings["disable_rtc"] is False assert settings["file_poll_interval"] == 1 - assert settings["document_cleanup_delay"] == 60 + assert settings["document_cleanup_delay"] == 0 assert settings["document_save_delay"] == 1 assert settings["ystore_class"] == SQLiteYStore From 1c5eda3541e7172de1cedd4cb3550d70f3b7a1ed Mon Sep 17 00:00:00 2001 From: Carlos Herrero <26092748+hbcarlos@users.noreply.github.com> Date: Wed, 18 Oct 2023 21:31:11 +0200 Subject: [PATCH 4/4] Improves tests --- tests/test_general.py | 46 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/tests/test_general.py b/tests/test_general.py index d7834449..188a1d27 100644 --- a/tests/test_general.py +++ b/tests/test_general.py @@ -3,7 +3,8 @@ from __future__ import annotations -from asyncio import create_task, sleep, wait +from asyncio import Event, create_task, sleep, wait +from typing import Any import nbformat from jupyter_ydoc import YNotebook, YUnicode @@ -19,16 +20,24 @@ async def test_clients(rtc_create_file, rtc_connect_doc_client): async def fn( format: str, type: str, path: str, doc: YUnicode, content: int | None = None ) -> None: - ydoc = doc.ydoc - test_array = ydoc.get_array("test") + + event = Event() + + def callback(e: Any) -> None: + nonlocal event + event.set() + + test_array = doc.ydoc.get_array("test") + test_array.observe(callback) async with await rtc_connect_doc_client(format, type, path) as ws, WebsocketProvider( - ydoc, ws + doc.ydoc, ws ): if content is not None: - with ydoc.begin_transaction() as txn: + with doc.ydoc.begin_transaction() as txn: test_array.extend(txn, [content]) - await sleep(0.2) + await event.wait() + await sleep(0.1) clients = [] n = 10 @@ -55,14 +64,23 @@ async def test_clients_insert_text(rtc_create_file, rtc_connect_doc_client): async def fn( format: str, type: str, path: str, doc: YUnicode, content: str | None = None ) -> None: + event = Event() + + def callback(target: str, e: Any) -> None: + nonlocal event + if target == "source": + event.set() + + doc.observe(callback) + async with await rtc_connect_doc_client(format, type, path) as ws, WebsocketProvider( doc.ydoc, ws ): if content is not None: with doc.ydoc.begin_transaction() as txn: doc._ysource.extend(txn, content) - - await sleep(0.2) + await event.wait() + await sleep(0.1) n = 10 content = "test" @@ -91,12 +109,22 @@ async def test_clients_insert_cell(rtc_create_notebook, rtc_connect_doc_client): async def fn( format: str, type: str, path: str, doc: YNotebook, content: str | None = "" ) -> None: + event = Event() + + def callback(target: str, e: Any) -> None: + nonlocal event + if target == "cells": + event.set() + + doc.observe(callback) + async with await rtc_connect_doc_client(format, type, path) as ws, WebsocketProvider( doc.ydoc, ws ): if content is not None: doc.append_cell(nbformat.v4.new_code_cell(content)) - await sleep(0.2) + await event.wait() + await sleep(0.1) n = 10 clients = []