From 6417fee4a19a09c4f997ab7b96a92d187824a623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Fri, 18 Mar 2016 14:26:31 +0100 Subject: [PATCH 1/8] updated rpmlint config --- package/yast2-rpmlintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/yast2-rpmlintrc b/package/yast2-rpmlintrc index 0d7a1fc0f..0f86a54ef 100644 --- a/package/yast2-rpmlintrc +++ b/package/yast2-rpmlintrc @@ -1 +1 @@ -addFilter("invalid-desktopfile") +addFilter("desktopfile-without-binary") From 2c82b7dd241a2df6cd1bbacca681c01b8f427d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 9 Jun 2015 22:32:54 +0200 Subject: [PATCH 2/8] file conflicts support --- .../packages/src/modules/PackageCallbacks.rb | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/library/packages/src/modules/PackageCallbacks.rb b/library/packages/src/modules/PackageCallbacks.rb index 949a73d35..92f8ed56c 100644 --- a/library/packages/src/modules/PackageCallbacks.rb +++ b/library/packages/src/modules/PackageCallbacks.rb @@ -2487,6 +2487,110 @@ def ProcessDone nil end + # handle the file conflict detection start callback + def FileConflictStart + log.info "Starting file conflict check..." + # TRANSLATORS: progress bar label + label = _("Checking file conflicts...") + + if Mode.commandline + CommandLine.PrintVerbose(label) + elsif UI.WidgetExists(:progressCurrentPackage) + # package slideshow with progress already present + UI.ChangeWidget(Id(:progressCurrentPackage), :Value, 0) + UI.ChangeWidget(Id(:progressCurrentPackage), :Label, label) + else + # TRANSLATORS: help text for the file conflict detection progress + help = _("

Detecting the file conflicts is in progress.

") + Progress.Simple(label, "", 100, help) + end + end + + # Handle the file conflict detection progress callback. + # @param progress [Fixnum] progress in percents + # @return [Boolean] true = continue, false = abort + def FileConflictProgress(progress) + log.debug "File conflict progress: #{progress}%" + + sleep(1) + + if Mode.commandline + CommandLine.PrintVerboseNoCR("#{CLEAR_PROGRESS_TEXT}#{progress}%") + elsif UI.WidgetExists(:progressCurrentPackage) + UI.ChangeWidget(Id(:progressCurrentPackage), :Value, progress) + else + UI.ChangeWidget(Id(:pb), :Value, progress) + end + + ui = UI.PollInput + ui != :abort && ui != :cancel + end + + # Handle the file conflict detection result callback. + # Ask to user whether to continue. In the AutoYaST mode an error is reported + # but the installation will continue ignoring the confliucts. + # @param excluded_packages [Array] packages ignored in the check + # (e.g. not available for check in the download-as-needed mode) + # @param conflicts [Array] list of translated descriptions of + # the detected file conflicts + # @return [Boolean] true = continue, false = abort + def FileConflictReport(excluded_packages, conflicts) + log.info "Excluded #{excluded_packages.size} packages in file conflict check" + log.debug "Excluded packages: #{excluded_packages.inspect}" + log.info "Found #{conflicts.size} conflicts: #{conflicts.join("\n\n")}" + + # just continue installing packages if there is no conflict + return true if conflicts.empty? + + # don't ask in autoyast or command line mode, just report/log the issues and continue + if Mode.autoinst || Mode.autoupgrade || Mode.commandline + # TRANSLATORS: An error message, %s is the actual list of detected conflicts + Report.Error(_("File conflicts detected, the conflicting files will " \ + "be overwritten:\n\n%s") % conflicts.join("\n")) + return true + end + + button_box = ButtonBox( + PushButton(Id(:continue), Opt(:default, :okButton), Label.ContinueButton), + PushButton(Id(:cancel), Opt(:cancelButton), Label.CancelButton) + ) + + # TRANSLATORS: A popup label, use max. 70 chars per line, use more lines if needed + label = _("File conflicts happen when two packages attempt to install\n" \ + "files with the same name but different contents. If you continue,\n" \ + "the conflicting files will be replaced losing the previous content.") + + # TRANSLATORS: Popup heading + heading = n_("A File Conflict Detected", "File Conflicts Detected", conflicts.size) + + dialog = VBox( + Left(Heading(heading)), + VSpacing(0.2), + Left(Label(label)), + MinSize(65, 15, RichText(Opt(:plainText), conflicts.join("\n\n"))), + button_box + ) + + UI.OpenDialog(dialog) + + begin + UI.SetFocus(Id(:continue)) + ret = UI.UserInput + log.info "User Input: #{ret}" + ret == :continue + ensure + UI.CloseDialog + end + end + + # Handle the file conflict detection finish callback. + def FileConflictFinish + log.info "File conflict check finished" + return if Mode.commandline + + Progress.Finish unless UI.WidgetExists(:progressCurrentPackage) + end + # Register callbacks for media change def SetMediaCallbacks Pkg.CallbackMediaChange( @@ -2759,6 +2863,13 @@ def SetProgressReportCallbacks nil end + def SetFileConflictsCallbacks + Pkg.CallbackFileConflictStart(fun_ref(method(:FileConflictStart), "void ()")) + Pkg.CallbackFileConflictProgress(fun_ref(method(:FileConflictProgress), "boolean (integer)")) + Pkg.CallbackFileConflictReport(fun_ref(method(:FileConflictReport), "boolean (list, list)")) + Pkg.CallbackFileConflictFinish(fun_ref(method(:FileConflictFinish), "void ()")) + end + # Register package manager callbacks def InitPackageCallbacks SetProcessCallbacks() @@ -2775,6 +2886,8 @@ def InitPackageCallbacks SetProgressReportCallbacks() + SetFileConflictsCallbacks() + # authentication callback Pkg.CallbackAuthentication( fun_ref( From 55ccbdf1f0ec0bed4d3b71a38471c2857a71e615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Fri, 18 Mar 2016 14:37:08 +0100 Subject: [PATCH 3/8] trailing whitespace removed --- library/packages/src/modules/PackageCallbacks.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/packages/src/modules/PackageCallbacks.rb b/library/packages/src/modules/PackageCallbacks.rb index 92f8ed56c..9fa76d42b 100644 --- a/library/packages/src/modules/PackageCallbacks.rb +++ b/library/packages/src/modules/PackageCallbacks.rb @@ -2500,7 +2500,7 @@ def FileConflictStart UI.ChangeWidget(Id(:progressCurrentPackage), :Value, 0) UI.ChangeWidget(Id(:progressCurrentPackage), :Label, label) else - # TRANSLATORS: help text for the file conflict detection progress + # TRANSLATORS: help text for the file conflict detection progress help = _("

