Skip to content

Commit

Permalink
Merge pull request #323 from reportportal/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
HardNorth authored Nov 30, 2022
2 parents 156ec50 + d43d35b commit fb09cc8
Show file tree
Hide file tree
Showing 16 changed files with 248 additions and 52 deletions.
41 changes: 25 additions & 16 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
# Copyright 2022 EPAM Systems
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Release Pytest agent

on:
push:
branches: ['master']
branches: [ 'master' ]
paths-ignore:
- '.github/**'
- CHANGELOG.md
Expand All @@ -20,19 +33,19 @@ env:

jobs:
deploy:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Generate versions
uses: HardNorth/github-version-generate@v1.1.2
uses: HardNorth/github-version-generate@v1
with:
version-source: file
version-file: ${{ env.VERSION_FILE }}
version-file-extraction-pattern: ${{ env.VERSION_EXTRACT_PATTERN }}

- name: Setup git credentials
uses: oleksiyrudenko/gha-git-credentials@v2
uses: oleksiyrudenko/gha-git-credentials@v2.1.1
with:
name: 'reportportal.io'
email: '[email protected]'
Expand All @@ -45,7 +58,7 @@ jobs:
git push --tags
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: '3.6'

Expand All @@ -62,7 +75,7 @@ jobs:
password: ${{ secrets.PYPI_PASSWORD }}

- name: Checkout develop branch
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: 'develop'
fetch-depth: 0
Expand All @@ -82,22 +95,18 @@ jobs:
- name: Read changelog Entry
id: readChangelogEntry
uses: mindsers/changelog-reader-action@v1.3.1
uses: mindsers/changelog-reader-action@v2
with:
version: ${{ env.RELEASE_VERSION }}
path: ./${{ env.CHANGE_LOG_FILE }}

- name: Create Release
id: createRelease
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: ncipollo/release-action@v1
with:
tag_name: ${{ env.RELEASE_VERSION }}
release_name: Release ${{ env.RELEASE_VERSION }}
body: ${{ steps.readChangelogEntry.outputs.log_entry }}
draft: false
prerelease: false
tag: ${{ env.RELEASE_VERSION }}
name: Release ${{ env.RELEASE_VERSION }}
body: ${{ steps.readChangelogEntry.outputs.changes }}

- name: Merge release branch into develop
id: mergeIntoDevelop
Expand Down
61 changes: 39 additions & 22 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
# Copyright 2022 EPAM Systems
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Tests

on: [push, pull_request]
on: [ push, pull_request ]

jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: [2.7, 3.6, 3.7, 3.8, 3.9, '3.10']
python-version: [ '2.7', '3.6', '3.7', '3.8', '3.9', '3.10' ]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions
- name: Test with tox
run: tox
- name: Upload coverage to Codecov
if: matrix.python-version == 3.6 && success()
uses: codecov/codecov-action@v1
with:
files: coverage.xml
flags: unittests
name: codecov-client-reportportal
path_to_write_report: codecov_report.txt
- name: Checkout repository
uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions
- name: Test with tox
run: tox

- name: Upload coverage to Codecov
if: matrix.python-version == 3.6 && success()
uses: codecov/codecov-action@v3
with:
files: coverage.xml
flags: unittests
name: codecov-client-reportportal
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ repos:
rev: v1.0.1
hooks:
- id: rst-linter
- repo: https://gitlab.com/pycqa/flake8.git
rev: 3.9.0
- repo: https://github.com/pycqa/flake8
rev: 5.0.4
hooks:
- id: flake8
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]
### Added
- Support for thread logs and `rp_thread_logging` flag, by @dagansandler

