Skip to content

Commit

Permalink
Merge pull request #170 from hrcorval/release_4.0.9
Browse files Browse the repository at this point in the history
Release 4.0.9
  • Loading branch information
hrcorval authored Dec 6, 2024
2 parents e171929 + 8808c60 commit 3a782e4
Show file tree
Hide file tree
Showing 23 changed files with 484 additions and 133 deletions.
32 changes: 27 additions & 5 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
name: Test behavex in latest python versions


on: [push, pull_request]

jobs:
test-behavex:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}

env:
PYTHONIOENCODING: utf-8
PYTHONLEGACYWINDOWSSTDIO: utf-8

strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']

steps:
- name: Checkout repository
Expand All @@ -20,11 +24,29 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- name: Install setuptools
run: |
python -m pip install 'setuptools>=61'
- name: Install behavex
run: |
python -m pip install --upgrade pip
python setup.py sdist
pip install ./dist/*.tar.gz
pip install dist/behavex-4.0.9.tar.gz
- name: Verify behavex command
run: behavex ./tests/features/*.feature
shell: bash
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
dir
behavex .\\tests\\features\\*.feature
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
uses: actions/upload-artifact@v4
with:
name: report-${{ matrix.os }}-py${{ matrix.python-version }}
path: /home/runner/work/behavex/behavex/output/report.json
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ repos:
hooks:
- id: bandit
exclude: tests
args: [--skip, "B322"] # ignore assert_used
args: [--skip, "B322,B110,B605,B607"] # ignore assert_used and B110

- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.6.4
Expand Down
15 changes: 15 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
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 (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:

* 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
-------------------------------------------------------------------------------
ENHANCEMENTS:
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
include LICENSE
include README.md
recursive-include behavex *.*
prune behavex\tests/
prune behavex\tests
6 changes: 3 additions & 3 deletions behavex/outputs/report_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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)
)

Expand Down Expand Up @@ -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):
Expand Down
17 changes: 10 additions & 7 deletions behavex/outputs/report_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
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,
match_for_execution, text)
from behavex.utils import (generate_hash, get_scenario_tags,
try_operate_descriptor)
from behavex.utils import (generate_hash, generate_uuid, get_scenario_tags,
retry_file_operation)


def add_step_info(step, parent_node):
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -150,13 +150,17 @@ 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
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
error_msg, error_lines, error_step, error_background = _get_error_scenario(
scenario
)
# pylint: disable=W0123
scenario_tags = get_scenario_tags(scenario)
if match_for_execution(scenario_tags):
# Scenario was selectable
scenario_info = {}
Expand All @@ -181,8 +185,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
Expand Down
24 changes: 20 additions & 4 deletions behavex/outputs/report_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
)

Expand Down Expand Up @@ -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)
6 changes: 3 additions & 3 deletions behavex/outputs/report_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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)
)

Expand Down
27 changes: 22 additions & 5 deletions behavex/progress_bar.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 3a782e4

Please sign in to comment.