From 3c4f076166669c1437784d1b2e3f9f1ddf7fbb63 Mon Sep 17 00:00:00 2001 From: Seth Bromberger Date: Wed, 25 Sep 2024 13:46:30 -0700 Subject: [PATCH] ci with python scaffolding --- .github/workflows/pr.yml | 61 +++++++++++++ .gitignore | 1 + test/test_clippy.py | 183 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 .github/workflows/pr.yml create mode 100644 test/test_clippy.py diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..3ed65b4 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,61 @@ +name: Pull Request Quality Check + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.9' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install clippy + + + - name: Install Boost + uses: MarkusJx/install-boost@v2.4.5 + id: install-boost + with: + # REQUIRED: Specify the required boost version + # A list of supported versions can be found here: + # https://github.com/MarkusJx/prebuilt-boost/blob/main/versions-manifest.json + boost_version: 1.83.0 + # OPTIONAL: Specify a platform version + # platform_version: 18.04 + # OPTIONAL: Specify a custom install location + boost_install_dir: /home/runner/work/boost + # OPTIONAL: Specify a toolset + toolset: gcc + # OPTIONAL: Specify an architecture + # arch: x86 + + - name: Build backend + id: build-backend + env: + BOOST_ROOT: ${{ steps.install-boost.outputs.BOOST_ROOT }} + run: | + echo BOOST_ROOT is $BOOST_ROOT /end/ + sudo apt install doxygen + # TMPDIR=$(mktemp -d) + # git clone https://github.com/LLNL/clippy-cpp --branch $GITHUB_HEAD_REF $TMPDIR + + mkdir -p build + cd build && cmake -DBOOST_ROOT=$BOOST_ROOT .. && make + ls -l build/test + BACKEND=$PWD/build/test + echo "BACKEND=$BACKEND" >> $GITHUB_ENV + + - name: Run Python test framework + env: + CLIPPY_BACKEND_PATH: ${{ env.BACKEND }} + run: | + echo "backend = $BACKEND" + pytest . diff --git a/.gitignore b/.gitignore index 8d43e4a..f4a71f8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ CMakeDoxyfile.in CMakeDoxygenDefaults.cmake DartConfiguration.tcl +__pycache__ diff --git a/test/test_clippy.py b/test/test_clippy.py new file mode 100644 index 0000000..6cd1d2c --- /dev/null +++ b/test/test_clippy.py @@ -0,0 +1,183 @@ +import pytest +import sys + +import clippy +from clippy.error import ClippyValidationError, ClippyInvalidSelectorError +from clippy.expressions import Selector + +import logging + +clippy.logger.setLevel(logging.WARN) +logging.getLogger().setLevel(logging.WARN) + + +@pytest.fixture() +def testbag(): + return clippy.TestBag() + + +@pytest.fixture() +def testset(): + return clippy.TestSet() + + +@pytest.fixture() +def testfun(): + return clippy.TestFunctions() + + +@pytest.fixture() +def testsel(): + return clippy.TestSelector() + + +def test_imports(): + assert "TestBag" in clippy.__dict__ + + +def test_bag(testbag): + + testbag.insert(41) + assert testbag.size() == 1 + testbag.insert(42) + assert testbag.size() == 2 + testbag.insert(41) + assert testbag.size() == 3 + testbag.remove(41) + assert testbag.size() == 2 + testbag.remove(99) + assert testbag.size() == 2 + + +def test_expression_gt_gte(testbag): + testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) + assert testbag.size() == 6 + testbag.remove_if(testbag.value > 51) + assert testbag.size() == 5 + testbag.remove_if(testbag.value >= 50) + assert testbag.size() == 3 + testbag.remove_if(testbag.value >= 99) + assert testbag.size() == 3 + + +def test_expression_lt_lte(testbag): + testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) + testbag.remove_if(testbag.value < 42) + assert testbag.size() == 4 + testbag.remove_if(testbag.value <= 51) + assert testbag.size() == 1 + + +def test_expression_eq_neq(testbag): + testbag.insert(10).insert(11).insert(12) + assert testbag.size() == 3 + testbag.remove_if(testbag.value != 11) + assert testbag.size() == 1 + testbag.remove_if(testbag.value == 11) + assert testbag.size() == 0 + + +def test_expresssion_add(testbag): + testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) + testbag.remove_if(testbag.value + 30 > 70) + assert testbag.size() == 1 + + +def test_expression_sub(testbag): + testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) + testbag.remove_if(testbag.value - 30 > 0) + assert testbag.size() == 1 + + +def test_expression_mul_div(testbag): + testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) + testbag.remove_if(testbag.value * 2 / 4 > 10) + assert testbag.size() == 1 + + +def test_expression_or(testbag): + testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) + testbag.remove_if((testbag.value < 41) | (testbag.value > 49)) + assert testbag.size() == 2 # 41, 42 + + +def test_expression_and(testbag): + testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) + testbag.remove_if((testbag.value > 40) & (testbag.value < 50)) + assert testbag.size() == 4 # 10, 50, 51, 52 + + +# TODO: not yet implemented +# def test_expression_floordiv(testbag): +# testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) +# testbag.remove_if(testbag.value * 2 // 4.2 > 10) +# assert testbag.size() == 1 + + +def test_expression_mod(testbag): + testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) + testbag.remove_if(testbag.value % 2 == 0) + assert testbag.size() == 2 + + +# TODO: not yet implemented +# def test_expression_pow(testbag): +# testbag.insert(10).insert(41).insert(42).insert(50).insert(51).insert(52) +# testbag.remove_if(testbag.value**2 > 1000) +# assert testbag.size() == 2 + + +def test_clippy_call_with_string(testfun): + assert testfun.call_with_string("Seth") == "Howdy, Seth" + with pytest.raises(ClippyValidationError): + testfun.call_with_string() + + +def test_clippy_returns_int(testfun): + assert testfun.returns_int() == 42 + + +def test_clippy_returns_string(testfun): + assert testfun.returns_string() == "asdf" + + +def test_clippy_returns_bool(testfun): + assert testfun.returns_bool() + + +def test_clippy_returns_dict(testfun): + d = testfun.returns_dict() + assert len(d) == 3 + assert d.get("a") == 1 + assert d.get("b") == 2 + assert d.get("c") == 3 + + +def test_clippy_returns_vec_int(testfun): + assert testfun.returns_vec_int() == [0, 1, 2, 3, 4, 5] + + +def test_clippy_returns_optional_string(testfun): + assert testfun.call_with_optional_string() == "Howdy, World" + assert testfun.call_with_optional_string(name="Seth") == "Howdy, Seth" + + +def test_selectors(testsel): + assert hasattr(testsel, "nodes") + + testsel.add(testsel.nodes, "b", desc="docstring for nodes.b").add( + testsel.nodes.b, "c", desc="docstring for nodes.b.c" + ) + assert hasattr(testsel.nodes, "b") + assert hasattr(testsel.nodes.b, "c") + assert testsel.nodes.b.__doc__ == "docstring for nodes.b" + assert testsel.nodes.b.c.__doc__ == "docstring for nodes.b.c" + + assert isinstance(testsel.nodes.b, Selector) + assert isinstance(testsel.nodes.b.c, Selector) + + with pytest.raises(ClippyInvalidSelectorError): + testsel.add(testsel.nodes, "_bad", desc="this is a bad selector name") + + # with pytest.raises(ClippyInvalidSelectorError): + # testsel.add(testsel, 'bad', desc="this is a top-level selector")