Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
735 changes: 735 additions & 0 deletions feature_integration_tests/LIFECYCLE_TESTS_SUMMARY.md

Large diffs are not rendered by default.

69 changes: 68 additions & 1 deletion feature_integration_tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ This directory contains Feature Integration Tests for the S-CORE project. It inc
- `test_ssh.py` — SSH connectivity tests
- `configs/` — Configuration files for ITF execution (DLT, QEMU bridge, etc.)

## Lifecycle FIT Summary

Lifecycle Feature Integration Tests validate end-to-end integration patterns for the S-CORE lifecycle stack across Rust and C++ scenarios.

- Coverage: 85/92 lifecycle requirements (92%)
- Modes:
- API integration mode (no running daemon required)
- Daemon integration mode (real Launch Manager behavior)
- Main validated areas:
- Process launching and dependency ordering (sequential/parallel)
- Conditional launching and run targets
- Process security/resources/termination
- Monitoring, recovery, control interface, logging, and configuration handling

For full lifecycle requirement mapping and detailed rationale, see `feature_integration_tests/LIFECYCLE_TESTS_SUMMARY.md`.

## Running Tests

### Python Test Cases (scenario-based FIT)
Expand All @@ -32,10 +48,61 @@ bazel test --config=linux-x86_64 //feature_integration_tests/test_cases:fit
To run specific test suites:

```sh
bazel test //feature_integration_tests/test_cases:fit_rust
bazel test --config=linux-x86_64 //feature_integration_tests/test_cases:fit_rust
bazel test --config=linux-x86_64 //feature_integration_tests/test_cases:fit_cpp
```

To run daemon integration test suites:

```sh
bazel test --config=linux-x86_64 //feature_integration_tests/test_cases:fit_daemon_rust
bazel test --config=linux-x86_64 //feature_integration_tests/test_cases:fit_daemon_cpp
```

To run lifecycle-focused tests only:

```sh
bazel test --config=linux-x86_64 //feature_integration_tests/test_cases:fit_rust --test_filter="*lifecycle*"
bazel test --config=linux-x86_64 //feature_integration_tests/test_cases:fit_cpp --test_filter="*lifecycle*"
```

Pytest direct local runs are also supported:

```sh
# All lifecycle tests (rust + cpp)
python3 -m pytest feature_integration_tests/test_cases/tests/lifecycle/ -q -v

# Rust only
python3 -m pytest feature_integration_tests/test_cases/tests/lifecycle/ -q -v -m rust

# C++ only
python3 -m pytest feature_integration_tests/test_cases/tests/lifecycle/ -q -v -m cpp
```

To build scenario executables from pytest before running tests:

```sh
# Default Bazel config: linux-x86_64
python3 -m pytest feature_integration_tests/test_cases/tests/lifecycle/ \
--build-scenarios \
--build-scenarios-timeout=600 \
-q -v

# Explicit Bazel config
python3 -m pytest feature_integration_tests/test_cases/tests/lifecycle/ \
--build-scenarios \
--bazel-config=linux-x86_64 \
--build-scenarios-timeout=600 \
-q -v

# Or via environment override
FIT_BAZEL_CONFIG=linux-x86_64 \
python3 -m pytest feature_integration_tests/test_cases/tests/lifecycle/ \
--build-scenarios \
--build-scenarios-timeout=600 \
-q -v
```

### ITF Tests (QEMU-based)

ITF tests run on a QEMU target and require the `itf-qnx-x86_64` config:
Expand Down
Empty file.
71 changes: 71 additions & 0 deletions feature_integration_tests/test_cases/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# *******************************************************************************
load("@pip_score_venv_test//:requirements.bzl", "all_requirements")
load("@rules_python//python:pip.bzl", "compile_pip_requirements")
load("@score_lifecycle_health//:defs.bzl", "launch_manager_config")
load("@score_tooling//python_basics:defs.bzl", "score_py_pytest")

# In order to update the requirements, change the `requirements.txt` file and run:
Expand All @@ -34,6 +35,11 @@ compile_pip_requirements(
],
)

