Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaime Resano <Jaime.Resano-Aisa@qt.io>2025-02-24 11:55:30 +0100
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2025-03-12 22:15:41 +0100
commit58dc331da48ed85bfa8cc431568db4d8705cdffd (patch)
tree80600eee7e4d8663ff4861ee943716729fde8e32 /sources/pyside6/tests
parent545ca796dbd93edb66dc3c21c74511fab8e9d0a3 (diff)
pyproject.toml: 3. Add pyside6-project tests for pyproject.toml changes
This patch adds tests for the pyside6-project CLI tool to validate the pyproject.toml changes. The tests ensure that the existing behavior is preserved and that the new features work as expected. Task-number: PYSIDE-2714 Change-Id: I096188c1d6d931a3970787f2906b83d2a987f4ed Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'sources/pyside6/tests')
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/Drumpad.pyproject (renamed from sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/.pyproject)0
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/pyproject.toml5
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/example_project/example_project.pyproject3
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/example_project/folder/label_in_folder.py9
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/example_project/main.py20
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/example_project/mainwindow.py22
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/example_project/pyproject.toml5
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/example_project/subproject/pyproject.toml5
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/example_project/subproject/subproject.pyproject3
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/example_project/subproject/subproject_button.py17
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/existing_pyproject_toml.pyproject3
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/expected_pyproject.toml22
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/main.py2
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/pyproject.toml20
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/zzz.py2
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/invalid_pyproject.pyproject3
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/main.py0
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/pyproject.toml2
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/valid_pyproject.pyproject3
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/common_file.py0
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/expected_pyproject.toml5
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/file1.py0
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/file2.py0
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/project1.pyproject3
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/project2.pyproject3
-rw-r--r--sources/pyside6/tests/tools/pyside6-project/test_pyside6_project.py424
26 files changed, 525 insertions, 56 deletions
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/.pyproject b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/Drumpad.pyproject
index a5f654c2b..a5f654c2b 100644
--- a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/.pyproject
+++ b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/Drumpad.pyproject
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/pyproject.toml b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/pyproject.toml
new file mode 100644
index 000000000..3eeb1a90d
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/example_drumpad/Python/pyproject.toml
@@ -0,0 +1,5 @@
+[project]
+name = "Drumpad"
+
+[tool.pyside6-project]
+files = ["autogen/settings.py", "main.py"]
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_project/example_project.pyproject b/sources/pyside6/tests/tools/pyside6-project/example_project/example_project.pyproject
new file mode 100644
index 000000000..08fa3eac3
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/example_project/example_project.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["mainwindow.py", "my_widget.py", "folder/file_in_folder.py", "main.py", "subproject/subproject.pyproject"]
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_project/folder/label_in_folder.py b/sources/pyside6/tests/tools/pyside6-project/example_project/folder/label_in_folder.py
new file mode 100644
index 000000000..ea3a6f5a1
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/example_project/folder/label_in_folder.py
@@ -0,0 +1,9 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from PySide6.QtWidgets import QLabel
+
+
+class LabelInFolder(QLabel):
+ def __init__(self):
+ super().__init__()
+ self.setText("Label in folder")
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_project/main.py b/sources/pyside6/tests/tools/pyside6-project/example_project/main.py
new file mode 100644
index 000000000..0f7f96747
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/example_project/main.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+import os
+
+from mainwindow import MainWindow
+from PySide6.QtWidgets import QApplication
+import sys
+
+
+def main():
+ app = QApplication(sys.argv)
+ window = MainWindow()
+ if os.getenv("PYSIDE_TESTING"):
+ return 0
+ window.show()
+ return app.exec()
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_project/mainwindow.py b/sources/pyside6/tests/tools/pyside6-project/example_project/mainwindow.py
new file mode 100644
index 000000000..fc259c6be
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/example_project/mainwindow.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from PySide6.QtWidgets import QMainWindow, QWidget, QVBoxLayout
+from folder.label_in_folder import LabelInFolder
+from subproject.subproject_button import SubprojectButton
+
+
+class MainWindow(QMainWindow):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.setWindowTitle("Main Window")
+
+ self.central_layout = QVBoxLayout()
+ self.central_widget = QWidget()
+ self.setCentralWidget(self.central_widget)
+ self.central_widget.setLayout(self.central_layout)
+
+ self.label_in_folder = LabelInFolder()
+ self.central_layout.addWidget(self.label_in_folder)
+
+ self.subproject_button = SubprojectButton()
+ self.central_layout.addWidget(self.subproject_button)
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_project/pyproject.toml b/sources/pyside6/tests/tools/pyside6-project/example_project/pyproject.toml
new file mode 100644
index 000000000..59cc29263
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/example_project/pyproject.toml
@@ -0,0 +1,5 @@
+[project]
+name = "example_project"
+
+[tool.pyside6-project]
+files = ["folder/file_in_folder.py", "main.py", "mainwindow.py", "my_widget.py", "subproject/subproject.pyproject"]
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_project/subproject/pyproject.toml b/sources/pyside6/tests/tools/pyside6-project/example_project/subproject/pyproject.toml
new file mode 100644
index 000000000..1ceb0ac0b
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/example_project/subproject/pyproject.toml
@@ -0,0 +1,5 @@
+[project]
+name = "subproject"
+
+[tool.pyside6-project]
+files = ["subproject_button.py"]
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_project/subproject/subproject.pyproject b/sources/pyside6/tests/tools/pyside6-project/example_project/subproject/subproject.pyproject
new file mode 100644
index 000000000..abfa1f461
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/example_project/subproject/subproject.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["subproject_button.py"]
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/example_project/subproject/subproject_button.py b/sources/pyside6/tests/tools/pyside6-project/example_project/subproject/subproject_button.py
new file mode 100644
index 000000000..40813ff86
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/example_project/subproject/subproject_button.py
@@ -0,0 +1,17 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from PySide6.QtWidgets import QPushButton, QApplication
+import sys
+
+
+class SubprojectButton(QPushButton):
+ def __init__(self):
+ super().__init__()
+ self.setText("Subproject button")
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ button = SubprojectButton()
+ button.show()
+ sys.exit(app.exec())
diff --git a/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/existing_pyproject_toml.pyproject b/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/existing_pyproject_toml.pyproject
new file mode 100644
index 000000000..7ddbd86fd
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/existing_pyproject_toml.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["zzz.py", "main.py"]
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/expected_pyproject.toml b/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/expected_pyproject.toml
new file mode 100644
index 000000000..be8aa949f
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/expected_pyproject.toml
@@ -0,0 +1,22 @@
+[project]
+name = "my_project"
+version = "0.1.0"
+description = "A sample Python project"
+authors = [
+ { name = "John Doe", email = "john.doe@example.com" },
+]
+optional-dependencies = { dev = ["pytest", "black"], docs = ["sphinx"] }
+
+# Comment
+
+[tool.black]
+line-length = 88
+target-version = ["py38"]
+
+# Another comment
+
+[tool.pyside6-project]
+files = ["main.py", "zzz.py"]
+[build-system]
+requires = ["setuptools >=42"]
+build-backend = "setuptools.build_meta"
diff --git a/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/main.py b/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/main.py
new file mode 100644
index 000000000..8d4c96674
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/main.py
@@ -0,0 +1,2 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
diff --git a/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/pyproject.toml b/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/pyproject.toml
new file mode 100644
index 000000000..ce2656502
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/pyproject.toml
@@ -0,0 +1,20 @@
+[project]
+name = "my_project"
+version = "0.1.0"
+description = "A sample Python project"
+authors = [
+ { name = "John Doe", email = "john.doe@example.com" },
+]
+optional-dependencies = { dev = ["pytest", "black"], docs = ["sphinx"] }
+
+# Comment
+
+[tool.black]
+line-length = 88
+target-version = ["py38"]
+
+# Another comment
+
+[build-system]
+requires = ["setuptools >=42"]
+build-backend = "setuptools.build_meta"
diff --git a/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/zzz.py b/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/zzz.py
new file mode 100644
index 000000000..8d4c96674
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/existing_pyproject_toml/zzz.py
@@ -0,0 +1,2 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
diff --git a/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/invalid_pyproject.pyproject b/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/invalid_pyproject.pyproject
new file mode 100644
index 000000000..6d6e84a36
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/invalid_pyproject.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py", 33]
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/main.py b/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/main.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/main.py
diff --git a/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/pyproject.toml b/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/pyproject.toml
new file mode 100644
index 000000000..37d23c948
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/pyproject.toml
@@ -0,0 +1,2 @@
+this is not a valid pyproject.toml file
+because it does not have a valid toml structure
diff --git a/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/valid_pyproject.pyproject b/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/valid_pyproject.pyproject
new file mode 100644
index 000000000..cc7a74a34
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/invalid_pyproject/valid_pyproject.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py"]
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/common_file.py b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/common_file.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/common_file.py
diff --git a/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/expected_pyproject.toml b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/expected_pyproject.toml
new file mode 100644
index 000000000..fda48f5ba
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/expected_pyproject.toml
@@ -0,0 +1,5 @@
+[project]
+name = "multiple_pyproject"
+
+[tool.pyside6-project]
+files = ["common_file.py", "file1.py", "file2.py"]
diff --git a/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/file1.py b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/file1.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/file1.py
diff --git a/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/file2.py b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/file2.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/file2.py
diff --git a/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/project1.pyproject b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/project1.pyproject
new file mode 100644
index 000000000..105f6f919
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/project1.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["file1.py", "common_file.py"]
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/project2.pyproject b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/project2.pyproject
new file mode 100644
index 000000000..623b08794
--- /dev/null
+++ b/sources/pyside6/tests/tools/pyside6-project/multiple_pyproject/project2.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["common_file.py", "file2.py"]
+}
diff --git a/sources/pyside6/tests/tools/pyside6-project/test_pyside6_project.py b/sources/pyside6/tests/tools/pyside6-project/test_pyside6_project.py
index 0e7982a53..d66395251 100644
--- a/sources/pyside6/tests/tools/pyside6-project/test_pyside6_project.py
+++ b/sources/pyside6/tests/tools/pyside6-project/test_pyside6_project.py
@@ -1,16 +1,18 @@
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import contextlib
+import difflib
+import importlib
import io
import os
import shutil
import sys
-import unittest
-from unittest import mock
-from unittest import TestCase
import tempfile
-import importlib
+import unittest
from pathlib import Path
+from unittest import TestCase
+from unittest import mock
+from unittest.mock import patch
sys.path.append(str(Path(__file__).resolve().parents[2]))
from init_paths import init_test_paths
@@ -18,11 +20,30 @@ from init_paths import init_test_paths
init_test_paths(False)
+def file_diff(expected_file: Path, actual_file: Path) -> str:
+ """
+ Get a unified diff between two files
+ """
+ target_text = expected_file.read_text(encoding="utf-8").splitlines()
+ generated_text = actual_file.read_text(encoding="utf-8").splitlines()
+
+ return "\n".join(difflib.unified_diff(
+ generated_text, target_text,
+ fromfile=str(actual_file),
+ tofile=str(expected_file),
+ lineterm=""
+ ))
+
+
class PySide6ProjectTestBase(TestCase):
+ # If a project name is specified, on each the test, the project folder will be copy to the
+ # temp dir and the current dir will be changed to the project folder
+ # The project name must match an existing folder in the folder where this file is located
+ project_name: str | None = None
+
@classmethod
def setUpClass(cls):
cls.pyside_root = Path(__file__).parents[5].resolve()
- cls.example_root = cls.pyside_root / "examples"
tools_path = cls.pyside_root / "sources" / "pyside-tools"
if tools_path not in sys.path:
sys.path.append(str(tools_path))
@@ -32,36 +53,42 @@ class PySide6ProjectTestBase(TestCase):
cls.current_dir = Path.cwd()
# print no outputs to stdout
sys.stdout = mock.MagicMock()
+ if cls.project_name:
+ cls.temp_project = Path(cls.temp_dir / cls.project_name).resolve()
+ os.chdir(cls.temp_dir)
+
+ def setUp(self):
+ super().setUp()
+ if self.project_name:
+ shutil.copytree(Path(__file__).parent / self.project_name, self.temp_project)
+ os.chdir(self.temp_project)
+
+ def tearDown(self):
+ super().tearDown()
+ if self.project_name:
+ os.chdir(self.temp_dir)
+ shutil.rmtree(self.temp_project)
@classmethod
def tearDownClass(cls):
os.chdir(cls.current_dir)
shutil.rmtree(cls.temp_dir)
- def setUp(self):
- os.chdir(self.temp_dir)
-
class TestPySide6ProjectDesignStudio(PySide6ProjectTestBase):
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- example_drumpad = Path(__file__).parent / "example_drumpad"
- cls.temp_example_drumpad = Path(
- shutil.copytree(example_drumpad, cls.temp_dir / "drumpad")
- ).resolve()
+ project_name = "example_drumpad"
def testDrumpadExample(self):
# This test compiles the .qrc file into a .py file and checks whether the compilation is
# carried out only when required
- compiled_resources_path = self.temp_example_drumpad / "Python" / "autogen" / "resources.py"
- resources_path = self.temp_example_drumpad / "Drumpad.qrc"
- requires_rebuild = self.project_lib.utils.requires_rebuild
+ compiled_resources_path = Path("Python") / "autogen" / "resources.py"
+ resources_path = Path("Drumpad.qrc")
+ requires_rebuild = self.project_lib.utils.requires_rebuild
+ pyproject_path = Path("Python") / "Drumpad.pyproject"
self.assertFalse(compiled_resources_path.exists())
-
- os.chdir(self.temp_example_drumpad / "Python")
- self.project.main(mode="build")
+ self.assertTrue(pyproject_path.exists())
+ self.project.main(mode="build", project_path=pyproject_path)
self.assertTrue(compiled_resources_path.exists())
self.assertFalse(requires_rebuild([resources_path], compiled_resources_path))
@@ -72,59 +99,96 @@ class TestPySide6ProjectDesignStudio(PySide6ProjectTestBase):
self.assertTrue(requires_rebuild([resources_path], compiled_resources_path))
- self.project.main(mode="build")
+ self.project.main(mode="build", project_path=pyproject_path)
self.assertFalse(requires_rebuild([resources_path], compiled_resources_path))
# Refresh the modification timestamp of one of the resources files
- list((self.temp_example_drumpad / "Resources").glob("*.txt"))[0].touch()
+ list((Path("Resources").glob("*.txt")))[0].touch()
self.assertTrue(requires_rebuild([resources_path], compiled_resources_path))
- self.project.main(mode="clean")
+ self.project.main(mode="clean", project_path=pyproject_path)
self.assertFalse(compiled_resources_path.exists())
+ def testMigrateDrumpadExample(self):
+ # The pyproject.toml file already contains the expected output
+ expected_pyproject_toml = Path("Python") / "pyproject.toml"
+ expected_pyproject_toml.rename(expected_pyproject_toml.parent / "expected_pyproject.toml")
+ existing_pyproject = Path("Python") / "Drumpad.pyproject"
+
+ with self.assertRaises(SystemExit) as context:
+ with patch("builtins.input", return_value="y"):
+ self.project.main(mode="migrate-pyproject",
+ project_path=existing_pyproject.as_posix())
+
+ self.assertEqual(0, context.exception.code)
+ generated_pyproject_toml = Path("Python") / "pyproject.toml"
+ self.assertTrue(generated_pyproject_toml.exists())
+ diff = file_diff(expected_pyproject_toml, generated_pyproject_toml)
+ self.assertFalse(diff, f"Generated pyproject.toml does not match:\n{diff}")
+
class TestPySide6ProjectNew(PySide6ProjectTestBase):
def testNewUi(self):
+ test_project_path = self.temp_dir / "NewUiProject"
with self.assertRaises(SystemExit) as context:
- self.project.main(mode="new-ui", project_dir="TestProject")
- test_project_path = Path("TestProject")
+ self.project.main(mode="new-ui", project_dir=test_project_path.as_posix())
+
self.assertTrue((test_project_path / "pyproject.toml").exists())
self.assertTrue((test_project_path / "mainwindow.ui").exists())
self.assertTrue((test_project_path / "main.py").exists())
- self.assertEqual(context.exception.code, 0)
+ self.assertEqual(0, context.exception.code)
shutil.rmtree(test_project_path)
- def testRaiseErrorOnExistingProject(self):
+ def testRaiseErrorOnExistingNonEmptyProject(self):
+ # Create a project twice to ensure that an error is raised
+ project_name = "TestProject"
with self.assertRaises(SystemExit) as context:
- self.project.main(mode="new-ui", project_dir="TestProject")
- self.assertEqual(context.exception.code, 0)
+ self.project.main(mode="new-ui", project_dir=project_name)
+
+ self.assertEqual(0, context.exception.code)
+
error_message = io.StringIO()
- with self.assertRaises(SystemExit) as context, contextlib.redirect_stderr(error_message):
- self.project.main(mode="new-ui", project_dir="TestProject")
- self.assertEqual(context.exception.code, 1)
- self.assertTrue(error_message.getvalue()) # some error message is printed
+ with self.assertRaises(SystemExit) as context:
+ with contextlib.redirect_stderr(error_message):
+ self.project.main(mode="new-ui", project_dir=project_name)
+
+ self.assertEqual(1, context.exception.code)
+ self.assertTrue(f"Can not create project at {project_name}: directory is not empty." in
+ error_message.getvalue())
shutil.rmtree(self.temp_dir / "TestProject")
+ def testRaiseErrorOnInvalidProjectName(self):
+ # Create a project with an empty project name
+ error_message = io.StringIO()
+ with self.assertRaises(SystemExit) as context:
+ with contextlib.redirect_stderr(error_message):
+ self.project.main(mode="new-ui", project_dir="asdf/?^%$#@!")
+
+ self.assertEqual(1, context.exception.code)
+ self.assertTrue("Invalid project name" in error_message.getvalue())
+
def testNewQuick(self):
+ test_project_path = Path("QuickProject")
+
with self.assertRaises(SystemExit) as context:
- self.project.main(mode="new-quick", project_dir="TestProject")
- test_project_path = Path("TestProject")
+ self.project.main(mode="new-quick", project_dir=str(test_project_path))
+
self.assertTrue((test_project_path / "pyproject.toml").exists())
self.assertTrue((test_project_path / "main.qml").exists())
self.assertTrue((test_project_path / "main.py").exists())
- self.assertEqual(context.exception.code, 0)
+ self.assertEqual(0, context.exception.code)
shutil.rmtree(test_project_path)
def testNewWidget(self):
+ project_dir = self.temp_dir / "inner_folder" / "another_folder" / "WidgetProject"
with self.assertRaises(SystemExit) as context:
- self.project.main(mode="new-widget", project_dir="TestProject")
- test_project_path = Path("TestProject")
- self.assertTrue((test_project_path / "pyproject.toml").exists())
- self.assertTrue((test_project_path / "main.py").exists())
- self.assertEqual(context.exception.code, 0)
- shutil.rmtree(test_project_path)
+ self.project.main(mode="new-widget", project_dir=project_dir.as_posix())
+ self.assertTrue((project_dir / "pyproject.toml").exists())
+ self.assertTrue((project_dir / "main.py").exists())
+ self.assertEqual(0, context.exception.code)
+ shutil.rmtree(project_dir)
def testRaiseErrorWhenNoProjectNameIsSpecified(self):
mode = "new-widget"
@@ -135,25 +199,273 @@ class TestPySide6ProjectNew(PySide6ProjectTestBase):
expected_msg = f"Error creating new project: {mode} requires a directory name or path"
self.assertTrue(expected_msg in error_message.getvalue())
+ def testCreateProjectLegacyPyProjectFile(self):
+ project_path = Path("TestPyProjectJSON")
+ mode = "new-widget"
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode=mode, project_dir=project_path.as_posix(), legacy_pyproject=True)
+ self.assertEqual(0, context.exception.code)
+ self.assertTrue((project_path / "main.py").exists())
+ self.assertTrue((project_path / f"{project_path.name}.pyproject").exists())
+
class TestPySide6ProjectRun(PySide6ProjectTestBase):
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
- example_widgets = cls.example_root / "widgets" / "widgets" / "tetrix"
- cls.temp_example_tetrix = Path(
- shutil.copytree(example_widgets, Path(cls.temp_dir) / "tetrix")
- ).resolve()
-
- def testRunEmptyProject(self):
- project_folder = self.temp_dir / "TestProject"
+ project_name = "example_project"
+
+ def testRaiseErrorWhenRunningEmptyProject(self):
+ # Create a new empty project in the temp dir
+ project_folder = self.temp_dir / "empty_project"
project_folder.mkdir()
os.chdir(project_folder)
+
error_message = io.StringIO()
- with self.assertRaises(SystemExit) as context, contextlib.redirect_stderr(error_message):
+ with self.assertRaises(SystemExit) as context:
+ with contextlib.redirect_stderr(error_message):
+ self.project.main(mode="run")
+
+ os.chdir(self.temp_dir)
+ shutil.rmtree(project_folder)
+
+ self.assertEqual(1, context.exception.code)
+ self.assertTrue("No project file found" in error_message.getvalue())
+
+ def testRunExampleProject(self):
+ # The project is executed in a subprocess. The proejct code reads the PYSIDE_TESTING
+ # environment variable to avoid starting the Qt event loop
+ os.environ["PYSIDE_TESTING"] = "1"
+ with self.assertRaises(SystemExit) as context:
self.project.main(mode="run")
- self.assertEqual(context.exception.code, 1)
- self.assertTrue(error_message.getvalue()) # some error message is printed
+ os.environ.pop("PYSIDE_TESTING")
+ self.assertEqual(0, context.exception.code)
+
+ self.assertEqual(Path("pyproject.toml").resolve(),
+ self.project_lib.resolve_valid_project_file())
+
+
+class TestPySide6ProjectExampleProject(PySide6ProjectTestBase):
+ """
+ Test of an example project with both pyproject.toml and .pyproject valid files.
+ Contains a subproject with its own pyproject.toml file and .pyproject file too
+ """
+ project_name = "example_project"
+
+ def testMigratePyProjectToToml(self):
+ # The existing pyproject.toml file contains the expected output
+ expected_pyproject_toml = Path("pyproject.toml").rename("expected_pyproject.toml")
+ expected_subproject_pyproject_toml = Path("subproject") / "pyproject.toml"
+ expected_subproject_pyproject_toml.rename(
+ expected_subproject_pyproject_toml.parent / "expected_subproject_pyproject.toml")
+
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode="migrate-pyproject")
+
+ self.assertEqual(0, context.exception.code)
+
+ generated_pyproject_toml = Path("pyproject.toml")
+ self.assertTrue(generated_pyproject_toml.exists())
+ diff = file_diff(expected_pyproject_toml, generated_pyproject_toml)
+ self.assertFalse(diff, f"Generated pyproject.toml does not match:\n{diff}")
+
+ generated_subproject_pyproject_toml = Path("subproject") / "pyproject.toml"
+ self.assertTrue(generated_subproject_pyproject_toml.exists())
+ diff = file_diff(expected_subproject_pyproject_toml, generated_subproject_pyproject_toml)
+ self.assertFalse(diff, f"Generated subproject/pyproject.toml does not match:\n{diff}")
+
+ def testMigratePyProjectToTomlSpecifyingPyProjectFile(self):
+ # The existing pyproject.toml file contains the expected output
+ existing_pyproject = Path("example_project.pyproject")
+ expected_pyproject_toml = Path("pyproject.toml")
+ expected_pyproject_toml.rename("example_project.toml")
+
+ expected_subproject_pyproject_toml = Path("subproject") / "pyproject.toml"
+ expected_subproject_pyproject_toml.rename(
+ expected_subproject_pyproject_toml.parent / "expected_pyproject.toml")
+
+ with self.assertRaises(SystemExit) as context:
+ with patch("builtins.input", return_value="y"):
+ self.project.main(mode="migrate-pyproject",
+ project_path=existing_pyproject.as_posix())
+
+ self.assertEqual(0, context.exception.code)
+
+ generated_pyproject_toml = Path("pyproject.toml")
+ self.assertTrue(generated_pyproject_toml.exists())
+ diff = file_diff(expected_pyproject_toml, generated_pyproject_toml)
+ self.assertFalse(diff, f"Generated pyproject.toml does not match:\n{diff}")
+
+ generated_subproject_pyproject_toml = Path("subproject") / "pyproject.toml"
+ self.assertTrue(generated_subproject_pyproject_toml.exists())
+ diff = file_diff(expected_subproject_pyproject_toml, generated_subproject_pyproject_toml)
+ self.assertFalse(diff, f"Generated subproject/pyproject.toml does not match:\n{diff}")
+
+
+class TestPySide6ProjectExistingPyProjectToml(PySide6ProjectTestBase):
+ """
+ Test for migrating a project with an existing pyproject.toml file which does not contain the
+ [tool.pyside6-project] section
+ """
+ project_name = "existing_pyproject_toml"
+
+ def testMigratePyProjectToTomlAlreadyExistingTomlFile(self):
+ with self.assertRaises(SystemExit) as context:
+ with patch("builtins.input", return_value="y"):
+ self.project.main(mode="migrate-pyproject")
+
+ self.assertEqual(0, context.exception.code)
+ diff = file_diff(Path("expected_pyproject.toml"),
+ Path("pyproject.toml"))
+ self.assertFalse(diff, f"Updated pyproject.toml does not match:\n{diff}")
+
+
+class TestPySide6ProjectInvalidPyProjectToml(PySide6ProjectTestBase):
+ """
+ Check the current behavior in a project with an existing invalid pyproject.toml file and
+ invalid_pyproject.pyproject file
+ """
+
+ project_name = "invalid_pyproject"
+
+ def testRunInvalidPyProjectTomlFile(self):
+ pyproject_toml = Path("pyproject.toml")
+ self.assertTrue(pyproject_toml.exists())
+ self.assertTrue(self.project_lib.parse_pyproject_toml(pyproject_toml).errors)
+
+ error_message = io.StringIO()
+ with contextlib.redirect_stderr(error_message):
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode="run", project_path=pyproject_toml.as_posix())
+
+ self.assertEqual(1, context.exception.code)
+ self.assertTrue("Invalid project file" in error_message.getvalue())
+
+ def testRunSpecifyingPyProjectJsonFile(self):
+ # Check that the *.pyproject file is used if the pyproject.toml is invalid when using
+ # pyside6-project run
+
+ pyproject_toml_file = Path("pyproject.toml")
+ self.assertTrue(pyproject_toml_file.exists())
+ # Ensure that pyproject.toml is considered invalid
+ self.assertTrue(self.project_lib.parse_pyproject_toml(pyproject_toml_file).errors)
+
+ valid_pyproject = Path("valid_pyproject.pyproject")
+ self.assertTrue(valid_pyproject.exists())
+
+ # Ensure that the project can still be run specifying a valid *.pyproject JSON file
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode="run", project_path=valid_pyproject.as_posix())
+
+ self.assertEqual(0, context.exception.code)
+ self.assertTrue(Path("main.py").exists())
+
+ def testErrorRaisesWhenRunningWithoutSpecifyingProjectFile(self):
+ # The project folder contains two *.pyproject JSON files.
+ # The tool should raise an error because the project file is not specified
+ error_message = io.StringIO()
+ with contextlib.redirect_stderr(error_message):
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode="run")
+ self.assertEqual(1, context.exception.code)
+ self.assertTrue("Multiple project files found" in error_message.getvalue())
+
+ def testRaiseErrorResolvingInvalidProjectFile(self):
+ # Simulate that the user is specifying an invalid project file
+ invalid_pyproject_file = Path("invalid_pyproject.pyproject")
+ self.assertTrue(invalid_pyproject_file.exists())
+
+ with self.assertRaises(ValueError) as context:
+ self.project_lib.resolve_valid_project_file(invalid_pyproject_file.as_posix())
+
+ exception_message = str(context.exception)
+ self.assertTrue("Invalid project file" in exception_message)
+ self.assertTrue(str(invalid_pyproject_file) in exception_message)
+
+ def testResolveValidProjectFile(self):
+ # Simulate that the user is specifying a valid project file
+ valid_pyproject_file = Path("valid_pyproject.pyproject")
+ actual_project_file = self.project_lib.resolve_valid_project_file(
+ valid_pyproject_file.as_posix())
+ self.assertEqual(valid_pyproject_file.resolve(), actual_project_file)
+
+ def testRaiseErrorResolvingInvalidPyProjectToml(self):
+ # Simulate that the user is specifying an invalid pyproject.toml file
+ pyproject_toml_file = Path("pyproject.toml")
+ self.assertTrue(pyproject_toml_file.exists())
+
+ with self.assertRaises(ValueError) as context:
+ self.project_lib.resolve_valid_project_file(pyproject_toml_file.as_posix())
+
+ exception_message = str(context.exception)
+ self.assertTrue("Invalid project file" in exception_message)
+ self.assertTrue(str(pyproject_toml_file) in exception_message)
+
+ def testMigrateInvalidPyProjectToml(self):
+ # Can not migrate a project with an invalid pyproject.toml file
+ error_message = io.StringIO()
+ with contextlib.redirect_stderr(error_message):
+ with self.assertRaises(SystemExit) as context:
+ with patch("builtins.input", return_value="y"):
+ self.project.main(mode="migrate-pyproject")
+
+ self.assertEqual(1, context.exception.code)
+ self.assertTrue("Invalid project file" in error_message.getvalue())
+
+ def testMigrateInvalidPyProjectTomlSpecifyingWrongFile(self):
+ # Test specifying the pyproject.toml file as the project file to be migrated
+ existing_invalid_pyproject_toml = Path("pyproject.toml")
+ self.assertTrue(
+ bool(self.project_lib.parse_pyproject_toml(existing_invalid_pyproject_toml).errors))
+
+ error_message = io.StringIO()
+ with contextlib.redirect_stderr(error_message):
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode="migrate-pyproject",
+ project_path=existing_invalid_pyproject_toml)
+
+ self.assertEqual(1, context.exception.code)
+ self.assertTrue("Cannot migrate non \"*.pyproject\" file" in error_message.getvalue())
+ self.assertTrue("pyproject.toml" in error_message.getvalue())
+
+
+def testRunInvalidPyProjectToml(self):
+ # Ensure that the .pyproject file is preferred over the invalid pyproject.toml file.
+ # This preserves the backward compatibility of the .pyproject file
+
+ # Remove the invalid invalid_pyproject.pyproject file first
+ Path("invalid_pyproject.pyproject").unlink()
+ self.assertFalse(Path("invalid_pyproject.pyproject").exists())
+
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode="run")
+
+ self.assertEqual(0, context.exception.code)
+ self.assertEqual(Path("valid_pyproject.pyproject").resolve(),
+ self.project_lib.resolve_valid_project_file())
+
+
+class TestPySide6ProjectMultiplePyProject(PySide6ProjectTestBase):
+ project_name = "multiple_pyproject"
+
+ def testCancelMigration(self):
+ # Ensure that the pyproject.toml is not created if the user cancels the operation
+ with self.assertRaises(SystemExit) as context:
+ with patch("builtins.input", return_value="n"):
+ self.project.main(mode="migrate-pyproject")
+
+ self.assertEqual(0, context.exception.code)
+ self.assertFalse(Path("pyproject.toml").exists())
+
+ def testMigrateMultiplePyProjectFilesToToml(self):
+ expected_pyproject_toml = Path("expected_pyproject.toml")
+ generated_pyproject_toml = Path("pyproject.toml")
+
+ with self.assertRaises(SystemExit) as context:
+ with patch("builtins.input", return_value="y"):
+ self.project.main(mode="migrate-pyproject")
+
+ self.assertEqual(0, context.exception.code)
+ self.assertTrue(generated_pyproject_toml.exists())
+ diff = file_diff(expected_pyproject_toml, generated_pyproject_toml)
+ self.assertFalse(diff, f"Generated pyproject.toml does not match:\n{diff}")
if __name__ == "__main__":