Skip to content

Commit e22d2ab

Browse files
Add test & fix (#262)
Co-authored-by: Nicolas Fußberger <145956508+NicolasFussberger@users.noreply.github.com>
1 parent c57f7ff commit e22d2ab

6 files changed

Lines changed: 296 additions & 3 deletions

File tree

score/launch_manager/src/daemon/src/process_group_manager/details/graph.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ bool Graph::queueHeadNodes(bool start)
178178
queueHeadNodesForExecution();
179179
}
180180

181-
return (nodes_in_flight_ > 0);
181+
return (executing_nodes > 0);
182182
}
183183

184184
inline uint32_t Graph::countExecutableNodes(bool start)
@@ -223,7 +223,15 @@ inline void Graph::tryQueueNode(const std::shared_ptr<ProcessInfoNode>& node)
223223
}
224224
else
225225
{
226-
LM_LOG_WARN() << "Failed to queue node for execution " << push_res.error();
226+
// This means the job will never be queued so we'll never get the nodeExecuted() call, we need to call it
227+
// here
228+
LM_LOG_ERROR() << "Failed to queue node for execution " << push_res.error();
229+
230+
abort(getLastExecutionError(), ControlClientCode::kSetStateFailed);
231+
markNodeInFlight(); // This will be decremented below, avoid it going negative
232+
// Also, we need to be careful not to recurse or deadlock here. The below function does not lock any mutex
233+
// nor call this function
234+
handleNonTransitionExecution(GraphState::kAborting);
227235
break;
228236
}
229237
}

