Skip to content

Commit ddfce16

Browse files
committed
fixes
1 parent a4d8a83 commit ddfce16

File tree

3 files changed

+57
-40
lines changed

3 files changed

+57
-40
lines changed

conftest.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ def clear_old_testsuite(request):
111111

112112
# Only create QCoreApplication if test doesn't need QApplication
113113
if not skip_qt_cleanup:
114-
config = QSettings(qsettingsformat, QSettings.SystemScope, QCoreApplication.organizationName(),
114+
config = QSettings(qsettingsformat, QSettings.SystemScope,
115+
QCoreApplication.organizationName(),
115116
QCoreApplication.applicationName())
116117
config.clear()
117118
config.sync()
@@ -121,7 +122,8 @@ def clear_old_testsuite(request):
121122
logging.info('Removing %s', cachedir)
122123
shutil.rmtree(cachedir)
123124

124-
config = QSettings(qsettingsformat, QSettings.UserScope, QCoreApplication.organizationName(),
125+
config = QSettings(qsettingsformat, QSettings.UserScope,
126+
QCoreApplication.organizationName(),
125127
QCoreApplication.applicationName())
126128
config.clear()
127129
config.sync()

tests-qt/test_systemtray.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,18 @@ def mock_dependencies():
164164
patch('nowplaying.settingsui.about_version_text'), \
165165
patch('nowplaying.trackrequests.Requests', MockTrackrequests), \
166166
patch('nowplaying.db.MetadataDB', MockMetadataDB), \
167-
patch('nowplaying.apicache.APIResponseCache.vacuum_database_file') as mock_api_vacuum:
167+
patch('nowplaying.apicache.APIResponseCache.vacuum_database_file') as mock_api_vacuum, \
168+
patch('nowplaying.systemtray.DatabaseVacuumThread') as mock_vacuum_thread:
168169

169170
mock_load_ui.return_value = MagicMock()
171+
# Configure vacuum thread mock to prevent actual thread creation
172+
mock_vacuum_thread_instance = MagicMock()
173+
mock_vacuum_thread.return_value = mock_vacuum_thread_instance
170174
yield {
171175
'load_ui': mock_load_ui,
172176
'api_vacuum': mock_api_vacuum,
177+
'vacuum_thread': mock_vacuum_thread,
178+
'vacuum_thread_instance': mock_vacuum_thread_instance,
173179
}
174180

175181

@@ -247,9 +253,9 @@ def test_vacuum_databases_on_startup_method(qtbot, mock_dependencies): # pylint
247253
# Verify the vacuum method exists and was called
248254
assert hasattr(tray, '_vacuum_databases_on_startup')
249255
assert hasattr(tray, 'vacuum_thread')
250-
251-
# Vacuum operations run in background thread, so we can't assert on timing
252-
# but we can verify the thread was created and started
256+
# Verify the vacuum thread was created and started
257+
mock_dependencies['vacuum_thread'].assert_called_once_with(requestswindow=tray.requestswindow)
258+
mock_dependencies['vacuum_thread_instance'].start.assert_called_once()
253259

254260

255261
def test_settings_window_integration(qtbot, mock_dependencies): # pylint: disable=unused-argument,redefined-outer-name

tests/test_subprocesses.py

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,24 @@ def mock_config():
2828

2929

3030
@pytest.fixture
31-
def subprocess_manager(mock_config):
31+
def subprocess_manager(mock_config): # pylint: disable=redefined-outer-name
3232
''' Create SubprocessManager for testing '''
3333
with patch('importlib.import_module'):
3434
manager = nowplaying.subprocesses.SubprocessManager(config=mock_config, testmode=True)
3535
# Mock the process modules
36-
for name in manager.processes:
37-
manager.processes[name]['module'] = MagicMock()
36+
for _, process_info in manager.processes.items():
37+
process_info['module'] = MagicMock()
3838
yield manager
3939

4040

41-
def test_subprocess_manager_init(mock_config):
41+
def test_subprocess_manager_init(mock_config): # pylint: disable=redefined-outer-name
4242
''' Test SubprocessManager initialization '''
43-
with patch('importlib.import_module') as mock_import:
43+
with patch('importlib.import_module'):
4444
manager = nowplaying.subprocesses.SubprocessManager(config=mock_config, testmode=True)
4545

4646
# Should import all expected modules
47-
expected_processes = ['trackpoll', 'obsws', 'twitchbot', 'discordbot', 'webserver', 'kickbot']
47+
expected_processes = ['trackpoll', 'obsws', 'twitchbot', 'discordbot',
48+
'webserver', 'kickbot']
4849
assert len(manager.processes) == len(expected_processes)
4950

5051
for process_name in expected_processes:
@@ -54,7 +55,7 @@ def test_subprocess_manager_init(mock_config):
5455
assert 'stopevent' in manager.processes[process_name]
5556

5657

57-
def test_subprocess_manager_beam_mode(mock_config):
58+
def test_subprocess_manager_beam_mode(mock_config): # pylint: disable=redefined-outer-name
5859
''' Test SubprocessManager in beam mode '''
5960
mock_config.cparser.value.side_effect = lambda key, **kwargs: {
6061
'control/beam': True,
@@ -70,7 +71,7 @@ def test_subprocess_manager_beam_mode(mock_config):
7071
assert process_name in manager.processes
7172

7273

73-
def test_start_process_with_conditions(subprocess_manager):
74+
def test_start_process_with_conditions(subprocess_manager): # pylint: disable=redefined-outer-name
7475
''' Test conditional process starting '''
7576
manager = subprocess_manager
7677

@@ -82,7 +83,7 @@ def test_start_process_with_conditions(subprocess_manager):
8283
mock_start.assert_called_once_with('twitchbot')
8384

8485

85-
def test_start_process_disabled(subprocess_manager):
86+
def test_start_process_disabled(subprocess_manager): # pylint: disable=redefined-outer-name
8687
''' Test that disabled processes don't start '''
8788
manager = subprocess_manager
8889
manager.config.cparser.value.side_effect = lambda key, **kwargs: {
@@ -94,7 +95,7 @@ def test_start_process_disabled(subprocess_manager):
9495
mock_start.assert_not_called()
9596

9697

97-
class MockProcess:
98+
class MockProcess: # pylint: disable=too-many-instance-attributes
9899
''' Mock multiprocessing.Process for testing '''
99100

100101
def __init__(self, target=None, name=None, args=None):
@@ -108,9 +109,11 @@ def __init__(self, target=None, name=None, args=None):
108109
self._closed = False
109110

110111
def start(self):
112+
''' Mock process start '''
111113
self._started = True
112114

113115
def join(self, timeout=None):
116+
''' Mock process join '''
114117
# Simulate quick shutdown for most processes
115118
if self.name in ['webserver', 'obsws']:
116119
self._alive = False
@@ -119,19 +122,22 @@ def join(self, timeout=None):
119122
self._alive = False
120123

121124
def terminate(self):
125+
''' Mock process terminate '''
122126
self._terminated = True
123127
self._alive = False
124128

125129
def is_alive(self):
130+
''' Mock process is_alive check '''
126131
return self._alive
127132

128133
def close(self):
134+
''' Mock process close '''
129135
self._closed = True
130136

131137

132138
# Ensure test doesn't hang - using manual timing instead of pytest-timeout
133139
@pytest.mark.performance
134-
def test_parallel_shutdown_performance(subprocess_manager):
140+
def test_parallel_shutdown_performance(subprocess_manager): # pylint: disable=redefined-outer-name
135141
''' Test that parallel shutdown is faster than sequential '''
136142
manager = subprocess_manager
137143

@@ -158,57 +164,57 @@ def test_parallel_shutdown_performance(subprocess_manager):
158164
assert manager.processes[name]['process'] is None
159165

160166

161-
def test_stop_process_parallel_graceful_shutdown(subprocess_manager):
167+
def test_stop_process_parallel_graceful_shutdown(subprocess_manager): # pylint: disable=redefined-outer-name
162168
''' Test graceful shutdown in parallel method '''
163169
manager = subprocess_manager
164170

165171
mock_process = MockProcess(name='webserver')
166-
mock_process._alive = True # Start alive
172+
mock_process._alive = True # Start alive # pylint: disable=protected-access
167173
manager.processes['webserver']['process'] = mock_process
168174

169175
# Mock join to simulate quick shutdown
170176
with patch.object(mock_process, 'join') as mock_join:
171177
mock_join.side_effect = lambda timeout: setattr(mock_process, '_alive', False)
172178

173-
manager._stop_process_parallel('webserver')
179+
manager._stop_process_parallel('webserver') # pylint: disable=protected-access
174180

175181
# Should call join with 8 second timeout
176182
mock_join.assert_called_with(8)
177183
assert manager.processes['webserver']['process'] is None
178184

179185

180-
def test_stop_process_parallel_forced_termination(subprocess_manager):
186+
def test_stop_process_parallel_forced_termination(subprocess_manager): # pylint: disable=redefined-outer-name
181187
''' Test forced termination when graceful shutdown fails '''
182188
manager = subprocess_manager
183189

184190
mock_process = MockProcess(name='trackpoll')
185191
# Simulate a stuck process that doesn't respond to graceful shutdown
186-
mock_process._alive = True
192+
mock_process._alive = True # pylint: disable=protected-access
187193
manager.processes['trackpoll']['process'] = mock_process
188194

189195
with patch.object(mock_process, 'join') as mock_join, \
190196
patch.object(mock_process, 'terminate') as mock_terminate:
191197

192198
# Mock join to keep process alive on first call, then die on second
193-
def mock_join_behavior(timeout):
199+
def mock_join_behavior(timeout): # pylint: disable=unused-argument
194200
if mock_join.call_count == 1:
195201
# First call (graceful) - process stays alive
196202
pass
197203
else:
198204
# Second call (after terminate) - process dies
199-
mock_process._alive = False
205+
mock_process._alive = False # pylint: disable=protected-access
200206

201207
mock_join.side_effect = mock_join_behavior
202208

203-
manager._stop_process_parallel('trackpoll')
209+
manager._stop_process_parallel('trackpoll') # pylint: disable=protected-access
204210

205211
# Should try graceful first, then terminate
206212
assert mock_join.call_count == 2
207213
mock_terminate.assert_called_once()
208214
assert manager.processes['trackpoll']['process'] is None
209215

210216

211-
def test_stop_process_parallel_twitchbot_special_handling(subprocess_manager):
217+
def test_stop_process_parallel_twitchbot_special_handling(subprocess_manager): # pylint: disable=redefined-outer-name
212218
''' Test special handling for twitchbot process '''
213219
manager = subprocess_manager
214220

@@ -222,13 +228,13 @@ def test_stop_process_parallel_twitchbot_special_handling(subprocess_manager):
222228
with patch.object(mock_process, 'join') as mock_join:
223229
mock_join.side_effect = lambda timeout: setattr(mock_process, '_alive', False)
224230

225-
manager._stop_process_parallel('twitchbot')
231+
manager._stop_process_parallel('twitchbot') # pylint: disable=protected-access
226232

227233
# Should call the special stop function with PID
228234
mock_stop_func.assert_called_once_with(mock_process.pid)
229235

230236

231-
def test_stop_process_parallel_error_handling(subprocess_manager):
237+
def test_stop_process_parallel_error_handling(subprocess_manager): # pylint: disable=redefined-outer-name
232238
''' Test error handling during parallel shutdown '''
233239
manager = subprocess_manager
234240

@@ -242,13 +248,13 @@ def test_stop_process_parallel_error_handling(subprocess_manager):
242248
mock_join.side_effect = lambda timeout: setattr(mock_process, '_alive', False)
243249

244250
# Should not raise exception, just log and continue
245-
manager._stop_process_parallel('webserver')
251+
manager._stop_process_parallel('webserver') # pylint: disable=protected-access
246252

247253
# Process should still be cleaned up
248254
assert manager.processes['webserver']['process'] is None
249255

250256

251-
def test_stop_all_processes_with_timeout(subprocess_manager):
257+
def test_stop_all_processes_with_timeout(subprocess_manager): # pylint: disable=redefined-outer-name
252258
''' Test that stop_all_processes handles timeouts gracefully '''
253259
manager = subprocess_manager
254260

@@ -270,7 +276,7 @@ def test_stop_all_processes_with_timeout(subprocess_manager):
270276
assert manager.processes[name]['process'] is None
271277

272278

273-
def test_stop_all_processes_with_no_running_processes(subprocess_manager):
279+
def test_stop_all_processes_with_no_running_processes(subprocess_manager): # pylint: disable=redefined-outer-name
274280
'''Test that stop_all_processes does not raise errors when all process slots are None'''
275281
manager = subprocess_manager
276282

@@ -286,7 +292,7 @@ def test_stop_all_processes_with_no_running_processes(subprocess_manager):
286292
assert manager.processes[name]['process'] is None
287293

288294

289-
def test_stop_all_processes_signals_all_first(subprocess_manager):
295+
def test_stop_all_processes_signals_all_first(subprocess_manager): # pylint: disable=redefined-outer-name
290296
''' Test that stop_all_processes signals all processes before joining '''
291297
manager = subprocess_manager
292298

@@ -299,7 +305,10 @@ def test_stop_all_processes_signals_all_first(subprocess_manager):
299305

300306
# Track when stopevent.set() is called
301307
original_set = manager.processes[name]['stopevent'].set
302-
manager.processes[name]['stopevent'].set = lambda n=name: signal_order.append(n) or original_set()
308+
# Create closure to capture name properly
309+
def make_set_wrapper(process_name, orig_set):
310+
return lambda: signal_order.append(process_name) or orig_set()
311+
manager.processes[name]['stopevent'].set = make_set_wrapper(name, original_set)
303312

304313
manager.stop_all_processes()
305314

@@ -310,7 +319,7 @@ def test_stop_all_processes_signals_all_first(subprocess_manager):
310319
assert expected in signal_order
311320

312321

313-
def test_legacy_methods(subprocess_manager):
322+
def test_legacy_methods(subprocess_manager): # pylint: disable=redefined-outer-name
314323
''' Test that legacy methods still work '''
315324
manager = subprocess_manager
316325

@@ -343,7 +352,7 @@ def test_legacy_methods(subprocess_manager):
343352

344353

345354
@pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific timeout test")
346-
def test_windows_process_termination_timeout(subprocess_manager):
355+
def test_windows_process_termination_timeout(subprocess_manager): # pylint: disable=redefined-outer-name
347356
''' Test that Windows gets longer termination timeouts '''
348357
manager = subprocess_manager
349358

@@ -359,7 +368,7 @@ def join(self, timeout=None):
359368
if self.join_call_count == 1:
360369
return # Process stays alive
361370
# Second call (after terminate) - now it dies with Windows timeout
362-
elif self.join_call_count == 2 and timeout and timeout >= 7:
371+
if self.join_call_count == 2 and timeout and timeout >= 7:
363372
self._alive = False
364373

365374
mock_process = WindowsSlowProcess(name='trackpoll')
@@ -368,7 +377,7 @@ def join(self, timeout=None):
368377
with patch.object(mock_process, 'join', wraps=mock_process.join) as mock_join, \
369378
patch.object(mock_process, 'terminate') as mock_terminate:
370379

371-
manager._stop_process_parallel('trackpoll')
380+
manager._stop_process_parallel('trackpoll') # pylint: disable=protected-access
372381

373382
# Should call join twice: first for graceful (8s), then for force (7s)
374383
assert mock_join.call_count == 2
@@ -378,7 +387,7 @@ def join(self, timeout=None):
378387
mock_join.assert_any_call(7)
379388

380389

381-
def test_cross_platform_timeout_behavior(subprocess_manager):
390+
def test_cross_platform_timeout_behavior(subprocess_manager): # pylint: disable=redefined-outer-name
382391
''' Test timeout behavior across platforms '''
383392
manager = subprocess_manager
384393

@@ -388,10 +397,10 @@ def test_cross_platform_timeout_behavior(subprocess_manager):
388397
with patch.object(mock_process, 'join') as mock_join:
389398
mock_join.side_effect = lambda timeout: setattr(mock_process, '_alive', False)
390399

391-
manager._stop_process_parallel('webserver')
400+
manager._stop_process_parallel('webserver') # pylint: disable=protected-access
392401

393402
# Should use 8 second graceful timeout on all platforms
394403
mock_join.assert_any_call(8)
395404

396405
# If termination was needed, should use 7 second timeout
397-
# (increased from 5 to accommodate Windows)
406+
# (increased from 5 to accommodate Windows)

0 commit comments

Comments
 (0)