Detecting the file conflicts is in progress.

") Progress.Simple(label, "", 100, help) end From 857c960f9cd32b137deadfe78c98d9dd36101103 Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Mon, 21 Mar 2016 14:43:57 +0100 Subject: [PATCH 4/8] Fixed the callbacks for CLI UI.PollInput without a dialog open will throw a C++ exception and terminate the program --- library/packages/src/modules/PackageCallbacks.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/packages/src/modules/PackageCallbacks.rb b/library/packages/src/modules/PackageCallbacks.rb index 9fa76d42b..3ff8ad972 100644 --- a/library/packages/src/modules/PackageCallbacks.rb +++ b/library/packages/src/modules/PackageCallbacks.rb @@ -2522,7 +2522,7 @@ def FileConflictProgress(progress) UI.ChangeWidget(Id(:pb), :Value, progress) end - ui = UI.PollInput + ui = UI.PollInput unless Mode.commandline ui != :abort && ui != :cancel end From c53a8f99d1db16a5141a258efcdf6ba31480c709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 22 Mar 2016 14:55:24 +0100 Subject: [PATCH 5/8] refactoring - move the file conflict callbacks ... to a separate file --- library/packages/src/Makefile.am | 1 + .../src/modules/FileConflictCallbacks.rb | 152 ++++++++++++++++++ .../packages/src/modules/PackageCallbacks.rb | 117 +------------- 3 files changed, 160 insertions(+), 110 deletions(-) create mode 100644 library/packages/src/modules/FileConflictCallbacks.rb diff --git a/library/packages/src/Makefile.am b/library/packages/src/Makefile.am index ff4b9dd63..5fc30da00 100644 --- a/library/packages/src/Makefile.am +++ b/library/packages/src/Makefile.am @@ -1,6 +1,7 @@ # Sources for yast2 module_DATA = \ + modules/FileConflictCallbacks.rb \ modules/PackageCallbacks.rb \ modules/SignatureCheckDialogs.rb \ modules/PackageLock.rb \ diff --git a/library/packages/src/modules/FileConflictCallbacks.rb b/library/packages/src/modules/FileConflictCallbacks.rb new file mode 100644 index 000000000..0529307e8 --- /dev/null +++ b/library/packages/src/modules/FileConflictCallbacks.rb @@ -0,0 +1,152 @@ + +# ------------------------------------------------------------------------------ +# Copyright (c) 2016 SUSE LLC +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of version 2 of the GNU General Public License as published by the +# Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# ------------------------------------------------------------------------------ +# + +require "yast" + +module Yast + # Provides the default Callbacks for Pkg:: + class FileConflictCallbacksClass < Module + include Yast::Logger + + def main + Yast.import "Pkg" + Yast.import "UI" + Yast.import "Progress" + Yast.import "Mode" + Yast.import "CommandLine" + Yast.import "Report" + Yast.import "Wizard" + Yast.import "Label" + + textdomain "base" + end + + # handle the file conflict detection start callback + def FileConflictStart + log.info "Starting the file conflict check..." + # TRANSLATORS: progress bar label + label = _("Checking file conflicts...") + + if Mode.commandline + CommandLine.PrintVerbose(label) + elsif UI.WidgetExists(:progressCurrentPackage) + # package slideshow with progress already present + UI.ChangeWidget(Id(:progressCurrentPackage), :Value, 0) + UI.ChangeWidget(Id(:progressCurrentPackage), :Label, label) + else + # TRANSLATORS: help text for the file conflict detection progress + help = _("

Detecting the file conflicts is in progress.

