Skip to content

feat(integration-tests): Clean up logging and error reporting in integration tests.#1802

Open
quinntaylormitchell wants to merge 15 commits intoy-scope:mainfrom
quinntaylormitchell:integration-test-logging-overhaul
Open

feat(integration-tests): Clean up logging and error reporting in integration tests.#1802
quinntaylormitchell wants to merge 15 commits intoy-scope:mainfrom
quinntaylormitchell:integration-test-logging-overhaul

Conversation

@quinntaylormitchell
Copy link
Collaborator

@quinntaylormitchell quinntaylormitchell commented Dec 18, 2025

Description

This PR modifies integration test run output. It provides a minimal amount of information via the command line, and stores more verbose information (including the output of all commands that were run by subprocess.run(), i.e. start-clp.sh, stop-clp.sh) in a log file that is per-test unique.

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

Ran uv run pytest -m 'package'; all tests pass and information is reported accurately.


Summary by CodeRabbit

  • Tests

    • Enhanced test logging infrastructure with per-run log files for improved debugging and error context.
    • Improved test output formatting with colour-coded test names for better readability.
    • Updated test reporting to include comprehensive log file paths and execution details.
  • Chores

    • Refined pytest configuration for better output formatting and logging.
    • Streamlined test utilities to provide consistent error messaging across test suites.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 18, 2025

Walkthrough

This PR introduces comprehensive logging infrastructure to the integration-tests framework, including per-run log file management, enhanced test output formatting, and updated pytest configuration. Functions across test utilities and fixtures now accept a pytest.FixtureRequest parameter to enable logging. The run_and_assert function is replaced with run_and_log_to_file to route command output to log files instead of direct assertions.

Changes

Cohort / File(s) Summary
Pytest Configuration
.pytest.ini, pyproject.toml
Updated pytest addopts to replace --capture=no, -rA, --verbose with --show-capture=no and --tb=short; added file logging configuration with millisecond timestamps and detailed source info; added new test markers (clp_s, clp_text, compression, core, package, search, startup); added Ruff lint ignore rule for TRY400.
Test Framework Setup
conftest.py
Added per-run log file setup with timestamp-based test_run_id; implemented three new pytest hooks (pytest_itemcollected, pytest_report_header, pytest_runtest_setup) for test name colorization, header reporting, and runtime log path configuration.
Logging Infrastructure
logging_utils.py
New module introducing ANSI color constants (BLUE, BOLD, RESET) and construct_log_err_msg() helper function to standardize error message formatting with reference to full test output logs.
Test Fixtures
fixtures/package_instance.py, fixtures/package_test_config.py
Extended fixture signatures to accept pytest.FixtureRequest parameter; added module-level loggers and lifecycle event logging (start/stop messages); updated control flow to rely on exception propagation rather than explicit pytest.fail blocks.
Test Utilities
utils/asserting_utils.py, utils/package_utils.py, utils/port_utils.py, utils/config.py
Added logging support throughout; renamed run_and_assert() to run_and_log_to_file() that writes command output to log files instead of returning CompletedProcess; updated function signatures to accept pytest.FixtureRequest; enhanced error reporting with construct_log_err_msg() helper.
Test Files
package_tests/clp_json/test_clp_json.py, package_tests/clp_text/test_clp_text.py, test_identity_transformation.py
Removed unused logging setup; updated pytest.mark.parametrize with ids argument for clearer test identification; replaced inline log messages with centralized logging; updated function signatures to pass pytest.FixtureRequest through test helper calls.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PR #1843: Modifies the same integration-tests modules (conftest.py, fixtures, utilities) and updates fixture/config types alongside logging enhancements.
  • PR #1437: Updates the same integration-tests fixtures and utilities, particularly replacing run_and_assert with run_and_log_to_file and modifying package lifecycle helpers.

Suggested reviewers

  • junhaoliao
  • kirkrodrigues
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of this changeset: restructuring logging and error handling throughout integration tests to reduce console noise while capturing details in per-test log files.
Docstring Coverage ✅ Passed Docstring coverage is 95.83% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Ruff (0.14.14)
integration-tests/tests/conftest.py