## [5.1.2]
### Added
- `rp_log_batch_payload_size` parameter, by @HardNorth
### Changed
- Feature [#311](https://github.com/reportportal/agent-python-pytest/issues/311):
Expand Down
9 changes: 5 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Pytest plugin for reporting test results of the Pytest to the Reportal Portal.
* Contribution
* Examples
* Launching
* Send attachement (screenshots)
* Send attachment (screenshots)
* Troubleshooting
* Integration with GA
* Copyright Notice
Expand Down Expand Up @@ -108,11 +108,12 @@ The following parameters are optional:
- :code:`rp_hierarchy_dirs_level = 0` - Directory starting hierarchy level (from pytest.ini level) (default `0`)
- :code:`rp_hierarchy_dirs = True` - Enables hierarchy for tests directories, default `False`. Doesn't support 'xdist' plugin.
- :code:`rp_hierarchy_dir_path_separator` - Path separator to display directories in test hierarchy. In case of empty value current system path separator will be used (os.path.sep)
- :code:`rp_hierarchy_code` - Enables hierarchy for inner classes and parametrized tests, default `False`
- :code:`rp_hierarchy_code` - Enables hierarchy for inner classes and parametrized tests, default `False`. Doesn't support 'xdist' plugin.
- :code:`rp_issue_system_url = https://bugzilla.some.com/show_bug.cgi?id={issue_id}` - issue URL (issue_id will be filled by parameter from pytest mark)
- :code:`rp_issue_id_marks = True` - Enables adding marks for issue ids (e.g. "issue:123456")
- :code:`rp_verify_ssl = True` - Verify SSL when connecting to the server
- :code:`rp_mode = DEFAULT` - DEBUG or DEFAULT launch mode. DEBUG launches are displayed in a separate tab and not visible to anyone except owner
- :code:`rp_thread_logging` - EXPERIMENTAL - Enables support for reporting logs from threads by patching the builtin Thread class. Use with caution.


If you like to override the above parameters from command line, or from CI environment based on your build, then pass
Expand Down Expand Up @@ -223,10 +224,10 @@ Example:
assert False
Send attachement (screenshots)
Send attachment (screenshots)
------------------------------

https://github.com/reportportal/client-Python#send-attachement-screenshots
https://github.com/reportportal/client-Python#send-attachment-screenshots

Test internal steps, aka "Nested steps"
---------------------------------------
Expand Down
Empty file added examples/threads/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions examples/threads/test_thread_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import logging
import threading

from reportportal_client.steps import step

log = logging.getLogger(__name__)


def worker():
log.info("TEST_INFO")
log.debug("TEST_DEBUG")


def test_log():
t = threading.Thread(target=worker)
log.info("TEST_BEFORE_THREADING")
with step("Some nesting where the thread logs should go"):
t.start()
t.join()
3 changes: 3 additions & 0 deletions pytest_reportportal/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ def __init__(self, pytest_config):
self.rp_log_level = get_actual_log_level(pytest_config, 'rp_log_level')
self.rp_log_format = self.find_option(pytest_config, 'rp_log_format')
self.rp_mode = self.find_option(pytest_config, 'rp_mode')
self.rp_thread_logging = self.find_option(
pytest_config, 'rp_thread_logging'
)
self.rp_parent_item_id = self.find_option(pytest_config,
'rp_parent_item_id')
self.rp_project = self.find_option(pytest_config,
Expand Down
28 changes: 27 additions & 1 deletion pytest_reportportal/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from pytest_reportportal import LAUNCH_WAIT_TIMEOUT
from .config import AgentConfig
from .rp_logging import patching_logger_class
from .rp_logging import patching_logger_class, patching_thread_class
from .service import PyTestServiceClass

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -204,6 +204,24 @@ def pytest_configure(config):
config.workerinput['py_test_service'])


@pytest.hookimpl(hookwrapper=True)
def pytest_runtestloop(session):
"""
Control start and finish of all test items in the session.
:param session: pytest.Session
:return: generator object
"""
config = session.config
if not config._rp_enabled:
yield
return

agent_config = config._reporter_config
with patching_thread_class(agent_config):
yield


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_protocol(item):
"""
Expand Down Expand Up @@ -341,6 +359,14 @@ def add_shared_option(name, help, default=None, action='store'):
help='Visibility of current launch [DEFAULT, DEBUG]',
default='DEFAULT'
)
add_shared_option(
name='rp_thread_logging',
help='EXPERIMENTAL: Report logs from threads. '
'This option applies a patch to the builtin Thread class, '
'and so it is turned off by default. Use with caution.',
default=False,
action='store_true'
)

parser.addini(
'rp_launch_attributes',
Expand Down
83 changes: 83 additions & 0 deletions pytest_reportportal/rp_logging.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,94 @@
"""RPLogger class for low-level logging in tests."""

import logging
import threading
from contextlib import contextmanager
from functools import wraps

from reportportal_client.client import RPClient

from reportportal_client._local import current, set_current
from reportportal_client import RPLogger


@contextmanager
def patching_thread_class(config):
"""
Add patch for Thread class.
Set the parent thread client as the child thread's local client
"""
if not config.rp_thread_logging:
# Do nothing
yield
else:
original_start = threading.Thread.start
original_run = threading.Thread.run
try:
def wrap_start(original_func):
@wraps(original_func)
def _start(self, *args, **kwargs):
"""Save the invoking thread's client if there is one."""
# Prevent an endless loop of workers being spawned
if "_monitor" not in self.name:
current_client = current()
self.parent_rp_client = current_client
return original_func(self, *args, **kwargs)

return _start

def wrap_run(original_func):
@wraps(original_func)
def _run(self, *args, **kwargs):
"""Create a new client for the invoked thread."""
client = None
if (
hasattr(self, "parent_rp_client")
and self.parent_rp_client
and not current()
):
parent = self.parent_rp_client
client = RPClient(
endpoint=parent.endpoint,
project=parent.project,
token=parent.token,
log_batch_size=parent.log_batch_size,
is_skipped_an_issue=parent.is_skipped_an_issue,
verify_ssl=parent.verify_ssl,
retries=config.rp_retries,
launch_id=parent.launch_id
)
if parent.current_item():
client._item_stack.append(
parent.current_item()
)
client.start()
try:
return original_func(self, *args, **kwargs)
finally:
if client:
# Stop the client and remove any references
client.terminate()
self.parent_rp_client = None
del self.parent_rp_client
set_current(None)

return _run

if not hasattr(threading.Thread, "patched"):
# patch
threading.Thread.patched = True
threading.Thread.start = wrap_start(original_start)
threading.Thread.run = wrap_run(original_run)
yield

finally:
if hasattr(threading.Thread, "patched"):
threading.Thread.start = original_start
threading.Thread.run = original_run
del threading.Thread.patched


@contextmanager
def patching_logger_class():
"""
Expand Down
Loading

0 comments on commit fb09cc8

Please sign in to comment.