") + Progress.Simple(label, "", 100, help) + end + end + + # Handle the file conflict detection progress callback. + # @param progress [Fixnum] progress in percents + # @return [Boolean] true = continue, false = abort + def FileConflictProgress(progress) + log.debug "File conflict progress: #{progress}%" + + # FIXME: remove this, just make the progress visible for testing + sleep(1) + + if Mode.commandline + CommandLine.PrintVerboseNoCR("#{CLEAR_PROGRESS_TEXT}#{progress}%") + elsif UI.WidgetExists(:progressCurrentPackage) + UI.ChangeWidget(Id(:progressCurrentPackage), :Value, progress) + else + UI.ChangeWidget(Id(:pb), :Value, progress) + end + + ui = UI.PollInput unless Mode.commandline + ui != :abort && ui != :cancel + end + + # Handle the file conflict detection result callback. + # Ask to user whether to continue. In the AutoYaST mode an error is reported + # but the installation will continue ignoring the confliucts. + # @param excluded_packages [Array] packages ignored in the check + # (e.g. not available for check in the download-as-needed mode) + # @param conflicts [Array] list of translated descriptions of + # the detected file conflicts + # @return [Boolean] true = continue, false = abort + def FileConflictReport(excluded_packages, conflicts) + log.info "Excluded #{excluded_packages.size} packages in file conflict check" + log.debug "Excluded packages: #{excluded_packages.inspect}" + log.info "Found #{conflicts.size} conflicts: #{conflicts.join("\n\n")}" + + # just continue installing packages if there is no conflict + return true if conflicts.empty? + + # don't ask in autoyast or command line mode, just report/log the issues and continue + if Mode.auto || Mode.commandline + # TRANSLATORS: An error message, %s is the actual list of detected conflicts + Report.Error(_("File conflicts detected, these conflicting files will " \ + "be overwritten:\n\n%s") % conflicts.join("\n")) + return true + end + + UI.OpenDialog(dialog(conflicts)) + + begin + UI.SetFocus(Id(:continue)) + ret = UI.UserInput + log.info "User Input: #{ret}" + ret == :continue + ensure + UI.CloseDialog + end + end + + # Handle the file conflict detection finish callback. + def FileConflictFinish + log.info "File conflict check finished" + return if Mode.commandline + + Progress.Finish unless UI.WidgetExists(:progressCurrentPackage) + end + + private + + # Construct the file conflicts dialog. + # @param [Array] conflicts file conflicts reported by libzypp + # (in human readable form) + # @return [Term] UI term + def dialog(conflicts) + button_box = ButtonBox( + PushButton(Id(:continue), Opt(:default, :okButton), Label.ContinueButton), + PushButton(Id(:abort), Opt(:cancelButton), Label.AbortButton) + ) + + # TRANSLATORS: A popup label, use max. 70 chars per line, use more lines if needed + label = _("File conflicts happen when two packages attempt to install\n" \ + "files with the same name but different contents. If you continue,\n" \ + "the conflicting files will be replaced losing the previous content.") + + # TRANSLATORS: Popup heading + heading = n_("A File Conflict Detected", "File Conflicts Detected", conflicts.size) + + VBox( + Left(Heading(heading)), + VSpacing(0.2), + Left(Label(label)), + MinSize(65, 15, RichText(Opt(:plainText), conflicts.join("\n\n"))), + button_box + ) + end + end + + FileConflictCallbacks = FileConflictCallbacksClass.new + FileConflictCallbacks.main +end diff --git a/library/packages/src/modules/PackageCallbacks.rb b/library/packages/src/modules/PackageCallbacks.rb index 3ff8ad972..6ee79e749 100644 --- a/library/packages/src/modules/PackageCallbacks.rb +++ b/library/packages/src/modules/PackageCallbacks.rb @@ -65,6 +65,7 @@ def main Yast.import "Progress" Yast.import "FileUtils" Yast.import "SignatureCheckCallbacks" + Yast.import "FileConflictCallbacks" @_provide_popup = false @_package_popup = false @@ -2487,110 +2488,6 @@ def ProcessDone nil end - # handle the file conflict detection start callback - def FileConflictStart - log.info "Starting file conflict check..." - # TRANSLATORS: progress bar label - label = _("Checking file conflicts...") - - if Mode.commandline - CommandLine.PrintVerbose(label) - elsif UI.WidgetExists(:progressCurrentPackage) - # package slideshow with progress already present - UI.ChangeWidget(Id(:progressCurrentPackage), :Value, 0) - UI.ChangeWidget(Id(:progressCurrentPackage), :Label, label) - else - # TRANSLATORS: help text for the file conflict detection progress - help = _("

Detecting the file conflicts is in progress.

