Skip to content

Commit

Permalink
Disable parts in interactive mode, and add http to displayed interact…
Browse files Browse the repository at this point in the history
…ive url (#1003)

* Disable Multitest parts in interactive mode

- this was a regression from a previous refactoring

* Make interactive and ui urls looks the same

- also add htttp scheme before url
- remove old now not used method

---------

Co-authored-by: Krisztian Notaisz <[email protected]>
Co-authored-by: M6AI <[email protected]>
  • Loading branch information
3 people authored Oct 10, 2023
1 parent 3bb95a7 commit bb1228d
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 70 deletions.
1 change: 1 addition & 0 deletions doc/newsfragments/2751_change.no_parts_in_interactive.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Parts are disabled in interactive mode to fix a recent regression on this.
60 changes: 2 additions & 58 deletions testplan/common/utils/networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,61 +14,5 @@ def port_to_int(port):
return socket.getservbyname(port)


def format_access_urls(host, port, url_path, ssl=False):
"""
Format a string to log to give HTTP access to a URL endpoint listening
on a particular host/port. Handles special 0.0.0.0 IP and formats IPV6
addresses. The returned string is indented by 4 spaces.
:param host: hostname or IP address
:type host: ``str``
:param port: port number
:type port: ``int``
:param url_path: path to format after host:port part of URL. A leading /
will be inserted if there isn't already one present.
:type url_path: ``str``
:return: a formatted string containing the connection URL(s)
:rtype: ``str``
"""
# Strip any whitespace and insert a leading / to paths if required.
url_path = url_path.strip()
if not url_path.startswith("/"):
url_path = "/" + url_path

scheme = "https" if ssl else "http"

# Handle 0.0.0.0 as a special case: this means that the URL can be accessed
# both via localhost or on any IP address this host owns.
if host == "0.0.0.0":
local_url = "{scheme}://localhost:{port}{path}".format(
scheme=scheme, port=port, path=url_path
)

try:
local_ip = socket.gethostbyname(socket.getfqdn())
network_url = "{scheme}://{host}:{port}{path}".format(
scheme=scheme, host=local_ip, port=port, path=url_path
)

return (
" Local: {local}\n"
" On Your Network: {network}".format(
local=local_url, network=network_url
)
)
except socket.gaierror:
return " {}".format(local_url)
else:
# Check for an IPv6 address. Web browsers require IPv6 addresses
# to be enclosed in [].
try:
if ipaddress.ip_address(host).version == 6:
host = "[{}]".format(host)
except ValueError:
# Expected if the host is a host name instead of an IP address.
pass

url = "{scheme}://{host}:{port}{path}".format(
scheme=scheme, host=host, port=port, path=url_path
)
return " {}".format(url)
def get_hostname_access_url(port, url_path):
return f"http://{socket.getfqdn()}:{port}{url_path}"
19 changes: 14 additions & 5 deletions testplan/runnable/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ def __init__(self, **options):
# Before saving test report, recursively generate unique strings in
# uuid4 format as report uid instead of original one. Skip this step
# when executing unit/functional tests or running in interactive mode.
self._reset_report_uid = self.cfg.interactive_port is None
self._reset_report_uid = not self._is_interactive_run()
self.scheduled_modules = [] # For interactive reload
self.remote_services = {}
self.runid_filename = uuid.uuid4().hex
Expand Down Expand Up @@ -455,7 +455,7 @@ def get_default_exporters(self):
test_exporters.WebServerExporter(ui_port=self.cfg.ui_port)
)
if (
self.cfg.interactive_port is None
not self._is_interactive_run()
and self.cfg.tracing_tests is not None
):
exporters.append(test_exporters.CoveredTestsExporter())
Expand Down Expand Up @@ -689,6 +689,12 @@ def discover(
**task_target_info.task_kwargs,
)

multitest_parts = (
None
if self._is_interactive_run()
else task_target_info.multitest_parts
)

