From 0e471d72df3010d14ac09502373b6a402a40c5b6 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Wed, 16 Oct 2024 18:37:51 -0300 Subject: [PATCH 01/53] Adding support for latest python versions --- .github/workflows/python-package.yml | 2 +- pyproject.toml | 2 +- setup.py | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 3e9b812..35b0351 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: - name: Checkout repository diff --git a/pyproject.toml b/pyproject.toml index 06a4851..8215908 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.8" +version = "4.0.9rc1" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index f973f25..92da602 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.8', + version='4.0.0rc1', license="MIT", platforms=['any'], python_requires='>=3.5', @@ -46,6 +46,8 @@ 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Topic :: Software Development :: Testing', ], ) From 99fc7205a7edce9cfa58ef3beb09a18914e6d716 Mon Sep 17 00:00:00 2001 From: iamkenos Date: Thu, 17 Oct 2024 11:55:18 +1100 Subject: [PATCH 02/53] fix: replace scenario_info.id_hash with uuid --- behavex/outputs/report_json.py | 5 ++--- behavex/utils.py | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/behavex/outputs/report_json.py b/behavex/outputs/report_json.py index f8673d0..b50c974 100644 --- a/behavex/outputs/report_json.py +++ b/behavex/outputs/report_json.py @@ -27,7 +27,7 @@ from behavex.outputs.report_utils import (get_environment_details, get_error_message, match_for_execution, text) -from behavex.utils import (generate_hash, get_scenario_tags, +from behavex.utils import (generate_hash, generate_uuid, get_scenario_tags, try_operate_descriptor) @@ -181,8 +181,7 @@ def _processing_scenarios(scenarios, scenario_list, id_feature): scenario_info['error_lines'] = error_lines scenario_info['error_step'] = error_step scenario_info['error_background'] = error_background - scenario_info['id_hash'] = generate_hash("{}:{}".format(scenario.filename, - scenario.line)) + scenario_info['id_hash'] = generate_uuid() if scenario.feature.name in global_vars.retried_scenarios: if ( scenario.name diff --git a/behavex/utils.py b/behavex/utils.py index 6c8d4dc..b0c21c1 100644 --- a/behavex/utils.py +++ b/behavex/utils.py @@ -18,6 +18,7 @@ import shutil import sys import time +import uuid from functools import reduce from tempfile import gettempdir @@ -583,3 +584,6 @@ def generate_hash(word): hash_int = int.from_bytes(truncated_hash, byteorder='big') # Ensure the result fits in 48 bits (optional, for consistency) return hash_int & 0xFFFFFFFFFFFF + +def generate_uuid(): + return uuid.uuid4().hex From a1d57494ecd758b264e536830720ffe3c1acaeb1 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 17 Oct 2024 10:22:48 -0300 Subject: [PATCH 03/53] Updating setuptools version in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8215908..50d4d28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=42", "wheel"] +requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] From df45f2554b97b1264ea922926cfddb665f6048e8 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 17 Oct 2024 10:39:44 -0300 Subject: [PATCH 04/53] Updating setup to be more resilient to setuptools, and updating the github actions --- .github/workflows/python-package.yml | 6 ++++++ pyproject.toml | 3 +++ setup.py | 15 ++++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 35b0351..33a368f 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -20,6 +20,12 @@ jobs: with: python-version: ${{ matrix.python-version }} + - name: Install setuptools + run: python -m pip install --upgrade setuptools + + - name: Build and install + run: pip install . + - name: Install behavex run: | python -m pip install --upgrade pip diff --git a/pyproject.toml b/pyproject.toml index 50d4d28..abf7820 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,3 +54,6 @@ exclude = ["tests"] [project.scripts] behavex = "behavex.runner:main" + +[tool.setuptools] +packages = ["behavex"] diff --git a/setup.py b/setup.py index 92da602..54d9e00 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,18 @@ # -*- coding: utf-8 -*- -from setuptools import find_packages, setup +import sys +from pathlib import Path + +# Add the project root to the Python path +project_root = Path(__file__).parent.resolve() +sys.path.insert(0, str(project_root)) + +try: + from setuptools import find_packages, setup +except ImportError: + from distutils.core import setup + + def find_packages(): + return [] with open('README.md', 'r') as fh: long_description = fh.read() From c0c235759be090d232f0bbee433642ec0637b852 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 17 Oct 2024 10:49:25 -0300 Subject: [PATCH 05/53] Reverting changes --- .github/workflows/python-package.yml | 6 ------ pyproject.toml | 7 +++---- setup.py | 17 ++--------------- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 33a368f..35b0351 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -20,12 +20,6 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Install setuptools - run: python -m pip install --upgrade setuptools - - - name: Build and install - run: pip install . - - name: Install behavex run: | python -m pip install --upgrade pip diff --git a/pyproject.toml b/pyproject.toml index abf7820..754fcb2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=61.0", "wheel"] +requires = ["setuptools>=61", "wheel"] build-backend = "setuptools.build_meta" [project] @@ -35,6 +35,8 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Testing" ] @@ -54,6 +56,3 @@ exclude = ["tests"] [project.scripts] behavex = "behavex.runner:main" - -[tool.setuptools] -packages = ["behavex"] diff --git a/setup.py b/setup.py index 54d9e00..b4c1f8a 100644 --- a/setup.py +++ b/setup.py @@ -1,25 +1,12 @@ # -*- coding: utf-8 -*- -import sys -from pathlib import Path - -# Add the project root to the Python path -project_root = Path(__file__).parent.resolve() -sys.path.insert(0, str(project_root)) - -try: - from setuptools import find_packages, setup -except ImportError: - from distutils.core import setup - - def find_packages(): - return [] +from setuptools import find_packages, setup with open('README.md', 'r') as fh: long_description = fh.read() setup( name='behavex', - version='4.0.0rc1', + version='4.0.9rc1', license="MIT", platforms=['any'], python_requires='>=3.5', From 1d0a6b36c1625b3ddf57836f4856c35762ed5439 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 17 Oct 2024 10:53:34 -0300 Subject: [PATCH 06/53] Installing setuptools as part of github actions --- .github/workflows/python-package.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 35b0351..0958fe4 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -20,6 +20,10 @@ jobs: with: python-version: ${{ matrix.python-version }} + - name: Install setuptools + run: | + python -m pip install 'setuptools<72.0.0' + - name: Install behavex run: | python -m pip install --upgrade pip From 9e5853d9b8a996683a404607e6c895a37b773747 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 17 Oct 2024 10:57:00 -0300 Subject: [PATCH 07/53] Updating setuptools version --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 0958fe4..68b50e5 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -22,7 +22,7 @@ jobs: - name: Install setuptools run: | - python -m pip install 'setuptools<72.0.0' + python -m pip install 'setuptools>=61' - name: Install behavex run: | From 81e97eb9349b8e8b83f857e29b8267b3615e5c81 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 17 Oct 2024 10:59:42 -0300 Subject: [PATCH 08/53] Updating supported python versions until 3.12.x --- pyproject.toml | 1 - setup.py | 1 - 2 files changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 754fcb2..fd66e98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,6 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", "Topic :: Software Development :: Testing" ] diff --git a/setup.py b/setup.py index b4c1f8a..110a9ec 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,6 @@ 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: 3.13', 'Topic :: Software Development :: Testing', ], ) From 3bb1ce13572bc2cf6ed8a765932a893e6d4b28da Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 17 Oct 2024 11:01:19 -0300 Subject: [PATCH 09/53] Updated python versions in python-package.yml --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 68b50e5..ec9e007 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: - name: Checkout repository From c8b95fe13ff48e0d9ffcc05d47e9897a681d2ded Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 15:59:24 -0300 Subject: [PATCH 10/53] Adapted the github workflow to extend the validations to multiple OSs (as before this the tests were executed only in ubuntu OS) --- .github/workflows/python-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index ec9e007..36cdb34 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -1,14 +1,14 @@ name: Test behavex in latest python versions - on: [push, pull_request] jobs: test-behavex: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: + os: [ubuntu-latest, windows-latest, macos-latest] python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: From eeafe65c3fd0d2bfad60118d541fd8fc43894be0 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 16:44:57 -0300 Subject: [PATCH 11/53] Fixing pyproject.toml to enable executions in Windows OSs --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index fd66e98..6fca6f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,9 @@ dependencies = [ "htmlmin", "csscompressor" ] +# Add dynamic field to handle entry points +dynamic = ["entry-points"] + classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", From 6009facc73d5bd6ba047db548fbdc224f5dd9c66 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 16:51:23 -0300 Subject: [PATCH 12/53] Additional fixes when installing BehaveX in Windows OSs as part of Github actions --- .github/workflows/python-package.yml | 14 ++++++++------ MANIFEST.in | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 36cdb34..a51f9ec 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -1,14 +1,14 @@ name: Test behavex in latest python versions + on: [push, pull_request] jobs: test-behavex: - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: @@ -24,11 +24,13 @@ jobs: run: | python -m pip install 'setuptools>=61' - - name: Install behavex + - name: Build package + run: python -m build + + - name: Install package run: | - python -m pip install --upgrade pip - python setup.py sdist - pip install ./dist/*.tar.gz + $package_file = Get-ChildItem dist\*.tar.gz | Select-Object -First 1 + pip install $package_file - name: Verify behavex command run: behavex ./tests/features/*.feature diff --git a/MANIFEST.in b/MANIFEST.in index d1f134f..80e9325 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ include LICENSE include README.md recursive-include behavex *.* -prune behavex\tests/ +prune behavex\tests From 693caedbe73dee2aa93d06cb466d8db42240e1de Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 17:01:08 -0300 Subject: [PATCH 13/53] Additional fixes when installing BehaveX in Windows OSs as part of Github actions --- .github/workflows/python-package.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index a51f9ec..3c4bc81 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -1,14 +1,14 @@ name: Test behavex in latest python versions - on: [push, pull_request] jobs: test-behavex: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: + os: [ubuntu-latest, windows-latest, macos-latest] python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: @@ -24,13 +24,11 @@ jobs: run: | python -m pip install 'setuptools>=61' - - name: Build package - run: python -m build - - - name: Install package + - name: Install behavex run: | - $package_file = Get-ChildItem dist\*.tar.gz | Select-Object -First 1 - pip install $package_file + python -m pip install --upgrade pip + python setup.py sdist + pip install dist/behavex-4.0.9rc1.tar.gz -f - name: Verify behavex command run: behavex ./tests/features/*.feature From 698c5ac0017e67a997f7f3468dac954f8ae24be0 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 17:02:39 -0300 Subject: [PATCH 14/53] Cosmetic change in github actions --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 3c4bc81..2e7836f 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -28,7 +28,7 @@ jobs: run: | python -m pip install --upgrade pip python setup.py sdist - pip install dist/behavex-4.0.9rc1.tar.gz -f + pip install dist/behavex-4.0.9rc1.tar.gz - name: Verify behavex command run: behavex ./tests/features/*.feature From 2ebc3af276032b43b3dc0463c60b8da2006e1a88 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 17:08:33 -0300 Subject: [PATCH 15/53] Updating tests execution path to work across multiple OSs --- .github/workflows/python-package.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 2e7836f..eae25ec 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -31,4 +31,9 @@ jobs: pip install dist/behavex-4.0.9rc1.tar.gz - name: Verify behavex command - run: behavex ./tests/features/*.feature + run: | + if [[ "$GITHUB_OS_NAME" == "Windows" ]]; then + behavex .\\tests\\features\\*.feature + else + behavex ./tests/features/*.feature + fi From 782030df0da32efe2341d3576d9edf869920e0d0 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 17:13:40 -0300 Subject: [PATCH 16/53] Updating tests execution path to work across multiple OSs --- .github/workflows/python-package.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index eae25ec..af7f45a 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -31,8 +31,9 @@ jobs: pip install dist/behavex-4.0.9rc1.tar.gz - name: Verify behavex command + shell: bash run: | - if [[ "$GITHUB_OS_NAME" == "Windows" ]]; then + if [ "$RUNNER_OS" == "Windows" ]; then behavex .\\tests\\features\\*.feature else behavex ./tests/features/*.feature From e925704a7bb0fdb0bafa6e597714049134b65873 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 17:17:03 -0300 Subject: [PATCH 17/53] Updating tests execution path to work across multiple OSs --- .github/workflows/python-package.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index af7f45a..66fed28 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -34,7 +34,8 @@ jobs: shell: bash run: | if [ "$RUNNER_OS" == "Windows" ]; then - behavex .\\tests\\features\\*.feature + dir + behavex .\\tests\\features\\ else behavex ./tests/features/*.feature fi From d59128da359c4ce4d5ae9063006141c9721add45 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 17:25:16 -0300 Subject: [PATCH 18/53] Fixing issue when trying to remove behave logs --- behavex/runner.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/behavex/runner.py b/behavex/runner.py index a5ef17d..7dce3b3 100644 --- a/behavex/runner.py +++ b/behavex/runner.py @@ -723,16 +723,17 @@ def _launch_behave(behave_args): Returns: tuple: Execution code and whether to generate a report. """ - # Save tags configuration to report only selected scenarios - # Check for tags in config file generate_report = True + execution_code = 0 + stdout_file = None + try: stdout_file = behave_args[behave_args.index('--outfile') + 1] execution_code = behave_script.main(behave_args) if not os.path.exists(stdout_file): - # Code 2 means the execution crashed and test was not properly executed execution_code = 2 generate_report = True + except KeyboardInterrupt: execution_code = 1 generate_report = False @@ -745,10 +746,22 @@ def _launch_behave(behave_args): except: execution_code = 2 generate_report = True - if os.path.exists(stdout_file): - with open(os.path.join(get_env('OUTPUT'), 'merged_behave_outputs.log'), 'a+') as behave_log_file: - behave_log_file.write(open(stdout_file, 'r').read()) - os.remove(stdout_file) + + if stdout_file and os.path.exists(stdout_file): + def _merge_and_remove(): + with open(os.path.join(get_env('OUTPUT'), 'merged_behave_outputs.log'), 'a+') as behave_log_file: + with open(stdout_file, 'r') as source_file: + behave_log_file.write(source_file.read()) + os.close(source_file.fileno()) + os.remove(stdout_file) + + try: + try_operate_descriptor(stdout_file, _merge_and_remove) + except Exception as remove_ex: + logging.warning(f"Could not remove stdout file {stdout_file}: {remove_ex}") + # Don't fail the execution if we can't remove the file + pass + return execution_code, generate_report @@ -843,9 +856,7 @@ def remove_temporary_files(parallel_processes, json_reports): json_reports = [json_reports] for json_report in json_reports: if 'features' in json_report and json_report['features']: - feature_name = os.path.join( - get_env('OUTPUT'), u'{}.tmp'.format(json_report['features'][0]['name']) - ) + feature_name = os.path.join(get_env('OUTPUT'), u'{}.tmp'.format(json_report['features'][0]['name'])) if os.path.exists(feature_name): try: os.remove(feature_name) From 7ec11cbbbc18bb8995794025371ba1f712edbeac Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 17:45:50 -0300 Subject: [PATCH 19/53] Renaming try_operate_descriptor by retry_file_operation --- .pre-commit-config.yaml | 2 +- behavex/outputs/report_html.py | 6 +++--- behavex/outputs/report_json.py | 4 ++-- behavex/outputs/report_utils.py | 24 ++++++++++++++++++++---- behavex/outputs/report_xml.py | 6 +++--- behavex/runner.py | 19 ++++++++++++------- behavex/utils.py | 20 ++++++++++---------- 7 files changed, 51 insertions(+), 30 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eb46272..baf41a9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: hooks: - id: bandit exclude: tests - args: [--skip, "B322"] # ignore assert_used + args: [--skip, "B322,B110"] # ignore assert_used and B110 - repo: https://github.com/pre-commit/mirrors-isort rev: v5.6.4 diff --git a/behavex/outputs/report_html.py b/behavex/outputs/report_html.py index 8acac91..7eb87b2 100644 --- a/behavex/outputs/report_html.py +++ b/behavex/outputs/report_html.py @@ -19,7 +19,7 @@ from behavex.outputs.report_utils import (gather_steps_with_definition, get_environment_details, get_save_function, - try_operate_descriptor) + retry_file_operation) def generate_report(output, joined=None, report=None): @@ -47,7 +47,7 @@ def _create_manifest(relative, page): os.makedirs(folder) file_manifest = os.path.join(folder, page.replace('html', 'manifest')) - try_operate_descriptor( + retry_file_operation( file_manifest, execution=get_save_function(file_manifest, output_text) ) @@ -80,7 +80,7 @@ def _create_files_report(content_to_file): # pylint: disable= W0703 except Exception as ex: print(ex) - try_operate_descriptor(path_file, get_save_function(path_file, content)) + retry_file_operation(path_file, get_save_function(path_file, content)) def get_metrics_variables(scenarios): diff --git a/behavex/outputs/report_json.py b/behavex/outputs/report_json.py index b50c974..6e9f2bb 100644 --- a/behavex/outputs/report_json.py +++ b/behavex/outputs/report_json.py @@ -28,7 +28,7 @@ get_error_message, match_for_execution, text) from behavex.utils import (generate_hash, generate_uuid, get_scenario_tags, - try_operate_descriptor) + retry_file_operation) def add_step_info(step, parent_node): @@ -105,7 +105,7 @@ def generate_json_report(feature_list): def write_json(): file_info.write(json.dumps(output)) - try_operate_descriptor(path_info, execution=write_json) + retry_file_operation(path_info, execution=write_json) if multiprocessing.current_process().name != 'MainProcess': return output except IOError: diff --git a/behavex/outputs/report_utils.py b/behavex/outputs/report_utils.py index 234b276..a482d63 100644 --- a/behavex/outputs/report_utils.py +++ b/behavex/outputs/report_utils.py @@ -190,7 +190,23 @@ def resolving_type(step, scenario, background=True): return step['step_type'].title() -def try_operate_descriptor(dest_path, execution, return_value=False): +def retry_file_operation(dest_path, execution, return_value=False): + """Executes a file operation with retry logic for handling file access conflicts. + + This function attempts to execute a file operation (like copy, delete, etc.) and + provides an interactive retry mechanism if the files are locked by another process. + + Args: + dest_path (str): The file system path where the operation will be performed + operation (callable): A function containing the file operation to execute + return_value (bool, optional): Whether to return the result of the operation. Defaults to False. + + Returns: + Any: The result of the operation if return_value is True, otherwise None + + Example: + >>> retry_file_operation('/path/to/dir', lambda: shutil.rmtree('/path/to/dir')) + """ passed = False following = True retry_number = 1 @@ -290,8 +306,8 @@ def copy_bootstrap_html_generator(output): bootstrap_path = ['outputs', 'bootstrap'] bootstrap_path = os.path.join(global_vars.execution_path, *bootstrap_path) if os.path.exists(dest_path): - try_operate_descriptor(dest_path, lambda: shutil.rmtree(dest_path)) - try_operate_descriptor( + retry_file_operation(dest_path, lambda: shutil.rmtree(dest_path)) + retry_file_operation( dest_path, lambda: shutil.copytree(bootstrap_path, dest_path) ) @@ -451,5 +467,5 @@ def get_environment_details(): environment_details = environment_details_raw_data.split(',') if environment_details_raw_data else [] return environment_details -def strip_ansi_codes(from_str: str): +def strip_ansi_codes(from_str: str): return re.sub(r'\x1B\[[0-?9;]*[mGJK]', '', from_str) diff --git a/behavex/outputs/report_xml.py b/behavex/outputs/report_xml.py index df095f8..4ca51c5 100644 --- a/behavex/outputs/report_xml.py +++ b/behavex/outputs/report_xml.py @@ -16,8 +16,8 @@ from behavex.global_vars import global_vars from behavex.outputs.jinja_mgr import TemplateHandler from behavex.outputs.report_utils import (get_save_function, - match_for_execution, text, - try_operate_descriptor) + match_for_execution, + retry_file_operation, text) from behavex.utils import get_scenario_tags @@ -101,7 +101,7 @@ def get_status(scenario_): junit_path = os.path.join(get_env('OUTPUT'), 'behave') path_output = os.path.join(junit_path, u'TESTS-' + name + u'.xml') - try_operate_descriptor( + retry_file_operation( path_output + '.xml', get_save_function(path_output, output_text) ) diff --git a/behavex/runner.py b/behavex/runner.py index 7dce3b3..4684389 100644 --- a/behavex/runner.py +++ b/behavex/runner.py @@ -44,8 +44,8 @@ from behavex.outputs.report_json import generate_execution_info from behavex.outputs.report_utils import (get_overall_status, match_for_execution, - pretty_print_time, text, - try_operate_descriptor) + pretty_print_time, + retry_file_operation, text) from behavex.progress_bar import ProgressBar from behavex.utils import (IncludeNameMatch, IncludePathsMatch, MatchInclude, cleanup_folders, configure_logging, @@ -752,11 +752,16 @@ def _merge_and_remove(): with open(os.path.join(get_env('OUTPUT'), 'merged_behave_outputs.log'), 'a+') as behave_log_file: with open(stdout_file, 'r') as source_file: behave_log_file.write(source_file.read()) - os.close(source_file.fileno()) - os.remove(stdout_file) - + # nosec B110 + try: + if not source_file.closed: + os.close(source_file.fileno()) + if os.path.exists(stdout_file): + os.remove(stdout_file) + except: + pass try: - try_operate_descriptor(stdout_file, _merge_and_remove) + retry_file_operation(stdout_file, _merge_and_remove) except Exception as remove_ex: logging.warning(f"Could not remove stdout file {stdout_file}: {remove_ex}") # Don't fail the execution if we can't remove the file @@ -1184,7 +1189,7 @@ def _load_json(): json_output = {'environment': [], 'features': [], 'steps_definition': []} if os.path.exists(path_info): - json_output = try_operate_descriptor(path_info, _load_json, return_value=True) + json_output = retry_file_operation(path_info, _load_json, return_value=True) return json_output diff --git a/behavex/utils.py b/behavex/utils.py index b0c21c1..1115395 100644 --- a/behavex/utils.py +++ b/behavex/utils.py @@ -33,7 +33,7 @@ from behavex.outputs.output_strings import TEXTS from behavex.outputs.report_utils import (get_save_function, get_string_hash, match_for_execution, - try_operate_descriptor) + retry_file_operation) LOGGING_CFG = ConfigObj(os.path.join(global_vars.execution_path, 'conf_logging.cfg')) LOGGING_LEVELS = { @@ -250,10 +250,10 @@ def copy_bootstrap_html_generator(): bootstrap_path = ['outputs', 'bootstrap'] bootstrap_path = os.path.join(global_vars.execution_path, *bootstrap_path) if os.path.exists(destination_path): - try_operate_descriptor( + retry_file_operation( destination_path, lambda: shutil.rmtree(destination_path) ) - try_operate_descriptor( + retry_file_operation( destination_path, lambda: shutil.copytree(bootstrap_path, destination_path) ) @@ -265,18 +265,18 @@ def cleanup_folders(): def execution(): return shutil.rmtree(output_folder, ignore_errors=True) - try_operate_descriptor(output_folder, execution) + retry_file_operation(output_folder, execution) if not os.path.exists(output_folder): - try_operate_descriptor(output_folder, lambda: os.makedirs(output_folder)) + retry_file_operation(output_folder, lambda: os.makedirs(output_folder)) # temp folder temp_folder = get_env('temp') def execution(): return shutil.rmtree(temp_folder, ignore_errors=True) - try_operate_descriptor(temp_folder, execution) + retry_file_operation(temp_folder, execution) if not os.path.exists(temp_folder): - try_operate_descriptor(temp_folder, lambda: os.makedirs(temp_folder)) + retry_file_operation(temp_folder, lambda: os.makedirs(temp_folder)) # behave folder behave_folder = os.path.join(get_env('OUTPUT'), 'behave') @@ -284,9 +284,9 @@ def execution(): def execution(): return shutil.rmtree(behave_folder, ignore_errors=True) - try_operate_descriptor(behave_folder, execution) + retry_file_operation(behave_folder, execution) if not os.path.exists(behave_folder): - try_operate_descriptor(behave_folder, lambda: os.makedirs(behave_folder)) + retry_file_operation(behave_folder, lambda: os.makedirs(behave_folder)) def set_env_variable(key, value): @@ -422,7 +422,7 @@ def set_behave_tags(): tags_line = ' '.join(tags) tags_line = tags_line.replace('~', 'not ') tags_line = tags_line.replace(',', ' or ') - try_operate_descriptor( + retry_file_operation( behave_tags, execution=get_save_function(behave_tags, tags_line) ) From 06bbcfede5f8d70c7133237b353142f53234fc1a Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 17:49:46 -0300 Subject: [PATCH 20/53] Fixing windows path --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 66fed28..c035ae6 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -35,7 +35,7 @@ jobs: run: | if [ "$RUNNER_OS" == "Windows" ]; then dir - behavex .\\tests\\features\\ + behavex tests\\features\\ else behavex ./tests/features/*.feature fi From 154dbd68b013598634d152f075ec8f39d4c21b9b Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 17:59:01 -0300 Subject: [PATCH 21/53] Fix done in explore_features to properly deal with Windows paths --- behavex/utils.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/behavex/utils.py b/behavex/utils.py index 1115395..699be98 100644 --- a/behavex/utils.py +++ b/behavex/utils.py @@ -173,12 +173,16 @@ def join_scenario_reports(json_reports): ) return list(result.values()) - def explore_features(features_path, features_list=None): if features_list is None: features_list = [] + + # Normalize path separators + features_path = os.path.normpath(features_path) + if global_vars.rerun_failures or ".feature:" in features_path: features_path = features_path.split(":")[0] + if os.path.isfile(features_path): if features_path.endswith('.feature'): path_feature = os.path.abspath(features_path) @@ -186,15 +190,20 @@ def explore_features(features_path, features_list=None): if feature: features_list.extend(feature.scenarios) else: - for node in os.listdir(features_path): - if os.path.isdir(os.path.join(features_path, node)): - explore_features(os.path.join(features_path, node), features_list) - else: - if node.endswith('.feature'): - path_feature = os.path.abspath(os.path.join(features_path, node)) + try: + for node in os.listdir(features_path): + node_path = os.path.join(features_path, node) + if os.path.isdir(node_path): + explore_features(node_path, features_list) + elif node.endswith('.feature'): + path_feature = os.path.abspath(node_path) feature = should_feature_be_run(path_feature) if feature: features_list.extend(feature.scenarios) + except OSError as e: + print(f"Error accessing path {features_path}: {e}") + return features_list + return features_list From a6127388e00398c2fb641247d7e0e0a34929a8ce Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 18:05:29 -0300 Subject: [PATCH 22/53] Updating windows tests path --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index c035ae6..0e6acca 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -35,7 +35,7 @@ jobs: run: | if [ "$RUNNER_OS" == "Windows" ]; then dir - behavex tests\\features\\ + behavex .\\tests\\features\\*.feature else behavex ./tests/features/*.feature fi From 8ce27b3527478d42c026a8f1a30e41be21d63044 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 22 Nov 2024 18:07:48 -0300 Subject: [PATCH 23/53] Updating windows tests path --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 0e6acca..66fed28 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -35,7 +35,7 @@ jobs: run: | if [ "$RUNNER_OS" == "Windows" ]; then dir - behavex .\\tests\\features\\*.feature + behavex .\\tests\\features\\ else behavex ./tests/features/*.feature fi From 6c8132b370ac0c4d6eaf96a65cd594489ef0fdd2 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Tue, 26 Nov 2024 16:16:33 -0300 Subject: [PATCH 24/53] Improvement done to properly manage the features paths across platforms --- .github/workflows/python-package.yml | 2 +- behavex/runner.py | 31 ++++++++++++---------------- behavex/utils.py | 26 +++++++++++++++++++++++ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 66fed28..0e6acca 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -35,7 +35,7 @@ jobs: run: | if [ "$RUNNER_OS" == "Windows" ]; then dir - behavex .\\tests\\features\\ + behavex .\\tests\\features\\*.feature else behavex ./tests/features/*.feature fi diff --git a/behavex/runner.py b/behavex/runner.py index 4684389..f01146f 100644 --- a/behavex/runner.py +++ b/behavex/runner.py @@ -51,12 +51,12 @@ cleanup_folders, configure_logging, copy_bootstrap_html_generator, create_execution_complete_callback_function, - explore_features, generate_hash, generate_reports, - get_json_results, get_logging_level, - get_scenario_tags, get_scenarios_instances, - get_text, join_feature_reports, - join_scenario_reports, len_scenarios, - print_env_variables, print_parallel, + expand_paths, explore_features, generate_hash, + generate_reports, get_json_results, + get_logging_level, get_scenario_tags, + get_scenarios_instances, get_text, + join_feature_reports, join_scenario_reports, + len_scenarios, print_env_variables, print_parallel, set_behave_tags, set_env_variable, set_environ_config, set_system_paths) @@ -110,24 +110,19 @@ def run(args): if execution_code == EXIT_ERROR: return EXIT_ERROR else: + # Handle paths parameter if len(get_param('paths')) > 0: - for path in get_param('paths'): - if not os.path.exists(path): - print('\nSpecified path was not found: {}'.format(path)) - exit() - paths = ",".join(get_param('paths')) + paths = ",".join(expand_paths(get_param('paths'))) os.environ['FEATURES_PATH'] = paths + + # Handle include_paths parameter if len(get_param('include_paths')) > 0: - for path in get_param('paths'): - if not os.path.exists(path): - print('\nSpecified path was not found: {}'.format(path)) - exit() - paths = ",".join(get_param('include_paths')) + include_paths = ",".join(expand_paths(get_param('include_paths'))) features_path = os.environ.get('FEATURES_PATH') if features_path == '' or features_path is None: - os.environ['FEATURES_PATH'] = paths + os.environ['FEATURES_PATH'] = include_paths else: - os.environ['FEATURES_PATH'] = features_path + ',' + paths + os.environ['FEATURES_PATH'] = features_path + ',' + include_paths features_path = os.environ.get('FEATURES_PATH') if features_path == '' or features_path is None: os.environ['FEATURES_PATH'] = 'features' diff --git a/behavex/utils.py b/behavex/utils.py index 699be98..6648bdd 100644 --- a/behavex/utils.py +++ b/behavex/utils.py @@ -9,6 +9,7 @@ import codecs import functools +import glob import hashlib import json import logging @@ -596,3 +597,28 @@ def generate_hash(word): def generate_uuid(): return uuid.uuid4().hex + +def expand_paths(paths): + """Expand glob patterns in paths and verify they exist. + + Args: + paths (list): List of paths that may contain glob patterns + + Returns: + list: List of expanded and verified paths + """ + expanded = [] + for path in paths: + # Handle glob patterns + if any(char in path for char in ['*', '?', '[']): + globbed = glob.glob(path) + if not globbed: + print('\nNo files found matching pattern: {}'.format(path)) + exit() + expanded.extend(globbed) + else: + if not os.path.exists(path): + print('\nSpecified path was not found: {}'.format(path)) + exit() + expanded.append(path) + return expanded From e0181769070b563c71d20d27f68926eb34ca657a Mon Sep 17 00:00:00 2001 From: anibalinn Date: Tue, 26 Nov 2024 16:24:28 -0300 Subject: [PATCH 25/53] Fixing encoding issue when printing the progress bar in Windows OSs --- behavex/runner.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/behavex/runner.py b/behavex/runner.py index f01146f..4aad27f 100644 --- a/behavex/runner.py +++ b/behavex/runner.py @@ -78,6 +78,17 @@ def main(): Parses command-line arguments and initiates the test run. """ + # Fix for Windows console encoding + if platform.system() == 'Windows': + import codecs + import sys + + # Force UTF-8 output encoding + if sys.stdout.encoding != 'utf-8': + sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict') + sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict') + os.environ['PYTHONIOENCODING'] = 'utf-8' + args = sys.argv[1:] exit_code = run(args) try: @@ -86,7 +97,6 @@ def main(): # force exit sys.exit(exit_code) - def run(args): """Run BehaveX with the given arguments. From 0fefd76b71af56264b14ab24d9a9273b869555cc Mon Sep 17 00:00:00 2001 From: anibalinn Date: Tue, 26 Nov 2024 16:40:11 -0300 Subject: [PATCH 26/53] Fixing import issues --- behavex/runner.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/behavex/runner.py b/behavex/runner.py index 4aad27f..1a43456 100644 --- a/behavex/runner.py +++ b/behavex/runner.py @@ -80,9 +80,6 @@ def main(): """ # Fix for Windows console encoding if platform.system() == 'Windows': - import codecs - import sys - # Force UTF-8 output encoding if sys.stdout.encoding != 'utf-8': sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict') From 19cb87c808714bf62102f3b5ba395325fb4be0da Mon Sep 17 00:00:00 2001 From: anibalinn Date: Tue, 26 Nov 2024 16:46:06 -0300 Subject: [PATCH 27/53] Additional encoding changes to properly del with Windows OS executions --- .pre-commit-config.yaml | 2 +- behavex/runner.py | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index baf41a9..e7d6068 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: hooks: - id: bandit exclude: tests - args: [--skip, "B322,B110"] # ignore assert_used and B110 + args: [--skip, "B322,B110,B605,B607"] # ignore assert_used and B110 - repo: https://github.com/pre-commit/mirrors-isort rev: v5.6.4 diff --git a/behavex/runner.py b/behavex/runner.py index 1a43456..0a66ad4 100644 --- a/behavex/runner.py +++ b/behavex/runner.py @@ -82,9 +82,20 @@ def main(): if platform.system() == 'Windows': # Force UTF-8 output encoding if sys.stdout.encoding != 'utf-8': - sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict') - sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict') - os.environ['PYTHONIOENCODING'] = 'utf-8' + # Handle both Python 3.x cases + try: + sys.stdout.reconfigure(encoding='utf-8') + sys.stderr.reconfigure(encoding='utf-8') + except AttributeError: + # Fallback for older Python versions + sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict') + sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict') + + # Ensure PYTHONIOENCODING is set + os.environ['PYTHONIOENCODING'] = 'utf-8' + + # Set console to use UTF-8 + os.system('chcp 65001 > NUL') args = sys.argv[1:] exit_code = run(args) From 493716470712c2e74cc03cdd128a4def4e2e9ad7 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Tue, 26 Nov 2024 17:08:41 -0300 Subject: [PATCH 28/53] Updating the progress bar for cross platform compatibility --- .github/workflows/python-package.yml | 4 ++++ behavex/runner.py | 19 ------------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 0e6acca..c6f6187 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -6,6 +6,10 @@ jobs: test-behavex: runs-on: ${{ matrix.os }} + env: + PYTHONIOENCODING: utf-8 + PYTHONLEGACYWINDOWSSTDIO: utf-8 + strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] diff --git a/behavex/runner.py b/behavex/runner.py index 0a66ad4..8cbac60 100644 --- a/behavex/runner.py +++ b/behavex/runner.py @@ -78,25 +78,6 @@ def main(): Parses command-line arguments and initiates the test run. """ - # Fix for Windows console encoding - if platform.system() == 'Windows': - # Force UTF-8 output encoding - if sys.stdout.encoding != 'utf-8': - # Handle both Python 3.x cases - try: - sys.stdout.reconfigure(encoding='utf-8') - sys.stderr.reconfigure(encoding='utf-8') - except AttributeError: - # Fallback for older Python versions - sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict') - sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict') - - # Ensure PYTHONIOENCODING is set - os.environ['PYTHONIOENCODING'] = 'utf-8' - - # Set console to use UTF-8 - os.system('chcp 65001 > NUL') - args = sys.argv[1:] exit_code = run(args) try: From cdaa9799dca8449870fef599b203d1d4fac077b7 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Tue, 26 Nov 2024 17:11:45 -0300 Subject: [PATCH 29/53] Updating the progress bar for cross platform compatibility --- behavex/progress_bar.py | 27 ++++++++++++++++++++++----- tests/features/progress_bar.feature | 10 +++++----- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/behavex/progress_bar.py b/behavex/progress_bar.py index 08258b4..58492f1 100644 --- a/behavex/progress_bar.py +++ b/behavex/progress_bar.py @@ -1,17 +1,34 @@ +import platform import sys import time from behavex.global_vars import global_vars +def get_progress_chars(): + """Get appropriate progress bar characters based on platform.""" + if platform.system() == 'Windows': + return { + 'bar': '#', + 'edge': '|', + 'empty': '-' + } + return { + 'bar': '█', + 'edge': '|', + 'empty': '-' + } + + class ProgressBar: - def __init__(self, prefix, total, bar_length=15, print_updates_in_new_lines=False): + def __init__(self, prefix, total, print_updates_in_new_lines=False): self.prefix = prefix self.total = total - self.bar_length = bar_length + self.print_updates_in_new_lines = print_updates_in_new_lines + self.progress_chars = get_progress_chars() + self.bar_length = 15 self.current_iteration = 0 self.start_time = global_vars.execution_start_time - self.print_in_new_lines = print_updates_in_new_lines def start(self, start_increment=0): self.current_iteration = start_increment @@ -34,11 +51,11 @@ def _print_progress_bar(self, new_line=False): else: percent = 100 * float(self.current_iteration / float(self.total)) filled_length = int(self.bar_length * self.current_iteration // self.total) - bar = '█' * filled_length + '-' * (self.bar_length - filled_length) + bar = self.progress_chars['bar'] * filled_length + self.progress_chars['empty'] * (self.bar_length - filled_length) elapsed_time = global_vars.execution_elapsed_time elapsed_formatted = time.strftime("%M:%S", time.gmtime(elapsed_time)) progress_bar_content = f"\r{prefix}{percent:.0f}%|{bar}| {self.current_iteration}/{self.total} [{elapsed_formatted}]\r" - if self.print_in_new_lines or new_line or percent == 100: + if self.print_updates_in_new_lines or new_line or percent == 100: print(progress_bar_content) else: sys.stdout.write(progress_bar_content) diff --git a/tests/features/progress_bar.feature b/tests/features/progress_bar.feature index 9d78e7e..431f449 100644 --- a/tests/features/progress_bar.feature +++ b/tests/features/progress_bar.feature @@ -8,11 +8,11 @@ Background: Scenario Outline: Progress bar should be shown when running tests in parallel When I run the behavex command with "" parallel processes and parallel scheme set as "" Then I should see the following behavex console outputs and exit code "1" - | output_line | - | PARALLEL_PROCESSES \| | - | PARALLEL_SCHEME \| | - | Exit code: 1 | - | Executed s: 100%\|███████████████\| | + | output_line | + | PARALLEL_PROCESSES \| | + | PARALLEL_SCHEME \| | + | Exit code: 1 | + | Executed s: 100%\| | And I should not see error messages in the output Examples: | parallel_scheme | parallel_processes | From 803300952c9db4c27fc532e17b771207228b8226 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Tue, 26 Nov 2024 17:22:53 -0300 Subject: [PATCH 30/53] Updated test scenarios to properly deal with multiplatform paths --- tests/features/steps/execution_steps.py | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/features/steps/execution_steps.py b/tests/features/steps/execution_steps.py index 7abd758..e076c11 100644 --- a/tests/features/steps/execution_steps.py +++ b/tests/features/steps/execution_steps.py @@ -18,52 +18,52 @@ def step_impl(context): @when('I run the behavex command with a passing test') @when('I run the behavex command with passing tests') def step_impl(context): - context.output_path = 'output/output_{}'.format(get_random_number(6)) - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features/passing_tests.feature'), '-o', context.output_path] + context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features', 'passing_tests.feature'), '-o', context.output_path] execute_command(context, execution_args) @when('I run the behavex command that renames scenarios and features') def step_impl(context): - context.output_path = 'output/output_{}'.format(get_random_number(6)) - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features/rename_tests.feature'), '-o', context.output_path] + context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features', 'rename_tests.feature'), '-o', context.output_path] execute_command(context, execution_args) @when('I run the behavex command with a failing test') def step_impl(context): - context.output_path = 'output/output_{}'.format(get_random_number(6)) - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features/failing_tests.feature'), '-o', context.output_path] + context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features', 'failing_tests.feature'), '-o', context.output_path] execute_command(context, execution_args) @when('I run the behavex command with a crashing test') def step_impl(context, parallel_processes="1", parallel_scheme='scenario'): - context.output_path = 'output/output_{}'.format(get_random_number(6)) + context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) execution_args = ['behavex', - os.path.join(tests_features_path, os.path.join(tests_features_path, 'crashing_features/crashing_tests.feature')), + os.path.join(tests_features_path, os.path.join(tests_features_path, 'crashing_features', 'crashing_tests.feature')), '-o', context.output_path] execute_command(context, execution_args) @when('I run the behavex command with a skipped test') def step_impl(context): - context.output_path = 'output/output_{}'.format(get_random_number(6)) - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features/skipped_tests.feature'), '-o', context.output_path] + context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features', 'skipped_tests.feature'), '-o', context.output_path] execute_command(context, execution_args) @when('I run the behavex command with an untested test') def step_impl(context): - context.output_path = 'output/output_{}'.format(get_random_number(6)) - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features/untested_tests.feature'), '-o', context.output_path] + context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features', 'untested_tests.feature'), '-o', context.output_path] execute_command(context, execution_args) @when('I run the behavex command with "{parallel_processes}" parallel processes and parallel scheme set as "{parallel_schema}"') def step_impl(context, parallel_processes, parallel_schema): - context.output_path = 'output/output_{}'.format(get_random_number(6)) - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features/'), '-o', context.output_path, '--parallel-processes', parallel_processes, '--parallel-scheme', parallel_schema] + context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features'), '-o', context.output_path, '--parallel-processes', parallel_processes, '--parallel-scheme', parallel_schema] execute_command(context, execution_args) @@ -78,10 +78,10 @@ def step_impl(context): scheme = context.table[0]['parallel_scheme'] processes = context.table[0]['parallel_processes'] tags = context.table[0]['tags'] - context.output_path = 'output/output_{}'.format(get_random_number(6)) + context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) tags_to_folder_name = get_tags_string(tags) tags_array = get_tags_arguments(tags) - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features/'), '-o', context.output_path, '--parallel-processes', processes, '--parallel-scheme', scheme] + tags_array + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features'), '-o', context.output_path, '--parallel-processes', processes, '--parallel-scheme', scheme] + tags_array execute_command(context, execution_args) @@ -90,16 +90,16 @@ def step_impl(context): tags = context.table[0]['tags'] tags_to_folder_name = get_tags_string(tags) tags_array = get_tags_arguments(tags) - context.output_path = 'output/output_{}'.format(get_random_number(6)) - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features/'), '-o', context.output_path] + tags_array + context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features'), '-o', context.output_path] + tags_array execute_command(context, execution_args) @when('I run the behavex command by performing a dry run') def step_impl(context): # generate a random number between 1 and 1000000 completing with zeroes to 6 digits - context.output_path = 'output/output_{}'.format(get_random_number(6)) - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features/'), '-o', context.output_path, '--dry-run'] + context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features'), '-o', context.output_path, '--dry-run'] execute_command(context, execution_args) From c909e192ec3d9f4b1ee261eef90934e5805aff0c Mon Sep 17 00:00:00 2001 From: anibalinn Date: Tue, 26 Nov 2024 19:00:11 -0300 Subject: [PATCH 31/53] upgrading to 4.0.9rc3 --- pyproject.toml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6fca6f7..f92e295 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.9rc1" +version = "4.0.9rc3" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index 110a9ec..524e2e8 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.9rc1', + version='4.0.9rc3', license="MIT", platforms=['any'], python_requires='>=3.5', From caca72827ec0ca01e5e88154d480f86d1806cacf Mon Sep 17 00:00:00 2001 From: anibalinn Date: Mon, 2 Dec 2024 18:41:50 -0300 Subject: [PATCH 32/53] Fixing issue when analyzing feature files in Windows OS --- behavex/utils.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/behavex/utils.py b/behavex/utils.py index 6648bdd..96560a5 100644 --- a/behavex/utils.py +++ b/behavex/utils.py @@ -527,17 +527,17 @@ def __init__(self, paths=None): self.features = [ os.path.abspath(path) for path in self.features_paths - if os.path.isfile(path) and ':' not in path + if os.path.isfile(path) and not self._has_line_number(path) ] self.scenarios = [ - "{}:{}".format(os.path.abspath(path.split(":")[0]), path.split(":")[1]) + "{}:{}".format(os.path.abspath(path.split(":")[0]), path.split(":")[-1]) for path in self.features_paths - if not os.path.isdir(path) and ':' in path + if not os.path.isdir(path) and self._has_line_number(path) ] self.folders = [ os.path.abspath(path) for path in self.features_paths - if os.path.isdir(path) and ':' not in path + if os.path.isdir(path) and not self._has_line_number(path) ] def __call__(self, *args, **kwargs): @@ -622,3 +622,10 @@ def expand_paths(paths): exit() expanded.append(path) return expanded + + +@staticmethod +def _has_line_number(path): + # Split path by colon and check if the last part is a number + parts = path.split(':') + return len(parts) > 1 and parts[-1].isdigit() From 19863636fcba44ea769a54d2cac00169b0615d0f Mon Sep 17 00:00:00 2001 From: anibalinn Date: Mon, 2 Dec 2024 18:46:46 -0300 Subject: [PATCH 33/53] Update RC version --- pyproject.toml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f92e295..0d35071 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.9rc3" +version = "4.0.9rc4" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index 524e2e8..7726d70 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.9rc3', + version='4.0.9rc4', license="MIT", platforms=['any'], python_requires='>=3.5', From c608eac3fb75b6d283f80bcd0f1655fe2d6638b4 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Mon, 2 Dec 2024 18:48:03 -0300 Subject: [PATCH 34/53] Update RC version --- pyproject.toml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0d35071..6fca6f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.9rc4" +version = "4.0.9rc1" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index 7726d70..110a9ec 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.9rc4', + version='4.0.9rc1', license="MIT", platforms=['any'], python_requires='>=3.5', From 80b98906f85032c6c61c91f2eabe7f33e2987d98 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Mon, 2 Dec 2024 18:51:44 -0300 Subject: [PATCH 35/53] Fixing issue when importing a method in IncludePathsMatch --- behavex/utils.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/behavex/utils.py b/behavex/utils.py index 96560a5..59e4217 100644 --- a/behavex/utils.py +++ b/behavex/utils.py @@ -558,6 +558,13 @@ def match(self, filename, scenario=None): def bool(self): return self.features and self.folders + @staticmethod + def _has_line_number(path): + # Split path by colon and check if the last part is a number + parts = path.split(':') + return len(parts) > 1 and parts[-1].isdigit() + + class IncludeNameMatch(metaclass=ExecutionSingleton): def __init__(self, expr=None): @@ -622,10 +629,3 @@ def expand_paths(paths): exit() expanded.append(path) return expanded - - -@staticmethod -def _has_line_number(path): - # Split path by colon and check if the last part is a number - parts = path.split(':') - return len(parts) > 1 and parts[-1].isdigit() From a97b89df1a44abb35545848924d1f7a234f948ac Mon Sep 17 00:00:00 2001 From: anibalinn Date: Tue, 3 Dec 2024 12:13:30 -0300 Subject: [PATCH 36/53] Adding scenarios to validate behavex arguments --- tests/features/behavex_arguments.feature | 19 +++++++++++++++++++ tests/features/steps/execution_steps.py | 9 +++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/features/behavex_arguments.feature diff --git a/tests/features/behavex_arguments.feature b/tests/features/behavex_arguments.feature new file mode 100644 index 0000000..628523c --- /dev/null +++ b/tests/features/behavex_arguments.feature @@ -0,0 +1,19 @@ +Feature: Behavex arguments + + @BEHAVEX_ARGUMENTS + Scenario Outline: Behavex arguments with separator + Given I have installed behavex + When I run the behavex command using "" separator with the following scheme, processes and tags + | parallel_scheme | parallel_processes | tags | + | | | | + Then I should see the following behavex console outputs and exit code "0" + | output_line | + | PARALLEL_PROCESSES \| | + | PARALLEL_SCHEME \| | + | Exit code: 0 | + Examples: + | argument_separator | parallel_processes | parallel_scheme | tags | + | blank | 1 | scenario | -t @PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | equal | 1 | scenario | -t=@PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | blank | 2 | scenario | -t @PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | equal | 2 | scenario | -t=@PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | diff --git a/tests/features/steps/execution_steps.py b/tests/features/steps/execution_steps.py index e076c11..51486a3 100644 --- a/tests/features/steps/execution_steps.py +++ b/tests/features/steps/execution_steps.py @@ -74,14 +74,18 @@ def step_impl(context, parallel_processes, parallel_scheme): @when('I run the behavex command with the following scheme, processes and tags') -def step_impl(context): +@when('I run the behavex command using "{argument_separator}" separator with the following scheme, processes and tags') +def run_command_with_scheme_processes_and_tags(context, argument_separator="equal"): scheme = context.table[0]['parallel_scheme'] processes = context.table[0]['parallel_processes'] tags = context.table[0]['tags'] context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) tags_to_folder_name = get_tags_string(tags) tags_array = get_tags_arguments(tags) - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features'), '-o', context.output_path, '--parallel-processes', processes, '--parallel-scheme', scheme] + tags_array + if argument_separator == 'equal': + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features'), '-o', context.output_path, '--parallel-processes=' + processes, '--parallel-scheme=' + scheme] + tags_array + else: + execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features'), '-o', context.output_path, '--parallel-processes', processes, '--parallel-scheme', scheme] + tags_array execute_command(context, execution_args) @@ -201,6 +205,7 @@ def execute_command(context, execution_args, print_output=True): execution_args += ['--parallel-processes', context.parallel_processes] if hasattr(context, 'parallel_scheme'): execution_args += ['--parallel-scheme', context.parallel_scheme] + logging.info("Executing command: {}".format(" ".join(execution_args))) context.result = subprocess.run(execution_args, capture_output=True, text=True) if print_output: logging.info(context.result.stdout) From 529008c2037c80c8a428fd73e66cd948b30a7a88 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Wed, 4 Dec 2024 14:10:35 -0300 Subject: [PATCH 37/53] Fixing execution issues in Windows OS when running BehaveX with a feature path different than the current path --- CHANGES.rst | 7 +++ behavex/runner.py | 38 ++++++------ behavex/utils.py | 65 ++++++++++++-------- pyproject.toml | 2 +- setup.py | 2 +- tests/features/behavex_arguments.feature | 77 +++++++++++++++++++++--- tests/features/steps/execution_steps.py | 43 ++++++++++--- 7 files changed, 172 insertions(+), 62 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index d26efcf..3a55715 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,13 @@ Version History =============================================================================== +Version: 4.0.9 +------------------------------------------------------------------------------- + +FIXES: + +* Fixing execution issues in Windows OS when running BehaveX with a feature path different than the current path. + Version: 4.0.8 ------------------------------------------------------------------------------- ENHANCEMENTS: diff --git a/behavex/runner.py b/behavex/runner.py index 8cbac60..ce32ca6 100644 --- a/behavex/runner.py +++ b/behavex/runner.py @@ -52,11 +52,12 @@ copy_bootstrap_html_generator, create_execution_complete_callback_function, expand_paths, explore_features, generate_hash, - generate_reports, get_json_results, - get_logging_level, get_scenario_tags, - get_scenarios_instances, get_text, - join_feature_reports, join_scenario_reports, - len_scenarios, print_env_variables, print_parallel, + generate_reports, get_feature_and_scenario_line, + get_json_results, get_logging_level, + get_scenario_tags, get_scenarios_instances, + get_text, join_feature_reports, + join_scenario_reports, len_scenarios, + print_env_variables, print_parallel, set_behave_tags, set_env_variable, set_environ_config, set_system_paths) @@ -118,7 +119,7 @@ def run(args): if len(get_param('include_paths')) > 0: include_paths = ",".join(expand_paths(get_param('include_paths'))) features_path = os.environ.get('FEATURES_PATH') - if features_path == '' or features_path is None: + if features_path == '' or features_path is None or not os.path.exists(features_path): os.environ['FEATURES_PATH'] = include_paths else: os.environ['FEATURES_PATH'] = features_path + ',' + include_paths @@ -380,19 +381,20 @@ def create_scenario_line_references(features): for scenario in scenarios: scenario_filename = text(scenario.filename) if global_vars.rerun_failures or ".feature:" in feature_path: - feature_without_scenario_line = feature_path.split(":")[0] - if feature_without_scenario_line not in updated_features: - updated_features[feature_without_scenario_line] = [] + if feature_path not in updated_features: + updated_features[feature_path] = [] if isinstance(scenario, ScenarioOutline): for scenario_outline_instance in scenario.scenarios: - if scenario_outline_instance.line == int(feature_path.split(":")[1]): - if scenario_outline_instance not in updated_features[feature_without_scenario_line]: - updated_features[feature_without_scenario_line].append(scenario_outline_instance) + outline_scenario_line = get_feature_and_scenario_line(scenario_outline_instance.name)[1] + if outline_scenario_line and scenario_outline_instance.line == int(outline_scenario_line): + if scenario_outline_instance not in updated_features[pure_feature_path]: + updated_features[feature_path].append(scenario_outline_instance) break else: - if scenario.line == int(feature_path.split(":")[1]): - if scenario not in updated_features[feature_without_scenario_line]: - updated_features[feature_without_scenario_line].append(scenario) + scenario_line = get_feature_and_scenario_line(feature_path)[1] + if scenario_line and scenario.line == int(scenario_line): + if scenario not in updated_features[feature_path]: + updated_features[feature_path].append(scenario) else: updated_features_path = scenario.feature.filename if updated_features_path not in updated_features: @@ -428,11 +430,13 @@ def launch_by_feature(features, parallel_features = [] for features_path in features: feature = features[features_path][0].feature + pure_feature_path, scenario_line = get_feature_and_scenario_line(features_path) + feature_filename = feature.filename if not scenario_line else "{}:{}".format(pure_feature_path, scenario_line) if 'SERIAL' in feature.tags: - serial_features.append({"feature_filename": feature.filename, + serial_features.append({"feature_filename": feature_filename, "feature_json_skeleton": _get_feature_json_skeleton(feature)}) else: - parallel_features.append({"feature_filename": feature.filename, + parallel_features.append({"feature_filename": feature_filename, "feature_json_skeleton": _get_feature_json_skeleton(feature)}) if show_progress_bar: total_features = len(serial_features) + len(parallel_features) diff --git a/behavex/utils.py b/behavex/utils.py index 59e4217..8b38786 100644 --- a/behavex/utils.py +++ b/behavex/utils.py @@ -174,31 +174,35 @@ def join_scenario_reports(json_reports): ) return list(result.values()) + def explore_features(features_path, features_list=None): if features_list is None: features_list = [] # Normalize path separators - features_path = os.path.normpath(features_path) - - if global_vars.rerun_failures or ".feature:" in features_path: - features_path = features_path.split(":")[0] - - if os.path.isfile(features_path): - if features_path.endswith('.feature'): - path_feature = os.path.abspath(features_path) - feature = should_feature_be_run(path_feature) + pure_feature_path, scenario_line = get_feature_and_scenario_line(features_path) + normalized_features_path = os.path.normpath(pure_feature_path) + if os.path.isfile(normalized_features_path): + if normalized_features_path.endswith('.feature'): + abs_feature_path = os.path.abspath(normalized_features_path) + feature = should_feature_be_run(abs_feature_path) if feature: - features_list.extend(feature.scenarios) + if scenario_line: + # iterate over scenarios and add the scenario that matches the scenario line + for scenario in feature.scenarios: + if scenario.line == int(scenario_line): + features_list.append(scenario) + else: + features_list.extend(feature.scenarios) else: try: - for node in os.listdir(features_path): - node_path = os.path.join(features_path, node) + for node in os.listdir(normalized_features_path): + node_path = os.path.join(normalized_features_path, node) if os.path.isdir(node_path): explore_features(node_path, features_list) elif node.endswith('.feature'): - path_feature = os.path.abspath(node_path) - feature = should_feature_be_run(path_feature) + abs_feature_path = os.path.abspath(node_path) + feature = should_feature_be_run(abs_feature_path) if feature: features_list.extend(feature.scenarios) except OSError as e: @@ -527,17 +531,17 @@ def __init__(self, paths=None): self.features = [ os.path.abspath(path) for path in self.features_paths - if os.path.isfile(path) and not self._has_line_number(path) + if os.path.isfile(path) and not has_scenario_line_number(path) ] self.scenarios = [ - "{}:{}".format(os.path.abspath(path.split(":")[0]), path.split(":")[-1]) + "{}:{}".format(os.path.abspath(get_feature_and_scenario_line(path)[0]), get_feature_and_scenario_line(path)[1]) for path in self.features_paths - if not os.path.isdir(path) and self._has_line_number(path) + if not os.path.isdir(path) and has_scenario_line_number(path) ] self.folders = [ os.path.abspath(path) for path in self.features_paths - if os.path.isdir(path) and not self._has_line_number(path) + if os.path.isdir(path) and not has_scenario_line_number(path) ] def __call__(self, *args, **kwargs): @@ -558,12 +562,6 @@ def match(self, filename, scenario=None): def bool(self): return self.features and self.folders - @staticmethod - def _has_line_number(path): - # Split path by colon and check if the last part is a number - parts = path.split(':') - return len(parts) > 1 and parts[-1].isdigit() - class IncludeNameMatch(metaclass=ExecutionSingleton): @@ -624,8 +622,23 @@ def expand_paths(paths): exit() expanded.extend(globbed) else: - if not os.path.exists(path): - print('\nSpecified path was not found: {}'.format(path)) + pure_feature_path, scenario_line = get_feature_and_scenario_line(path) + normalized_features_path = os.path.normpath(pure_feature_path) + if not os.path.exists(pure_feature_path): + print('\nSpecified path was not found: {}'.format(pure_feature_path)) exit() expanded.append(path) return expanded + + +def has_scenario_line_number(path): + # Split path by colon and check if the last part is a number + parts = path.split(':') + return len(parts) > 1 and parts[-1].isdigit() + +def get_feature_and_scenario_line(path): + if has_scenario_line_number(path): + parts = path.split(':') + return [':'.join(parts[:-1]), parts[-1]] + else: + return [path, None] diff --git a/pyproject.toml b/pyproject.toml index 6fca6f7..0d35071 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.9rc1" +version = "4.0.9rc4" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index 110a9ec..7726d70 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.9rc1', + version='4.0.9rc4', license="MIT", platforms=['any'], python_requires='>=3.5', diff --git a/tests/features/behavex_arguments.feature b/tests/features/behavex_arguments.feature index 628523c..9d26e62 100644 --- a/tests/features/behavex_arguments.feature +++ b/tests/features/behavex_arguments.feature @@ -1,9 +1,9 @@ Feature: Behavex arguments @BEHAVEX_ARGUMENTS - Scenario Outline: Behavex arguments with separator + Scenario Outline: Validate BehaveX arguments with separator Given I have installed behavex - When I run the behavex command using "" separator with the following scheme, processes and tags + When I run the behavex command using "" separator for "passing_tests.feature" feature with the following scheme, processes and tags | parallel_scheme | parallel_processes | tags | | | | | Then I should see the following behavex console outputs and exit code "0" @@ -11,9 +11,72 @@ Feature: Behavex arguments | PARALLEL_PROCESSES \| | | PARALLEL_SCHEME \| | | Exit code: 0 | + And I should not see error messages in the output + And I should see the same number of scenarios in the reports Examples: - | argument_separator | parallel_processes | parallel_scheme | tags | - | blank | 1 | scenario | -t @PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | - | equal | 1 | scenario | -t=@PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | - | blank | 2 | scenario | -t @PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | - | equal | 2 | scenario | -t=@PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | argument_separator | parallel_processes | parallel_scheme | tags | + | blank | 1 | scenario | -t @PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | equal | 1 | scenario | -t=@PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | blank | 1 | scenario | -t @PASSING_TAG_1,PASSING_TAG_3 -t ~@PASSING_TAG_2 | + | equal | 1 | scenario | -t @PASSING_TAG_1,PASSING_TAG_3 -t ~@PASSING_TAG_2 | + | blank | 2 | scenario | -t @PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | equal | 2 | scenario | -t=@PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | blank | 2 | scenario | -t @PASSING_TAG_1,PASSING_TAG_3 -t ~@PASSING_TAG_2 | + | equal | 2 | scenario | -t @PASSING_TAG_1,PASSING_TAG_3 -t ~@PASSING_TAG_2 | + | blank | 1 | feature | -t @PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | equal | 1 | feature | -t=@PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | blank | 1 | feature | -t @PASSING_TAG_1,PASSING_TAG_3 -t ~@PASSING_TAG_2 | + | equal | 1 | feature | -t @PASSING_TAG_1,PASSING_TAG_3 -t ~@PASSING_TAG_2 | + | blank | 2 | feature | -t @PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | equal | 2 | feature | -t=@PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | + | blank | 2 | feature | -t @PASSING_TAG_1,PASSING_TAG_3 -t ~@PASSING_TAG_2 | + | equal | 2 | feature | -t @PASSING_TAG_1,PASSING_TAG_3 -t ~@PASSING_TAG_2 | + Examples: + | argument_separator | parallel_processes | parallel_scheme | tags | + | blank | 1 | scenario | | + | equal | 1 | scenario | | + | blank | 2 | scenario | | + | equal | 2 | scenario | | + | blank | 1 | feature | | + | equal | 1 | feature | | + | blank | 2 | feature | | + | equal | 2 | feature | | + + @BEHAVEX_ARGUMENTS + Scenario Outline: Validate BehaveX arguments considering feature name with scenario line + When I run the behavex command using "" separator for "passing_tests.feature:13" feature with the following scheme, processes and tags + | parallel_scheme | parallel_processes | tags | + | | | | + Then I should see the following behavex console outputs and exit code "0" + | output_line | + | PARALLEL_PROCESSES \| | + | PARALLEL_SCHEME \| | + | Exit code: 0 | + And I should not see error messages in the output + And I should see the same number of scenarios in the reports not considering the skipped scenarios + Examples: + | argument_separator | parallel_processes | parallel_scheme | tags | + | blank | 1 | scenario | | + | equal | 1 | feature | | + | blank | 2 | scenario | | + | equal | 2 | feature | | + + + @BEHAVEX_ARGUMENTS + Scenario Outline: Validate BehaveX arguments with multiple feature paths + When I run the behavex command using "" separator for "passing_tests.feature" and "failing_tests.feature" features with the following scheme, processes and tags + | parallel_scheme | parallel_processes | tags | + | | | | + Then I should see the following behavex console outputs and exit code "1" + | output_line | + | PARALLEL_PROCESSES \| | + | PARALLEL_SCHEME \| | + | Exit code: 1 | + And I should not see exception messages in the output + And I should see the same number of scenarios in the reports not considering the skipped scenarios + Examples: + | argument_separator | parallel_processes | parallel_scheme | tags | + | blank | 1 | scenario | | + | equal | 1 | feature | | + | blank | 2 | scenario | | + | equal | 2 | feature | | diff --git a/tests/features/steps/execution_steps.py b/tests/features/steps/execution_steps.py index 51486a3..2f36496 100644 --- a/tests/features/steps/execution_steps.py +++ b/tests/features/steps/execution_steps.py @@ -75,17 +75,33 @@ def step_impl(context, parallel_processes, parallel_scheme): @when('I run the behavex command with the following scheme, processes and tags') @when('I run the behavex command using "{argument_separator}" separator with the following scheme, processes and tags') -def run_command_with_scheme_processes_and_tags(context, argument_separator="equal"): +@when('I run the behavex command using "{argument_separator}" separator for "{feature_name}" feature with the following scheme, processes and tags') +@when('I run the behavex command using "{argument_separator}" separator for "{feature_name}" and "{feature_name_2}" features with the following scheme, processes and tags') +def run_command_with_scheme_processes_and_tags(context, argument_separator="equal", feature_name=None, feature_name_2=None): scheme = context.table[0]['parallel_scheme'] processes = context.table[0]['parallel_processes'] tags = context.table[0]['tags'] context.output_path = os.path.join('output', 'output_{}'.format(get_random_number(6))) tags_to_folder_name = get_tags_string(tags) - tags_array = get_tags_arguments(tags) + if not tags: + tags_array = [] + else: + tags_array = get_tags_arguments(tags) + if feature_name: + if feature_name_2: + feature_path = os.path.join(tests_features_path, 'secondary_features', feature_name) + feature_path_2 = os.path.join(tests_features_path, 'secondary_features', feature_name_2) + else: + feature_path = os.path.join(tests_features_path, 'secondary_features', feature_name) + else: + feature_path = os.path.join(tests_features_path, 'secondary_features') if argument_separator == 'equal': - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features'), '-o', context.output_path, '--parallel-processes=' + processes, '--parallel-scheme=' + scheme] + tags_array + execution_args = ['behavex', feature_path, '-o', context.output_path, '--parallel-processes=' + processes, '--parallel-scheme=' + scheme] + tags_array else: - execution_args = ['behavex', os.path.join(tests_features_path, 'secondary_features'), '-o', context.output_path, '--parallel-processes', processes, '--parallel-scheme', scheme] + tags_array + execution_args = ['behavex', feature_path, '-o', context.output_path, '--parallel-processes', processes, '--parallel-scheme', scheme] + tags_array + if feature_name_2: + # append the second feature path to the execution arguments in index 2 + execution_args.insert(2, feature_path_2) execute_command(context, execution_args) @@ -138,17 +154,21 @@ def step_impl(context): logging.info(f"Total scenarios in the JUnit reports: {total_scenarios_in_junit_reports}") total_scenarios_in_console_output = get_total_scenarios_in_console_output(context) logging.info(f"Total scenarios in the console output: {total_scenarios_in_console_output}") - assert total_scenarios_in_html_report == total_scenarios_in_junit_reports == total_scenarios_in_console_output, f"Expected {total_scenarios} scenarios in the reports and the console output, but found {total_scenarios_in_html_report} in the HTML report, {total_scenarios_in_junit_reports} in the JUnit reports, and {total_scenarios_in_console} in the console output" + assert total_scenarios_in_html_report == total_scenarios_in_junit_reports == total_scenarios_in_console_output, f"Expected total scenarios to match, but found {total_scenarios_in_html_report} in the HTML report, {total_scenarios_in_junit_reports} in the JUnit reports, and {total_scenarios_in_console_output} in the console output" @then('I should see the same number of scenarios in the reports') -def step_impl(context): +def verify_total_scenarios_in_reports(context, consider_skipped_scenarios=True): total_scenarios_in_html_report = get_total_scenarios_in_html_report(context) logging.info(f"Total scenarios in the HTML report: {total_scenarios_in_html_report}") - total_scenarios_in_junit_reports = get_total_scenarios_in_junit_reports(context) + total_scenarios_in_junit_reports = get_total_scenarios_in_junit_reports(context, consider_skipped_scenarios) logging.info(f"Total scenarios in the JUnit reports: {total_scenarios_in_junit_reports}") - assert total_scenarios_in_html_report == total_scenarios_in_junit_reports, f"Expected {total_scenarios} scenarios in the reports, but found {total_scenarios_in_html_report} in the HTML report, {total_scenarios_in_junit_reports} in the JUnit reports" + assert total_scenarios_in_html_report == total_scenarios_in_junit_reports, f"Expected total scenarios to match, but found {total_scenarios_in_html_report} in the HTML report, {total_scenarios_in_junit_reports} in the JUnit reports" + +@then('I should see the same number of scenarios in the reports not considering the skipped scenarios') +def step_impl(context): + verify_total_scenarios_in_reports(context, consider_skipped_scenarios=False) def get_tags_arguments(tags): tags_array = [] @@ -187,14 +207,17 @@ def get_total_scenarios_in_html_report(context): return html_content.count('data-scenario-tags=') -def get_total_scenarios_in_junit_reports(context): +def get_total_scenarios_in_junit_reports(context, consider_skipped_scenarios=True): junit_folder = os.path.abspath(os.path.join(context.output_path, 'behave')) total_scenarios_in_junit_reports = 0 for file in os.listdir(junit_folder): if file.endswith('.xml'): with open(os.path.join(junit_folder, file), 'r') as file: xml_content = file.read() - total_scenarios_in_junit_reports += xml_content.count(' Date: Wed, 4 Dec 2024 14:22:46 -0300 Subject: [PATCH 38/53] Updating library version --- pyproject.toml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0d35071..6fca6f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.9rc4" +version = "4.0.9rc1" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index 7726d70..110a9ec 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.9rc4', + version='4.0.9rc1', license="MIT", platforms=['any'], python_requires='>=3.5', From 457ff07542339a065e12b9a718f8d0465657999b Mon Sep 17 00:00:00 2001 From: anibalinn Date: Wed, 4 Dec 2024 16:40:02 -0300 Subject: [PATCH 39/53] Updating changes done in latest version and library version --- .github/workflows/python-package.yml | 2 +- CHANGES.rst | 8 ++++++++ pyproject.toml | 2 +- setup.py | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index c6f6187..eef2b24 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -32,7 +32,7 @@ jobs: run: | python -m pip install --upgrade pip python setup.py sdist - pip install dist/behavex-4.0.9rc1.tar.gz + pip install dist/behavex-4.0.9rc4.tar.gz - name: Verify behavex command shell: bash diff --git a/CHANGES.rst b/CHANGES.rst index 3a55715..36c2745 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,9 +4,17 @@ Version History Version: 4.0.9 ------------------------------------------------------------------------------- +ENHANCEMENTS: + +* Adding support for latest Python versions (3.12) +* Performing cross-platform validations as part of github actions workflow +* Enabling adding scenario lines in feature paths when running BehaveX + + FIXES: * Fixing execution issues in Windows OS when running BehaveX with a feature path different than the current path. +* Fixing encoding issues in progress bar in Windows OS Version: 4.0.8 ------------------------------------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 6fca6f7..0d35071 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.9rc1" +version = "4.0.9rc4" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index 110a9ec..7726d70 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.9rc1', + version='4.0.9rc4', license="MIT", platforms=['any'], python_requires='>=3.5', From d4218bd714476f85a52f21d2b8fad5fd638e9754 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 5 Dec 2024 11:47:54 -0300 Subject: [PATCH 40/53] Adding method needed for backward compatibility reasons --- .github/workflows/python-package.yml | 2 +- behavex/utils.py | 6 ++++++ pyproject.toml | 2 +- setup.py | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index eef2b24..646169b 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -32,7 +32,7 @@ jobs: run: | python -m pip install --upgrade pip python setup.py sdist - pip install dist/behavex-4.0.9rc4.tar.gz + pip install dist/behavex-4.0.9rc5.tar.gz - name: Verify behavex command shell: bash diff --git a/behavex/utils.py b/behavex/utils.py index 8b38786..32af33d 100644 --- a/behavex/utils.py +++ b/behavex/utils.py @@ -303,6 +303,12 @@ def execution(): retry_file_operation(behave_folder, lambda: os.makedirs(behave_folder)) + +# Implemented for backward compatibility with other libraries that use this function +def try_operate_descriptor(dest_path, execution, return_value=False): + return retry_file_operation(dest_path, execution, return_value) + + def set_env_variable(key, value): if value: os.environ[key] = str(value) diff --git a/pyproject.toml b/pyproject.toml index 0d35071..4abd6fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.9rc4" +version = "4.0.9rc5" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index 7726d70..bd49152 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.9rc4', + version='4.0.9rc5', license="MIT", platforms=['any'], python_requires='>=3.5', From 10f46d0894857c161c65845361057b66799e2304 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 5 Dec 2024 11:48:46 -0300 Subject: [PATCH 41/53] Cosmetic update in CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 36c2745..d4dd45d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,7 +7,7 @@ Version: 4.0.9 ENHANCEMENTS: * Adding support for latest Python versions (3.12) -* Performing cross-platform validations as part of github actions workflow +* Performing cross-platform validations as part of github actions workflow (Linux, Windows and MacOS) * Enabling adding scenario lines in feature paths when running BehaveX From 4e3f196f2d7b861fc1a6f4028972f3c9be05aa4b Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 5 Dec 2024 15:18:24 -0300 Subject: [PATCH 42/53] Adding more scenarios and fixing issue with internal BehaveX tag not removed from scenarios --- CHANGES.rst | 2 +- behavex/outputs/report_json.py | 6 ++- tests/features/behavex_arguments.feature | 31 +++++++++++++ tests/features/dry_run.feature | 2 + tests/features/failing_scenarios.feature | 1 + tests/features/parallel_executions.feature | 1 + tests/features/passing_scenarios.feature | 4 ++ tests/features/renaming_scenarios.feature | 1 + .../secondary_features/skipped_tests.feature | 2 +- tests/features/skipped_scenarios.feature | 1 + tests/features/steps/execution_steps.py | 45 ++++++++++++++++++- 11 files changed, 92 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index d4dd45d..8011904 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,7 +9,7 @@ ENHANCEMENTS: * Adding support for latest Python versions (3.12) * Performing cross-platform validations as part of github actions workflow (Linux, Windows and MacOS) * Enabling adding scenario lines in feature paths when running BehaveX - +* Fixing issue when performing dry runs, as internal @BHX_MANUAL_DRY_RUN tag was not removed from the scenario tags FIXES: diff --git a/behavex/outputs/report_json.py b/behavex/outputs/report_json.py index 6e9f2bb..8cac259 100644 --- a/behavex/outputs/report_json.py +++ b/behavex/outputs/report_json.py @@ -22,7 +22,7 @@ from behave.model import ScenarioOutline from behave.step_registry import registry -from behavex.conf_mgr import get_env +from behavex.conf_mgr import get_env, get_param from behavex.global_vars import global_vars from behavex.outputs.report_utils import (get_environment_details, get_error_message, @@ -150,7 +150,11 @@ def _processing_background_feature(feature): def _processing_scenarios(scenarios, scenario_list, id_feature): scenario_outline_index = 0 overall_status = 'passed' + is_dry_run = get_param('dry_run') for scenario in scenarios: + # Remove BHX_MANUAL_DRY_RUN tag if it is a dry run + if is_dry_run and 'BHX_MANUAL_DRY_RUN' in scenario_tags: + scenario.tags.remove('BHX_MANUAL_DRY_RUN') # Set MANUAL to False in order filter regardless of it error_msg, error_lines, error_step, error_background = _get_error_scenario( scenario diff --git a/tests/features/behavex_arguments.feature b/tests/features/behavex_arguments.feature index 9d26e62..1376db3 100644 --- a/tests/features/behavex_arguments.feature +++ b/tests/features/behavex_arguments.feature @@ -13,6 +13,7 @@ Feature: Behavex arguments | Exit code: 0 | And I should not see error messages in the output And I should see the same number of scenarios in the reports + And I should see the generated HTML report does not contain internal BehaveX variables and tags Examples: | argument_separator | parallel_processes | parallel_scheme | tags | | blank | 1 | scenario | -t @PASSING_TAG_1,@PASSING_TAG_2,@PASSING_TAG_3 | @@ -74,9 +75,39 @@ Feature: Behavex arguments | Exit code: 1 | And I should not see exception messages in the output And I should see the same number of scenarios in the reports not considering the skipped scenarios + And I should see the generated HTML report does not contain internal BehaveX variables and tags Examples: | argument_separator | parallel_processes | parallel_scheme | tags | | blank | 1 | scenario | | | equal | 1 | feature | | | blank | 2 | scenario | | | equal | 2 | feature | | + + +@BEHAVEX_ARGUMENTS @SCENARIO_NAME + Scenario Outline: Validate BehaveX arguments by running a scenario by its name + When I run the behavex command with scenario name "" and the following scheme, processes and tags + | parallel_scheme | parallel_processes | tags | + | | | | + Then I should see the following behavex console outputs and exit code "0" + | output_line | + | PARALLEL_PROCESSES \| | + | PARALLEL_SCHEME \| | + | Exit code: 0 | + And I should not see exception messages in the output + And I should see the HTML report was generated and contains "" scenarios + And I should see the same number of scenarios in the reports not considering the skipped scenarios + And I should see the generated HTML report does not contain internal BehaveX variables and tags + Examples: + | argument_separator | parallel_processes | parallel_scheme | tags | test_scenario_name | total_scenarios | + | blank | 1 | scenario | | This test should pass and contains | 4 | + | equal | 1 | feature | | This test should pass and contains | 4 | + | blank | 2 | scenario | | This test should pass and contains | 4 | + | equal | 2 | feature | | This test should pass and contains | 4 | + | equal | 2 | feature | | Non existing scenario | 0 | + | equal | 2 | scenario | | Non existing scenario | 0 | + | equal | 2 | scenario | | This test should pass and contains a tag | 1 | + | equal | 2 | scenario | -t PASSING_TAG_1 | This test should pass and contains a tag | 1 | + | equal | 2 | feature | -t PASSING_TAG_1 | This test should pass and contains a tag | 1 | + | equal | 2 | scenario | -t @PASSING_TAG_1 | This test should pass and contains a tag | 1 | + | equal | 2 | feature | -t @PASSING_TAG_1 | This test should pass and contains a tag | 1 | diff --git a/tests/features/dry_run.feature b/tests/features/dry_run.feature index 68b2b49..f414ebf 100644 --- a/tests/features/dry_run.feature +++ b/tests/features/dry_run.feature @@ -9,3 +9,5 @@ Feature: Dry run | Dry run completed | | Exit code: 0 | And I should not see error messages in the output + And I should see the HTML report was generated and contains scenarios + And I should see the generated HTML report does not contain internal BehaveX variables and tags diff --git a/tests/features/failing_scenarios.feature b/tests/features/failing_scenarios.feature index c8740b9..fba39e0 100644 --- a/tests/features/failing_scenarios.feature +++ b/tests/features/failing_scenarios.feature @@ -10,3 +10,4 @@ Feature: Failing Scenarios | Exit code: 1 | And I should not see exception messages in the output And I should see the same number of scenarios in the reports and the console output + And I should see the generated HTML report does not contain internal BehaveX variables and tags diff --git a/tests/features/parallel_executions.feature b/tests/features/parallel_executions.feature index 1fab49f..6d86a57 100644 --- a/tests/features/parallel_executions.feature +++ b/tests/features/parallel_executions.feature @@ -31,6 +31,7 @@ Feature: Parallel executions | 1 scenario passed, 0 failed | And I should not see error messages in the output And I should see the same number of scenarios in the reports and the console output + And I should see the generated HTML report does not contain internal BehaveX variables and tags Examples: | parallel_scheme | parallel_processes | tags | | scenario | 3 | -t=@PASSING_TAG_3 -t=@PASSING_TAG_3_1 | diff --git a/tests/features/passing_scenarios.feature b/tests/features/passing_scenarios.feature index 4ed05d0..b63ba4d 100644 --- a/tests/features/passing_scenarios.feature +++ b/tests/features/passing_scenarios.feature @@ -10,6 +10,8 @@ Feature: Passing Scenarios | Exit code: 0 | And I should not see error messages in the output And I should see the same number of scenarios in the reports and the console output + And I should see the generated HTML report does not contain internal BehaveX variables and tags + @PASSING Scenario: Passing tests with AND tags @@ -23,6 +25,7 @@ Feature: Passing Scenarios | Exit code: 0 | And I should not see error messages in the output And I should see the same number of scenarios in the reports + And I should see the generated HTML report does not contain internal BehaveX variables and tags @PASSING @WIP Scenario: Passing tests with NOT tags @@ -36,3 +39,4 @@ Feature: Passing Scenarios | Exit code: 0 | And I should not see error messages in the output And I should see the same number of scenarios in the reports and the console output + And I should see the generated HTML report does not contain internal BehaveX variables and tags diff --git a/tests/features/renaming_scenarios.feature b/tests/features/renaming_scenarios.feature index b39cc2a..19c3ccb 100644 --- a/tests/features/renaming_scenarios.feature +++ b/tests/features/renaming_scenarios.feature @@ -22,6 +22,7 @@ Feature: Renaming Scenarios | Exit code: 0 | And I should not see error messages in the output And I should see the same number of scenarios in the reports and the console output + And I should see the generated HTML report does not contain internal BehaveX variables and tags Examples: | parallel_scheme | parallel_processes | | scenario | 3 | diff --git a/tests/features/secondary_features/skipped_tests.feature b/tests/features/secondary_features/skipped_tests.feature index 089f308..01a6e9d 100644 --- a/tests/features/secondary_features/skipped_tests.feature +++ b/tests/features/secondary_features/skipped_tests.feature @@ -1,6 +1,6 @@ Feature: Skipped Tests - @skip + @SKIP Scenario: This test should be skipped Given a condition to skip the scenario Then I perform the condition diff --git a/tests/features/skipped_scenarios.feature b/tests/features/skipped_scenarios.feature index 1065a91..415896c 100644 --- a/tests/features/skipped_scenarios.feature +++ b/tests/features/skipped_scenarios.feature @@ -10,3 +10,4 @@ Feature: Skipped Scenarios | Exit code: 0 And I should not see error messages in the output And I should see the same number of scenarios in the reports and the console output + And I should see the generated HTML report does not contain internal BehaveX variables and tags diff --git a/tests/features/steps/execution_steps.py b/tests/features/steps/execution_steps.py index 2f36496..a1a8d0f 100644 --- a/tests/features/steps/execution_steps.py +++ b/tests/features/steps/execution_steps.py @@ -74,10 +74,11 @@ def step_impl(context, parallel_processes, parallel_scheme): @when('I run the behavex command with the following scheme, processes and tags') +@when('I run the behavex command with scenario name "{scenario_name}" and the following scheme, processes and tags') @when('I run the behavex command using "{argument_separator}" separator with the following scheme, processes and tags') @when('I run the behavex command using "{argument_separator}" separator for "{feature_name}" feature with the following scheme, processes and tags') @when('I run the behavex command using "{argument_separator}" separator for "{feature_name}" and "{feature_name_2}" features with the following scheme, processes and tags') -def run_command_with_scheme_processes_and_tags(context, argument_separator="equal", feature_name=None, feature_name_2=None): +def run_command_with_scheme_processes_and_tags(context, scenario_name=None, argument_separator="equal", feature_name=None, feature_name_2=None): scheme = context.table[0]['parallel_scheme'] processes = context.table[0]['parallel_processes'] tags = context.table[0]['tags'] @@ -102,6 +103,9 @@ def run_command_with_scheme_processes_and_tags(context, argument_separator="equa if feature_name_2: # append the second feature path to the execution arguments in index 2 execution_args.insert(2, feature_path_2) + if scenario_name: + execution_args.append('--name') + execution_args.append(scenario_name.replace(' ', '\\ ')) execute_command(context, execution_args) @@ -170,6 +174,38 @@ def verify_total_scenarios_in_reports(context, consider_skipped_scenarios=True): def step_impl(context): verify_total_scenarios_in_reports(context, consider_skipped_scenarios=False) + +@then('I should see the HTML report was generated and contains scenarios') +@then('I should see the HTML report was generated and contains "{total_scenarios}" scenarios') +def verify_total_scenarios_in_html_report(context, total_scenarios=None, consider_skipped_scenarios=True): + total_scenarios_in_html_report = get_total_scenarios_in_html_report(context) + logging.info(f"Total scenarios in the HTML report: {total_scenarios_in_html_report}") + if total_scenarios is not None: + assert total_scenarios_in_html_report == int(total_scenarios), f"Expected the HTML report to contain {total_scenarios} scenarios, but found {total_scenarios_in_html_report}" + else: + assert total_scenarios_in_html_report > 0, "Expected the HTML report to be generated and contain scenarios" + + +@then('I should see the generated HTML report contains the "{string_to_search}" string') +def verify_string_in_html_report(context, string_to_search, string_should_be_present=True): + total_string_instances_in_html_report = get_string_instances_from_html_report(context, string_to_search) + logging.info(f"Total instances of '{string_to_search}' in the HTML report: {total_string_instances_in_html_report}") + if string_should_be_present: + assert total_string_instances_in_html_report > 0, f"Expected the HTML report to contain the string '{string_to_search}'" + else: + assert total_string_instances_in_html_report == 0, f"Expected the HTML report to not contain the string '{string_to_search}'" + + +@then('I should see the generated HTML report does not contain internal BehaveX variables and tags') +def verify_string_not_in_html_report(context): + internal_behavex_variables_and_tags = ["BHX_", "BHX_TAG_"] + for variable_or_tag in internal_behavex_variables_and_tags: + total_string_instances_in_html_report = get_string_instances_from_html_report(context, variable_or_tag) + logging.info(f"Total instances of '{variable_or_tag}' in the HTML report: {total_string_instances_in_html_report}") + assert total_string_instances_in_html_report == 0, f"Expected the HTML report to not contain the string '{variable_or_tag}'" + + + def get_tags_arguments(tags): tags_array = [] for tag in tags.split(' '): @@ -207,6 +243,13 @@ def get_total_scenarios_in_html_report(context): return html_content.count('data-scenario-tags=') +def get_string_instances_from_html_report(context, string_to_search): + report_path = os.path.abspath(os.path.join(context.output_path, 'report.html')) + with open(report_path, 'r') as file: + html_content = file.read() + return html_content.lower().count(string_to_search.lower()) + + def get_total_scenarios_in_junit_reports(context, consider_skipped_scenarios=True): junit_folder = os.path.abspath(os.path.join(context.output_path, 'behave')) total_scenarios_in_junit_reports = 0 From 99d3e88e0146e8b4f8f57a44f0c4f2338d4ada6c Mon Sep 17 00:00:00 2001 From: anibalinn Date: Thu, 5 Dec 2024 15:19:18 -0300 Subject: [PATCH 43/53] Updating RC version --- .github/workflows/python-package.yml | 2 +- pyproject.toml | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 646169b..3ee9ae9 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -32,7 +32,7 @@ jobs: run: | python -m pip install --upgrade pip python setup.py sdist - pip install dist/behavex-4.0.9rc5.tar.gz + pip install dist/behavex-4.0.9rc6.tar.gz - name: Verify behavex command shell: bash diff --git a/pyproject.toml b/pyproject.toml index 4abd6fd..eff9153 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.9rc5" +version = "4.0.9rc6" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index bd49152..010d057 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.9rc5', + version='4.0.9rc6', license="MIT", platforms=['any'], python_requires='>=3.5', From 1243933959287291def4856e82eda7b990c2be5b Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 6 Dec 2024 11:15:47 -0300 Subject: [PATCH 44/53] Improvement done in github actions --- .github/workflows/python-package.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 3ee9ae9..ca87f8d 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -43,3 +43,5 @@ jobs: else behavex ./tests/features/*.feature fi + # print the contents of failures.txt + cat failures.txt From d0d91f5643204cf63f8f91837208075b0b77552f Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 6 Dec 2024 11:26:55 -0300 Subject: [PATCH 45/53] cosmetic change --- .github/workflows/python-package.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index ca87f8d..65f3c38 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -42,6 +42,5 @@ jobs: behavex .\\tests\\features\\*.feature else behavex ./tests/features/*.feature + cat report.json fi - # print the contents of failures.txt - cat failures.txt From 95dc88cabd03f2ffe46abcb321462330e8d9df76 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 6 Dec 2024 11:45:27 -0300 Subject: [PATCH 46/53] cosmetic change --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 65f3c38..4078c36 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -42,5 +42,5 @@ jobs: behavex .\\tests\\features\\*.feature else behavex ./tests/features/*.feature - cat report.json + cat /home/runner/work/behavex/behavex/output/report.json fi From 2a5416d06ec05b8843608a02c8926f326540830b Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 6 Dec 2024 12:19:40 -0300 Subject: [PATCH 47/53] Fix done when managing tags --- .github/workflows/python-package.yml | 1 - behavex/outputs/report_json.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 4078c36..3ee9ae9 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -42,5 +42,4 @@ jobs: behavex .\\tests\\features\\*.feature else behavex ./tests/features/*.feature - cat /home/runner/work/behavex/behavex/output/report.json fi diff --git a/behavex/outputs/report_json.py b/behavex/outputs/report_json.py index 8cac259..5b36a56 100644 --- a/behavex/outputs/report_json.py +++ b/behavex/outputs/report_json.py @@ -153,6 +153,7 @@ def _processing_scenarios(scenarios, scenario_list, id_feature): is_dry_run = get_param('dry_run') for scenario in scenarios: # Remove BHX_MANUAL_DRY_RUN tag if it is a dry run + scenario_tags = get_scenario_tags(scenario) if is_dry_run and 'BHX_MANUAL_DRY_RUN' in scenario_tags: scenario.tags.remove('BHX_MANUAL_DRY_RUN') # Set MANUAL to False in order filter regardless of it @@ -160,7 +161,6 @@ def _processing_scenarios(scenarios, scenario_list, id_feature): scenario ) # pylint: disable=W0123 - scenario_tags = get_scenario_tags(scenario) if match_for_execution(scenario_tags): # Scenario was selectable scenario_info = {} From 208f45ee72020909f7d2ac85af53376995c9f5eb Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 6 Dec 2024 12:34:13 -0300 Subject: [PATCH 48/53] Updating RC version --- .github/workflows/python-package.yml | 2 +- pyproject.toml | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 3ee9ae9..9708cb8 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -32,7 +32,7 @@ jobs: run: | python -m pip install --upgrade pip python setup.py sdist - pip install dist/behavex-4.0.9rc6.tar.gz + pip install dist/behavex-4.0.9rc7.tar.gz - name: Verify behavex command shell: bash diff --git a/pyproject.toml b/pyproject.toml index eff9153..f366961 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.9rc6" +version = "4.0.9rc7" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index 010d057..0a725b7 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.9rc6', + version='4.0.9rc7', license="MIT", platforms=['any'], python_requires='>=3.5', From a84ff0b71c86f07d62dcc77c6e0bbe8774ffba7d Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 6 Dec 2024 13:15:29 -0300 Subject: [PATCH 49/53] Adding report.json as a github actions artifact --- .github/workflows/python-package.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 9708cb8..fb94d04 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -43,3 +43,17 @@ jobs: else behavex ./tests/features/*.feature fi + + # Set report.json to be available as an artifact for debugging + - name: Set report.json to be available as an artifact + run: | + if [ "$RUNNER_OS" == "Windows" ]; then + dir + type .\\tests\\features\\output\\report.json + else + cat ./tests/features/output/report.json + fi + uses: actions/upload-artifact@v4 + with: + name: report.json + path: output/report.json From 6500f6d76454c4db12878a1f3d90c2b1e271a630 Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 6 Dec 2024 13:18:36 -0300 Subject: [PATCH 50/53] Adding report.json as a github actions artifact --- .github/workflows/python-package.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index fb94d04..c65b59c 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -46,14 +46,7 @@ jobs: # Set report.json to be available as an artifact for debugging - name: Set report.json to be available as an artifact - run: | - if [ "$RUNNER_OS" == "Windows" ]; then - dir - type .\\tests\\features\\output\\report.json - else - cat ./tests/features/output/report.json - fi uses: actions/upload-artifact@v4 with: name: report.json - path: output/report.json + path: ./tests/features/output/report.json From 6b5afe5d7f3f95cdb4e69e2306d1a83609d32c4d Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 6 Dec 2024 13:20:58 -0300 Subject: [PATCH 51/53] Adding report.json as a github actions artifact --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index c65b59c..dea1151 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -49,4 +49,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: report.json - path: ./tests/features/output/report.json + path: /home/runner/work/behavex/behavex/output/report.json From 4e9722387fb8d2f5b08d61cfcdb8d53aa962d7cb Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 6 Dec 2024 13:38:03 -0300 Subject: [PATCH 52/53] Adding report.json as a github actions artifact --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index dea1151..67fbd4f 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -48,5 +48,5 @@ jobs: - name: Set report.json to be available as an artifact uses: actions/upload-artifact@v4 with: - name: report.json + name: report-${{ matrix.os }}-py${{ matrix.python-version }} path: /home/runner/work/behavex/behavex/output/report.json From 8808c6088fdf4e84a9d8b9e08ae9fc5b1d5c7f0e Mon Sep 17 00:00:00 2001 From: anibalinn Date: Fri, 6 Dec 2024 15:44:30 -0300 Subject: [PATCH 53/53] final release --- .github/workflows/python-package.yml | 2 +- pyproject.toml | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 67fbd4f..9ba34cc 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -32,7 +32,7 @@ jobs: run: | python -m pip install --upgrade pip python setup.py sdist - pip install dist/behavex-4.0.9rc7.tar.gz + pip install dist/behavex-4.0.9.tar.gz - name: Verify behavex command shell: bash diff --git a/pyproject.toml b/pyproject.toml index f366961..66687b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "behavex" -version = "4.0.9rc7" +version = "4.0.9" description = "Agile testing framework on top of Behave (BDD)." readme = "README.md" license = { text = "MIT" } diff --git a/setup.py b/setup.py index 0a725b7..abe51ad 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='behavex', - version='4.0.9rc7', + version='4.0.9', license="MIT", platforms=['any'], python_requires='>=3.5',