Skip to content

Commit 35660ca

Browse files
authored
Merge pull request #6847 from BOINC/vko_add_client-auth-file_test
[windows][installer] add client_auth.xml creation test
2 parents 90bb1b9 + 483ee01 commit 35660ca

File tree

3 files changed

+187
-7
lines changed

3 files changed

+187
-7
lines changed

.github/workflows/windows.yml

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This file is part of BOINC.
22
# http://boinc.berkeley.edu
3-
# Copyright (C) 2025 University of California
3+
# Copyright (C) 2026 University of California
44
#
55
# BOINC is free software; you can redistribute it and/or modify it
66
# under the terms of the GNU Lesser General Public License
@@ -213,7 +213,7 @@ jobs:
213213
matrix:
214214
type: [install, upgrade_from_alpha, upgrade_from_stable]
215215
platform: [x64, arm64]
216-
installation_type: [normal, service, test_acct_mgr_login]
216+
installation_type: [normal, service, test_acct_mgr_login, test_client_auth_file]
217217
include:
218218
- platform: x64
219219
runner: windows-latest
@@ -223,14 +223,22 @@ jobs:
223223
argument_list_alpha: "/quiet /qn /l*v alpha.log"
224224
argument_list_release: "/quiet /qn /l*v release.log"
225225
argument_list_install: "/quiet /qn /l*v install.log"
226+
argument_list_prepare: ""
226227
- installation_type: service
227228
argument_list_alpha: "/quiet /qn /norestart /l*v alpha.log ENABLEPROTECTEDAPPLICATIONEXECUTION3=1"
228229
argument_list_release: "/quiet /qn /norestart /l*v release.log ENABLEPROTECTEDAPPLICATIONEXECUTION3=1"
229230
argument_list_install: "/quiet /qn /norestart /l*v install.log ENABLEPROTECTEDAPPLICATIONEXECUTION3=1"
231+
argument_list_prepare: ""
230232
- installation_type: test_acct_mgr_login
231233
argument_list_alpha: "/quiet /qn /l*v alpha.log"
232234
argument_list_release: "/quiet /qn /l*v release.log"
233235
argument_list_install: "/quiet /qn /l*v install.log ACCTMGR_LOGIN=test ACCTMGR_PASSWORDHASH=0123456789abcdef"
236+
argument_list_prepare: ""
237+
- installation_type: test_client_auth_file
238+
argument_list_alpha: "/quiet /qn /norestart /l*v alpha.log ENABLEPROTECTEDAPPLICATIONEXECUTION3=1"
239+
argument_list_release: "/quiet /qn /norestart /l*v release.log ENABLEPROTECTEDAPPLICATIONEXECUTION3=1"
240+
argument_list_install: "/quiet /qn /norestart /l*v install.log ENABLEPROTECTEDAPPLICATIONEXECUTION3=1 BOINC_PROJECT_USERNAME=test_user BOINC_PROJECT_PASSWORD=qwerty123456!@#$%^"
241+
argument_list_prepare: '--create-user --username test_user --password "qwerty123456!@#$%^"'
234242
fail-fast: false
235243
steps:
236244
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8
@@ -266,20 +274,25 @@ jobs:
266274
run: |
267275
7z x win_installer.7z
268276
277+
- name: Prepare installation for further testing
278+
if: success() && matrix.argument_list_prepare != ''
279+
shell: cmd
280+
run: python ./tests/windows_installer_integration_tests_prepare.py ${{matrix.argument_list_prepare}}
281+
269282
- name: Run installation
270283
if: success()
271284
shell: powershell
272285
run: Start-Process "./installer_setup.exe" -ArgumentList "${{matrix.argument_list_install}}" -Wait
273286

274287
- name: Run installation tests
275288
if: success()
276-
run: python ./tests/windows_installer_integration_tests.py --installation-type ${{matrix.installation_type}}
289+
run: python ./tests/windows_installer_integration_tests.py --installation-type ${{matrix.installation_type}} --type ${{matrix.type}}
277290

278291
- name: Upload logs on failure
279292
if: failure()
280293
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
281294
with:
282-
name: windows_installer_test_logs_${{matrix.platform}}_${{matrix.type}}_${{github.event.pull_request.head.sha}}
295+
name: windows_installer_test_logs_${{matrix.platform}}_${{matrix.type}}_${{matrix.installation_type}}_${{github.event.pull_request.head.sha}}
283296
path: |
284297
install.log
285298
alpha.log

tests/windows_installer_integration_tests.py

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@
2626
import testhelper as testhelper
2727

2828
class IntegrationTests:
29-
def __init__(self, installation_type="normal"):
29+
def __init__(self, installation_type="normal", test_type="install"):
3030
self.testhelper = testhelper.TestHelper()
3131
self.installation_type = installation_type
32+
self.test_type = test_type
3233
self.result = True
3334
self.result &= self.test_version()
3435
self.result &= self.test_files_exist()
@@ -39,6 +40,10 @@ def __init__(self, installation_type="normal"):
3940
self.result &= self.test_service_groups_and_memberships()
4041
if self.installation_type == "test_acct_mgr_login":
4142
self.result &= self.test_acct_mgr_login()
43+
if self.installation_type == "test_client_auth_file":
44+
self.result &= self.test_client_auth_file()
45+
if self.test_type == "install":
46+
self.result &= self.test_boinc_master_user_only_exists()
4247

4348
def _get_test_executable_file_path(self, filename):
4449
return pathlib.Path("C:\\Program Files\\BOINC\\") / filename
@@ -146,6 +151,12 @@ def test_service_users_exist(self):
146151
ts.expect_true(self._check_user_exists("boinc_project"), "Test 'boinc_project' user exists")
147152
return ts.result()
148153

154+
def test_boinc_master_user_only_exists(self):
155+
ts = testset.TestSet("Test 'boinc_master' user only exists")
156+
ts.expect_true(self._check_user_exists("boinc_master"), "Test 'boinc_master' user exists")
157+
ts.expect_false(self._check_user_exists("boinc_project"), "Test 'boinc_project' user does not exist")
158+
return ts.result()
159+
149160
def test_service_groups_and_memberships(self):
150161
ts = testset.TestSet("Test BOINC service groups and memberships")
151162
ts.expect_true(self._check_group_exists("boinc_admins"), "Test 'boinc_admins' group exists")
@@ -186,16 +197,57 @@ def test_acct_mgr_login(self):
186197

187198
return ts.result()
188199

200+
def test_client_auth_file(self):
201+
ts = testset.TestSet("Test client auth file")
202+
client_auth_file = self._get_test_data_file_path("client_auth.xml")
203+
ts.expect_true(os.path.exists(client_auth_file), "Test 'client_auth.xml' file exists in 'C:\\ProgramData\\BOINC\\'")
204+
205+
if os.path.exists(client_auth_file):
206+
try:
207+
tree = ET.parse(client_auth_file)
208+
root = tree.getroot()
209+
210+
ts.expect_equal("client_authorization", root.tag, "Test root element is 'client_authorization'")
211+
212+
boinc_project_element = root.find("boinc_project")
213+
ts.expect_true(boinc_project_element is not None, "Test 'boinc_project' element exists")
214+
215+
if boinc_project_element is not None:
216+
username_element = boinc_project_element.find("username")
217+
password_element = boinc_project_element.find("password")
218+
219+
ts.expect_true(username_element is not None, "Test 'username' element exists")
220+
ts.expect_true(password_element is not None, "Test 'password' element exists")
221+
222+
if username_element is not None:
223+
ts.expect_equal("test_user", username_element.text, "Test 'username' element contains 'test_user'")
224+
225+
if password_element is not None:
226+
ts.expect_equal("cXdlcnR5MTIzNDU2IUAjJCVe", password_element.text.strip(), "Test 'password' element contains 'cXdlcnR5MTIzNDU2IUAjJCVe'")
227+
228+
except ET.ParseError as e:
229+
ts.expect_true(False, f"Test XML file is well-formed (Parse error: {e})")
230+
except Exception as e:
231+
ts.expect_true(False, f"Test XML file can be processed (Error: {e})")
232+
233+
return ts.result()
234+
189235
if __name__ == "__main__":
190236
parser = argparse.ArgumentParser(description="BOINC Windows Installer Integration Tests")
191237
parser.add_argument(
192238
"--installation-type",
193239
type=str,
194240
default="normal",
195-
help="Installation type (normal or service), default is 'normal'"
241+
help="Installation type: 'normal' (default), 'service', 'test_acct_mgr_login', or 'test_client_auth_file'"
242+
)
243+
parser.add_argument(
244+
"--type",
245+
type=str,
246+
default="install",
247+
help="Test type: 'install' (default) or 'upgrade_from_alpha' or 'upgrade_from_stable'"
196248
)
197249
args = parser.parse_args()
198250

199-
if not IntegrationTests(installation_type=args.installation_type).result:
251+
if not IntegrationTests(installation_type=args.installation_type, test_type=args.type).result:
200252
sys.exit(1)
201253
sys.exit(0)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# This file is part of BOINC.
2+
# https://boinc.berkeley.edu
3+
# Copyright (C) 2026 University of California
4+
#
5+
# BOINC is free software; you can redistribute it and/or modify it
6+
# under the terms of the GNU Lesser General Public License
7+
# as published by the Free Software Foundation,
8+
# either version 3 of the License, or (at your option) any later version.
9+
#
10+
# BOINC is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13+
# See the GNU Lesser General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU Lesser General Public License
16+
# along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17+
18+
"""
19+
Windows Installer Integration Tests Preparation Script
20+
21+
This script prepares the Windows environment for installer integration tests.
22+
Currently supports creating test users on Windows.
23+
"""
24+
25+
import argparse
26+
import platform
27+
import subprocess
28+
import sys
29+
30+
31+
def check_windows():
32+
"""Verify that the script is running on Windows."""
33+
if platform.system() != 'Windows':
34+
print("Error: This script must be run on Windows.", file=sys.stderr)
35+
sys.exit(1)
36+
37+
38+
def create_windows_user(username, password):
39+
"""
40+
Create a new Windows user account.
41+
42+
Args:
43+
username: The username for the new account
44+
password: The password for the new account
45+
46+
Returns:
47+
bool: True if user was created successfully, False otherwise
48+
"""
49+
try:
50+
# Use net user command to create a new local user account
51+
cmd = ['net', 'user', username, password, '/add', '/y']
52+
result = subprocess.run(
53+
cmd,
54+
capture_output=True,
55+
text=True,
56+
check=False
57+
)
58+
59+
if result.returncode == 0:
60+
print(f"Successfully created user: {username}")
61+
return True
62+
else:
63+
error_msg = result.stderr.strip() or result.stdout.strip() or "Unknown error occurred"
64+
print(f"Error creating user: {error_msg}", file=sys.stderr)
65+
return False
66+
67+
except Exception as e:
68+
print(f"Exception while creating user: {e}", file=sys.stderr)
69+
return False
70+
71+
72+
def main():
73+
"""Main entry point for the script."""
74+
parser = argparse.ArgumentParser(
75+
description='Prepare Windows environment for installer integration tests'
76+
)
77+
78+
parser.add_argument(
79+
'--create-user',
80+
action='store_true',
81+
help='Create a new Windows user account'
82+
)
83+
84+
parser.add_argument(
85+
'--username',
86+
type=str,
87+
help='Username for the new account (required with --create-user)'
88+
)
89+
90+
parser.add_argument(
91+
'--password',
92+
type=str,
93+
help='Password for the new account (required with --create-user)'
94+
)
95+
96+
args = parser.parse_args()
97+
98+
# Check if running on Windows
99+
check_windows()
100+
101+
# Validate arguments
102+
if args.create_user:
103+
if not args.username or not args.password:
104+
parser.error('--create-user requires both --username and --password')
105+
106+
# Create the user
107+
success = create_windows_user(args.username, args.password)
108+
sys.exit(0 if success else 1)
109+
else:
110+
parser.print_help()
111+
sys.exit(0)
112+
113+
114+
if __name__ == '__main__':
115+
main()

0 commit comments

Comments
 (0)