�[1;31mruff failed�[0m
�[1mCause:�[0m Failed to load extended configuration /tools/yscope-dev-utils/exports/lint-configs/python/ruff.toml (/integration-tests/pyproject.toml extends /tools/yscope-dev-utils/exports/lint-configs/python/ruff.toml)
�[1mCause:�[0m Failed to read /tools/yscope-dev-utils/exports/lint-configs/python/ruff.toml
�[1mCause:�[0m No such file or directory (os error 2)

integration-tests/tests/test_identity_transformation.py

�[1;31mruff failed�[0m
�[1mCause:�[0m Failed to load extended configuration /tools/yscope-dev-utils/exports/lint-configs/python/ruff.toml (/integration-tests/pyproject.toml extends /tools/yscope-dev-utils/exports/lint-configs/python/ruff.toml)
�[1mCause:�[0m Failed to read /tools/yscope-dev-utils/exports/lint-configs/python/ruff.toml
�[1mCause:�[0m No such file or directory (os error 2)

integration-tests/tests/utils/asserting_utils.py

�[1;31mruff failed�[0m
�[1mCause:�[0m Failed to load extended configuration /tools/yscope-dev-utils/exports/lint-configs/python/ruff.toml (/integration-tests/pyproject.toml extends /tools/yscope-dev-utils/exports/lint-configs/python/ruff.toml)
�[1mCause:�[0m Failed to read /tools/yscope-dev-utils/exports/lint-configs/python/ruff.toml
�[1mCause:�[0m No such file or directory (os error 2)

  • 2 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@junhaoliao junhaoliao self-requested a review December 18, 2025 20:25
Copy link
Member

@junhaoliao junhaoliao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with the understanding that this is a draft, i just took a quick look

.gitignore Outdated
.task/
build/
dist/
__* No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are we trying to exclude here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __pytest_logs directory that stores the log file for each test run.

That being said, right now the __pytest_logs directory gets created/stored under the clp/integration-tests directory. Do you think it might be better to have it under the build/integration-tests directory instead (used for other integration test stuff as well)? Then we wouldn't have to modify this file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup i agree we can move to build/

in general we want to be more explicit about what to ignore, so if we keep the folder in the current location, it would be better to just write __pytest_logs. That said, it really depends on which part of the paths we want to be fuzzy about. in general wildcards like path/*/subpath and *.log are fine in my opinion

log_cli_date_format = %Y-%m-%d %H:%M:%S,%f
log_cli_format = %(name)s %(asctime)s [%(levelname)s] %(message)s
log_cli_level = INFO
log_cli_format = %(levelname)-8s %(asctime)-25s %(message)s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason to change this? i thought we were trying to match references like

logging_formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only reason was aesthetics, cause this is the format of the logs that will be reported to the user via the command line. If there is some reason we want to match references like you describe above, I can change it; I just wasn't aware we were doing that


log_file_mode = a
log_file_level = DEBUG
log_file_format = %(levelname)-8s %(asctime)-30s [%(name)s:%(filename)s:%(lineno)d]: %(message)s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason to use a different format string between the cli and the log file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to provide more information to the dev; I don't want to crowd the cli output, especially for a successful (no failure) test run. I think it's safe to assume that the main reason a dev would be looking at the test log would be if something fails, and in the event of failure, they'll need more information.

log_cli_format = %(levelname)-8s %(asctime)-25s %(message)s
log_cli_date_format = %Y-%m-%d %H:%M:%S

log_file_mode = a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of appending to the same file, can isolate the logs from different test runs instead? e.g., use set_log_path with the default log_file_mode = w. see the test code at pytest-dev/pytest#4752 for example

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this isn't here, the test output file will contain all of the pytest logging messages followed by all of the output from the various subprocesses called during the test run, like this:

INFO 2025-01-06 event1
INFO 2025-01-06 event2
INFO 2025-01-06 event3

<output following event 1>
<output following event 2>
<output following event 3>

It seems better to me to have everything written to the log file in order, so that it looks like this:

INFO 2025-01-06 event1
<output following event 1>
INFO 2025-01-06 event2
<output following event 2>
INFO 2025-01-06 event3
<output following event 3>

The logs from different test runs are already isolated by the code in conftest.py.


def run_and_assert(cmd: list[str], **kwargs: Any) -> subprocess.CompletedProcess[Any]:
def run_and_assert(
request: pytest.FixtureRequest, cmd: list[str], **kwargs: Any
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update below docstring for the param

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean the request? if so, right on, thank you



def stop_clp_package(package_config: PackageConfig) -> None:
def stop_clp_package(request: pytest.FixtureRequest, package_config: PackageConfig) -> None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

param docstring for request

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right on, thanks


try:
run_and_assert(request, stop_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS)
except SubprocessError:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in what case would this be thrown here? i thought we would have caught this in run_and_assert? like also if there are other SubprocessError than CalledProcessError that might be thrown but uncaught in run_and_assert, why don't we modify run_and_assert to handle such in the first place?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's a good point. See my message that I sent you offline.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


try:
run_and_assert(request, start_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS)
except SubprocessError:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto -

in what case would this be thrown here? i thought we would have caught this in run_and_assert? like also if there are other SubprocessError than CalledProcessError that might be thrown but uncaught in run_and_assert, why don't we modify run_and_assert to handle such in the first place?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto my other comment -



def start_clp_package(package_config: PackageConfig) -> None:
def start_clp_package(request: pytest.FixtureRequest, package_config: PackageConfig) -> None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

param docstring for request

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right on, thanks

@quinntaylormitchell quinntaylormitchell requested review from junhaoliao and removed request for junhaoliao January 31, 2026 18:18
@quinntaylormitchell quinntaylormitchell changed the title feat(integration-tests): system-wide overhaul of logging and error reporting. feat(integration-tests): Clean up logging and error reporting in integration tests. Jan 31, 2026
@quinntaylormitchell quinntaylormitchell marked this pull request as ready for review January 31, 2026 18:21
@quinntaylormitchell quinntaylormitchell requested a review from a team as a code owner January 31, 2026 18:21
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@integration-tests/tests/conftest.py`:
- Around line 33-41: Change the parser.addini for "log_file_path" to use
type="string" (not "paths") and keep the default as the generated Path string;
update code that calls Path(config.getini("log_file_path")) to handle a string
return (no list) — look for uses of config.getini("log_file_path") in this file
and where the path is constructed. Also ensure the parent directory
"__pytest_logs" is created before any file write by adding a directory-creation
step (mkdir(parents=True, exist_ok=True)) at the point where the log file path
is first resolved/used (e.g., just after building log_file_path in this module
and before any write operations in asserting_utils.py).
- Around line 44-51: The current pytest_itemcollected hook overwrites
item._nodeid using item.name which loses module/class context; update the
assignment in pytest_itemcollected to use item.nodeid instead of item.name so
the prettified _nodeid preserves the full, globally unique identifier (i.e., set
item._nodeid = f"{BOLD}{BLUE}{item.nodeid}{RESET}" inside the
pytest_itemcollected function).

In `@integration-tests/tests/utils/logging_utils.py`:
- Line 5: Remove the unused GREEN constant from
integration-tests/tests/utils/logging_utils.py to eliminate dead code: locate
the symbol GREEN and delete its definition (the "\033[32m" assignment) or, if
it's intended for future use, add a clear explanatory comment above the GREEN
declaration indicating why it's kept; ensure no imports or references to GREEN
exist elsewhere before removal.

In `@integration-tests/tests/utils/package_utils.py`:
- Around line 38-44: The except block around run_and_assert should catch
OS-level launch failures and include the exception text: change the handler from
except SubprocessError: to except (SubprocessError, OSError) as e and include
str(e) in the error log and pytest.fail message so missing/non-executable
scripts are surfaced; update the logger.error call that uses
construct_log_err_msg(err_msg) to include the exception string (e.g.,
f"{err_msg} Error: {e}") and similarly append the exception text to the
pytest.fail call; relevant symbols: run_and_assert, SubprocessError,
package_test_config.mode_config.mode_name, construct_log_err_msg,
DEFAULT_CMD_TIMEOUT_SECONDS.

Comment on lines 33 to 41
# Sets up a unique log file for this test run, and stores the path to the file.
test_run_id = str(uuid.uuid4())[-4:]
log_file_path = Path("__pytest_logs") / f"testrun_{test_run_id}.log"
parser.addini(
"log_file_path",
help="Path to the log file for this test.",
type="paths",
default=log_file_path,
)
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "conftest.py" | head -20

Repository: y-scope/clp

Length of output: 92


🏁 Script executed:

ls -la integration-tests/tests/ | head -20

Repository: y-scope/clp

Length of output: 632


🏁 Script executed:

wc -l integration-tests/tests/conftest.py

Repository: y-scope/clp

Length of output: 93


🏁 Script executed:

cat -n integration-tests/tests/conftest.py

Repository: y-scope/clp

Length of output: 2848


🏁 Script executed:

rg "getini.*log_file_path" integration-tests/tests/

Repository: y-scope/clp

Length of output: 377


🏁 Script executed:

rg "type.*paths" integration-tests/ --type py

Repository: y-scope/clp

Length of output: 112


🌐 Web query:

pytest type="paths" addini behavior default value

💡 Result:

In pytest plugin code, when you register an ini option with:

parser.addini("myopt", "...", type="paths")

and the user does not set myopt in pytest.ini/pyproject.toml, then:

  • config.getini("myopt") returns the registered default if you passed default=... to addini. (docs.pytest.org)
  • If you did not pass default=..., then for type="paths" the implicit default is an empty list [] (same for pathlist, args, linelist). (docs.pytest.org)

Citations:


🏁 Script executed:

rg "__pytest_logs" integration-tests/

Repository: y-scope/clp

Length of output: 163


🏁 Script executed:

rg "log_file_path" integration-tests/ --type py -B 2 -A 2

Repository: y-scope/clp

Length of output: 2387


🏁 Script executed:

find integration-tests/ -name "pytest.ini" -o -name "pyproject.toml" | xargs cat 2>/dev/null

Repository: y-scope/clp

Length of output: 1325


Change log_file_path ini type to string and ensure parent directory exists.

The type="paths" option returns a list when configured by users, causing Path(config.getini(...)) to fail with TypeError in line 61, 78, and in asserting_utils.py. Additionally, the parent directory __pytest_logs must be created before the first file write in asserting_utils.py.

Proposed fix
-    log_file_path = Path("__pytest_logs") / f"testrun_{test_run_id}.log"
-    parser.addini(
-        "log_file_path",
-        help="Path to the log file for this test.",
-        type="paths",
-        default=log_file_path,
-    )
+    log_file_path = Path("__pytest_logs") / f"testrun_{test_run_id}.log"
+    log_file_path.parent.mkdir(parents=True, exist_ok=True)
+    parser.addini(
+        "log_file_path",
+        help="Path to the log file for this test.",
+        type="string",
+        default=str(log_file_path),
+    )
🤖 Prompt for AI Agents
In `@integration-tests/tests/conftest.py` around lines 33 - 41, Change the
parser.addini for "log_file_path" to use type="string" (not "paths") and keep
the default as the generated Path string; update code that calls
Path(config.getini("log_file_path")) to handle a string return (no list) — look
for uses of config.getini("log_file_path") in this file and where the path is
constructed. Also ensure the parent directory "__pytest_logs" is created before
any file write by adding a directory-creation step (mkdir(parents=True,
exist_ok=True)) at the point where the log file path is first resolved/used
(e.g., just after building log_file_path in this module and before any write
operations in asserting_utils.py).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i agree with the rabbit about the mkdir(parents=True) comment especially now we want to output the logs into build/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@junhaoliao, thank you for confirming! Yes, ensuring the parent directory is created with mkdir(parents=True, exist_ok=True) will prevent any FileNotFoundError when the log file is first written.

I understand you'd like the logs to go into the build/ directory instead of __pytest_logs/. That makes sense for keeping build artifacts organized. Just make sure to update the path accordingly when implementing the fix:

log_file_path = Path("build") / f"testrun_{test_run_id}.log"
log_file_path.parent.mkdir(parents=True, exist_ok=True)

Also remember to change the type="paths" to type="string" to ensure config.getini("log_file_path") returns a string rather than a list.

    /)_/)
   ( ^.^)  Glad we're on the same page!
   (")_(")

🧠 Learnings used
Learnt from: Bill-hbrhbr
Repo: y-scope/clp PR: 1100
File: integration-tests/tests/fixtures/integration_test_logs.py:54-56
Timestamp: 2025-08-17T16:10:38.722Z
Learning: For PR `#1100` (feat(integration-tests): Add CLP package integration tests boilerplate), do not raise cache weakness problems related to the pytest cache implementation in the integration test logs fixtures.

Learnt from: junhaoliao
Repo: y-scope/clp PR: 1152
File: components/clp-package-utils/clp_package_utils/general.py:0-0
Timestamp: 2025-08-19T14:41:28.901Z
Learning: In the CLP codebase, prefer explicit failures over automatic directory creation in utility functions like dump_config. The user junhaoliao prefers to let file operations fail when parent directories don't exist, as this helps catch implementation errors during development rather than masking setup issues with automatic directory creation.

Learnt from: haiqi96
Repo: y-scope/clp PR: 1100
File: integration-tests/tests/test_identity_transformation.py:87-95
Timestamp: 2025-08-08T21:15:10.905Z
Learning: In the CLP project's integration tests (Python code), variable names should use "logs" (plural) rather than "log" (singular) when referring to test logs or log-related entities, as this aligns with the naming conventions used throughout the codebase.

Learnt from: junhaoliao
Repo: y-scope/clp PR: 0
File: :0-0
Timestamp: 2025-11-17T22:58:50.056Z
Learning: In the y-scope/clp repository, when enabling new linting tools (ruff, mypy) on Python components, the team uses an incremental approach: first enable the tools with errors allowed (exit code 0), apply only safe auto-fixable fixes, then address remaining issues in follow-up PRs. During the initial enablement PR, reviews should focus on correctness of auto-fixes rather than flagging new code quality issues.

Learnt from: junhaoliao
Repo: y-scope/clp PR: 1405
File: components/clp-package-utils/pyproject.toml:5-15
Timestamp: 2025-10-13T03:24:35.074Z
Learning: In the y-scope/clp repository, the Python 3.9 to 3.10 version requirement change was intentionally deferred to a separate PR (after PR `#1405`) to reduce review effort, as decided in an offline discussion between junhaoliao and kirkrodrigues.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure that the existence of the parent directory will be handled by the logger; I've tried it from scratch and there is no FileNotFoundError when the log file is first written

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member

@junhaoliao junhaoliao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no major issue found except the log file writing concurrency issue. i originally proposed https://github.com/y-scope/clp/pull/1802/files#r2660585892 to solve the issue, but now i agree with the rationale for keeping all logs in the same file. However, the concurrency problem is still valid, please see if this proposal works - "would it be better to use just Python loggers to log the output of the sub processes"

.gitignore Outdated
.task/
build/
dist/
__* No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup i agree we can move to build/

in general we want to be more explicit about what to ignore, so if we keep the folder in the current location, it would be better to just write __pytest_logs. That said, it really depends on which part of the paths we want to be fuzzy about. in general wildcards like path/*/subpath and *.log are fine in my opinion

.gitignore Outdated
.task/
build/
dist/
__*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(some response to a previous discussion somehow refuses to be shown in the "Files changed" tab)

duplicating the comment here for visibility -

yup i agree we can move to build/

in general we want to be more explicit about what to ignore, so if we keep the folder in the current location, it would be better to just write __pytest_logs. That said, it really depends on which part of the paths we want to be fuzzy about. in general wildcards like path/*/subpath and *.log are fine in my opinion

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to build/

"log_file_path",
help="Path to the log file for this test.",
type="paths",
default=log_file_path,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.pytest.org/en/stable/reference/reference.html#pytest.Parser.addini:~:text=paths%3A%20a%20list%20of%20pathlib%2EPath%2C%20separated%20as%20in%20a%20shell

seems like with type="paths", the parser returns a list of Path objects when we actually specify any value in the config file?

shall we use [log_file_path] or change to type="string" which might make more sense?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll just store it as a string

Comment on lines 33 to 41
# Sets up a unique log file for this test run, and stores the path to the file.
test_run_id = str(uuid.uuid4())[-4:]
log_file_path = Path("__pytest_logs") / f"testrun_{test_run_id}.log"
parser.addini(
"log_file_path",
help="Path to the log file for this test.",
type="paths",
default=log_file_path,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i agree with the rabbit about the mkdir(parents=True) comment especially now we want to output the logs into build/

pytest.fail(f"Command timed out: {' '.join(cmd)}: {e}")
return proc
log_file_path = Path(request.config.getini("log_file_path"))
with log_file_path.open("ab") as log_file:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the pytest logging plugin writes to the same file handle, right? any concurrency concerns?

I understand the outputs are usually flushed only on newlines, but this feels a bit fragile.

would it be better to use just Python loggers to log the output of the sub processes once any content is available?

something like opening a (combined) stream then iterating line-by-line should work?

proc = subprocess.Popen(
    cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs
)
for line in proc.stdout:
    logger.debug("%s", line.decode(errors="replace").rstrip())
proc.wait()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, it might be worth using a prefix in the logs otherwise we easily get mixed contents from different commands if we ever call this multiple times concurrently.

e.g.
logger.debug("[%s] %s", cmd, line.decode...)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really have any concurrency concerns here, because pytest only runs sequentially (the only way to run it in parallel is to use plugins that we don't use). With that in mind, it seems to me like there is no way that the logging file would be accessed by multiple processes at the same time, and by the same token, there's no way that this would get called multiple times concurrently


def start_clp_package(package_test_config: PackageTestConfig) -> None:
def start_clp_package(
request: pytest.FixtureRequest, package_test_config: PackageTestConfig
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing docstring

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right on


def stop_clp_package(package_test_config: PackageTestConfig) -> None:
def stop_clp_package(
request: pytest.FixtureRequest, package_test_config: PackageTestConfig
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing docstring

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right on

RESET = "\033[0m"


def construct_log_err_msg(err_msg: str) -> str:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing param / return dosctring

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank u


:param item:
"""
item._nodeid = f"{BOLD}{BLUE}{item.name}{RESET}" # noqa: SLF001
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a heads up -

The ANSI constants are terminal (print("...")) formatting concerns, not logging (logger.info("...")) concerns.

We usually don't want to mix them together, though here the complexity is fine to be tolerated as i'm fine keeping it if you think it helps visualization. Normally, if we want facier colorful outputs, we should look into creating a custom LoggerAdapter like explained by https://docs.python.org/3/howto/logging-cookbook.html

(anyways, don't bother until it becomes more complex like we don't only add colorful prefixes but also colorful postfixes, lol

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, I'll keep that in mind

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
integration-tests/tests/utils/package_utils.py (2)

18-45: 🧹 Nitpick | 🔵 Trivial

Include exception details in error message for easier debugging.

The exception handler catches SubprocessError and OSError but discards the exception details. Including the exception text would help identify the root cause (e.g., missing script, permission denied, non-zero exit code) without requiring users to dig through log files.

Proposed fix
     try:
         run_and_log_to_file(request, start_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS)
-    except (SubprocessError, OSError):
+    except (SubprocessError, OSError) as err:
         mode_name = package_test_config.mode_config.mode_name
-        err_msg = f"The '{mode_name}' package failed to start."
+        err_msg = f"The '{mode_name}' package failed to start: {err}"
         logger.error(construct_log_err_msg(err_msg))
         pytest.fail(err_msg)

48-75: 🧹 Nitpick | 🔵 Trivial

Same suggestion: include exception details in stop error message.

Apply the same pattern to the stop function for consistency.

Proposed fix
     try:
         run_and_log_to_file(request, stop_cmd, timeout=DEFAULT_CMD_TIMEOUT_SECONDS)
-    except (SubprocessError, OSError):
+    except (SubprocessError, OSError) as err:
         mode_name = package_test_config.mode_config.mode_name
-        err_msg = f"The '{mode_name}' package failed to stop."
+        err_msg = f"The '{mode_name}' package failed to stop: {err}"
         logger.error(construct_log_err_msg(err_msg))
         pytest.fail(err_msg)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants