Skip to content

Commit

Permalink
New class for UI plug-in info + test
Browse files Browse the repository at this point in the history
  • Loading branch information
shundhammer committed Sep 15, 2021
1 parent 11fa6fe commit 4ce310b
Show file tree
Hide file tree
Showing 3 changed files with 269 additions and 5 deletions.
7 changes: 2 additions & 5 deletions library/system/src/lib/yast2/shared_lib_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,21 @@
require "yast"

module Yast
# Class to get information about shared libraries used by a process from its
# /proc/self/maps file.
# Class to get information about shared libraries used by a process (by
# default the current process) from its /proc/self/maps file.
#
# You can also get information for a different process by specifying its
# /proc/$pid/maps file.
#
# For testing, a fixed file can be used.
class SharedLibInfo
include Yast::Logger

# @return [Array<String>] Complete paths of the shared libs
attr_reader :shared_libs

# Constructor.
# @param maps_file [String] name of the maps file to use
#
def initialize(maps_file = "/proc/self/maps")
log.info("Creating SharedLibInfo from #{maps_file}")
clear
read(maps_file)
end
Expand Down
95 changes: 95 additions & 0 deletions library/system/src/lib/yast2/ui_plugin_info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# ------------------------------------------------------------------------------
# Copyright (c) 2021 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"
require "yast2/shared_lib_info"

module Yast
# Class to get information about UI plug-ins used by a process (by default
# the current process) from its /proc/self/maps file.
#
# You can also get information for a different process by specifying its
# /proc/$pid/maps file.
#
# For testing, a fixed file can be used.
class UiPluginInfo < SharedLibInfo
include Yast::Logger

# Constructor.
# @param maps_file [String] name of the maps file to use
#
def initialize(maps_file = "/proc/self/maps")
@ui_plugins = nil # Lazy init
super(maps_file)
log.info("Creating SharedLibInfo from #{maps_file}")
end

# Find the UI plug-ins among the shared libs.
#
# @return [Array<String>] Complete paths of the UI plug-ins
#
def ui_plugins
@ui_plugins ||= shared_libs.select { |lib| lib =~ /yui\/libyui-/ }
log.info("UI plug-ins: #{@ui_plugins}")
@ui_plugins
end

# Return the short name of a UI plug-in, i.e. only the lib base name with
# any leading "libyui-" removed, i.e. something like "qt", "ncurses".
#
# @param ui_plugin [String] full name (with or without path) of the UI plug-in
# @return [String] corresponding short name
#
def short_name(ui_plugin)
name = SharedLibInfo.lib_basename(ui_plugin)
return nil if name.nil?

name.gsub(/^libyui-/, "")
end

# Find the main UI plug-in (if there are several).
# This is generally the one with the shortest name.
#
# @return [String, nil] Short name of the main UI plug-in
def main_ui_plugin
return nil if ui_plugins.empty?

plugins = ui_plugins.map { |p| SharedLibInfo.lib_basename(p) }
main_plugin = plugins.min { |a, b| a.size <=> b.size }
short_name(main_plugin)
end

# Find the SO number of the UI main plug-in.
#
# @return [String, nil] SO number (e.g. "15.0.0")
#
def ui_so_number
ui_short_name = main_ui_plugin
return nil if ui_short_name.nil?

plugin = ui_plugins.find { |p| p =~ /#{ui_short_name}\.so/ }
SharedLibInfo.so_number(plugin)
end

# Find the SO major number of the UI main plug-in.
#
# @return [String, nil] SO number (e.g. "15")
#
def ui_so_major
so = ui_so_number
return nil if so.nil?

so.split(".").first
end
end
end
172 changes: 172 additions & 0 deletions library/system/test/ui_plugin_info_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#! /usr/bin/env rspec

require_relative "./test_helper"
require "yast2/ui_plugin_info"

PROC_MAPS_PATH = File.join(__dir__, "data/proc-maps")

def stored_proc_maps(scenario)
File.join(PROC_MAPS_PATH, "proc-maps-#{scenario}")
end

describe Yast::UiPluginInfo do
describe "#new" do
let(:subject) { described_class.new(maps_file) }

context "empty" do
let(:maps_file) { nil }

it "does not crash and burn" do
expect(subject.ui_plugins).to eq []
end
end

context "with the Qt UI" do
let(:maps_file) { stored_proc_maps("qt") }

it "finds the Qt UI plug-in" do
expect(subject.ui_plugins).to eq ["/usr/lib64/yui/libyui-qt.so.15.0.0"]
end
end

context "with the Qt UI + Qt-Pkg" do
let(:maps_file) { stored_proc_maps("qt-pkg") }

it "finds the Qt UI plug-in and the Qt-Pkg plug-in" do
expect(subject.ui_plugins).to eq ["/usr/lib64/yui/libyui-qt-pkg.so.15.0.0", "/usr/lib64/yui/libyui-qt.so.15.0.0"]
end
end
end

describe "#main_ui_plugin" do
let(:subject) { described_class.new(maps_file) }

context "empty" do
let(:maps_file) { nil }

it "does not crash and burn" do
expect(subject.main_ui_plugin).to eq nil
end
end

context "with the Qt UI" do
let(:maps_file) { stored_proc_maps("qt") }

it "identifies the UI as \"qt\"" do
expect(subject.main_ui_plugin).to eq "qt"
end
end

context "with the Qt UI + Qt-Pkg extension" do
let(:maps_file) { stored_proc_maps("qt-pkg") }

it "identifies the UI as \"qt\"" do
expect(subject.main_ui_plugin).to eq "qt"
end
end

context "with the Qt UI + Qt-Graph extension" do
let(:maps_file) { stored_proc_maps("qt-graph") }

it "identifies the UI as \"qt\"" do
expect(subject.main_ui_plugin).to eq "qt"
end
end

context "with the NCurses UI" do
let(:maps_file) { stored_proc_maps("ncurses") }

it "identifies the UI as \"ncurses\"" do
expect(subject.main_ui_plugin).to eq "ncurses"
end
end

context "with the NCurses UI + NCurses-Pkg extension" do
let(:maps_file) { stored_proc_maps("ncurses") }

it "identifies the UI as \"ncurses\"" do
expect(subject.main_ui_plugin).to eq "ncurses"
end
end

context "without any UI" do
let(:maps_file) { stored_proc_maps("no-ui") }

it "returns nil for the UI" do
expect(subject.main_ui_plugin).to eq nil
end
end
end

describe "#ui_so_number" do
let(:subject) { described_class.new(maps_file) }

context "empty" do
let(:maps_file) { nil }

it "does not crash and burn" do
expect(subject.ui_so_number).to eq nil
end
end

context "with the Qt UI" do
let(:maps_file) { stored_proc_maps("qt") }

it "identifies the UI SO number as 15.0.0" do
expect(subject.ui_so_number).to eq "15.0.0"
end
end

context "with the Qt UI + Qt-Pkg extension" do
let(:maps_file) { stored_proc_maps("qt-pkg") }

it "identifies the UI SO number as 15.0.0" do
expect(subject.ui_so_number).to eq "15.0.0"
end
end

context "without any UI" do
let(:maps_file) { stored_proc_maps("no-ui") }

it "returns nil" do
expect(subject.ui_so_number).to eq nil
end
end
end

describe "#ui_so_major" do
let(:subject) { described_class.new(maps_file) }

context "empty" do
let(:maps_file) { nil }

it "does not crash and burn" do
expect(subject.ui_so_major).to eq nil
end
end

context "with the Qt UI" do
let(:maps_file) { stored_proc_maps("qt") }

it "identifies the UI SO major number as 15" do
expect(subject.ui_so_major).to eq "15"
end
end

context "with the Qt UI + Qt-Pkg extension" do
let(:maps_file) { stored_proc_maps("qt-pkg") }

it "identifies the UI SO major number as 15" do
expect(subject.ui_so_major).to eq "15"
end
end

context "without any UI" do
let(:maps_file) { stored_proc_maps("no-ui") }

it "returns nil" do
expect(subject.ui_so_major).to eq nil
end
end
end
end

0 comments on commit 4ce310b

Please sign in to comment.