") - Progress.Simple(label, "", 100, help) - end - end - - # Handle the file conflict detection progress callback. - # @param progress [Fixnum] progress in percents - # @return [Boolean] true = continue, false = abort - def FileConflictProgress(progress) - log.debug "File conflict progress: #{progress}%" - - sleep(1) - - if Mode.commandline - CommandLine.PrintVerboseNoCR("#{CLEAR_PROGRESS_TEXT}#{progress}%") - elsif UI.WidgetExists(:progressCurrentPackage) - UI.ChangeWidget(Id(:progressCurrentPackage), :Value, progress) - else - UI.ChangeWidget(Id(:pb), :Value, progress) - end - - ui = UI.PollInput unless Mode.commandline - ui != :abort && ui != :cancel - end - - # Handle the file conflict detection result callback. - # Ask to user whether to continue. In the AutoYaST mode an error is reported - # but the installation will continue ignoring the confliucts. - # @param excluded_packages [Array] packages ignored in the check - # (e.g. not available for check in the download-as-needed mode) - # @param conflicts [Array] list of translated descriptions of - # the detected file conflicts - # @return [Boolean] true = continue, false = abort - def FileConflictReport(excluded_packages, conflicts) - log.info "Excluded #{excluded_packages.size} packages in file conflict check" - log.debug "Excluded packages: #{excluded_packages.inspect}" - log.info "Found #{conflicts.size} conflicts: #{conflicts.join("\n\n")}" - - # just continue installing packages if there is no conflict - return true if conflicts.empty? - - # don't ask in autoyast or command line mode, just report/log the issues and continue - if Mode.autoinst || Mode.autoupgrade || Mode.commandline - # TRANSLATORS: An error message, %s is the actual list of detected conflicts - Report.Error(_("File conflicts detected, the conflicting files will " \ - "be overwritten:\n\n%s") % conflicts.join("\n")) - return true - end - - button_box = ButtonBox( - PushButton(Id(:continue), Opt(:default, :okButton), Label.ContinueButton), - PushButton(Id(:cancel), Opt(:cancelButton), Label.CancelButton) - ) - - # TRANSLATORS: A popup label, use max. 70 chars per line, use more lines if needed - label = _("File conflicts happen when two packages attempt to install\n" \ - "files with the same name but different contents. If you continue,\n" \ - "the conflicting files will be replaced losing the previous content.") - - # TRANSLATORS: Popup heading - heading = n_("A File Conflict Detected", "File Conflicts Detected", conflicts.size) - - dialog = VBox( - Left(Heading(heading)), - VSpacing(0.2), - Left(Label(label)), - MinSize(65, 15, RichText(Opt(:plainText), conflicts.join("\n\n"))), - button_box - ) - - UI.OpenDialog(dialog) - - begin - UI.SetFocus(Id(:continue)) - ret = UI.UserInput - log.info "User Input: #{ret}" - ret == :continue - ensure - UI.CloseDialog - end - end - - # Handle the file conflict detection finish callback. - def FileConflictFinish - log.info "File conflict check finished" - return if Mode.commandline - - Progress.Finish unless UI.WidgetExists(:progressCurrentPackage) - end - # Register callbacks for media change def SetMediaCallbacks Pkg.CallbackMediaChange( @@ -2863,11 +2760,11 @@ def SetProgressReportCallbacks nil end - def SetFileConflictsCallbacks - Pkg.CallbackFileConflictStart(fun_ref(method(:FileConflictStart), "void ()")) - Pkg.CallbackFileConflictProgress(fun_ref(method(:FileConflictProgress), "boolean (integer)")) - Pkg.CallbackFileConflictReport(fun_ref(method(:FileConflictReport), "boolean (list, list)")) - Pkg.CallbackFileConflictFinish(fun_ref(method(:FileConflictFinish), "void ()")) + def SetFileConflictCallbacks + Pkg.CallbackFileConflictStart(fun_ref(FileConflictCallbacks.method(:FileConflictStart), "void ()")) + Pkg.CallbackFileConflictProgress(fun_ref(FileConflictCallbacks.method(:FileConflictProgress), "boolean (integer)")) + Pkg.CallbackFileConflictReport(fun_ref(FileConflictCallbacks.method(:FileConflictReport), "boolean (list, list)")) + Pkg.CallbackFileConflictFinish(fun_ref(FileConflictCallbacks.method(:FileConflictFinish), "void ()")) end # Register package manager callbacks @@ -2886,7 +2783,7 @@ def InitPackageCallbacks SetProgressReportCallbacks() - SetFileConflictsCallbacks() + SetFileConflictCallbacks() # authentication callback Pkg.CallbackAuthentication( From 78f693cbdc13d1621b8e0a57789d661f6b5326c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 22 Mar 2016 16:47:14 +0100 Subject: [PATCH 6/8] added missing constant --- library/packages/src/modules/FileConflictCallbacks.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library/packages/src/modules/FileConflictCallbacks.rb b/library/packages/src/modules/FileConflictCallbacks.rb index 0529307e8..a8b922f20 100644 --- a/library/packages/src/modules/FileConflictCallbacks.rb +++ b/library/packages/src/modules/FileConflictCallbacks.rb @@ -16,10 +16,18 @@ require "yast" module Yast - # Provides the default Callbacks for Pkg:: + # Provides the default file conflicts callbacks for Pkg:: + # + # Notes: Libzypp runs the check only in the DownloadInAdvance mode, + # in other modes the check is skipped. That basically means the check is + # skipped during the initial installation (it uses DownloadAsNeeded mode). + # class FileConflictCallbacksClass < Module include Yast::Logger + # text to clean progress bar in command line + CLEAR_PROGRESS_TEXT = "\b" * 10 + " " * 10 + "\b" * 10 + def main Yast.import "Pkg" Yast.import "UI" From 6667b1e4b085e47503cc794611033cc7ef476d3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Thu, 24 Mar 2016 19:28:08 +0100 Subject: [PATCH 7/8] move the file conflicts to lib/packages/file_conflict_callbacks.rb added initial unit tests --- library/packages/src/Makefile.am | 2 +- .../lib/packages/file_conflict_callbacks.rb | 169 ++++++++++++ .../src/modules/FileConflictCallbacks.rb | 160 ------------ .../packages/src/modules/PackageCallbacks.rb | 9 +- library/packages/test/Makefile.am | 1 + .../test/file_conflict_callbacks_test.rb | 247 ++++++++++++++++++ 6 files changed, 422 insertions(+), 166 deletions(-) create mode 100644 library/packages/src/lib/packages/file_conflict_callbacks.rb delete mode 100644 library/packages/src/modules/FileConflictCallbacks.rb create mode 100644 library/packages/test/file_conflict_callbacks_test.rb diff --git a/library/packages/src/Makefile.am b/library/packages/src/Makefile.am index 5fc30da00..0216584c1 100644 --- a/library/packages/src/Makefile.am +++ b/library/packages/src/Makefile.am @@ -1,7 +1,6 @@ # Sources for yast2 module_DATA = \ - modules/FileConflictCallbacks.rb \ modules/PackageCallbacks.rb \ modules/SignatureCheckDialogs.rb \ modules/PackageLock.rb \ @@ -24,6 +23,7 @@ ylibdir = "${yast2dir}/lib/packages" ylib_DATA = \ lib/packages/commit_result.rb \ lib/packages/dummy_callbacks.rb \ + lib/packages/file_conflict_callbacks.rb \ lib/packages/update_message.rb \ lib/packages/update_messages_view.rb diff --git a/library/packages/src/lib/packages/file_conflict_callbacks.rb b/library/packages/src/lib/packages/file_conflict_callbacks.rb new file mode 100644 index 000000000..96c2fcdf9 --- /dev/null +++ b/library/packages/src/lib/packages/file_conflict_callbacks.rb @@ -0,0 +1,169 @@ + +# ------------------------------------------------------------------------------ +# Copyright (c) 2016 SUSE LLC +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of version 2 of the GNU General Public License as published by the +# Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# ------------------------------------------------------------------------------ +# + +require "yast" + +module Packages + # Default file conflicts callbacks for package bindings. To register the + # callbacks in Yast::Pkg just call {Package::FileConflictCallbacks.register} + class FileConflictCallbacks + class << self + include Yast::Logger + include Yast::I18n + include Yast::UIShortcuts + + # register the file conflict callbacks + def register + Yast.import "Pkg" + Yast.import "UI" + Yast.import "Progress" + Yast.import "Mode" + Yast.import "CommandLine" + Yast.import "Report" + Yast.import "Label" + Yast.import "PackageCallbacks" + + textdomain "base" + + register_file_conflict_callbacks + end + + private + + def fun_ref(*args) + Yast::FunRef.new(*args) + end + + def register_file_conflict_callbacks + Yast::Pkg.CallbackFileConflictStart(fun_ref(method(:start), "void ()")) + Yast::Pkg.CallbackFileConflictProgress(fun_ref(method(:progress), + "boolean (integer)")) + Yast::Pkg.CallbackFileConflictReport(fun_ref(method(:report), + "boolean (list, list)")) + Yast::Pkg.CallbackFileConflictFinish(fun_ref(method(:finish), "void ()")) + + nil + end + + # handle the file conflict detection start callback + def start + log.info "Starting the file conflict check..." + # TRANSLATORS: progress bar label + label = _("Checking file conflicts...") + + if Yast::Mode.commandline + Yast::CommandLine.PrintVerbose(label) + elsif Yast::UI.WidgetExists(:progressCurrentPackage) + # package slideshow with progress already present + Yast::UI.ChangeWidget(Id(:progressCurrentPackage), :Value, 0) + Yast::UI.ChangeWidget(Id(:progressCurrentPackage), :Label, label) + else + # TRANSLATORS: help text for the file conflict detection progress + help = _("