launch_manager_config(
name = "daemon_lifecycle_configs",
config = "//feature_integration_tests/test_cases/configs:daemon_launch_manager_config.json",
)

# Tests targets
score_py_pytest(
name = "fit_rust",
Expand All @@ -45,7 +51,9 @@ score_py_pytest(
],
data = [
"conftest.py",
"daemon_helpers.py",
"fit_scenario.py",
"lifecycle_scenario.py",
"persistency_scenario.py",
"test_properties.py",
"//feature_integration_tests/test_scenarios/rust:rust_test_scenarios",
Expand All @@ -67,10 +75,65 @@ score_py_pytest(
],
data = [
"conftest.py",
"daemon_helpers.py",
"fit_scenario.py",
"lifecycle_scenario.py",
"persistency_scenario.py",
"test_properties.py",
"//feature_integration_tests/test_scenarios/cpp:cpp_test_scenarios",
],
pytest_config = "//:pyproject.toml",
deps = all_requirements,
)

# Daemon integration tests (Rust)
score_py_pytest(
name = "fit_daemon_rust",
srcs = glob(["tests/**/*.py"]),
args = [
"-m rust",
"--traces=all",
"--rust-target-path=$(rootpath //feature_integration_tests/test_scenarios/rust:rust_test_scenarios)",
"-k with_daemon",
],
data = [
"conftest.py",
"daemon_helpers.py",
"fit_scenario.py",
"lifecycle_scenario.py",
"persistency_scenario.py",
"test_properties.py",
"//feature_integration_tests/test_scenarios/rust:rust_test_scenarios",
"@score_lifecycle_health//examples/rust_supervised_app",
"@score_lifecycle_health//src/launch_manager_daemon:launch_manager",
],
env = {
"RUST_BACKTRACE": "1",
},
pytest_config = "//:pyproject.toml",
deps = all_requirements,
)

# Daemon integration tests (C++)
score_py_pytest(
name = "fit_daemon_cpp",
srcs = glob(["tests/**/*.py"]),
args = [
"-m cpp",
"--traces=all",
"--cpp-target-path=$(rootpath //feature_integration_tests/test_scenarios/cpp:cpp_test_scenarios)",
"-k with_daemon",
],
data = [
"conftest.py",
"daemon_helpers.py",
"fit_scenario.py",
"lifecycle_scenario.py",
"persistency_scenario.py",
"test_properties.py",
"//feature_integration_tests/test_scenarios/cpp:cpp_test_scenarios",
"@score_lifecycle_health//examples/cpp_supervised_app",
"@score_lifecycle_health//src/launch_manager_daemon:launch_manager",
],
pytest_config = "//:pyproject.toml",
deps = all_requirements,
Expand All @@ -83,3 +146,11 @@ test_suite(
":fit_rust",
],
)

test_suite(
name = "fit_daemon",
tests = [
":fit_daemon_cpp",
":fit_daemon_rust",
],
)
16 changes: 16 additions & 0 deletions feature_integration_tests/test_cases/configs/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

exports_files([
"daemon_launch_manager_config.json",
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
{
"schema_version": 1,
"defaults": {
"deployment_config": {
"bin_dir": "bin/",
"ready_recovery_action": {
"restart": {
"number_of_attempts": 1,
"delay_before_restart": 0.5
}
}
},
"component_properties": {
"application_profile": {
"application_type": "Reporting",
"is_self_terminating": false,
"alive_supervision": {
"reporting_cycle": 0.1,
"min_indications": 1,
"max_indications": 3,
"failed_cycles_tolerance": 1
}
},
"depends_on": [],
"process_arguments": [],
"ready_condition": {
"process_state": "Running"
}
},
"run_target": {
"transition_timeout": 5,
"recovery_action": {
"switch_run_target": {
"run_target": "fallback_run_target"
}
}
},
"alive_supervision": {
"evaluation_cycle": 0.5
}
},
"components": {
"state_manager": {
"description": "State Manager application",
"component_properties": {
"binary_name": "control_daemon",
"application_profile": {
"application_type": "State_Manager",
"alive_supervision": {
"min_indications": 0
}
},
"depends_on": []
}
},
"cpp_supervised_app": {
"component_properties": {
"binary_name": "cpp_supervised_app",
"application_profile": {
"application_type": "Reporting_And_Supervised"
},
"process_arguments": [
"-d50"
]
},
"deployment_config": {
"environmental_variables": {
"PROCESSIDENTIFIER": "cpp_supervised_app",
"CONFIG_PATH": "etc/hmproc_cpp_supervised_app.bin",
"IDENTIFIER": "cpp_supervised_app"
}
}
},
"rust_supervised_app": {
"component_properties": {
"binary_name": "rust_supervised_app",
"application_profile": {
"application_type": "Reporting_And_Supervised"
},
"process_arguments": [
"-d50"
]
},
"deployment_config": {
"environmental_variables": {
"PROCESSIDENTIFIER": "rust_supervised_app",
"CONFIG_PATH": "etc/hmproc_rust_supervised_app.bin",
"IDENTIFIER": "rust_supervised_app"
}
}
}
},
"run_targets": {
"Startup": {
"description": "Minimal functionality of the system",
"depends_on": [
"cpp_supervised_app",
"rust_supervised_app",
"state_manager"
],
"recovery_action": {
"switch_run_target": {
"run_target": "fallback_run_target"
}
}
}
},
"initial_run_target": "Startup",
"fallback_run_target": {
"description": "Switching off everything",
"depends_on": [
"state_manager"
],
"transition_timeout": 1.5
},
"alive_supervision": {
"evaluation_cycle": 0.5
}
}
38 changes: 31 additions & 7 deletions feature_integration_tests/test_cases/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************
import os
import subprocess
from pathlib import Path

import pytest
from testing_utils import BazelTools


# Cmdline options
Expand Down Expand Up @@ -61,6 +62,12 @@ def pytest_addoption(parser):
default=180.0,
help="Build command timeout in seconds. Default: %(default)s",
)
parser.addoption(
"--bazel-config",
type=str,
default=os.environ.get("FIT_BAZEL_CONFIG", "linux-x86_64"),
help=('Bazel config used when --build-scenarios is enabled (default: env FIT_BAZEL_CONFIG or "linux-x86_64").'),
)
parser.addoption(
"--default-execution-timeout",
type=float,
Expand Down Expand Up @@ -88,18 +95,35 @@ def pytest_sessionstart(session):
# Build scenarios.
if session.config.getoption("--build-scenarios"):
build_timeout = session.config.getoption("--build-scenarios-timeout")
bazel_config = session.config.getoption("--bazel-config")

def _build_target(target_name: str) -> None:
command = ["bazel", "build", f"--config={bazel_config}", target_name]
result = subprocess.run(
command,
capture_output=True,
text=True,
check=False,
timeout=build_timeout,
)
if result.returncode != 0:
stderr_tail = "\n".join(result.stderr.strip().splitlines()[-40:])
raise RuntimeError(
"Failed to run build with pytest --build-scenarios.\n"
f"Command: {' '.join(command)}\n"
f"Return code: {result.returncode}\n"
f"stderr (last lines):\n{stderr_tail}"
)

# Build Rust test scenarios.
print("Building Rust test scenarios executable...")
rust_tools = BazelTools(option_prefix="rust", build_timeout=build_timeout)
print(f"Building Rust test scenarios executable with --config={bazel_config}...")
rust_target_name = session.config.getoption("--rust-target-name")
rust_tools.build(rust_target_name)
_build_target(rust_target_name)

# Build C++ test scenarios.
print("Building C++ test scenarios executable...")
cpp_tools = BazelTools(option_prefix="cpp", build_timeout=build_timeout)
print(f"Building C++ test scenarios executable with --config={bazel_config}...")
cpp_target_name = session.config.getoption("--cpp-target-name")
cpp_tools.build(cpp_target_name)
_build_target(cpp_target_name)

except Exception as e:
pytest.exit(str(e), returncode=1)
Loading
Loading