if task_target_info.target_params:
for param in task_target_info.target_params:
if isinstance(param, dict):
Expand All @@ -707,15 +713,15 @@ def discover(
tasks.extend(
self._get_tasks(
task_arguments,
task_target_info.multitest_parts,
multitest_parts,
runtime_data,
)
)
else:
tasks.extend(
self._get_tasks(
task_arguments,
task_target_info.multitest_parts,
multitest_parts,
runtime_data,
)
)
Expand Down Expand Up @@ -860,7 +866,7 @@ def add(
self.cfg.test_lister.log_test_info(task_info.materialized_test)
return None

if self.cfg.interactive_port is not None:
if self._is_interactive_run():
self._register_task_for_interactive(task_info)
# for interactive always use the local runner
resource = local_runner
Expand All @@ -875,6 +881,9 @@ def add(
)
return uid

def _is_interactive_run(self):
return self.cfg.interactive_port is not None

def _register_task(self, resource, target, uid, metadata):
self._tests[uid] = resource
self._test_metadata.append(metadata)
Expand Down
6 changes: 3 additions & 3 deletions testplan/runnable/interactive/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from testplan.common import config, entity
from testplan.common.report import Report
from testplan.common.utils.networking import get_hostname_access_url
from testplan.runnable.interactive import http, reloader, resource_loader
from testplan.report import (
TestReport,
Expand Down Expand Up @@ -859,9 +860,8 @@ def _display_connection_info(self):
)

self.logger.user_info(
"\nInteractive Testplan web UI is running. Access it at: %s:%s/interactive",
socket.getfqdn(),
str(port),
"\nInteractive Testplan web UI is running. Access it at: %s",
get_hostname_access_url(port, "/interactive"),
)

def _initial_report(self):
Expand Down
6 changes: 3 additions & 3 deletions testplan/web_ui/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from testplan import defaults
from testplan.common.utils.thread import interruptible_join
from testplan.common.utils.timing import wait
from testplan.common.utils.networking import format_access_urls
from testplan.common.utils.networking import get_hostname_access_url
from testplan.common.utils.logger import TESTPLAN_LOGGER

from .web_app import WebServer, TESTPLAN_UI_STATIC_DIR
Expand Down Expand Up @@ -91,8 +91,8 @@ def display(self):
self._report_url = f"http://localhost:{port}/testplan/local"

TESTPLAN_LOGGER.user_info(
"View the JSON report in the browser:\n%s",
format_access_urls(host, port, "/testplan/local"),
"View the JSON report in the browser: %s",
get_hostname_access_url(port, "/testplan/local"),
)

def wait_for_kb_interrupt(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@

_TIMEOUT = 120
_REQUEST_TIMEOUT = 0.5
_URL_RE = re.compile(r"^\s*Local: (?P<url>[^\s]+)\s*$")
_URL_RE = re.compile(
r"^View the JSON report in the browser: (?P<url>[^\s]+)\s*$"
)


@pytest.yield_fixture(
Expand Down
31 changes: 31 additions & 0 deletions tests/functional/testplan/runners/pools/test_auto_part.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import tempfile
from pathlib import Path

import pytest

Expand Down Expand Up @@ -38,6 +39,36 @@ def test_auto_parts_discover():
assert pool.size == 2


def test_auto_parts_discover_interactive(runpath):
mockplan = TestplanMock(
"plan",
runpath=runpath,
merge_scheduled_parts=True,
auto_part_runtime_limit=45,
plan_runtime_target=200,
interactive_port=0,
runtime_data={
"Proj1-suite": {
"execution_time": 199.99,
"setup_time": 5,
}
},
)
pool = ProcessPool(name="MyPool", size="auto")
mockplan.add_resource(pool)
current_folder = Path(__file__).resolve().parent
mockplan.schedule_all(
path=current_folder / "discover_tasks",
name_pattern=r".*auto_parts_tasks\.py$",
resource="MyPool",
)

local_pool = mockplan.resources.get(mockplan.resources.first())
# validate that only on etask added to the local pool without split
assert len(pool.added_items) == 0
assert len(local_pool.added_items) == 1


def test_auto_weight_discover():
with tempfile.TemporaryDirectory() as runpath:
mockplan = TestplanMock(
Expand Down

0 comments on commit bb1228d

Please sign in to comment.