Skip to content

Commit

Permalink
Improve wireless bitrate handling
Browse files Browse the repository at this point in the history
  • Loading branch information
imobachgs committed Feb 22, 2021
1 parent 1e4300a commit 762a439
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 35 deletions.
97 changes: 97 additions & 0 deletions src/lib/y2network/bitrate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Copyright (c) [2021] 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.

module Y2Network
# Represents a bitrate
#
# This class is not as generic as Y2Storage::DiskSize and it is used to support
# parsing bitrates information from `iwlist`. However, it could be extended
# in the future if needed.
class Bitrate
include Comparable

# Exception when trying to parse a string as a bitrate
class ParseError < StandardError; end

UNITS = ["b", "kb", "Mb", "Gb"].freeze
private_constant :UNITS

PARSE_REGEXP = /\A(\d+(?:.\d+)?)\ *(\w+)?/.freeze
private_constant :PARSE_REGEXP

class << self
# Parses a string and converts the value to a string
#
# @example Parsing Mb/s
# bitrate = Bitrate.parse("54 Mb/s")
# bitrate.to_i #=> 54000000
# bitrate.to_s #=> "54 Mb/s"
#
# @param str [String] String to parse
# @return [Bitrate]
# @raise ParseError
def parse(str)
match = PARSE_REGEXP.match(str)
raise ParseError unless match

number, unit = match.captures
unit ||= "b"
power = UNITS.index(unit)
raise ParseError unless power

new(number.to_i * (1000**power))
end
end

# @param bits [Integer] Bits
def initialize(bits)
@bits = bits
end

# @return [Integer] Return the bits
def to_i
@bits
end

# Returns the string representation of the bitrate
#
# It automatically selects the unit to use depending on the bitrate value.
#
# @return [String] String representation
def to_s
power = (0..UNITS.length - 1).to_a.reverse.find do |u|
to_i > (1000**u)
end

units = UNITS[power]
number = @bits.to_f / (1000**power)
number_str = number.to_s.sub(".0", "") # strip insignificant zeroes

"#{number_str} #{units}/s"
end

# Compare two bitrates
#
# @param other [Bitrate]
# @return [Integer]
def <=>(other)
to_i <=> other.to_i
end
end
end
2 changes: 1 addition & 1 deletion src/lib/y2network/widgets/wireless_networks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def items
network.essid,
network.mode,
network.channel,
"54 Mbit/s",
network.rates.min.to_s,
network.quality,
"WPA2"
]
Expand Down
14 changes: 7 additions & 7 deletions src/lib/y2network/wireless_cell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,22 @@ class WirelessCell
# @!attribute [r] mode
# @return [WirelessMode,nil] Wireless mode
# @!attribute [r] channel
# @return [Integer,nil] Wireless channel
# @!attribute [r] rate
# @return [Array<String>] Wireles rates
# @return [Integer] Wireless channel
# @!attribute [r] rates
# @return [Array<Bitrate>] Wireles rates
# @!attribute [r] quality
# @return [Integer] Signal quality
# @!attribute [r] security
# @return [Array<String>] Security mechanisms
attr_reader :address, :essid, :mode, :channel, :rate, :quality, :security
attr_reader :address, :essid, :mode, :channel, :rates, :quality, :security

# rubocop:disable Metrics/ParameterLists
def initialize(address:, essid:, mode:, channel:, rate:, quality:, security:)
def initialize(address:, essid:, mode:, channel:, rates:, quality:, security:)
@address = address
@essid = essid
@mode = mode
@channel = channel
@rate = rate
@rates = rates
@quality = quality
@security = security
end
Expand All @@ -59,7 +59,7 @@ def initialize(address:, essid:, mode:, channel:, rate:, quality:, security:)
# @return [Hash<Symbol,Object>] Hash containing cell properties, using the property
# names as keys.
def to_h
{ address: address, essid: essid, mode: mode, channel: channel, rate: rate,
{ address: address, essid: essid, mode: mode, channel: channel, rates: rates,
quality: quality, security: security }
end
end
Expand Down
6 changes: 3 additions & 3 deletions src/lib/y2network/wireless_network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
module Y2Network
# Each instance of this class represents a wireless network
class WirelessNetwork
attr_reader :essid, :mode, :channel, :rate, :quality, :security
attr_reader :essid, :mode, :channel, :rates, :quality, :security

class << self
# Returns the wireless networks found through a given interface
Expand All @@ -48,11 +48,11 @@ def all(iface_name)
end

# Constructor
def initialize(essid:, mode:, channel:, rate:, quality:, security:)
def initialize(essid:, mode:, channel:, rates:, quality:, security:)
@essid = essid
@mode = mode
@channel = channel
@rate = rate
@rates = rates
@quality = quality
@security = security
end
Expand Down
5 changes: 3 additions & 2 deletions src/lib/y2network/wireless_scanner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

require "yast"
require "yast2/execute"
require "y2network/bitrate"
require "y2network/wireless_network"
require "y2network/wireless_cell"

Expand Down Expand Up @@ -113,7 +114,7 @@ def cell_from_raw_data(raw_cell)
essid: fetch_essid(fields),
mode: field_single_value("Mode", fields),
channel: fetch_channel(fields),
rate: fetch_bit_rates(fields),
rates: fetch_bit_rates(fields),
quality: fetch_quality(fields),
security: fetch_security(fields)
)
Expand Down Expand Up @@ -189,7 +190,7 @@ def fetch_bit_rates(fields)
.join("\n")
.gsub("\n", ";")
.split(";")
.map(&:strip)
.map { |b| Bitrate.parse(b.strip) }
end