Detecting the file conflicts is in progress.

") + Yast::Progress.Simple(label, "", 100, help) + end + end + + # Handle the file conflict detection progress callback. + # @param [Fixnum] progress progress in percents + # @return [Boolean] true = continue, false = abort + def progress(progress) + log.debug "File conflict progress: #{progress}%" + + if Yast::Mode.commandline + Yast::CommandLine.PrintVerboseNoCR("#{Yast::PackageCallbacksClass::CLEAR_PROGRESS_TEXT}#{progress}%") + elsif Yast::UI.WidgetExists(:progressCurrentPackage) + Yast::UI.ChangeWidget(Id(:progressCurrentPackage), :Value, progress) + else + Yast::UI.ChangeWidget(Id(:pb), :Value, progress) + end + + ui = Yast::UI.PollInput unless Yast::Mode.commandline + ui != :abort && ui != :cancel + end + + # Handle the file conflict detection result callback. + # Ask to user whether to continue. In the AutoYaST mode an error is reported + # but the installation will continue ignoring the confliucts. + # @param excluded_packages [Array] packages ignored in the check + # (e.g. not available for check in the download-as-needed mode) + # @param conflicts [Array] list of translated descriptions of + # the detected file conflicts + # @return [Boolean] true = continue, false = abort + def report(excluded_packages, conflicts) + log.info "Excluded #{excluded_packages.size} packages in file conflict check" + log.debug "Excluded packages: #{excluded_packages.inspect}" + log.info "Found #{conflicts.size} conflicts: #{conflicts.join("\n\n")}" + + # just continue installing packages if there is no conflict + return true if conflicts.empty? + + # don't ask in autoyast or command line mode, just report/log the issues and continue + if Yast::Mode.auto || Yast::Mode.commandline + # TRANSLATORS: An error message, %s is the actual list of detected conflicts + Yast::Report.Error(_("File conflicts detected, these conflicting files will " \ + "be overwritten:\n\n%s") % conflicts.join("\n")) + return true + end + + Yast::UI.OpenDialog(dialog(conflicts)) + + begin + Yast::UI.SetFocus(Id(:continue)) + ret = Yast::UI.UserInput + log.info "User Input: #{ret}" + ret == :continue + ensure + Yast::UI.CloseDialog + end + end + + # Handle the file conflict detection finish callback. + def finish + log.info "File conflict check finished" + return if Yast::Mode.commandline + + Yast::Progress.Finish unless Yast::UI.WidgetExists(:progressCurrentPackage) + end + + # Construct the file conflicts dialog. + # @param [Array] conflicts file conflicts reported by libzypp + # (in human readable form) + # @return [Term] UI term + def dialog(conflicts) + button_box = ButtonBox( + PushButton(Id(:continue), Opt(:default, :okButton), Yast::Label.ContinueButton), + PushButton(Id(:abort), Opt(:cancelButton), Yast::Label.AbortButton) + ) + + # TRANSLATORS: A popup label, use max. 70 chars per line, use more lines if needed + label = _("File conflicts happen when two packages attempt to install\n" \ + "files with the same name but different contents. If you continue,\n" \ + "the conflicting files will be replaced losing the previous content.") + + # TRANSLATORS: Popup heading + heading = n_("A File Conflict Detected", "File Conflicts Detected", conflicts.size) + + VBox( + Left(Heading(heading)), + VSpacing(0.2), + Left(Label(label)), + MinSize(65, 15, RichText(Opt(:plainText), conflicts.join("\n\n"))), + button_box + ) + end + end + end +end diff --git a/library/packages/src/modules/FileConflictCallbacks.rb b/library/packages/src/modules/FileConflictCallbacks.rb deleted file mode 100644 index a8b922f20..000000000 --- a/library/packages/src/modules/FileConflictCallbacks.rb +++ /dev/null @@ -1,160 +0,0 @@ - -# ------------------------------------------------------------------------------ -# Copyright (c) 2016 SUSE LLC -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of version 2 of the GNU General Public License as published by the -# Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# ------------------------------------------------------------------------------ -# - -require "yast" - -module Yast - # Provides the default file conflicts callbacks for Pkg:: - # - # Notes: Libzypp runs the check only in the DownloadInAdvance mode, - # in other modes the check is skipped. That basically means the check is - # skipped during the initial installation (it uses DownloadAsNeeded mode). - # - class FileConflictCallbacksClass < Module - include Yast::Logger - - # text to clean progress bar in command line - CLEAR_PROGRESS_TEXT = "\b" * 10 + " " * 10 + "\b" * 10 - - def main - Yast.import "Pkg" - Yast.import "UI" - Yast.import "Progress" - Yast.import "Mode" - Yast.import "CommandLine" - Yast.import "Report" - Yast.import "Wizard" - Yast.import "Label" - - textdomain "base" - end - - # handle the file conflict detection start callback - def FileConflictStart - log.info "Starting the file conflict check..." - # TRANSLATORS: progress bar label - label = _("Checking file conflicts...") - - if Mode.commandline - CommandLine.PrintVerbose(label) - elsif UI.WidgetExists(:progressCurrentPackage) - # package slideshow with progress already present - UI.ChangeWidget(Id(:progressCurrentPackage), :Value, 0) - UI.ChangeWidget(Id(:progressCurrentPackage), :Label, label) - else - # TRANSLATORS: help text for the file conflict detection progress - help = _("

