You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by xu...@apache.org on 2023/03/17 07:43:33 UTC

[incubator-opendal] branch main updated: feat(tests): Introducing BDD tests for all bindings (#1654)

This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 9b77d960 feat(tests): Introducing BDD tests for all bindings (#1654)
9b77d960 is described below

commit 9b77d9608ea333383b4a962d75c8ef313f82e372
Author: Xuanwo <gi...@xuanwo.io>
AuthorDate: Fri Mar 17 15:43:28 2023 +0800

    feat(tests): Introducing BDD tests for all bindings (#1654)
    
    * feat(tests): Introducing BDD tests for all bindings
    
    Signed-off-by: Xuanwo <gi...@xuanwo.io>
    
    * Address comments
    
    Signed-off-by: Xuanwo <gi...@xuanwo.io>
    
    ---------
    
    Signed-off-by: Xuanwo <gi...@xuanwo.io>
---
 .github/workflows/bindings_python.yml   |   4 +-
 bindings/python/README.md               |   2 +-
 bindings/python/pyproject.toml          |   2 +-
 bindings/python/tests/binding.feature   |   1 +
 bindings/python/tests/steps/binding.py  |  78 ++++++++++++++++++++++
 bindings/python/tests/test_core.py      | 114 --------------------------------
 bindings/tests/README.md                |   3 +
 bindings/tests/features/binding.feature |  34 ++++++++++
 licenserc.toml                          |   1 +
 9 files changed, 121 insertions(+), 118 deletions(-)

diff --git a/.github/workflows/bindings_python.yml b/.github/workflows/bindings_python.yml
index 610cea3a..c75197fb 100644
--- a/.github/workflows/bindings_python.yml
+++ b/.github/workflows/bindings_python.yml
@@ -51,10 +51,10 @@ jobs:
         working-directory: "bindings/python"
         run: |
           python -m pip install -e .[test]
-      - name: Run pytest
+      - name: Run behave
         working-directory: "bindings/python"
         run: |
-          python -m pytest
+          python -m behave tests
 
   linux:
     runs-on: ubuntu-latest
diff --git a/bindings/python/README.md b/bindings/python/README.md
index 473668e4..03a4bcb1 100644
--- a/bindings/python/README.md
+++ b/bindings/python/README.md
@@ -62,5 +62,5 @@ Running some tests:
 
 ```shell
 maturin develop -E test
-pytest
+behave tests
 ```
diff --git a/bindings/python/pyproject.toml b/bindings/python/pyproject.toml
index 356b6f63..052b9187 100644
--- a/bindings/python/pyproject.toml
+++ b/bindings/python/pyproject.toml
@@ -32,7 +32,7 @@ readme = "README.md"
 requires-python = ">=3.7"
 
 [project.optional-dependencies]
-test = ["pytest", "pytest-asyncio"]
+test = ["behave"]
 
 [project.urls]
 Documentation = "https://docs.rs/opendal/"
diff --git a/bindings/python/tests/binding.feature b/bindings/python/tests/binding.feature
new file mode 120000
index 00000000..fcb71d38
--- /dev/null
+++ b/bindings/python/tests/binding.feature
@@ -0,0 +1 @@
+../../tests/features/binding.feature
\ No newline at end of file
diff --git a/bindings/python/tests/steps/binding.py b/bindings/python/tests/steps/binding.py
new file mode 100644
index 00000000..49aa45a9
--- /dev/null
+++ b/bindings/python/tests/steps/binding.py
@@ -0,0 +1,78 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from behave import given, when, then
+from behave.api.async_step import async_run_until_complete
+import opendal
+
+@given('A new OpenDAL Blocking Operator')
+def step_impl(context):
+    context.op = opendal.Operator("memory")
+
+@when('Blocking write path "{filename}" with content "{content}"')
+def step_impl(context, filename, content):
+    context.op.write(filename, content.encode())
+
+@then('The blocking file "{filename}" should exist')
+def step_impl(context, filename):
+    context.op.stat(filename)
+
+@then('The blocking file "{filename}" entry mode must be file')
+def step_impl(context, filename):
+    assert context.op.stat(filename).mode.is_file()
+
+@then('The blocking file "{filename}" content length must be "{size:d}"')
+def step_impl(context, filename, size):
+    assert context.op.stat(filename).content_length == size
+
+@then('The blocking file "{filename}" must have content "{content}"')
+def step_impl(context, filename, content):
+    bs = context.op.read(filename)
+    assert bs == content.encode()
+
+@given('A new OpenDAL Async Operator')
+@async_run_until_complete
+async def step_impl(context):
+    context.op = opendal.AsyncOperator("memory")
+
+@when('Async write path "{filename}" with content "{content}"')
+@async_run_until_complete
+async def step_impl(context, filename, content):
+    await context.op.write(filename, content.encode())
+
+@then('The async file "{filename}" should exist')
+@async_run_until_complete
+async def step_impl(context, filename):
+    await context.op.stat(filename)
+
+@then('The async file "{filename}" entry mode must be file')
+@async_run_until_complete
+async def step_impl(context, filename):
+    meta = await context.op.stat(filename)
+    assert meta.mode.is_file()
+
+@then('The async file "{filename}" content length must be "{size:d}"')
+@async_run_until_complete
+async def step_impl(context, filename, size):
+    meta = await context.op.stat(filename)
+    assert meta.content_length == size
+
+@then('The async file "{filename}" must have content "{content}"')
+@async_run_until_complete
+async def step_impl(context, filename, content):
+    bs = await context.op.read(filename)
+    assert bs == content.encode()
diff --git a/bindings/python/tests/test_core.py b/bindings/python/tests/test_core.py
deleted file mode 100644
index 6322e26a..00000000
--- a/bindings/python/tests/test_core.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-import os
-import opendal
-import pytest
-from opendal import layers
-
-
-def test_blocking():
-    op = opendal.Operator("memory", layers=[layers.RetryLayer()])
-    op.write("test", b"Hello, World!")
-    bs = op.read("test")
-    assert bs == b"Hello, World!", bs
-    meta = op.stat("test")
-    assert meta.content_length == 13, meta.content_length
-    assert meta.mode.is_file()
-    assert [str(entry) for entry in op.list("/")] == ["test"]
-    assert [str(entry) for entry in op.scan("/")] == ["test"]
-
-    reader = op.open_reader("test")
-    bs = reader.read(5)
-    assert bs == b"Hello", bs
-    bs = reader.read()
-    assert bs == b", World!", bs
-    reader.seek(0, os.SEEK_SET)
-    bs = reader.read()
-    assert bs == b"Hello, World!", bs
-    with op.open_reader("test") as f:
-        bs = f.read()
-        assert bs == b"Hello, World!", bs
-
-    op.delete("test")
-
-    op.create_dir("test/")
-
-
-@pytest.mark.asyncio
-async def test_async():
-    op = opendal.AsyncOperator(
-        "memory", layers=[layers.RetryLayer(), layers.ConcurrentLimitLayer(10)]
-    )
-    await op.write("test", b"Hello, World!")
-    bs = await op.read("test")
-    assert bs == b"Hello, World!", bs
-    meta = await op.stat("test")
-    assert meta.content_length == 13, meta.content_length
-    assert meta.mode.is_file()
-    assert [str(entry) async for entry in await op.list("/")] == ["test"]
-    assert [str(entry) async for entry in await op.scan("/")] == ["test"]
-
-    reader = op.open_reader("test")
-    bs = await reader.read(5)
-    assert bs == b"Hello", bs
-    bs = await reader.read()
-    assert bs == b", World!", bs
-    await reader.seek(0, os.SEEK_SET)
-    bs = await reader.read()
-    assert bs == b"Hello, World!", bs
-    async with op.open_reader("test") as f:
-        bs = await f.read()
-        assert bs == b"Hello, World!", bs
-
-    await op.delete("test")
-
-    await op.create_dir("test/")
-
-
-def test_blocking_fs(tmp_path):
-    op = opendal.Operator("fs", root=str(tmp_path))
-    op.write("test.txt", b"Hello, World!")
-    bs = op.read("test.txt")
-    assert bs == b"Hello, World!", bs
-    meta = op.stat("test.txt")
-    assert meta.content_length == 13, meta.content_length
-    assert [str(entry) for entry in op.list("/")] == ["test.txt"]
-    assert [str(entry) for entry in op.scan("/")] == ["test.txt", "/"]
-
-    op.create_dir("test/")
-
-
-@pytest.mark.asyncio
-async def test_async_fs(tmp_path):
-    op = opendal.AsyncOperator("fs", root=str(tmp_path))
-    await op.write("test.txt", b"Hello, World!")
-    bs = await op.read("test.txt")
-    assert bs == b"Hello, World!", bs
-    meta = await op.stat("test.txt")
-    assert meta.content_length == 13, meta.content_length
-
-    await op.create_dir("test/")
-
-
-def test_error():
-    op = opendal.Operator("memory")
-    with pytest.raises(FileNotFoundError):
-        op.read("test")
-
-    with pytest.raises(NotImplementedError):
-        opendal.Operator("foobar")
diff --git a/bindings/tests/README.md b/bindings/tests/README.md
new file mode 100644
index 00000000..5c49d3f6
--- /dev/null
+++ b/bindings/tests/README.md
@@ -0,0 +1,3 @@
+# OpenDAL Binding Tests
+
+OpenDAL will use [Cucumber](https://github.com/cucumber) for [BDD](Behaviour-Driven Development) tests. This module will provide [Gherkin](https://cucumber.io/docs/gherkin/) files and all bindings should implement againest them.
diff --git a/bindings/tests/features/binding.feature b/bindings/tests/features/binding.feature
new file mode 100644
index 00000000..22c08ab1
--- /dev/null
+++ b/bindings/tests/features/binding.feature
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+Feature: OpenDAL Binding
+
+    Scenario: OpenDAL Blocking Operations
+        Given A new OpenDAL Blocking Operator
+        When Blocking write path "test" with content "Hello, World!"
+        Then The blocking file "test" should exist
+        Then The blocking file "test" entry mode must be file
+        Then The blocking file "test" content length must be "13"
+        Then The blocking file "test" must have content "Hello, World!"
+
+    Scenario: OpenDAL Async Operations
+        Given A new OpenDAL Async Operator
+        When Async write path "test" with content "Hello, World!"
+        Then The async file "test" should exist
+        Then The async file "test" entry mode must be file
+        Then The async file "test" content length must be "13"
+        Then The async file "test" must have content "Hello, World!"
diff --git a/licenserc.toml b/licenserc.toml
index 645fbf31..d4a7f6c3 100644
--- a/licenserc.toml
+++ b/licenserc.toml
@@ -36,6 +36,7 @@ excludes = [
   "bindings/python/opendal.pyi",
   "**/__pycache__",
   "**/.pytest_cache",
+  "**/venv/**",
   "website/build/**",
   "website/static/.nojekyll",
   "website/.docusaurus",