From 7640cfd960aaee0d23489d0ba868e74eda929dac Mon Sep 17 00:00:00 2001 From: tharun571 Date: Fri, 12 Jul 2024 08:47:04 +0530 Subject: [PATCH] Add testing frameworks for notebooks, update documentation --- .github/workflows/main.yml | 2 +- docs/source/debug.rst | 5 +- environment-dev.yml | 2 + test/Notebooks/xeus-cpp.ipynb | 643 ++++++++++++++++++++++++++++++++++ test/test_xcpp_kernel.py | 49 ++- 5 files changed, 697 insertions(+), 4 deletions(-) create mode 100644 test/Notebooks/xeus-cpp.ipynb diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7b85661c..7bf9ddfe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -139,7 +139,7 @@ jobs: shell: bash -l {0} run: | cd test - pytest -sv . --reruns 5 + pytest -sv . - name: Python tests Windows Systems if: ${{ runner.os == 'windows' }} diff --git a/docs/source/debug.rst b/docs/source/debug.rst index 04776e29..39ec291b 100644 --- a/docs/source/debug.rst +++ b/docs/source/debug.rst @@ -8,6 +8,7 @@ These steps are performed using the GNU Debugger (GDB), so ensure it is installe .. code-block:: bash + cd build cmake -D CMAKE_BUILD_TYPE=Debug -D CMAKE_PREFIX_PATH=$CONDA_PREFIX -D CMAKE_INSTALL_PREFIX=$CONDA_PREFIX -D CMAKE_INSTALL_LIBDIR=lib .. In the same folder, run the command and copy the JSON displayed in the terminal. @@ -34,5 +35,5 @@ Testing The source code for the c++ tests is located in `test/test_interpreter.cpp`. The source code for the python tests is located in `test/test_xcpp_kernel.py`. Write the necessary tests and build the project as described in the repository's README or contributing guidelines. -Then, execute `build/test/test_xeus_cpp` from the top level directory to verify if the c++ tests were successful, -and `pytest -sv build/test/test_xcpp_kernel.py` to execute the python tests. \ No newline at end of file +Then, execute `build/test/test_xeus_cpp` from the top level directory to verify if the c++ tests were successful. +and in the test directory run `pytest -sv build/test/test_xcpp_kernel.py` to execute the python tests. \ No newline at end of file diff --git a/environment-dev.yml b/environment-dev.yml index 6243d8c7..bb8ac8e8 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -17,6 +17,8 @@ dependencies: # Test dependencies - pytest - jupyter_kernel_test>=0.5,<0.6 + - papermill + - nbformat - nbval - pytest-rerunfailures - doctest \ No newline at end of file diff --git a/test/Notebooks/xeus-cpp.ipynb b/test/Notebooks/xeus-cpp.ipynb new file mode 100644 index 00000000..7daa6b2a --- /dev/null +++ b/test/Notebooks/xeus-cpp.ipynb @@ -0,0 +1,643 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "be1c8c6c-3fbe-4f53-9deb-8496c43d26ad", + "metadata": {}, + "source": [ + "## Output and error streams\n", + "\n", + "`std::cout` and `std::cerr` are redirected to the notebook frontend." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "56c6f89e-205e-4099-bfbe-4d84e45f4390", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "#include " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9bd7f767-6c22-4a1b-a1e2-cd4184fd0367", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "some output" + ] + } + ], + "source": [ + "std::cout << \"some output\";" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9622f20f-5925-4544-a97b-aada3a14209a", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "some error" + ] + } + ], + "source": [ + "std::cerr << \"some error\";" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "7af0c962-17dc-47d4-9772-b8a06e2bda3a", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n" + ] + } + ], + "source": [ + "int j = 5;\n", + "std::cout << j << std::endl;" + ] + }, + { + "cell_type": "markdown", + "id": "526a7dba-4001-47d5-a116-95423118e100", + "metadata": {}, + "source": [ + "# Interpreting the C++ programming language\n", + "\n", + "You can define functions, classes, templates, etc ..." + ] + }, + { + "cell_type": "markdown", + "id": "e5b116ce-ced1-4aa4-b14e-ef7d2606202e", + "metadata": {}, + "source": [ + "## Functions" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "86b08f22-e16c-4eac-917d-ae6eeb7ec7cb", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "double sqr(double a)\n", + "{\n", + " return a * a;\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5aff6711-54bf-4280-a496-c9f7c683eee5", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6.25\n" + ] + } + ], + "source": [ + "double a = 2.5;\n", + "double asqr = sqr(a);\n", + "std::cout << asqr << std::endl;" + ] + }, + { + "cell_type": "markdown", + "id": "5b3959b0-9dc7-41a4-bba1-e20abd0765f7", + "metadata": {}, + "source": [ + "## Classes" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d981a53b-8185-49c5-8a30-02453cc1b9e9", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "class Foo\n", + "{\n", + "public:\n", + "\n", + " virtual ~Foo() {}\n", + " \n", + " virtual void print(double value) const\n", + " {\n", + " std::cout << \"Foo value = \" << value << std::endl;\n", + " }\n", + "};" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "195d513f-d5cb-4e3d-a6cb-ae99dfcd9aab", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Foo value = 1.2\n" + ] + } + ], + "source": [ + "Foo bar;\n", + "bar.print(1.2);" + ] + }, + { + "cell_type": "markdown", + "id": "9ecc1588-cb6e-4676-bb16-b9938e980b06", + "metadata": {}, + "source": [ + "## Polymorphism" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "4df90bea-5c9e-462e-bd20-d80fd169b7b6", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "class Bar : public Foo\n", + "{\n", + "public:\n", + "\n", + " virtual ~Bar() {}\n", + " \n", + " virtual void print(double value) const\n", + " {\n", + " std::cout << \"Bar value = \" << 2 * value << std::endl;\n", + " }\n", + "};" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "f7dbbcc2-0f00-48eb-8bb9-94e871afa2a7", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Bar value = 2.4\n" + ] + } + ], + "source": [ + "Foo* bar2 = new Bar;\n", + "bar2->print(1.2);\n", + "delete bar2;" + ] + }, + { + "cell_type": "markdown", + "id": "094f4ca7-0aa5-4121-bfff-bf5db1d42c5d", + "metadata": {}, + "source": [ + "## Templates" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "0df4f3a5-25a1-4548-ba63-54887c770dad", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "#include \n", + "\n", + "template \n", + "class FooT\n", + "{\n", + "public:\n", + " \n", + " explicit FooT(const T& t) : m_t(t) {}\n", + " \n", + " void print() const\n", + " {\n", + " std::cout << typeid(T).name() << \" m_t = \" << m_t << std::endl;\n", + " }\n", + " \n", + "private:\n", + " \n", + " T m_t;\n", + "};" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e7bcab70-b9db-409c-aa04-9c64b413e266", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "d m_t = 1.2\n" + ] + } + ], + "source": [ + "FooT foot(1.2);\n", + "foot.print();" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "de6ba06d-ed19-40b6-b31d-b8782fb81174", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "#include \"xcpp/xdisplay.hpp\"" + ] + }, + { + "cell_type": "markdown", + "id": "387d6a7a-7ca4-41d6-9809-4040641db338", + "metadata": {}, + "source": [ + "### Audio example" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "5aab5534-8420-4341-bf59-546d5f24bbed", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "#include \n", + "#include \n", + "\n", + "#include \"nlohmann/json.hpp\"\n", + "\n", + "#include \"xeus/xbase64.hpp\"\n", + "\n", + "namespace nl = nlohmann;\n", + "\n", + "namespace au\n", + "{\n", + " struct audio\n", + " { \n", + " inline audio(const std::string& filename)\n", + " {\n", + " std::ifstream fin(filename, std::ios::binary); \n", + " m_buffer << fin.rdbuf();\n", + " }\n", + " \n", + " std::stringstream m_buffer;\n", + " };\n", + " \n", + " nl::json mime_bundle_repr(const audio& a)\n", + " {\n", + " auto bundle = nl::json::object();\n", + " bundle[\"text/html\"] =\n", + " std::string(\"\";\n", + " return bundle;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "0b9e8220-fe5a-498e-93a1-a93e71bda629", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "au::audio drums(\"audio/audio.wav\");" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "faee9bb7-22e4-49ad-8483-38cc1a044b19", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "xcpp::display(drums);" + ] + }, + { + "cell_type": "markdown", + "id": "d32274d3-61eb-4763-9b10-b3d4db09ae5c", + "metadata": {}, + "source": [ + "### Update-display" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "5ba5126e-ea3a-4393-84a1-99fac70ae5e4", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "#include \n", + "#include \"xcpp/xdisplay.hpp\"\n", + "\n", + "#include \"nlohmann/json.hpp\"\n", + "\n", + "namespace nl = nlohmann;\n", + "\n", + "namespace ht\n", + "{\n", + " struct html\n", + " { \n", + " inline html(const std::string& content)\n", + " {\n", + " m_content = content;\n", + " }\n", + " std::string m_content;\n", + " };\n", + "\n", + " nl::json mime_bundle_repr(const html& a)\n", + " {\n", + " auto bundle = nl::json::object();\n", + " bundle[\"text/html\"] = a.m_content;\n", + " return bundle;\n", + " }\n", + "}\n", + "\n", + "// A blue rectangle\n", + "ht::html rect(R\"(\n", + "
\n", + "Original\n", + "
)\");" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "a3de3009-bf70-4adf-9471-900be0bc3d2d", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "Updated\n", + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "xcpp::display(rect, \"some_display_id\");" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "629fa3d5-6c5d-40ad-937d-6257ae6c827d", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "// Update the rectangle to be red\n", + "rect.m_content = R\"(\n", + "
\n", + "Updated\n", + "
)\";\n", + "\n", + "xcpp::display(rect, \"some_display_id\", true);" + ] + }, + { + "cell_type": "markdown", + "id": "d4828c73-b061-4f8f-9966-f45d570c6567", + "metadata": {}, + "source": [ + "### Clear output" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "8391fa79-f365-4792-b1f6-b2f68d79a3d6", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [], + "source": [ + "#include \n", + "#include \n", + "#include \n", + "\n", + "#include \"xcpp/xdisplay.hpp\"" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "16ab8930-6639-4c8d-855f-58ec70a62c17", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "goodbye\n" + ] + } + ], + "source": [ + "std::cout << \"hello\" << std::endl;\n", + "std::this_thread::sleep_for(std::chrono::seconds(1));\n", + "xcpp::clear_output(); // will flicker when replacing \"hello\" with \"goodbye\"\n", + "std::this_thread::sleep_for(std::chrono::seconds(1));\n", + "std::cout << \"goodbye\" << std::endl;" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "347331f2-98cf-4111-989c-7e34b3d0a309", + "metadata": { + "vscode": { + "languageId": "c++" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "goodbye\n" + ] + } + ], + "source": [ + "std::cout << \"hello\" << std::endl;\n", + "std::this_thread::sleep_for(std::chrono::seconds(1));\n", + "xcpp::clear_output(true); // prevents flickering\n", + "std::this_thread::sleep_for(std::chrono::seconds(1));\n", + "std::cout << \"goodbye\" << std::endl;" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "C++20", + "language": "cpp", + "name": "xcpp20" + }, + "language_info": { + "codemirror_mode": "text/x-c++src", + "file_extension": ".cpp", + "mimetype": "text/x-c++src", + "name": "C++", + "version": "20" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/test/test_xcpp_kernel.py b/test/test_xcpp_kernel.py index 8f45b7de..f02b68d9 100644 --- a/test/test_xcpp_kernel.py +++ b/test/test_xcpp_kernel.py @@ -9,7 +9,8 @@ import unittest import jupyter_kernel_test import platform - +import nbformat +import papermill as pm class XCppCompleteTests(jupyter_kernel_test.KernelTests): @@ -132,6 +133,52 @@ class XCppTests(jupyter_kernel_test.KernelTests): } ] + # Tests for Notebooks + class XCppNotebookTests(unittest.TestCase): + + notebook_names = [ + 'xeus-cpp' + # Add more notebook names as needed + ] + + def test_notebooks(self): + for name in self.notebook_names: + + inp = f'Notebooks/{name}.ipynb' + out = f'Notebooks/{name}_output.ipynb' + + with open(inp) as f: + input_nb = nbformat.read(f, as_version=4) + + try: + # Execute the notebook + executed_notebook = pm.execute_notebook( + inp, + out, + log_output=True, + kernel_name='xcpp20' + ) + + if executed_notebook is None: # Explicit check for None or any other condition + self.fail(f"Execution of notebook {name} returned None") + except Exception as e: + self.fail(f"Notebook {name} failed to execute: {e}") + + with open(out) as f: + output_nb = nbformat.read(f, as_version=4) + + check = True + + # Iterate over the cells in the input and output notebooks + for i, (input_cell, output_cell) in enumerate(zip(input_nb.cells, output_nb.cells)): + if input_cell.cell_type == 'code' and output_cell.cell_type == 'code': + # If one cell has output and the other doesn't, set check to False + if bool(input_cell.outputs) != bool(output_cell.outputs): + self.fail(f"Cell {i} in notebook {name} has mismatched output presence") + else: + if input_cell.outputs != output_cell.outputs: + self.fail(f"{input_output.get('text')} != {output_output.get('text')} Cell {i} in notebook {name} has mismatched output type") + class XCppTests2(jupyter_kernel_test.KernelTests):