Detecting the file conflicts is in progress.

") - Progress.Simple(label, "", 100, help) - end - end - - # Handle the file conflict detection progress callback. - # @param progress [Fixnum] progress in percents - # @return [Boolean] true = continue, false = abort - def FileConflictProgress(progress) - log.debug "File conflict progress: #{progress}%" - - # FIXME: remove this, just make the progress visible for testing - sleep(1) - - if Mode.commandline - CommandLine.PrintVerboseNoCR("#{CLEAR_PROGRESS_TEXT}#{progress}%") - elsif UI.WidgetExists(:progressCurrentPackage) - UI.ChangeWidget(Id(:progressCurrentPackage), :Value, progress) - else - UI.ChangeWidget(Id(:pb), :Value, progress) - end - - ui = UI.PollInput unless Mode.commandline - ui != :abort && ui != :cancel - end - - # Handle the file conflict detection result callback. - # Ask to user whether to continue. In the AutoYaST mode an error is reported - # but the installation will continue ignoring the confliucts. - # @param excluded_packages [Array] packages ignored in the check - # (e.g. not available for check in the download-as-needed mode) - # @param conflicts [Array] list of translated descriptions of - # the detected file conflicts - # @return [Boolean] true = continue, false = abort - def FileConflictReport(excluded_packages, conflicts) - log.info "Excluded #{excluded_packages.size} packages in file conflict check" - log.debug "Excluded packages: #{excluded_packages.inspect}" - log.info "Found #{conflicts.size} conflicts: #{conflicts.join("\n\n")}" - - # just continue installing packages if there is no conflict - return true if conflicts.empty? - - # don't ask in autoyast or command line mode, just report/log the issues and continue - if Mode.auto || Mode.commandline - # TRANSLATORS: An error message, %s is the actual list of detected conflicts - Report.Error(_("File conflicts detected, these conflicting files will " \ - "be overwritten:\n\n%s") % conflicts.join("\n")) - return true - end - - UI.OpenDialog(dialog(conflicts)) - - begin - UI.SetFocus(Id(:continue)) - ret = UI.UserInput - log.info "User Input: #{ret}" - ret == :continue - ensure - UI.CloseDialog - end - end - - # Handle the file conflict detection finish callback. - def FileConflictFinish - log.info "File conflict check finished" - return if Mode.commandline - - Progress.Finish unless UI.WidgetExists(:progressCurrentPackage) - end - - private - - # Construct the file conflicts dialog. - # @param [Array] conflicts file conflicts reported by libzypp - # (in human readable form) - # @return [Term] UI term - def dialog(conflicts) - button_box = ButtonBox( - PushButton(Id(:continue), Opt(:default, :okButton), Label.ContinueButton), - PushButton(Id(:abort), Opt(:cancelButton), Label.AbortButton) - ) - - # TRANSLATORS: A popup label, use max. 70 chars per line, use more lines if needed - label = _("File conflicts happen when two packages attempt to install\n" \ - "files with the same name but different contents. If you continue,\n" \ - "the conflicting files will be replaced losing the previous content.") - - # TRANSLATORS: Popup heading - heading = n_("A File Conflict Detected", "File Conflicts Detected", conflicts.size) - - VBox( - Left(Heading(heading)), - VSpacing(0.2), - Left(Label(label)), - MinSize(65, 15, RichText(Opt(:plainText), conflicts.join("\n\n"))), - button_box - ) - end - end - - FileConflictCallbacks = FileConflictCallbacksClass.new - FileConflictCallbacks.main -end diff --git a/library/packages/src/modules/PackageCallbacks.rb b/library/packages/src/modules/PackageCallbacks.rb index 6ee79e749..f4c02b3cb 100644 --- a/library/packages/src/modules/PackageCallbacks.rb +++ b/library/packages/src/modules/PackageCallbacks.rb @@ -25,6 +25,7 @@ require "yast" require "uri" require "packages/dummy_callbacks" +require "packages/file_conflict_callbacks" module Yast # Provides the default Callbacks for Pkg:: @@ -65,7 +66,6 @@ def main Yast.import "Progress" Yast.import "FileUtils" Yast.import "SignatureCheckCallbacks" - Yast.import "FileConflictCallbacks" @_provide_popup = false @_package_popup = false @@ -2761,14 +2761,13 @@ def SetProgressReportCallbacks end def SetFileConflictCallbacks - Pkg.CallbackFileConflictStart(fun_ref(FileConflictCallbacks.method(:FileConflictStart), "void ()")) - Pkg.CallbackFileConflictProgress(fun_ref(FileConflictCallbacks.method(:FileConflictProgress), "boolean (integer)")) - Pkg.CallbackFileConflictReport(fun_ref(FileConflictCallbacks.method(:FileConflictReport), "boolean (list, list)")) - Pkg.CallbackFileConflictFinish(fun_ref(FileConflictCallbacks.method(:FileConflictFinish), "void ()")) + log.warn "Registering file conflict callbacks" + ::Packages::FileConflictCallbacks.register end # Register package manager callbacks def InitPackageCallbacks + log.warn "*** INIT Registering callbacks" SetProcessCallbacks() SetProvideCallbacks() diff --git a/library/packages/test/Makefile.am b/library/packages/test/Makefile.am index d02cc2d3e..2a83ec62b 100644 --- a/library/packages/test/Makefile.am +++ b/library/packages/test/Makefile.am @@ -1,6 +1,7 @@ TESTS = \ commit_result_test.rb \ dummy_callbacks_test.rb \ + file_conflict_callbacks_test.rb \ package_callbacks_test.rb \ packages_ui_test.rb \ product_test.rb \ diff --git a/library/packages/test/file_conflict_callbacks_test.rb b/library/packages/test/file_conflict_callbacks_test.rb new file mode 100644 index 000000000..45f3c3bb3 --- /dev/null +++ b/library/packages/test/file_conflict_callbacks_test.rb @@ -0,0 +1,247 @@ +#! /usr/bin/env rspec + +require_relative "test_helper" + +require "packages/file_conflict_callbacks" + +# a helper class to replace Yast::Pkg +class DummyPkg + # remember the registered file conflict callback handlers to test them later + attr_reader :fc_start, :fc_progress, :fc_report, :fc_finish + + def CallbackFileConflictStart(func) + @fc_start = func + end + + def CallbackFileConflictProgress(func) + @fc_progress = func + end + + def CallbackFileConflictReport(func) + @fc_report = func + end + + def CallbackFileConflictFinish(func) + @fc_finish = func + end +end + +describe Packages::FileConflictCallbacks do + let(:dummy_pkg) { DummyPkg.new } + + before do + stub_const("Yast::Pkg", dummy_pkg) + + # do not print anything + Yast.import "CommandLine" + allow(Yast::CommandLine).to receive(:Print) + allow(Yast::CommandLine).to receive(:PrintVerbose) + allow(Yast::CommandLine).to receive(:PrintVerboseNoCR) + end + + describe ".register" do + it "calls the Pkg methods for registering the file conflicts handlers" do + expect(dummy_pkg).to receive(:CallbackFileConflictStart) + expect(dummy_pkg).to receive(:CallbackFileConflictProgress) + expect(dummy_pkg).to receive(:CallbackFileConflictReport) + expect(dummy_pkg).to receive(:CallbackFileConflictFinish) + + Packages::FileConflictCallbacks.register + end + end + + describe "the registered start callback handler" do + let(:start_cb) do + Packages::FileConflictCallbacks.register + dummy_pkg.fc_start + end + + context "in the command line mode" do + before do + allow(Yast::Mode).to receive(:commandline).and_return(true) + end + + it "does not call any UI method" do + ui = double("no method call expected") + stub_const("Yast::UI", ui) + + start_cb.call + end + end + + context "in UI mode" do + # TODO + it "should initialize the progress" + it "should open a new progress" + end + end + + describe "the registered progress callback handler" do + let(:progress_cb) do + Packages::FileConflictCallbacks.register + dummy_pkg.fc_progress + end + + # fake progress value (percent) + let(:progress) { 42 } + + context "in the command line mode" do + before do + allow(Yast::Mode).to receive(:commandline).and_return(true) + end + + it "does not call any UI method" do + ui = double("no method call expected") + stub_const("Yast::UI", ui) + + progress_cb.call(progress) + end + + it "prints the current progress" do + expect(Yast::CommandLine).to receive(:PrintVerboseNoCR).with(/42%/) + + progress_cb.call(progress) + end + + it "returns true to continue" do + expect(progress_cb.call(progress)).to eq(true) + end + end + + context "in UI mode" do + it "returns false to abort if user clicks Abort" do + expect(Yast::UI).to receive(:PollInput).and_return(:abort) + + expect(progress_cb.call(progress)).to eq(false) + end + + it "returns true to continue when no user input" do + expect(Yast::UI).to receive(:PollInput).and_return(nil) + + expect(progress_cb.call(progress)).to eq(true) + end + + it "returns true to continue on unknown user input" do + expect(Yast::UI).to receive(:PollInput).and_return(:next) + + expect(progress_cb.call(progress)).to eq(true) + end + end + end + + describe "the registered report callback handler" do + let(:report_cb) do + Packages::FileConflictCallbacks.register + dummy_pkg.fc_report + end + + context "no conflict found" do + let(:conflicts) { [] } + let(:excluded) { [] } + + before do + allow(Yast::Mode).to receive(:commandline).and_return(true) + end + + it "does not check the command line mode, it behaves same as in the UI mode" do + expect(Yast::Mode).to_not receive(:commandline) + report_cb.call(excluded, conflicts) + end + + it "does not call any UI method" do + ui = double("no method call expected") + stub_const("Yast::UI", ui) + + report_cb.call(excluded, conflicts) + end + + it "returns true to continue" do + expect(report_cb.call(excluded, conflicts)).to eq(true) + end + end + + context "conflicts found" do + let(:conflicts) { [ "conflict1!", "conflict2!" ] } + let(:excluded) { [] } + + context "in the command line mode" do + before do + allow(Yast::Mode).to receive(:commandline).and_return(true) + end + + it "does not call any UI method" do + ui = double("no method call expected") + stub_const("Yast::UI", ui) + + report_cb.call(excluded, conflicts) + end + + it "prints the found conflicts" do + expect(Yast::Report).to receive(:Error) + + report_cb.call(excluded, conflicts) + end + + it "returns true to continue" do + expect(report_cb.call(excluded, conflicts)).to eq(true) + end + end + + context "in AutoYaST mode" do + # TODO + end + + context "in UI mode" do + before do + allow(Yast::UI).to receive(:OpenDialog) + allow(Yast::UI).to receive(:CloseDialog) + allow(Yast::UI).to receive(:SetFocus) + end + + it "opens a Popup dialog, waits for user input and closes the dialog" do + expect(Yast::UI).to receive(:OpenDialog).ordered + expect(Yast::UI).to receive(:UserInput).ordered + expect(Yast::UI).to receive(:CloseDialog).ordered + + report_cb.call(excluded, conflicts) + end + + it "returns false to abort if user clicks Abort" do + expect(Yast::UI).to receive(:UserInput).and_return(:abort) + + expect(report_cb.call(excluded, conflicts)).to eq(false) + end + + it "returns true to continue if user clicks Continue" do + expect(Yast::UI).to receive(:UserInput).and_return(:continue) + + expect(report_cb.call(excluded, conflicts)).to eq(true) + end + end + end + end + + describe "the registered finish callback handler" do + let(:finish_cb) do + Packages::FileConflictCallbacks.register + dummy_pkg.fc_finish + end + + context "in the command line mode" do + before do + allow(Yast::Mode).to receive(:commandline).and_return(true) + end + + it "does not call any UI method" do + ui = double("no method call expected") + stub_const("Yast::UI", ui) + + finish_cb.call + end + end + + context "in UI mode" do + # TODO + end + end +end From f34d972d6f5688c37e5685c70b4d87c26e36188c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Slez=C3=A1k?= Date: Tue, 29 Mar 2016 09:49:12 +0200 Subject: [PATCH 8/8] set also the progress label --- library/packages/src/lib/packages/file_conflict_callbacks.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/packages/src/lib/packages/file_conflict_callbacks.rb b/library/packages/src/lib/packages/file_conflict_callbacks.rb index 96c2fcdf9..d69616ce7 100644 --- a/library/packages/src/lib/packages/file_conflict_callbacks.rb +++ b/library/packages/src/lib/packages/file_conflict_callbacks.rb @@ -73,6 +73,8 @@ def start # TRANSLATORS: help text for the file conflict detection progress help = _("

Detecting the file conflicts is in progress.

") Yast::Progress.Simple(label, "", 100, help) + # Set also the progress bar widget label + Yast::Progress.Title(label) end end