From affc85212d78683dfa8fa390e6a2beb418a58d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Mon, 27 Nov 2017 14:29:38 +0000 Subject: [PATCH] Add a DiskSelector dialog --- src/lib/autoinstall/dialogs/disk_selector.rb | 152 +++++++++++++++++++ test/lib/dialogs/disk_selector_test.rb | 132 ++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 src/lib/autoinstall/dialogs/disk_selector.rb create mode 100644 test/lib/dialogs/disk_selector_test.rb diff --git a/src/lib/autoinstall/dialogs/disk_selector.rb b/src/lib/autoinstall/dialogs/disk_selector.rb new file mode 100644 index 000000000..5fc54eea5 --- /dev/null +++ b/src/lib/autoinstall/dialogs/disk_selector.rb @@ -0,0 +1,152 @@ +# encoding: utf-8 + +# Copyright (c) [2017] 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 "ui/dialog" +require "y2storage" + +Yast.import "Label" +Yast.import "UI" + +module Y2Autoinstallation + module Dialogs + # Ask the user for which device to use + # + # This dialog will be used by the partitioning section preprocessor (see + # {Y2Autoinstallation::PartitioningPreprocessor}) in order to determine + # which device to use for a given section. + class DiskSelector < UI::Dialog + # @return [Y2Storage::Devicegraph] Devicegraph used to find disks + attr_reader :devicegraph + # @return [Array] Device names that should be omitted + attr_reader :blacklist + + # Constructor + # + # @param devicegraph [Y2Storage::Devicegraph] Devicegraph used to find disks. + # By default, the probed devicegraph is used. + # @param blacklist [Array] Device names that should be omitted. + # These disks will be filtered out. + def initialize(devicegraph = nil, blacklist: []) + @devicegraph = devicegraph || Y2Storage::StorageManager.instance.probed + @blacklist = blacklist + end + + # Dialog content + # + # @return [Yast::Term] Dialog content + # @see #disks_content + # @see #empty_content + def dialog_content + options.empty? ? empty_content : disks_content + end + + # Dialog content when some disks are available + # + # @return [Yast::Term] Dialog content + def disks_content + VBox( + Label(_("Choose a hard disk")), + RadioButtonGroup( + Id(:options), + VBox(*options) + ), + ButtonBox(*buttons) + ) + end + + # Dialog content when no disks are found + # + # @return [Yast::Term] + def empty_content + VBox( + Label(_("No disks found.")), + ButtonBox(abort_button) + ) + end + + # Handler for the `Continue` button + def ok_handler + finish_dialog(Yast::UI::QueryWidget(Id(:options), :Value)) + end + + # Handler for the `Abort` button + def abort_handler + finish_dialog(:abort) + end + + # Handler for the `Skip` button + def skip_handler + finish_dialog(:skip) + end + + protected + + # Returns a list of options containing available disks + # + # @return [Array] List of options + def options + return @options if @options + first_disk = disks.first + @options = disks.each_with_index.map do |disk, idx| + Left(RadioButton(Id(disk.name), "#{idx + 1}: #{label(disk)}", first_disk == disk)) + end + end + + # Returns a list of available disks in the system + # + # Blacklisted disks are filtered out. + # + # @return [Array] List of disks + def disks + @disks ||= devicegraph.disk_devices.reject do |disk| + blacklist.include?(disk.name) + end + end + + # Dialog buttons + # + # @return [Array] + def buttons + [ + PushButton(Id(:ok), Opt(:okButton, :key_F10, :default), Yast::Label.ContinueButton), + PushButton(Id(:skip), Yast::Label.SkipButton), + abort_button + ] + end + + # Abort button + # + # @return [Yast::Term] + def abort_button + PushButton(Id(:abort), Opt(:cancel_button, :key_F9), Yast::Label.AbortButton) + end + + # Disk label to show in the list of options + # + # @param [Y2Storage::Device] Disk + # @return [String] Label + def label(disk) + "#{disk.basename}, #{disk.hwinfo.model}, #{disk.hwinfo.vendor}" + end + end + end +end + diff --git a/test/lib/dialogs/disk_selector_test.rb b/test/lib/dialogs/disk_selector_test.rb new file mode 100644 index 000000000..e765ca8e3 --- /dev/null +++ b/test/lib/dialogs/disk_selector_test.rb @@ -0,0 +1,132 @@ +#!/usr/bin/env rspec +# encoding: utf-8 + +# Copyright (c) [2017] 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 "autoinstall/dialogs/disk_selector" +require "ostruct" + +describe Y2Autoinstallation::Dialogs::DiskSelector do + subject(:dialog) { described_class.new(devicegraph) } + extend Yast::I18n + + let(:devicegraph) do + instance_double(Y2Storage::Devicegraph, disk_devices: disks) + end + + let(:sda) { double("sda", name: "/dev/sda", basename: "sda", hwinfo: hwinfo) } + let(:sdb) { double("sdb", name: "/dev/sdb", basename: "sdb", hwinfo: hwinfo) } + let(:hwinfo) { OpenStruct.new(model: "Model", vendor: "Vendor") } + let(:disks) { [sda, sdb] } + + before do + allow(Yast::UI).to receive(:OpenDialog).and_return(true) + allow(Yast::UI).to receive(:CloseDialog).and_return(true) + end + + describe "#dialog_content" do + it "displays all options" do + expect(dialog).to receive(:RadioButton).with(Id("/dev/sda"), "1: sda, Model, Vendor", true) + expect(dialog).to receive(:RadioButton).with(Id("/dev/sdb"), "2: sdb, Model, Vendor", false) + dialog.dialog_content + end + + it "displays ok, skip and abort buttons" do + expect(dialog).to receive(:PushButton).with(Id(:ok), anything, anything) + expect(dialog).to receive(:PushButton).with(Id(:skip), anything) + expect(dialog).to receive(:PushButton).with(Id(:abort), anything, anything) + dialog.dialog_content + end + + context "when a disk must be omitted" do + subject(:dialog) { described_class.new(devicegraph, blacklist: ["/dev/sda"]) } + + it "does not list the blacklisted disk" do + expect(dialog).to_not receive(:RadioButton).with(Id("/dev/sda"), anything) + expect(dialog).to receive(:RadioButton).with(Id("/dev/sdb"), "1: sdb, Model, Vendor", true) + dialog.dialog_content + end + end + + context "when no disk are found" do + let(:disks) { [] } + + it "shows an error message" do + expect(dialog).to receive(:Label).with(_("No disks found.")) + dialog.dialog_content + end + + it "only shows the abort button" do + expect(dialog).to_not receive(:PushButton).with(Id(:ok), anything, anything) + expect(dialog).to_not receive(:PushButton).with(Id(:skip), anything) + expect(dialog).to receive(:PushButton).with(Id(:abort), anything, anything) + dialog.dialog_content + end + end + + context "when no eligible disks are found" do + subject(:dialog) { described_class.new(devicegraph, blacklist: ["/dev/sda", "/dev/sdb"]) } + + it "shows an error message" do + expect(dialog).to receive(:Label).with(_("No disks found.")) + dialog.dialog_content + end + + it "only shows the abort button" do + expect(dialog).to_not receive(:PushButton).with(Id(:ok), anything, anything) + expect(dialog).to receive(:PushButton).with(Id(:abort), anything, anything) + dialog.dialog_content + end + end + end + + describe "#run" do + before do + allow(Yast::UI).to receive(:UserInput).and_return(button) + allow(Yast::UI).to receive(:QueryWidget).with(Id(:options), :Value) + .and_return("/dev/sda") + end + + context "when the user presses 'Continue'" do + let(:button) { :ok } + + it "returns the selected disk" do + expect(dialog.run).to eq("/dev/sda") + end + end + + context "when the user presses 'Abort'" do + let(:button) { :abort } + + it "returns :abort" do + expect(dialog.run).to eq(:abort) + end + end + + context "when the user presses 'Skip'" do + let(:button) { :skip } + + it "returns :skip" do + expect(dialog.run).to eq(:skip) + end + end + end +end