From a2076352088ff169359374f4d367766025f3288b Mon Sep 17 00:00:00 2001 From: Pekka T Savolainen Date: Fri, 11 Oct 2024 18:36:16 +0300 Subject: [PATCH] Update Add/Update SpineOpt Wizard - Required SpineOpt version is now 0.8.3 - Introduce new option to Troubleshooting section, which shows the instructions to Install or Update SpineOpt manually - Remove Reset Registry button and show the instructions how to reset the registry manually - Add Copy log button to Total Failure page - Update links Re Issue #2973 --- spinetoolbox/config.py | 2 +- spinetoolbox/ui_main.py | 12 +- .../widgets/add_up_spine_opt_wizard.py | 238 ++++++++++-------- spinetoolbox/widgets/install_julia_wizard.py | 7 +- tests/widgets/test_AddUpSpineOptWizard.py | 52 ++-- 5 files changed, 176 insertions(+), 135 deletions(-) diff --git a/spinetoolbox/config.py b/spinetoolbox/config.py index e3882ee35..9b556add4 100644 --- a/spinetoolbox/config.py +++ b/spinetoolbox/config.py @@ -18,7 +18,7 @@ LATEST_PROJECT_VERSION = 13 # For the Add/Update SpineOpt wizard -REQUIRED_SPINE_OPT_VERSION = "0.6.9" +REQUIRED_SPINE_OPT_VERSION = "0.8.3" # Invalid characters for directory names # NOTE: "." is actually valid in a directory name but this is diff --git a/spinetoolbox/ui_main.py b/spinetoolbox/ui_main.py index 16a0afd6b..dfbcec7dd 100644 --- a/spinetoolbox/ui_main.py +++ b/spinetoolbox/ui_main.py @@ -128,7 +128,7 @@ class ToolboxUI(QMainWindow): def __init__(self): from .ui.mainwindow import Ui_MainWindow # pylint: disable=import-outside-toplevel - super().__init__(flags=Qt.Window) + super().__init__(flags=Qt.WindowType.Window) self.set_app_style() self.set_error_mode() self._qsettings = QSettings("SpineProject", "Spine Toolbox", self) @@ -227,16 +227,16 @@ def __init__(self): def eventFilter(self, obj, ev): # Save/restore splitter states when hiding/showing execution lists if obj == self.ui.listView_console_executions: - if ev.type() == QEvent.Hide: + if ev.type() == QEvent.Type.Hide: self._qsettings.setValue("mainWindow/consoleSplitterPosition", self.ui.splitter_console.saveState()) - elif ev.type() == QEvent.Show: + elif ev.type() == QEvent.Type.Show: splitter_state = self._qsettings.value("mainWindow/consoleSplitterPosition", defaultValue="false") if splitter_state != "false": self.ui.splitter_console.restoreState(splitter_state) return super().eventFilter(obj, ev) def _setup_properties_title(self): - self.label_item_name.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) + self.label_item_name.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignVCenter) self.label_item_name.setMinimumHeight(28) self._button_item_dir.setIcon(QIcon(":icons/folder-open-regular.svg")) layout = QHBoxLayout(self._properties_title) @@ -316,7 +316,9 @@ def connect_signals(self): # Consoles self.jupyter_console_requested.connect(self._setup_jupyter_console) self.kernel_shutdown.connect(self._handle_kernel_shutdown) - self.persistent_console_requested.connect(self._setup_persistent_console, Qt.BlockingQueuedConnection) + self.persistent_console_requested.connect( + self._setup_persistent_console, Qt.ConnectionType.BlockingQueuedConnection + ) @staticmethod def set_app_style(): diff --git a/spinetoolbox/widgets/add_up_spine_opt_wizard.py b/spinetoolbox/widgets/add_up_spine_opt_wizard.py index fe96373eb..96c470909 100644 --- a/spinetoolbox/widgets/add_up_spine_opt_wizard.py +++ b/spinetoolbox/widgets/add_up_spine_opt_wizard.py @@ -26,6 +26,7 @@ QWidget, QWizard, QWizardPage, + QApplication, ) from ..config import REQUIRED_SPINE_OPT_VERSION from ..execution_managers import QProcessExecutionManager @@ -42,7 +43,6 @@ class _PageId(IntEnum): FAILURE = auto() TROUBLESHOOT_PROBLEMS = auto() TROUBLESHOOT_SOLUTION = auto() - RESET_REGISTRY = auto() ADD_UP_SPINE_OPT_AGAIN = auto() TOTAL_FAILURE = auto() @@ -69,7 +69,6 @@ def __init__(self, parent, julia_exe, julia_project): self.setPage(_PageId.FAILURE, FailurePage(self)) self.setPage(_PageId.TROUBLESHOOT_PROBLEMS, TroubleshootProblemsPage(self)) self.setPage(_PageId.TROUBLESHOOT_SOLUTION, TroubleshootSolutionPage(self)) - self.setPage(_PageId.RESET_REGISTRY, ResetRegistryPage(self)) self.setPage(_PageId.ADD_UP_SPINE_OPT_AGAIN, AddUpSpineOptAgainPage(self)) self.setPage(_PageId.TOTAL_FAILURE, TotalFailurePage(self)) self.setStartId(_PageId.INTRO) @@ -94,7 +93,7 @@ def nextId(self): class SelectJuliaPage(QWizardPage): def __init__(self, parent, julia_exe, julia_project): super().__init__(parent) - self.setTitle("Select Julia project") + self.setTitle("Select Julia") self._julia_exe = julia_exe self._julia_project = julia_project self._julia_exe_line_edit = QLineEdit() @@ -103,14 +102,14 @@ def __init__(self, parent, julia_exe, julia_project): self.registerField("julia_exe*", self._julia_exe_line_edit) self.registerField("julia_project", self._julia_project_line_edit) layout = QVBoxLayout(self) - layout.addWidget(QLabel("Julia executable:")) + layout.addWidget(QLabel("Julia executable")) julia_exe_widget = QWidget() julia_exe_layout = QHBoxLayout(julia_exe_widget) julia_exe_layout.addWidget(self._julia_exe_line_edit) julia_exe_button = QPushButton("Browse") julia_exe_layout.addWidget(julia_exe_button) layout.addWidget(julia_exe_widget) - layout.addWidget(QLabel("Julia project (directory):")) + layout.addWidget(QLabel("Julia project/environment (directory)")) julia_project_widget = QWidget() julia_project_layout = QHBoxLayout(julia_project_widget) julia_project_layout.addWidget(self._julia_project_line_edit) @@ -134,7 +133,7 @@ def _select_julia_exe(self, _): @Slot(bool) def _select_julia_project(self, _): julia_project = QFileDialog.getExistingDirectory( - self, "Select Julia project (directory)", self.field("julia_project") + self, "Select Julia project/environment (directory)", self.field("julia_project") ) if not julia_project: return @@ -179,11 +178,11 @@ def initializePage(self): self._exec_mngr = QProcessExecutionManager(self, julia_exe, args, silent=True) self.completeChanged.emit() self._exec_mngr.execution_finished.connect(self._handle_check_install_finished) - qApp.setOverrideCursor(QCursor(Qt.BusyCursor)) # pylint: disable=undefined-variable + QApplication.setOverrideCursor(QCursor(Qt.CursorShape.BusyCursor)) # pylint: disable=undefined-variable self._exec_mngr.start_execution() def _handle_check_install_finished(self, ret): - qApp.restoreOverrideCursor() # pylint: disable=undefined-variable + QApplication.restoreOverrideCursor() # pylint: disable=undefined-variable self._exec_mngr.execution_finished.disconnect(self._handle_check_install_finished) if self.wizard().currentPage() is not self: return @@ -212,20 +211,20 @@ def _handle_check_install_finished(self, ret): return msg = ( f"SpineOpt version {spine_opt_version} is installed, " - f"but version {REQUIRED_SPINE_OPT_VERSION} is required." + f"but version {REQUIRED_SPINE_OPT_VERSION} or higher is required." ) self.layout().addWidget(WrapLabel(msg)) self.wizard().required_action = "update" self.setFinalPage(False) self.setCommitPage(True) - self.setButtonText(QWizard.CommitButton, "Update SpineOpt") + self.setButtonText(QWizard.WizardButton.CommitButton, "Update SpineOpt") self.completeChanged.emit() return self.layout().addWidget(QLabel("SpineOpt is not installed.")) self.wizard().required_action = "add" self.setFinalPage(False) self.setCommitPage(True) - self.setButtonText(QWizard.CommitButton, "Install SpineOpt") + self.setButtonText(QWizard.WizardButton.CommitButton, "Install SpineOpt") self.completeChanged.emit() def nextId(self): @@ -255,11 +254,11 @@ def initializePage(self): self.msg_success.emit(f"SpineOpt {process} started") cmd = julia_exe + " " + " ".join(args) self.msg.emit(f"$ {cmd}") - qApp.setOverrideCursor(QCursor(Qt.BusyCursor)) # pylint: disable=undefined-variable + QApplication.setOverrideCursor(QCursor(Qt.CursorShape.BusyCursor)) # pylint: disable=undefined-variable self._exec_mngr.start_execution() def _handle_spine_opt_add_up_finished(self, ret): - qApp.restoreOverrideCursor() # pylint: disable=undefined-variable + QApplication.restoreOverrideCursor() # pylint: disable=undefined-variable self._exec_mngr.execution_finished.disconnect(self._handle_spine_opt_add_up_finished) if self.wizard().currentPage() is not self: return @@ -270,9 +269,10 @@ def _handle_spine_opt_add_up_finished(self, ret): configured = {"add": "installed", "update": "updated"}[self.wizard().required_action] self.msg_success.emit(f"SpineOpt successfully {configured}") return - process = {"add": "installation", "update": "updatee"}[self.wizard().required_action] + process = {"add": "installation", "update": "update"}[self.wizard().required_action] self.msg_error.emit(f"SpineOpt {process} failed") self.wizard().process_log = self._log.toHtml() + self.wizard().process_log_plain = self._log.toPlainText() def nextId(self): if self._successful: @@ -296,13 +296,16 @@ def nextId(self): class FailurePage(QWizardPage): - def __init__(self, parent): - super().__init__(parent) + def initializePage(self): + process = {"add": "Installation", "update": "Update"}[self.wizard().required_action] + self.setTitle(f"{process} failed") check_box = QCheckBox("Troubleshoot problems") check_box.setChecked(True) self.registerField("troubleshoot", check_box) layout = QVBoxLayout(self) - msg = "Apologies." + msg = ("Apologies. Please see the Troubleshoot problems section " + "by clicking Next or click Cancel to close " + "the wizard.") layout.addWidget(WrapLabel(msg)) layout.addStretch() layout.addWidget(check_box) @@ -314,10 +317,6 @@ def __init__(self, parent): def _handle_check_box_clicked(self, checked=False): self.setFinalPage(not checked) - def initializePage(self): - process = {"add": "Installation", "update": "Update"}[self.wizard().required_action] - self.setTitle(f"{process} failed") - def nextId(self): if self.field("troubleshoot"): return _PageId.TROUBLESHOOT_PROBLEMS @@ -329,10 +328,11 @@ def __init__(self, parent): super().__init__(parent) self.setTitle("Troubleshooting") msg = "Select your problem from the list." - self._button1 = QRadioButton("Installing SpineOpt fails with one of the following messages (or similar):") - msg1a = MonoSpaceFontTextBrowser(self) - msg1b = MonoSpaceFontTextBrowser(self) - msg1a.append( + self._button1 = QRadioButton("None of the below") + self._button2 = QRadioButton("Installing SpineOpt fails with one of the following messages (or similar):") + msg2a = MonoSpaceFontTextBrowser(self) + msg2b = MonoSpaceFontTextBrowser(self) + msg2a.append( """ \u22ee
Updating git-repo `https://github.com/spine-tools/SpineJuliaRegistry`
@@ -341,7 +341,7 @@ def __init__(self, parent): \u22ee """ ) - msg1b.append( + msg2b.append( """ \u22ee
Updating git-repo `https://github.com/spine-tools/SpineJuliaRegistry`
@@ -350,9 +350,9 @@ def __init__(self, parent): \u22ee """ ) - self._button2 = QRadioButton("On Windows 7, installing SpineOpt fails with the following message (or similar):") - msg2 = MonoSpaceFontTextBrowser(self) - msg2.append( + self._button3 = QRadioButton("On Windows 7, installing SpineOpt fails with the following message (or similar):") + msg3 = MonoSpaceFontTextBrowser(self) + msg3.append( """ \u22ee
Downloading artifact: OpenBLAS32
@@ -371,13 +371,15 @@ def __init__(self, parent): layout.addWidget(WrapLabel(msg)) layout.addStretch() layout.addWidget(self._button1) - layout.addWidget(msg1a) - layout.addWidget(msg1b) layout.addStretch() layout.addWidget(self._button2) - layout.addWidget(msg2) + layout.addWidget(msg2a) + layout.addWidget(msg2b) + layout.addStretch() + layout.addWidget(self._button3) + layout.addWidget(msg3) layout.addStretch() - button_view_log = QPushButton("View process log") + button_view_log = QPushButton("View log") widget_view_log = QWidget() layout_view_log = QHBoxLayout(widget_view_log) layout_view_log.addStretch() @@ -386,16 +388,18 @@ def __init__(self, parent): layout.addStretch() self.registerField("problem1", self._button1) self.registerField("problem2", self._button2) + self.registerField("problem3", self._button3) self._button1.toggled.connect(lambda _: self.completeChanged.emit()) self._button2.toggled.connect(lambda _: self.completeChanged.emit()) + self._button3.toggled.connect(lambda _: self.completeChanged.emit()) button_view_log.clicked.connect(self._show_log) def isComplete(self): - return self.field("problem1") or self.field("problem2") + return self.field("problem1") or self.field("problem2") or self.field("problem3") @Slot(bool) def _show_log(self, _=False): - log_widget = QWidget(self, f=Qt.Window) + log_widget = QWidget(self, f=Qt.WindowType.Window) layout = QVBoxLayout(log_widget) log = MonoSpaceFontTextBrowser(log_widget) log.append(self.wizard().process_log) @@ -412,97 +416,108 @@ def __init__(self, parent): self.setCommitPage(True) QVBoxLayout(self) - def cleanupPage(self): - super().cleanupPage() - self.wizard().reset_registry = False - def initializePage(self): _clear_layout(self.layout()) if self.field("problem1"): self._initialize_page_solution1() - elif self.field("problem2"): + if self.field("problem2"): self._initialize_page_solution2() + elif self.field("problem3"): + self._initialize_page_solution3() def _initialize_page_solution1(self): - self.wizard().reset_registry = True + action = {"add": "Install SpineOpt", "update": "Update SpineOpt"}[self.wizard().required_action] + julia = self.field("julia_exe") + env = self.field("julia_project") + if not env: + install_cmds = f""" + julia> import Pkg
+ julia> Pkg.Registry.add("General")
+ julia> Pkg.Registry.add(Pkg.RegistrySpec(url="https://github.com/spine-tools/SpineJuliaRegistry"))
+ julia> Pkg.add("SpineOpt")
""" + else: + install_cmds = f""" + julia> import Pkg
+ julia> cd("{env}")
+ julia> Pkg.activate(".")
+ julia> Pkg.Registry.add("General")
+ julia> Pkg.Registry.add(Pkg.RegistrySpec(url="https://github.com/spine-tools/SpineJuliaRegistry"))
+ julia> Pkg.add("SpineOpt")
""" + if not env: + update_cmds = """ + julia> import Pkg
+ julia> Pkg.update("SpineOpt")
""" + else: + update_cmds = f""" + julia> import Pkg
+ julia> cd("{env}")
+ julia> Pkg.activate(".")
+ julia> Pkg.update("SpineOpt")
""" + action_cmds = {"Install SpineOpt": install_cmds, "Update SpineOpt": update_cmds} + self.setTitle("What now?") + msg_browser = MonoSpaceFontTextBrowser(self) + msg_browser.append(action_cmds[action]) + label1_txt = ( + "

    " + f"
  1. Click the {action} button to try again.
  2. " + f"
  3. {action} manually. Open your favorite terminal (ie. Command prompt) and start the " + f"Julia REPL using command:

    {julia}

    " + "In the Julia REPL, enter the following commands (gray text, not the green one):

" + ) + label2_txt = ( + "

See also up-to-date " + "installation " + "instructions in SpineOpt documentation.

" + ) + self.layout().addWidget(HyperTextLabel(label1_txt)) + self.layout().addWidget(msg_browser) + self.layout().addWidget(HyperTextLabel(label2_txt)) + self.setButtonText(QWizard.WizardButton.CommitButton, action) + + def _initialize_page_solution2(self): + action = {"add": "Install SpineOpt", "update": "Update SpineOpt"}[self.wizard().required_action] + julia = self.field("julia_exe") self.setTitle("Reset Julia General Registry") description = ( "

The issue you're facing can be due to an error in the installation of the Julia General registry " - "from the Julia Package Server.

" - "

The simplest solution is to delete any trace of the registry and install it again, from GitHub.

" - "

However, this will also remove all your installed packages.

" + "from the Julia Package Server. The simplest solution is to delete any trace of the registry and install " + "it again from GitHub.

" + "

To do this, open your favorite terminal (ie. Command prompt) and start the Julia REPL using " + f"command:

{julia}

In the Julia REPL, enter the commands below (gray text, " + "not the green one). Afterwards, try Add/Update SpineOpt again.

" + "

NOTE: this will also remove all your installed packages.

" ) + cmds = f""" + julia> import Pkg
+ julia> Pkg.Registry.rm("SpineRegistry")
+ julia> Pkg.Registry.rm("General")
+ julia> Pkg.Registry.add()
""" + cmd_browser = MonoSpaceFontTextBrowser(self) + cmd_browser.append(cmds) self.layout().addWidget(HyperTextLabel(description)) - self.setButtonText(QWizard.CommitButton, "Reset registry") + self.layout().addWidget(cmd_browser) + self.setButtonText(QWizard.WizardButton.CommitButton, action) - def _initialize_page_solution2(self): + def _initialize_page_solution3(self): action = {"add": "Install SpineOpt", "update": "Update SpineOpt"}[self.wizard().required_action] - self.setTitle("Update Windows Managemet Framework") + self.setTitle("Update Windows Management Framework") description = ( - "

The issue you're facing can be solved by installing Windows Managemet Framework 3 or greater, " + "

The issue you're facing can be solved by updating Windows Management Framework to 5.1 or greater, " "as follows:

    " - "
  • Install .NET 4.5 " - "from here.
  • " - "
  • Install Windows management framework 3 or later " - "from here.
  • " + "
  • Install latest .NET Framework (minimum 4.5) " + "from here.
  • " + "
  • Install Windows management framework 5.1 or later " + "from here.
  • " f"
  • {action} again.
  • " "

" ) self.layout().addWidget(HyperTextLabel(description)) - self.setButtonText(QWizard.CommitButton, action) + self.setButtonText(QWizard.WizardButton.CommitButton, action) def nextId(self): - if self.field("problem1"): - return _PageId.RESET_REGISTRY return _PageId.ADD_UP_SPINE_OPT_AGAIN -class ResetRegistryPage(QWizardProcessPage): - def initializePage(self): - code = ( - "using Pkg; " - 'rm(joinp ath(DEPOT_PATH[1], "registries", "General"); force=true, recursive=true); ' - 'withenv("JULIA_PKG_SERVER"=>"") do pkg"registry add" end' - ) - self.setTitle("Resetting Julia General Registry") - julia_exe = self.field("julia_exe") - julia_project = self.field("julia_project") - args = [f"--project={julia_project}", "-e", code] - self._exec_mngr = QProcessExecutionManager(self, julia_exe, args, semisilent=True) - self.completeChanged.emit() - self._exec_mngr.execution_finished.connect(self._handle_registry_reset_finished) - self.msg_success.emit("Registry reset started") - cmd = julia_exe + " " + " ".join(args) - self.msg.emit(f"$ {cmd}") - qApp.setOverrideCursor(QCursor(Qt.BusyCursor)) # pylint: disable=undefined-variable - self._exec_mngr.start_execution() - - def _handle_registry_reset_finished(self, ret): - qApp.restoreOverrideCursor() # pylint: disable=undefined-variable - self._exec_mngr.execution_finished.disconnect(self._handle_registry_reset_finished) - if self.wizard().currentPage() is not self: - return - self._exec_mngr = None - self._successful = ret == 0 - if self._successful: - self.msg_success.emit("Registry successfully reset") - self.setCommitPage(True) - action = {"add": "Install SpineOpt", "update": "Update SpineOpt"}[self.wizard().required_action] - self.setButtonText(QWizard.CommitButton, action) - else: - # FIXME: Rather, add a button to copy log to clipboard? - # self.wizard().process_log = self._log.toHtml() - self.msg_error.emit("Registry reset failed") - self.completeChanged.emit() - - def nextId(self): - if self._successful: - return _PageId.ADD_UP_SPINE_OPT_AGAIN - return _PageId.TOTAL_FAILURE - - class AddUpSpineOptAgainPage(AddUpSpineOptPage): def nextId(self): if self._successful: @@ -513,10 +528,29 @@ def nextId(self): class TotalFailurePage(QWizardPage): def __init__(self, parent): super().__init__(parent) + self._copy_label = QLabel() + + def initializePage(self): self.setTitle("Troubleshooting failed") - msg = "

Please open an issue with SpineOpt." + msg = ("

Please open an issue with SpineOpt." + "
Copy the log and paste it into the issue description.

") layout = QVBoxLayout(self) layout.addWidget(HyperTextLabel(msg)) + copy_widget = QWidget() + copy_button = QPushButton("Copy log") + self._copy_label.setText("Log copied to clipboard") + self._copy_label.hide() + layout_copy = QHBoxLayout(copy_widget) + layout_copy.addWidget(copy_button) + layout_copy.addWidget(self._copy_label) + layout_copy.addStretch() + layout.addWidget(copy_widget) + copy_button.clicked.connect(self._handle_copy_clicked) + + @Slot(bool) + def _handle_copy_clicked(self, _=False): + self._copy_label.show() + QApplication.clipboard().setText(self.wizard().process_log_plain) # pylint: disable=undefined-variable def nextId(self): return -1 diff --git a/spinetoolbox/widgets/install_julia_wizard.py b/spinetoolbox/widgets/install_julia_wizard.py index d5956c27f..15bafee7a 100644 --- a/spinetoolbox/widgets/install_julia_wizard.py +++ b/spinetoolbox/widgets/install_julia_wizard.py @@ -31,6 +31,7 @@ QWidget, QWizard, QWizardPage, + QApplication, ) from spine_engine.utils.helpers import resolve_current_python_interpreter from ..config import APPLICATION_PATH @@ -156,7 +157,7 @@ def __init__(self, parent): install_dir_button.clicked.connect(self._select_install_dir) symlink_dir_button.clicked.connect(self._select_symlink_dir) self.setCommitPage(True) - self.setButtonText(QWizard.CommitButton, "Install Julia") + self.setButtonText(QWizard.WizardButton.CommitButton, "Install Julia") def initializePage(self): self._install_dir_line_edit.setText(jill_install.default_install_dir()) @@ -215,12 +216,12 @@ def initializePage(self): self.msg_success.emit("Julia installation started") cmd = python + " " + " ".join(args) self.msg.emit(f"$ {cmd}") - qApp.setOverrideCursor(QCursor(Qt.BusyCursor)) # pylint: disable=undefined-variable + QApplication.setOverrideCursor(QCursor(Qt.CursorShape.BusyCursor)) # pylint: disable=undefined-variable self._exec_mngr.start_execution() @Slot(int) def _handle_julia_install_finished(self, ret): - qApp.restoreOverrideCursor() # pylint: disable=undefined-variable + QApplication.restoreOverrideCursor() # pylint: disable=undefined-variable self._exec_mngr.execution_finished.disconnect(self._handle_julia_install_finished) if self.wizard().currentPage() != self: return diff --git a/tests/widgets/test_AddUpSpineOptWizard.py b/tests/widgets/test_AddUpSpineOptWizard.py index a73bfb19f..128cfcd69 100644 --- a/tests/widgets/test_AddUpSpineOptWizard.py +++ b/tests/widgets/test_AddUpSpineOptWizard.py @@ -41,7 +41,7 @@ def test_spine_opt_installation_succeeds(self): wizard.restart() self.assertEqual("Welcome", wizard.currentPage().title()) wizard.next() - self.assertEqual("Select Julia project", wizard.currentPage().title()) + self.assertEqual("Select Julia", wizard.currentPage().title()) with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: MockQProcess.return_value = MockInstantQProcess(finished_args=(0, MockQProcess.NormalExit)) wizard.next() @@ -60,7 +60,7 @@ def test_spine_opt_update_succeeds(self): wizard.restart() self.assertEqual("Welcome", wizard.currentPage().title()) wizard.next() - self.assertEqual("Select Julia project", wizard.currentPage().title()) + self.assertEqual("Select Julia", wizard.currentPage().title()) with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: # We need the process to return a version that's lower than required curr_ver_split = [int(x) for x in REQUIRED_SPINE_OPT_VERSION.split(".")] @@ -84,7 +84,7 @@ def test_spine_opt_already_up_to_date(self): wizard.restart() self.assertEqual("Welcome", wizard.currentPage().title()) wizard.next() - self.assertEqual("Select Julia project", wizard.currentPage().title()) + self.assertEqual("Select Julia", wizard.currentPage().title()) with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: stdout = REQUIRED_SPINE_OPT_VERSION.encode() MockQProcess.return_value = MockInstantQProcess(finished_args=(0, MockQProcess.NormalExit), stdout=stdout) @@ -97,7 +97,7 @@ def _make_failed_wizard(self): wizard.restart() self.assertEqual("Welcome", wizard.currentPage().title()) wizard.next() - self.assertEqual("Select Julia project", wizard.currentPage().title()) + self.assertEqual("Select Julia", wizard.currentPage().title()) with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: MockQProcess.return_value = MockInstantQProcess(finished_args=(0, MockQProcess.NormalExit)) wizard.next() @@ -120,38 +120,48 @@ def test_registry_reset_succeeds(self): wizard = self._make_failed_wizard() wizard.next() self.assertEqual("Troubleshooting", wizard.currentPage().title()) - wizard.setField("problem1", True) + wizard.setField("problem2", True) wizard.next() self.assertEqual("Reset Julia General Registry", wizard.currentPage().title()) self.assertTrue(wizard.currentPage().isCommitPage()) - self.assertEqual("Reset registry", wizard.currentPage().buttonText(QWizard.WizardButton.CommitButton)) + self.assertEqual("Install SpineOpt", wizard.currentPage().buttonText(QWizard.WizardButton.CommitButton)) with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: MockQProcess.return_value = MockInstantQProcess(finished_args=(0, MockQProcess.NormalExit)) wizard.next() - self.assertEqual("Resetting Julia General Registry", wizard.currentPage().title()) - self.assertTrue(wizard.currentPage().isCommitPage()) + self.assertEqual("Installing SpineOpt", wizard.currentPage().title()) + wizard.next() + self.assertEqual("Installation successful", wizard.currentPage().title()) + self.assertTrue(wizard.currentPage().isFinalPage()) + + def test_what_now_page(self): + wizard = self._make_failed_wizard() + wizard.next() + self.assertEqual("Troubleshooting", wizard.currentPage().title()) + wizard.setField("problem1", True) + wizard.next() + self.assertEqual("What now?", wizard.currentPage().title()) self.assertEqual("Install SpineOpt", wizard.currentPage().buttonText(QWizard.WizardButton.CommitButton)) with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: - MockQProcess.return_value = MockInstantQProcess(finished_args=(0, MockQProcess.NormalExit)) + MockQProcess.return_value = MockInstantQProcess(finished_args=(-1, MockQProcess.NormalExit)) wizard.next() self.assertEqual("Installing SpineOpt", wizard.currentPage().title()) wizard.next() - self.assertEqual("Installation successful", wizard.currentPage().title()) + self.assertEqual("Troubleshooting failed", wizard.currentPage().title()) self.assertTrue(wizard.currentPage().isFinalPage()) def test_registry_reset_fails(self): wizard = self._make_failed_wizard() wizard.next() self.assertEqual("Troubleshooting", wizard.currentPage().title()) - wizard.setField("problem1", True) + wizard.setField("problem2", True) wizard.next() self.assertEqual("Reset Julia General Registry", wizard.currentPage().title()) self.assertTrue(wizard.currentPage().isCommitPage()) - self.assertEqual("Reset registry", wizard.currentPage().buttonText(QWizard.WizardButton.CommitButton)) + self.assertEqual("Install SpineOpt", wizard.currentPage().buttonText(QWizard.WizardButton.CommitButton)) with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: MockQProcess.return_value = MockInstantQProcess(finished_args=(-1, MockQProcess.NormalExit)) wizard.next() - self.assertEqual("Resetting Julia General Registry", wizard.currentPage().title()) + self.assertEqual("Installing SpineOpt", wizard.currentPage().title()) wizard.next() self.assertEqual("Troubleshooting failed", wizard.currentPage().title()) self.assertTrue(wizard.currentPage().isFinalPage()) @@ -160,16 +170,10 @@ def test_registry_reset_succeeds_but_installing_spine_opt_fails_again_afterwards wizard = self._make_failed_wizard() wizard.next() self.assertEqual("Troubleshooting", wizard.currentPage().title()) - wizard.setField("problem1", True) + wizard.setField("problem2", True) wizard.next() self.assertEqual("Reset Julia General Registry", wizard.currentPage().title()) self.assertTrue(wizard.currentPage().isCommitPage()) - self.assertEqual("Reset registry", wizard.currentPage().buttonText(QWizard.WizardButton.CommitButton)) - with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: - MockQProcess.return_value = MockInstantQProcess(finished_args=(0, MockQProcess.NormalExit)) - wizard.next() - self.assertEqual("Resetting Julia General Registry", wizard.currentPage().title()) - self.assertTrue(wizard.currentPage().isCommitPage()) self.assertEqual("Install SpineOpt", wizard.currentPage().buttonText(QWizard.WizardButton.CommitButton)) with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: MockQProcess.return_value = MockInstantQProcess(finished_args=(-1, MockQProcess.NormalExit)) @@ -183,9 +187,9 @@ def test_updating_wmf_succeeds(self): wizard = self._make_failed_wizard() wizard.next() self.assertEqual("Troubleshooting", wizard.currentPage().title()) - wizard.setField("problem2", True) + wizard.setField("problem3", True) wizard.next() - self.assertEqual("Update Windows Managemet Framework", wizard.currentPage().title()) + self.assertEqual("Update Windows Management Framework", wizard.currentPage().title()) self.assertTrue(wizard.currentPage().isCommitPage()) self.assertEqual("Install SpineOpt", wizard.currentPage().buttonText(QWizard.WizardButton.CommitButton)) with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: @@ -200,9 +204,9 @@ def test_updating_wmf_fails(self): wizard = self._make_failed_wizard() wizard.next() self.assertEqual("Troubleshooting", wizard.currentPage().title()) - wizard.setField("problem2", True) + wizard.setField("problem3", True) wizard.next() - self.assertEqual("Update Windows Managemet Framework", wizard.currentPage().title()) + self.assertEqual("Update Windows Management Framework", wizard.currentPage().title()) self.assertTrue(wizard.currentPage().isCommitPage()) self.assertEqual("Install SpineOpt", wizard.currentPage().buttonText(QWizard.WizardButton.CommitButton)) with mock.patch("spinetoolbox.execution_managers.QProcess") as MockQProcess: