mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-09-22 23:08:08 +03:00
Fix cross-platform test assumptions
Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
@@ -246,6 +246,12 @@ Linux, and macOS with Python 3.13 and PySide6 6.8.3. The publication workflow re
|
|||||||
that job before PyPI publication. Opt-in tests and packaged/manual smoke checks remain
|
that job before PyPI publication. Opt-in tests and packaged/manual smoke checks remain
|
||||||
separate; local Windows results do not establish the other CI targets.
|
separate; local Windows results do not establish the other CI targets.
|
||||||
|
|
||||||
|
The settings harness compares canonical paths on every access so macOS temporary-directory
|
||||||
|
aliases and Windows short names do not look like sandbox escapes. Its isolation regressions
|
||||||
|
cover an aliased root and rejection of paths outside that root. The QR responsiveness test
|
||||||
|
queues unrelated work after generation starts and requires delivery before completion;
|
||||||
|
it does not assume a platform-specific order between zero-delay Qt timers.
|
||||||
|
|
||||||
The Home/Log workflow regressions exercise debounced typing, explicit submit/clear, navigation catch-up, focus-scoped
|
The Home/Log workflow regressions exercise debounced typing, explicit submit/clear, navigation catch-up, focus-scoped
|
||||||
Find/edit shortcuts, and live log filtering. These are real Qt tests in `test_qt_interactions.py` and
|
Find/edit shortcuts, and live log filtering. These are real Qt tests in `test_qt_interactions.py` and
|
||||||
`test_ui_behavior.py`; no production log source or profile network is started. The two optional pause regressions
|
`test_ui_behavior.py`; no production log source or profile network is started. The two optional pause regressions
|
||||||
|
|||||||
+3
-1
@@ -108,7 +108,9 @@ def _initializeSettingsSandbox():
|
|||||||
global _settingsDirectory
|
global _settingsDirectory
|
||||||
|
|
||||||
if _settingsDirectory is not None:
|
if _settingsDirectory is not None:
|
||||||
return Path(_settingsDirectory.name)
|
# TemporaryDirectory may retain a macOS /var alias or Windows short
|
||||||
|
# path. Match the canonical root used on initialization and by Qt.
|
||||||
|
return Path(_settingsDirectory.name).resolve()
|
||||||
|
|
||||||
_settingsDirectory = tempfile.TemporaryDirectory(
|
_settingsDirectory = tempfile.TemporaryDirectory(
|
||||||
prefix='furious-tests-settings-root-'
|
prefix='furious-tests-settings-root-'
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ from PySide6.QtTest import QTest
|
|||||||
from PySide6.QtWidgets import QPushButton, QVBoxLayout, QWidget
|
from PySide6.QtWidgets import QPushButton, QVBoxLayout, QWidget
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
import tests.support as SupportModule
|
||||||
|
|
||||||
from tests.support import (
|
from tests.support import (
|
||||||
application,
|
application,
|
||||||
@@ -58,7 +61,9 @@ class TestHarnessIsolationTest(unittest.TestCase):
|
|||||||
|
|
||||||
assertIsolatedSettings(outer)
|
assertIsolatedSettings(outer)
|
||||||
|
|
||||||
self.assertIn(settingsSandboxPath(), Path(outer.fileName()).parents)
|
self.assertIn(
|
||||||
|
settingsSandboxPath(), Path(outer.fileName()).resolve().parents
|
||||||
|
)
|
||||||
|
|
||||||
with isolatedSettings() as inner:
|
with isolatedSettings() as inner:
|
||||||
innerIdentity = (app.organizationName(), app.applicationName())
|
innerIdentity = (app.organizationName(), app.applicationName())
|
||||||
@@ -87,7 +92,35 @@ class TestHarnessIsolationTest(unittest.TestCase):
|
|||||||
|
|
||||||
assertIsolatedSettings(settings)
|
assertIsolatedSettings(settings)
|
||||||
|
|
||||||
self.assertIn(settingsSandboxPath(), Path(settings.fileName()).parents)
|
self.assertIn(
|
||||||
|
settingsSandboxPath(), Path(settings.fileName()).resolve().parents
|
||||||
|
)
|
||||||
|
|
||||||
|
def testExistingSandboxRootUsesTheSameCanonicalPath(self):
|
||||||
|
"""Normalize reused temporary-directory aliases before containment checks."""
|
||||||
|
root = settingsSandboxPath().resolve()
|
||||||
|
alias = root / '..' / root.name
|
||||||
|
|
||||||
|
# TemporaryDirectory.name can retain an alias even though QSettings
|
||||||
|
# returns the canonical path (macOS /var or Windows short user names).
|
||||||
|
with mock.patch.object(SupportModule._settingsDirectory, 'name', str(alias)):
|
||||||
|
self.assertEqual(settingsSandboxPath(), root)
|
||||||
|
|
||||||
|
with isolatedSettings() as settings:
|
||||||
|
assertIsolatedSettings(settings)
|
||||||
|
settings.setValue('alias-fixture', 'isolated')
|
||||||
|
settings.sync()
|
||||||
|
self.assertEqual(settings.value('alias-fixture'), 'isolated')
|
||||||
|
|
||||||
|
def testSettingsOutsideSandboxAreStillRejected(self):
|
||||||
|
"""Reject a sibling path even when its name shares the sandbox prefix."""
|
||||||
|
root = settingsSandboxPath().resolve()
|
||||||
|
outside = root.with_name(root.name + '-outside') / 'settings.ini'
|
||||||
|
settings = QtCore.QSettings(str(outside), QtCore.QSettings.Format.IniFormat)
|
||||||
|
|
||||||
|
# Constructing this object does not write; rejection must precede writes.
|
||||||
|
with self.assertRaisesRegex(AssertionError, 'escaped sandbox'):
|
||||||
|
assertIsolatedSettings(settings)
|
||||||
|
|
||||||
|
|
||||||
class NavigationBehaviorTest(unittest.TestCase):
|
class NavigationBehaviorTest(unittest.TestCase):
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ class QRCodeExportScalabilityTest(unittest.TestCase):
|
|||||||
self.assertEqual(appendExportItem.call_count, expected)
|
self.assertEqual(appendExportItem.call_count, expected)
|
||||||
|
|
||||||
def testGenerationYieldsToAnUnrelatedQtEvent(self):
|
def testGenerationYieldsToAnUnrelatedQtEvent(self):
|
||||||
"""Deliver unrelated work after one attempt and before batch completion."""
|
"""Deliver unrelated work during generation, before batch completion."""
|
||||||
profiles = self.profiles(6)
|
profiles = self.profiles(6)
|
||||||
attempts = []
|
attempts = []
|
||||||
marker = []
|
marker = []
|
||||||
@@ -178,6 +178,12 @@ class QRCodeExportScalabilityTest(unittest.TestCase):
|
|||||||
"""Record one exported snapshot."""
|
"""Record one exported snapshot."""
|
||||||
attempts.append(profile.itemRemark)
|
attempts.append(profile.itemRemark)
|
||||||
|
|
||||||
|
if len(attempts) == 1:
|
||||||
|
# Zero-timer ordering is platform-dependent. Queue the marker
|
||||||
|
# only once generation has started, then require it to run
|
||||||
|
# before all profiles finish rather than after exactly one.
|
||||||
|
QtCore.QTimer.singleShot(0, lambda: marker.append(len(attempts)))
|
||||||
|
|
||||||
return f'socks://{len(attempts)}.example:1080#Fixture'
|
return f'socks://{len(attempts)}.example:1080#Fixture'
|
||||||
|
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
@@ -191,13 +197,14 @@ class QRCodeExportScalabilityTest(unittest.TestCase):
|
|||||||
side_effect=self.qrImage,
|
side_effect=self.qrImage,
|
||||||
):
|
):
|
||||||
result = window.startExportByIndex(range(len(profiles)))
|
result = window.startExportByIndex(range(len(profiles)))
|
||||||
QtCore.QTimer.singleShot(0, lambda: marker.append(len(attempts)))
|
|
||||||
|
|
||||||
self.assertIs(result, window)
|
self.assertIs(result, window)
|
||||||
self.assertEqual(attempts, [])
|
self.assertEqual(attempts, [])
|
||||||
self.assertTrue(window.isVisible())
|
self.assertTrue(window.isVisible())
|
||||||
self.assertTrue(waitFor(lambda: bool(marker)))
|
self.assertTrue(waitFor(lambda: bool(marker)))
|
||||||
self.assertEqual(marker, [1])
|
self.assertEqual(len(marker), 1)
|
||||||
|
self.assertGreaterEqual(marker[0], 1)
|
||||||
|
self.assertLess(marker[0], len(profiles))
|
||||||
self.assertTrue(waitFor(lambda: not window.isExporting()))
|
self.assertTrue(waitFor(lambda: not window.isExporting()))
|
||||||
|
|
||||||
self.assertEqual(len(attempts), len(profiles))
|
self.assertEqual(len(attempts), len(profiles))
|
||||||
|
|||||||
Reference in New Issue
Block a user