Skip to content

Commit f4a9037

Browse files
cferreiragonzmergify[bot]
authored andcommitted
Improve windows tests (#6373)
* Refs #24338: Fix System tests * Refs #24338: Fix IDL tests * Refs #24338: Improve deletion of WriterHistoryTests --------- Signed-off-by: Carlos Ferreira González <carlosferreira@eprosima.com> (cherry picked from commit e8294c0) # Conflicts: # test/feature/idl_parser/CMakeLists.txt # test/system/tools/fds/tests.py # test/unittest/rtps/history/WriterHistoryTests.cpp
1 parent d96f0eb commit f4a9037

5 files changed

Lines changed: 282 additions & 13 deletions

File tree

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Copyright 2024 Proyectos y Sistemas de Mantenimiento SL (eProsima).
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/thirdparty/dds-types-test/IDL")
16+
# Update submodule
17+
message(STATUS "Updating submodule thirdparty/dds-types-test")
18+
execute_process(
19+
COMMAND git submodule update --recursive --init "thirdparty/dds-types-test"
20+
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
21+
RESULT_VARIABLE UPDATE_SUBMODULE_RESULT
22+
)
23+
# A result different than 0 means that the submodule could not be updated.
24+
if(NOT UPDATE_SUBMODULE_RESULT EQUAL 0)
25+
message(FATAL_ERROR "Cannot configure Git submodule thirdparty/dds-types-test")
26+
endif()
27+
endif()
28+
29+
if(WIN32)
30+
add_definitions(
31+
-D_WIN32_WINNT=0x0601
32+
-D_CRT_SECURE_NO_WARNINGS
33+
)
34+
35+
find_program(PREPROCESSOR_EXEC cl.exe)
36+
if(PREPROCESSOR_EXEC)
37+
message(STATUS "Found preprocessor: ${PREPROCESSOR_EXEC}")
38+
# Add quotes to handle spaces in the path
39+
set(PREPROCESSOR_EXEC "\\\"${PREPROCESSOR_EXEC}\\\"")
40+
else()
41+
message(FATAL_ERROR "Preprocessor not found. Some of IdlParserTests might fail.")
42+
endif()
43+
endif()
44+
45+
configure_file(IdlParserTests.hpp.in config/IdlParserTests.hpp @ONLY)
46+
add_executable(IdlParserTests IdlParserTests.cpp)
47+
target_compile_definitions(IdlParserTests PRIVATE
48+
$<$<AND:$<NOT:$<BOOL:${WIN32}>>,$<STREQUAL:"${CMAKE_BUILD_TYPE}","Debug">>:__DEBUG>
49+
$<$<BOOL:${INTERNAL_DEBUG}>:__INTERNALDEBUG> # Internal debug activated.
50+
)
51+
target_include_directories(IdlParserTests
52+
PRIVATE
53+
${CMAKE_CURRENT_BINARY_DIR}/config
54+
${PROJECT_SOURCE_DIR}/test/utils/
55+
)
56+
target_link_libraries(IdlParserTests
57+
GTest::gtest
58+
$<$<BOOL:${WIN32}>:iphlpapi$<SEMICOLON>Shlwapi>
59+
$<$<BOOL:${WIN32}>:ws2_32>
60+
fastcdr
61+
fastdds
62+
)
63+
gtest_discover_tests(IdlParserTests PROPERTIES ${IDLPARSERTESTS_ENV_PROPERTIES})
64+
65+
message(STATUS "Copying IDL directory from ${PROJECT_SOURCE_DIR}/thirdparty/dds-types-test/IDL for idl_parser testing")
66+
file(COPY
67+
${PROJECT_SOURCE_DIR}/thirdparty/dds-types-test/IDL/
68+
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/IDL
69+
)
70+
file(COPY
71+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/no_path_included.idl
72+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/extra_structures.idl
73+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/extra_unions.idl
74+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/appendable_annotation.idl
75+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/default_annotation.idl
76+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/extensibility_annotation.idl
77+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/final_annotation.idl
78+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/id_annotation.idl
79+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/key_annotation.idl
80+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/mutable_annotation.idl
81+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/optional_annotation.idl
82+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/position_annotation.idl
83+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/bit_bound_annotation.idl
84+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/external_annotation.idl
85+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/nested_annotation.idl
86+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/try_construct_annotation.idl
87+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/value_annotation.idl
88+
${PROJECT_SOURCE_DIR}/test/feature/idl_parser/idl_extra_cases/default_literal_annotation.idl
89+
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/IDL
90+
)

test/system/tools/fds/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ if(Python3_Interpreter_FOUND)
9696
if(WIN32)
9797
add_test(
9898
NAME system.tools.fastdds.${TEST}
99-
COMMAND powershell "-File" ${PWS_LAUNCHER}
99+
COMMAND powershell.exe -NoProfile -ExecutionPolicy Bypass -File ${PWS_LAUNCHER}
100100
${Python3_EXECUTABLE}
101101
${CMAKE_CURRENT_SOURCE_DIR}/tests.py
102102
$<TARGET_FILE:fast-discovery-server>

test/system/tools/fds/launcher.ps1

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,18 @@ Param(
2424
$test_name
2525
)
2626

27-
$test = Start-Process -Passthru -Wait `
28-
-FilePath $python_path `
29-
-ArgumentList ($test_script, $tool_path, $test_name) `
30-
-WindowStyle Hidden
27+
try
28+
{
29+
& $python_path $test_script $tool_path $test_name
30+
$exit_code = $LASTEXITCODE
31+
}
32+
catch
33+
{
34+
throw "Failed to launch test '$test_name': $($_.Exception.Message)"
35+
}
3136

32-
if( $test.ExitCode -ne 0 )
37+
if ($exit_code -ne 0)
3338
{
34-
$error_message = "Test: $test_name failed with exit code $($test.ExitCode)."
39+
$error_message = "Test: $test_name failed with exit code $exit_code."
3540
throw $error_message
3641
}

test/system/tools/fds/tests.py

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
import signal
5353
import os
5454

55+
if os.name == 'nt':
56+
import ctypes
57+
5558
from xml.dom import minidom
5659
from xml.etree.ElementTree import XML
5760

@@ -64,16 +67,31 @@ def signal_handler(signum, frame):
6467
def send_command(command):
6568
print("Executing command: " + str(command))
6669

67-
# this subprocess cannot be executed in shell=True or using bash
70+
creationflags = 0
71+
if os.name == 'nt':
72+
# Give the child its own console so we can deliver CTRL_C_EVENT to
73+
# it without also affecting the launcher (PowerShell). We cannot use
74+
# Start-Process -WindowStyle Hidden on Windows containers, and
75+
# CREATE_NEW_PROCESS_GROUP + CTRL_BREAK_EVENT does not work either
76+
# because the server only installs a SIGINT handler, not SIGBREAK.
77+
creationflags = subprocess.CREATE_NEW_CONSOLE
78+
79+
# This subprocess cannot be executed in shell=True or using bash
6880
# because a background script will not broadcast the signals
6981
# it receives
7082
proc = subprocess.Popen(command,
7183
stdout=subprocess.PIPE,
84+
<<<<<<< HEAD
7285
universal_newlines=True
86+
=======
87+
stderr=subprocess.PIPE,
88+
universal_newlines=True,
89+
creationflags=creationflags,
90+
>>>>>>> e8294c00d (Improve windows tests (#6373))
7391
)
7492

75-
# sleep to let the server run
76-
time.sleep(1)
93+
# Sleep to let the server run
94+
time.sleep(3)
7795

7896
# 1. An exit code of 0 means everything was alright
7997
# 2. An exit code of 1 means the tool's process terminated before even
@@ -83,17 +101,44 @@ def send_command(command):
83101
# output was different than expected
84102
exit_code = 0
85103

86-
# direct this script to ignore SIGINT
104+
# If the process already exited due to failure (e.g. bad arguments, missing XML),
105+
# skip signalling entirely and collect the output.
106+
if proc.poll() is not None:
107+
output, err = proc.communicate()
108+
return output, err, exit_code
109+
110+
# Direct this script to ignore SIGINT
87111
signal.signal(signal.SIGINT, signal_handler)
88112

89-
# send SIGINT to process and wait for processing
113+
# On Windows, detach from the launcher's console and attach to the child's brand-new
114+
# console. From that attached state, GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)
115+
# broadcasts CTRL+C to every process in the attached console. This reaches the server's
116+
# SIGINT handler and triggers the clean-shutdown path (which prints "### Server shut down ###")
117+
# without leaking the signal to PowerShell.
118+
kernel32 = None
119+
if os.name == 'nt':
120+
kernel32 = ctypes.windll.kernel32
121+
ATTACH_PARENT_PROCESS = -1
122+
CTRL_C_EVENT = 0
123+
kernel32.FreeConsole()
124+
if not kernel32.AttachConsole(proc.pid):
125+
# Restore our console and bail out hard if the attached failed
126+
kernel32.AttachConsole(ATTACH_PARENT_PROCESS)
127+
proc.kill()
128+
print('Could not attach to child console to send CTRL_C')
129+
sys.exit(2)
130+
# Ignore CTRL+C in our own process so the broadcast does not terminate the Python launcher.
131+
kernel32.SetConsoleCtrlHandler(None, True)
132+
133+
# Send signal to process and wait for processing
90134
lease = 0
91135
while True:
92136

93137
if os.name == 'posix':
94138
proc.send_signal(signal.SIGINT)
95139
elif os.name == 'nt':
96-
proc.send_signal(signal.CTRL_C_EVENT)
140+
# pid == 0 targets all processes attached to our (the child's) console.
141+
kernel32.GenerateConsoleCtrlEvent(0, 0)
97142

98143
time.sleep(1)
99144
lease += 1
@@ -104,6 +149,12 @@ def send_command(command):
104149
else:
105150
break
106151

152+
# Restore the launcher's console attachment on Windows before returning.
153+
if os.name == 'nt':
154+
kernel32.FreeConsole()
155+
kernel32.AttachConsole(-1) # ATTACH_PARENT_PROCESS
156+
kernel32.SetConsoleCtrlHandler(None, False)
157+
107158
# Check whether SIGINT was able to terminate the process
108159
if proc.poll() is None:
109160
# SIGINT couldn't terminate the process. Kill it and exit with code 2

test/unittest/rtps/history/WriterHistoryTests.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ void cache_change_fragment(
7777
{
7878
ASSERT_EQ(result, 0);
7979
}
80+
81+
RTPSDomain::removeRTPSWriter(writer);
82+
RTPSDomain::removeRTPSParticipant(participant);
83+
delete history;
8084
}
8185

8286
/**
@@ -92,6 +96,125 @@ TEST(WriterHistoryTests, final_high_mark_for_frag_overflow)
9296
}
9397
}
9498

99+
<<<<<<< HEAD
100+
=======
101+
TEST(WriterHistoryTests, add_change_with_undefined_instance_handle_and_no_payload)
102+
{
103+
uint32_t domain_id = 0;
104+
105+
RTPSParticipantAttributes p_attr;
106+
RTPSParticipant* participant = RTPSDomain::createParticipant(
107+
domain_id, true, p_attr);
108+
109+
ASSERT_NE(participant, nullptr);
110+
111+
HistoryAttributes h_attr;
112+
WriterHistory* history = new WriterHistory(h_attr);
113+
114+
WriterAttributes w_attr;
115+
// The topic must be keyed to use instance handles
116+
w_attr.endpoint.topicKind = WITH_KEY;
117+
RTPSWriter* writer = RTPSDomain::createRTPSWriter(participant, w_attr, history);
118+
119+
ASSERT_NE(writer, nullptr);
120+
121+
// Any of these changes have a well defined instance handle
122+
CacheChange_t* change = history->create_change(ALIVE);
123+
// Valid because ALIVE changes don't enforce defined instance handles
124+
ASSERT_TRUE(history->add_change(change));
125+
// Rest are invalid because instance handle is enforced to be defined
126+
change = history->create_change(NOT_ALIVE_DISPOSED);
127+
ASSERT_FALSE(history->add_change(change));
128+
change = history->create_change(NOT_ALIVE_UNREGISTERED);
129+
ASSERT_FALSE(history->add_change(change));
130+
change = history->create_change(NOT_ALIVE_DISPOSED_UNREGISTERED);
131+
ASSERT_FALSE(history->add_change(change));
132+
133+
RTPSDomain::removeRTPSWriter(writer);
134+
RTPSDomain::removeRTPSParticipant(participant);
135+
delete history;
136+
}
137+
138+
TEST(WriterHistoryTests, add_change_with_defined_instance_handle_and_no_payload)
139+
{
140+
uint32_t domain_id = 0;
141+
142+
RTPSParticipantAttributes p_attr;
143+
RTPSParticipant* participant = RTPSDomain::createParticipant(
144+
domain_id, true, p_attr);
145+
146+
ASSERT_NE(participant, nullptr);
147+
148+
HistoryAttributes h_attr;
149+
WriterHistory* history = new WriterHistory(h_attr);
150+
151+
WriterAttributes w_attr;
152+
// The topic must be keyed to use instance handles
153+
w_attr.endpoint.topicKind = WITH_KEY;
154+
RTPSWriter* writer = RTPSDomain::createRTPSWriter(participant, w_attr, history);
155+
156+
ASSERT_NE(writer, nullptr);
157+
158+
// All these changes have a well defined instance handle and they must be added successfully
159+
CacheChange_t* change = history->create_change(ALIVE);
160+
// Setting any value makes the handle defined because of its = operator
161+
change->instanceHandle.value[0] = 1;
162+
ASSERT_TRUE(history->add_change(change));
163+
change = history->create_change(NOT_ALIVE_DISPOSED);
164+
change->instanceHandle.value[0] = 1;
165+
ASSERT_TRUE(history->add_change(change));
166+
change = history->create_change(NOT_ALIVE_UNREGISTERED);
167+
change->instanceHandle.value[0] = 1;
168+
ASSERT_TRUE(history->add_change(change));
169+
change = history->create_change(NOT_ALIVE_DISPOSED_UNREGISTERED);
170+
change->instanceHandle.value[0] = 1;
171+
ASSERT_TRUE(history->add_change(change));
172+
173+
RTPSDomain::removeRTPSWriter(writer);
174+
RTPSDomain::removeRTPSParticipant(participant);
175+
delete history;
176+
}
177+
178+
TEST(WriterHistoryTests, add_change_with_payload_but_undefined_handle)
179+
{
180+
uint32_t domain_id = 0;
181+
182+
RTPSParticipantAttributes p_attr;
183+
RTPSParticipant* participant = RTPSDomain::createParticipant(
184+
domain_id, true, p_attr);
185+
186+
ASSERT_NE(participant, nullptr);
187+
188+
HistoryAttributes h_attr;
189+
WriterHistory* history = new WriterHistory(h_attr);
190+
191+
WriterAttributes w_attr;
192+
// The topic must be keyed to use instance handles
193+
w_attr.endpoint.topicKind = WITH_KEY;
194+
RTPSWriter* writer = RTPSDomain::createRTPSWriter(participant, w_attr, history);
195+
196+
ASSERT_NE(writer, nullptr);
197+
198+
CacheChange_t* change = history->create_change(ALIVE);
199+
// This len simulates a payload
200+
change->serializedPayload.length = 10;
201+
ASSERT_TRUE(history->add_change(change));
202+
change = history->create_change(NOT_ALIVE_DISPOSED);
203+
change->serializedPayload.length = 10;
204+
ASSERT_TRUE(history->add_change(change));
205+
change = history->create_change(NOT_ALIVE_UNREGISTERED);
206+
change->serializedPayload.length = 10;
207+
ASSERT_TRUE(history->add_change(change));
208+
change = history->create_change(NOT_ALIVE_DISPOSED_UNREGISTERED);
209+
change->serializedPayload.length = 10;
210+
ASSERT_TRUE(history->add_change(change));
211+
212+
RTPSDomain::removeRTPSWriter(writer);
213+
RTPSDomain::removeRTPSParticipant(participant);
214+
delete history;
215+
}
216+
217+
>>>>>>> e8294c00d (Improve windows tests (#6373))
95218
} // namespace rtps
96219
} // namespace fastdds
97220
} // namespace eprosima

0 commit comments

Comments
 (0)