score/launch_manager/src/daemon/src/process_group_manager/details/graph.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ class Graph final {
320320

321321
/// @brief Queue head nodes to start a graph. Return false if there were no head nodes
322322
/// @param start true if this run of the graph is to start processes, false it it is to stop them
323-
/// @return true if one or more head nodes were queued
323+
/// @return true if one or more head nodes were found
324324
bool queueHeadNodes(bool start);
325325

326326
/// @brief Queue the jobs to kick-off a stop process graph
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# *******************************************************************************
2+
# Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
#
4+
# See the NOTICE file(s) distributed with this work for additional
5+
# information regarding copyright ownership.
6+
#
7+
# This program and the accompanying materials are made available under the
8+
# terms of the Apache License Version 2.0 which is available at
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# SPDX-License-Identifier: Apache-2.0
12+
# *******************************************************************************
13+
load("@rules_pkg//pkg:mappings.bzl", "pkg_attributes", "pkg_files")
14+
load("@rules_pkg//pkg:tar.bzl", "pkg_tar")
15+
load("//:defs.bzl", "launch_manager_config")
16+
load("//tests/utils/bazel:integration.bzl", "integration_test")
17+
18+
launch_manager_config(
19+
name = "lm_process_wrong_binary_failure_config",
20+
config = "//tests/integration/process_wrong_binary_failure:process_wrong_binary_failure.json",
21+
flatbuffer_out_dir = "etc",
22+
)
23+
24+
cc_binary(
25+
name = "control_client_mock",
26+
srcs = ["control_client_mock.cpp"],
27+
deps = [
28+
"//score/launch_manager:control_cc",
29+
"//score/launch_manager:lifecycle_cc",
30+
"//tests/utils/test_helper",
31+
"@googletest//:gtest_main",
32+
],
33+
)
34+
35+
pkg_files(
36+
name = "process_wrong_binary_failure_main_files",
37+
srcs = [
38+
":control_client_mock",
39+
"//score/launch_manager",
40+
"//tests/utils/test_helper:verification_process",
41+
],
42+
attributes = pkg_attributes(mode = "0755"),
43+
prefix = "tests/process_wrong_binary_failure",
44+
)
45+
46+
pkg_files(
47+
name = "process_wrong_binary_failure_etc_files",
48+
srcs = [":lm_process_wrong_binary_failure_config"],
49+
prefix = "tests/process_wrong_binary_failure",
50+
)
51+
52+
pkg_tar(
53+
name = "process_wrong_binary_failure_binaries",
54+
srcs = [
55+
":process_wrong_binary_failure_etc_files",
56+
":process_wrong_binary_failure_main_files",
57+
],
58+
)
59+
60+
integration_test(
61+
name = "process_wrong_binary_failure",
62+
srcs = ["process_wrong_binary_failure.py"],
63+
tags = ["integration"],
64+
test_binaries = ":process_wrong_binary_failure_binaries",
65+
deps = ["//tests/utils/testing_utils"],
66+
)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/********************************************************************************
2+
* Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Apache License Version 2.0 which is available at
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* SPDX-License-Identifier: Apache-2.0
12+
********************************************************************************/
13+
#include <gtest/gtest.h>
14+
15+
#include "tests/utils/test_helper/test_helper.hpp"
16+
#include <score/mw/lifecycle/control_client.h>
17+
#include <score/mw/lifecycle/report_running.h>
18+
19+
TEST(MissingBinaryFailure, ControlClientMock)
20+
{
21+
score::mw::lifecycle::ControlClient client;
22+
23+
ASSERT_TRUE(check_clean({test_end_location, fallback_file}));
24+
25+
TEST_STEP("Report kRunning from ControlClientMock")
26+
{
27+
score::mw::lifecycle::report_running();
28+
}
29+
30+
TEST_STEP("Activate RunTarget containing a component with a missing binary")
31+
{
32+
score::cpp::stop_token stop_token;
33+
auto result = client.ActivateRunTarget("run_target_with_missing_binary").Get(stop_token);
34+
EXPECT_FALSE(result.has_value()) << "Activating a run target with a missing binary should fail.";
35+
}
36+
// Limitation: we cannot wait for the transition to fallback to complete
37+
sleep(1);
38+
TEST_STEP("Verify fallback run target was activated")
39+
{
40+
EXPECT_TRUE(std::filesystem::exists(fallback_file)) << "Fallback run target should have been activated";
41+
}
42+
43+
TEST_STEP("Activate RunTarget Off")
44+
{
45+
client.ActivateRunTarget("Off");
46+
}
47+
}
48+
49+
int main(int argc, char** argv)
50+
{
51+
return TestRunner(__FILE__, TerminationBehavior::kContinue, TerminationNotification::kTestEnd).RunTests();
52+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
{
2+
"schema_version": 1,
3+
"defaults": {
4+
"deployment_config": {
5+
"bin_dir": "/tmp/tests/process_wrong_binary_failure",
6+
"ready_timeout": 1.0,
7+
"shutdown_timeout": 1.0,
8+
"ready_recovery_action": {
9+
"restart": {
10+
"number_of_attempts": 0
11+
}
12+
},
13+
"recovery_action": {
14+
"switch_run_target": {
15+
"run_target": "fallback_run_target"
16+
}
17+
},
18+
"environmental_variables": {
19+
"LD_LIBRARY_PATH": "/opt/lib"
20+
},
21+
"sandbox": {
22+
"uid": 0,
23+
"gid": 0,
24+
"scheduling_policy": "SCHED_OTHER",
25+
"scheduling_priority": 0
26+
}
27+
},
28+
"component_properties": {
29+
"application_profile": {
30+
"application_type": "Reporting",
31+
"is_self_terminating": true,
32+
"alive_supervision": {
33+
"reporting_cycle": 0.1,
34+
"min_indications": 1,
35+
"max_indications": 3,
36+
"failed_cycles_tolerance": 1
37+
}
38+
},
39+
"ready_condition": {
40+
"process_state": "Running"
41+
}
42+
}
43+
},
44+
"components": {
45+
"control_client_mock": {
46+
"component_properties": {
47+
"binary_name": "control_client_mock",
48+
"application_profile": {
49+
"application_type": "State_Manager",
50+
"alive_supervision": {
51+
"min_indications": 0
52+
}
53+
}
54+
},
55+
"deployment_config": {
56+
"ready_timeout": 1.0,
57+
"shutdown_timeout": 1.0,
58+
"environmental_variables": {
59+
"PROCESSIDENTIFIER": "control_client_mock"
60+
}
61+
}
62+
},
63+
"component_with_missing_binary": {
64+
"component_properties": {
65+
"binary_name": "abc_complex_reporting_process",
66+
"process_arguments": [
67+
"1500.0"
68+
]
69+
},
70+
"deployment_config": {
71+
"ready_timeout": 1.0,
72+
"shutdown_timeout": 1.0,
73+
"environmental_variables": {
74+
"PROCESSIDENTIFIER": "component_with_missing_binary"
75+
}
76+
}
77+
},
78+
"verification_component": {
79+
"component_properties": {
80+
"binary_name": "verification_process",
81+
"application_profile": {
82+
"application_type": "Native",
83+
"is_self_terminating": true
84+
},
85+
"ready_condition": {
86+
"process_state": "Terminated"
87+
}
88+
}
89+
}
90+
},
91+
"run_targets": {
92+
"Startup": {
93+
"depends_on": [
94+
"control_client_mock"
95+
],
96+
"recovery_action": {
97+
"switch_run_target": {
98+
"run_target": "fallback_run_target"
99+
}
100+
}
101+
},
102+
"run_target_with_missing_binary": {
103+
"depends_on": [
104+
"control_client_mock",
105+
"component_with_missing_binary"
106+
],
107+
"recovery_action": {
108+
"switch_run_target": {
109+
"run_target": "fallback_run_target"
110+
}
111+
}
112+
},
113+
"Off": {
114+
"depends_on": []
115+
}
116+
},
117+
"initial_run_target": "Startup",
118+
"alive_supervision": {
119+
"evaluation_cycle": 0.05
120+
},
121+
"fallback_run_target": {
122+
"depends_on": [
123+
"control_client_mock",
124+
"verification_component"
125+
]
126+
}
127+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# *******************************************************************************
2+
# Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
#
4+
# See the NOTICE file(s) distributed with this work for additional
5+
# information regarding copyright ownership.
6+
#
7+
# This program and the accompanying materials are made available under the
8+
# terms of the Apache License Version 2.0 which is available at
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# SPDX-License-Identifier: Apache-2.0
12+
# *******************************************************************************
13+
from tests.utils.testing_utils.run_until_file_deployed import run_until_file_deployed
14+
from tests.utils.testing_utils.setup_test import setup_test
15+
from tests.utils.testing_utils.test_results import assert_test_results
16+
from attribute_plugin import add_test_properties
17+
18+
19+
@add_test_properties(
20+
partially_verifies=[],
21+
test_type="interface-test",
22+
derivation_technique="error-guessing",
23+
)
24+
def test_process_wrong_binary_failure(
25+
target, setup_test, assert_test_results, remote_test_dir
26+
):
27+
"""
28+
Objective: Verifies that activating a run target containing a component whose binary does not exist
29+
results in a failure and triggers the configured recovery action (switch to fallback run target).
30+
"""
31+
32+
run_until_file_deployed(
33+
target=target,
34+
binary_path=str(remote_test_dir / "launch_manager"),
35+
file_path=remote_test_dir.parent / "test_end",
36+
cwd=str(remote_test_dir),
37+
timeout_s=6,
38+
)
39+
40+
assert_test_results({"control_client_mock.xml"})

0 commit comments

Comments
 (0)