# Returns the quality from the list of fields
Expand Down
90 changes: 90 additions & 0 deletions test/y2network/bitrate_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Copyright (c) [2021] 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 "y2network/bitrate"

describe Y2Network::Bitrate do
describe ".parse" do
context "when the string contains a 'kb/s'" do
it "returns an object representing the same value" do
bitrate = described_class.parse("54 kb/s")
expect(bitrate.to_i).to eq(54_000)
end
end

context "when the string contains a 'Mb/s'" do
it "returns an object representing the same value" do
bitrate = described_class.parse("54 Mb/s")
expect(bitrate.to_i).to eq(54_000_000)
end
end

context "when the string contains a 'Gb/s'" do
it "returns an object representing the same value" do
bitrate = described_class.parse("1 Gb/s")
expect(bitrate.to_i).to eq(1_000_000_000)
end
end

context "when the unit is missing from the string" do
it "returns an object representing the same value" do
bitrate = described_class.parse("64")
expect(bitrate.to_i).to eq(64)
end
end

context "when the string cannot be parsed" do
it "raises a ParseError exception" do
expect { described_class.parse("something") }
.to raise_error(Y2Network::Bitrate::ParseError)
end
end
end

describe "#to_s" do
context "Gb/s" do
it "returns" do
bitrate = described_class.new(54000000000)
expect(bitrate.to_s).to eq("54 Gb/s")
end
end

context "Mb/s" do
it "returns" do
bitrate = described_class.new(54000000)
expect(bitrate.to_s).to eq("54 Mb/s")
end
end

context "kb/s" do
it "returns" do
bitrate = described_class.new(54500)
expect(bitrate.to_s).to eq("54.5 kb/s")
end
end

context "bits" do
it "returns" do
bitrate = described_class.new(128)
expect(bitrate.to_s).to eq("128 b/s")
end
end
end
end
28 changes: 13 additions & 15 deletions test/y2network/widgets/wireless_essid_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,25 @@
let(:essid) { Y2Network::Widgets::WirelessEssidName.new(builder) }
let(:installed) { true }
let(:initial_stage) { false }
let(:available_networks) { ["YaST", "Guests"] }
let(:selected_network) do
instance_double(Y2Network::WirelessNetwork, essid: "YaST")
end
let(:wireless_dialog) do
instance_double(Y2Network::Dialogs::WirelessNetworks, run: selected_network)
end

subject { described_class.new(builder, update: essid) }

before do
allow(subject).to receive(:scan_supported?).and_return(installed)
allow(Yast::Package).to receive(:Installed).and_return(installed)
allow(Yast::Stage).to receive(:initial).and_return(initial_stage)
allow(subject).to receive(:fetch_essid_list).and_return(available_networks)
allow(essid).to receive(:update_essid_list)
allow(essid).to receive(:value=)
allow(Y2Network::Dialogs::WirelessNetworks).to receive(:new)
.and_return(wireless_dialog)
end

describe "#handle" do
let(:networks_dialog) do
instance_double(Y2Network::Dialogs::WirelessNetworks, run: nil)
end

before do
allow(Y2Network::Dialogs::WirelessNetworks).to receive(:new)
.and_return(networks_dialog)
end

context "when the package for scanning wireless networks is not installed" do
let(:installed) { false }

Expand All @@ -72,7 +70,6 @@
it "returns without scanning the available network" do
allow(Yast::Package).to receive(:Install).and_return(false)
expect(Yast::Popup).to receive(:Error).with(/was not installed/)
expect(subject).to_not receive(:fetch_essid_list)

expect(subject.handle).to eql(nil)
end
Expand All @@ -81,12 +78,13 @@

context "when the package for scanning wireless networks is installed" do
it "scans the list of available essids" do
expect(subject).to receive(:fetch_essid_list).and_return(available_networks)
expect(Y2Network::Dialogs::WirelessNetworks).to receive(:new)
.and_return(wireless_dialog)
subject.handle
end

it "updates the widget with the list of the available essids with the obtained one" do
expect(essid).to receive(:update_essid_list).with(available_networks)
expect(essid).to receive(:value=).with(selected_network.essid)
subject.handle
end
end
Expand Down
8 changes: 5 additions & 3 deletions test/y2network/widgets/wireless_networks_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

require_relative "../../test_helper"
require "y2network/widgets/wireless_networks"
require "y2network/bitrate"
require "y2network/wireless_network"
require "cwm/rspec"

Expand All @@ -28,21 +29,22 @@
describe "#update" do
let(:network) do
Y2Network::WirelessNetwork.new(
essid: "MY_WIFI", mode: "Master", channel: 10, rate: ["54 Mb/s"],
essid: "MY_WIFI", mode: "Master", channel: 10,
rates: [Y2Network::Bitrate.parse("54 Mb/s")],
quality: 70, security: []
)
end

it "refreshes the list of networks" do
expect(subject).to receive(:change_items) do |args|
expect(args).to eq(
[["MY_WIFI", "MY_WIFI", "Master", 10, "54 Mbit/s", 70, "WPA2"]]
[["MY_WIFI", "MY_WIFI", "Master", 10, "54 Mb/s", 70, "WPA2"]]
)
end
subject.update([network])
end

context "when a network was alreay selected" do
context "when a network was already selected" do
before do
allow(Yast::UI).to receive(:QueryWidget)
.with(Id(subject.widget_id), :SelectedItems)
Expand Down
Loading

0 comments on commit 762a439

Please sign in to comment.