diff --git a/service/lib/agama/dbus/y2dir/software/modules/PackageCallbacks.rb b/service/lib/agama/dbus/y2dir/software/modules/PackageCallbacks.rb index 506c2145ad..6887b88388 100644 --- a/service/lib/agama/dbus/y2dir/software/modules/PackageCallbacks.rb +++ b/service/lib/agama/dbus/y2dir/software/modules/PackageCallbacks.rb @@ -41,6 +41,10 @@ def InitPackageCallbacks(logger = nil) Agama::Software::Callbacks::Media.new( questions_client, logger ).setup + + Agama::Software::Callbacks::Provide.new( + questions_client, logger + ).setup end # Returns the client to ask questions @@ -59,4 +63,3 @@ def questions_client PackageCallbacks = PackageCallbacksClass.new PackageCallbacks.main end - diff --git a/service/lib/agama/software/callbacks.rb b/service/lib/agama/software/callbacks.rb index a6e65f3e86..bbf5b90775 100644 --- a/service/lib/agama/software/callbacks.rb +++ b/service/lib/agama/software/callbacks.rb @@ -29,4 +29,5 @@ module Callbacks require "agama/software/callbacks/media" require "agama/software/callbacks/progress" +require "agama/software/callbacks/provide" require "agama/software/callbacks/signature" diff --git a/service/lib/agama/software/callbacks/provide.rb b/service/lib/agama/software/callbacks/provide.rb new file mode 100644 index 0000000000..be1add7113 --- /dev/null +++ b/service/lib/agama/software/callbacks/provide.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +# Copyright (c) [2025] SUSE LLC +# +# All Rights Reserved. +# +# 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. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "agama/question" + +Yast.import "Pkg" + +module Agama + module Software + module Callbacks + # Provide callbacks + class Provide + include Yast::I18n + + # From https://github.com/openSUSE/libzypp/blob/d90a93fc2a248e6592bd98114f82a0b88abadb72/zypp/ZYppCallbacks.h#L111 + NO_ERROR = 0 + NOT_FOUND = 1 + IO_ERROR = 2 + INVALID = 3 + + # Constructor + # + # @param questions_client [Agama::DBus::Clients::Questions] + # @param logger [Logger] + def initialize(questions_client, logger) + @questions_client = questions_client + @logger = logger + end + + # Register the callbacks + def setup + Pkg.CallbackDoneProvide( + fun_ref(method(:done_provide), "string (integer, string, string)") + ) + end + + # Media change callback + # + # @return [String] "I" for ignore, "R" for retry and "C" for abort (not implemented) + # @see https://github.com/yast/yast-yast2/blob/19180445ab935a25edd4ae0243aa7a3bcd09c9de/library/packages/src/modules/PackageCallbacks.rb#L620 + def done_provide(error, reason, name) + args = [error, reason, name] + logger.debug "DoneProvide callback: #{args.inspect}" + + message = case error + when NO_ERROR, NOT_FOUND + # "Not found" (error 1) is handled by the MediaChange callback. + nil + when IO_ERROR + Yast::Builtins.sformat(_("Package %1 could not be downloaded (input/output error)."), + name) + when INVALID + Yast::Builtins.sformat(_("Package %1 is broken, integrity check has failed."), name) + else + logger.warn "DoneProvide: unknown error: '#{error}'" + end + + return if message.nil? + + question = Agama::Question.new( + qclass: "software.provide_error", + text: message, + options: [:Retry, :Ignore], + default_option: :Retry, + data: { "reason" => reason } + ) + questions_client.ask(question) do |question_client| + (question_client.answer == :Retry) ? "R" : "I" + end + end + + private + + # @return [Agama::DBus::Clients::Questions] + attr_reader :questions_client + + # @return [Logger] + attr_reader :logger + end + end + end +end diff --git a/service/test/agama/software/callbacks/provide_test.rb b/service/test/agama/software/callbacks/provide_test.rb new file mode 100644 index 0000000000..0692a7a2d5 --- /dev/null +++ b/service/test/agama/software/callbacks/provide_test.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +# Copyright (c) [2022-2023] SUSE LLC +# +# All Rights Reserved. +# +# 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. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../../test_helper" +require "agama/software/callbacks/provide" +require "agama/dbus/clients/questions" +require "agama/dbus/clients/question" + +describe Agama::Software::Callbacks::Provide do + subject { described_class.new(questions_client, logger) } + + let(:questions_client) { instance_double(Agama::DBus::Clients::Questions) } + + let(:logger) { Logger.new($stdout, level: :warn) } + + let(:answer) { :Retry } + + describe "#media_changed" do + before do + allow(questions_client).to receive(:ask).and_yield(question_client) + allow(question_client).to receive(:answer).and_return(answer) + end + + let(:question_client) { instance_double(Agama::DBus::Clients::Question) } + + context "when the file is not found" do + it "does not register a question" do + expect(questions_client).to_not receive(:ask) + subject.done_provide(1, "Some dummy reason", "dummy-package") + end + end + + context "when the there is an I/O error" do + it "registers a question informing of the error" do + expect(questions_client).to receive(:ask) do |q| + expect(q.text).to include("could not be downloaded") + end + subject.done_provide(2, "Some dummy reason", "dummy-package") + end + end + + context "when the there is an I/O error" do + it "registers a question informing of the error" do + expect(questions_client).to receive(:ask) do |q| + expect(q.text).to include("integrity check has failed") + end + subject.done_provide(3, "Some dummy reason", "dummy-package") + end + end + + context "when the user answers :Retry" do + let(:answer) { :Retry } + + it "returns 'R'" do + ret = subject.done_provide( + 2, "Some dummy reason", "dummy-package" + ) + expect(ret).to eq("R") + end + end + + context "when the user answers :Skip" do + let(:answer) { :Ignore } + + it "returns 'I'" do + ret = subject.done_provide( + 2, "Some dummy reason", "dummy-package" + ) + expect(ret).to eq("I") + end + end + end +end