Hello community,

here is the log from the commit of package yast2-packager for openSUSE:Factory 
checked in at 2016-08-18 10:20:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/yast2-packager (Old)
 and      /work/SRC/openSUSE:Factory/.yast2-packager.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "yast2-packager"

Changes:
--------
--- /work/SRC/openSUSE:Factory/yast2-packager/yast2-packager.changes    
2016-08-10 19:54:28.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.yast2-packager.new/yast2-packager.changes       
2016-08-18 10:20:22.000000000 +0200
@@ -1,0 +2,36 @@
+Tue Aug 16 14:16:01 UTC 2016 - [email protected]
+
+- Remove the icon from the add-on selection popup (bsc#875201)
+- 3.1.114
+
+-------------------------------------------------------------------
+Tue Aug 16 08:31:23 CEST 2016 - [email protected]
+
+- Added lazy loading of licenses into AcceptanceNeeded in case of
+  base product in the initial installation to fix a problem when
+  explicit acceptance of the product license was always required
+  (bnc#993285)
+- 3.1.113
+
+-------------------------------------------------------------------
+Mon Aug 15 13:59:58 CEST 2016 - [email protected]
+
+- Fixed parameter error while logging errors. It belongs to
+  bnc#991935.
+- 3.1.112
+
+-------------------------------------------------------------------
+Thu Aug 11 12:23:47 UTC 2016 - [email protected]
+
+- Fix automatic add-on handling when using CD/DVD medias as source
+  (bsc#991935)
+- 3.1.111
+
+-------------------------------------------------------------------
+Wed Aug 10 16:39:10 UTC 2016 - [email protected]
+
+- List addons below base product in Software proposal during
+  installation. (bsc#992304)
+- 3.1.110
+
+-------------------------------------------------------------------

Old:
----
  yast2-packager-3.1.109.tar.bz2

New:
----
  yast2-packager-3.1.114.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ yast2-packager.spec ++++++
--- /var/tmp/diff_new_pack.0r9TKd/_old  2016-08-18 10:20:23.000000000 +0200
+++ /var/tmp/diff_new_pack.0r9TKd/_new  2016-08-18 10:20:23.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-packager
-Version:        3.1.109
+Version:        3.1.114
 Release:        0
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build

++++++ yast2-packager-3.1.109.tar.bz2 -> yast2-packager-3.1.114.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/yast2-packager-3.1.109/package/yast2-packager.changes 
new/yast2-packager-3.1.114/package/yast2-packager.changes
--- old/yast2-packager-3.1.109/package/yast2-packager.changes   2016-08-09 
20:55:13.000000000 +0200
+++ new/yast2-packager-3.1.114/package/yast2-packager.changes   2016-08-16 
16:51:47.000000000 +0200
@@ -1,4 +1,40 @@
 -------------------------------------------------------------------
+Tue Aug 16 14:16:01 UTC 2016 - [email protected]
+
+- Remove the icon from the add-on selection popup (bsc#875201)
+- 3.1.114
+
+-------------------------------------------------------------------
+Tue Aug 16 08:31:23 CEST 2016 - [email protected]
+
+- Added lazy loading of licenses into AcceptanceNeeded in case of
+  base product in the initial installation to fix a problem when
+  explicit acceptance of the product license was always required
+  (bnc#993285)
+- 3.1.113
+
+-------------------------------------------------------------------
+Mon Aug 15 13:59:58 CEST 2016 - [email protected]
+
+- Fixed parameter error while logging errors. It belongs to
+  bnc#991935.
+- 3.1.112
+
+-------------------------------------------------------------------
+Thu Aug 11 12:23:47 UTC 2016 - [email protected]
+
+- Fix automatic add-on handling when using CD/DVD medias as source
+  (bsc#991935)
+- 3.1.111
+
+-------------------------------------------------------------------
+Wed Aug 10 16:39:10 UTC 2016 - [email protected]
+
+- List addons below base product in Software proposal during
+  installation. (bsc#992304)
+- 3.1.110
+
+-------------------------------------------------------------------
 Thu Jul 28 10:40:32 UTC 2016 - [email protected]
 
 - allow KDE to use packager for opening rpms (boo#954143)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-packager-3.1.109/package/yast2-packager.spec 
new/yast2-packager-3.1.114/package/yast2-packager.spec
--- old/yast2-packager-3.1.109/package/yast2-packager.spec      2016-08-09 
20:55:13.000000000 +0200
+++ new/yast2-packager-3.1.114/package/yast2-packager.spec      2016-08-16 
16:51:47.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           yast2-packager
-Version:        3.1.109
+Version:        3.1.114
 Release:        0
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-packager-3.1.109/src/modules/AddOnProduct.rb 
new/yast2-packager-3.1.114/src/modules/AddOnProduct.rb
--- old/yast2-packager-3.1.109/src/modules/AddOnProduct.rb      2016-08-09 
20:55:14.000000000 +0200
+++ new/yast2-packager-3.1.114/src/modules/AddOnProduct.rb      2016-08-16 
16:51:47.000000000 +0200
@@ -1370,11 +1370,8 @@
 
       UI.OpenDialog(
         VBox(
-          HBox(
-            HSquash(MarginBox(0.5, 0.2, Icon.Simple("yast-addon"))),
-            # TRANSLATORS: popup heading
-            Left(Heading(Id(:search_heading), _("Additional Products")))
-          ),
+          # TRANSLATORS: popup heading
+          Left(Heading(Id(:search_heading), _("Additional Products"))),
           VSpacing(0.5),
           # TRANSLATORS: additional dialog information
           Left(
@@ -1572,27 +1569,21 @@
       new_repo = { "enabled" => true, "base_urls" => [url], "prod_dir" => pth }
 
       # BNC #714027: Possibility to adjust repository priority (usually higher)
-      Ops.set(new_repo, "priority", priority) if Ops.greater_than(priority, -1)
+      new_repo["priority"] = priority if priority > -1
 
-      Builtins.y2milestone(
-        "Adding Repository: %1, product path: %2",
-        URL.HidePassword(url),
-        pth
-      )
+      log.info "Adding Repository: #{URL.HidePassword(url)}, product path: 
#{pth}"
       new_repo_id = Pkg.RepositoryAdd(new_repo)
+      log.info "New repository id: #{new_repo_id}"
 
-      if new_repo_id == nil || Ops.less_than(new_repo_id, 0)
-        Builtins.y2error("Unable to add product: %1", URL.HidePassword(url))
+      if new_repo_id == nil || new_repo_id < 0
+        log.error "Unable to add product: #{URL.HidePassword(url)}"
         # TRANSLATORS: error message, %1 is replaced with product URL
-        Report.Error(
-          Builtins.sformat(
-            _("Unable to add product %1."),
-            URL.HidePassword(url)
-          )
-        )
+        Report.Error(format(_("Unable to add product %s."), 
URL.HidePassword(url)))
         return nil
       end
 
+      # Make sure all changes are refreshed
+      Pkg.SourceSaveAll
       # download metadata, build repo cache
       Pkg.SourceRefreshNow(new_repo_id)
       # load resolvables to zypp pool
@@ -1601,6 +1592,12 @@
       new_repo_id
     end
 
+
+    # Repository network schemes
+    NETWORK_SCHEMES = ["http", "https", "ftp", "nfs", "cifs", "slp"]
+    # Repository CD/DVD schemes
+    CD_SCHEMES = ["cd", "dvd"]
+
     # Auto-integrate add-on products in specified file (usually 
add_on_products or
     # add_on_products.xml file)
     #
@@ -1620,6 +1617,11 @@
     #                  <product_item>
     #                          <!-- Product name visible in UI when offered to 
user (optional item) -->
     #                          <name>Add-on Name to Display</name>
+    #           <!--
+    #             Check product's name (optional item). If set to false, 
<name> won't be checked
+    #             against product's name found in the media (CD/DVD only).
+    #           -->
+    #           <check_name config:type="boolean">true</check_name>
     #                          <!-- Product URL (mandatory item) -->
     #                          <url>http://product.repository/url/</url>
     #                          <!-- Product path, default is "/" (optional 
item) -->
@@ -1667,109 +1669,59 @@
     #          $[ "file" : "/local/path/to/an/add_on_products/file.xml", 
"type":"xml" ]
     #      ]
     def AddPreselectedAddOnProducts(filelist)
-      filelist = deep_copy(filelist)
-      if filelist == nil || filelist == []
-        Builtins.y2milestone(
-          "No add-on products defined on the media or by inst-sys"
-        )
+      if filelist.nil? || filelist.empty?
+        log.info "No add-on products defined on the media or by inst-sys"
         return true
       end
 
       base_url = GetBaseProductURL()
-      Builtins.y2milestone("Base URL: %1", URL.HidePassword(base_url))
+      log.info "Base URL: #{URL.HidePassword(base_url)}"
 
       # Processes all add_on_products files found
-      Builtins.foreach(filelist) do |add_on_products_file|
-        filename = Ops.get(add_on_products_file, "file", "")
-        type = Ops.get(add_on_products_file, "type", "")
-        add_products = []
-        # new xml format
-        if type == "xml"
-          add_products = ParseXMLBasedAddOnProductsFile(filename, base_url)
-          # old fallback
-        elsif type == "plain"
-          add_products = ParsePlainAddOnProductsFile(filename, base_url)
-        else
-          Builtins.y2error("Unsupported type: %1", type)
-          next false
-        end
-        repo_id = -1
-        Builtins.y2milestone("Adding products: %1", add_products)
-        Builtins.foreach(add_products) do |one_product|
-          url = Ops.get_string(one_product, "url", "")
-          pth = Ops.get_string(one_product, "path", "")
-          priority = Ops.get_integer(one_product, "priority", -1)
-          prodname = Ops.get_string(one_product, "name", "")
+      filelist.each do |file|
+        add_products = parse_add_on_products_file(
+          file.fetch("file", ""), file.fetch("type", ""), base_url
+        )
+        next unless add_products
+
+        log.info "Adding products: #{add_products}"
+        add_products.each do |one_product|
+          url = one_product.fetch("url", "")
+          pth = one_product.fetch("path", "")
+          priority = one_product.fetch("priority", -1).to_i
+          prodname = one_product.fetch("name", "")
+          check_name = one_product.fetch("check_name", true)
           # Check URL and setup network if required or prompt to insert CD/DVD
           parsed = URL.Parse(url)
-          scheme = Builtins.tolower(Ops.get_string(parsed, "scheme", ""))
+          scheme = parsed.fetch("scheme", "").downcase
           # check if network needs to be configured
-          if Builtins.contains(
-              ["http", "https", "ftp", "nfs", "cifs", "slp"],
-              scheme
-            )
+          if NETWORK_SCHEMES.include?(scheme)
             inc_ret = Convert.to_symbol(
               WFM.CallFunction("inst_network_check", [])
             )
             Builtins.y2milestone("inst_network_check ret: %1", inc_ret)
           end
+
           # a CD/DVD repository
-          if Builtins.contains(["cd", "dvd"], scheme)
-            # if the CD/DVD product is known just try if it's there
-            # and ask if not
-            if prodname != ""
-              found = false
-
-              while !found
-                repo_id = AddRepo(url, pth, priority)
-                next false if repo_id == nil
-
-                prod2 = Pkg.SourceProductData(repo_id)
-                if Ops.get_string(prod2, "label", "") == prodname
-                  found = true
-                else
-                  Builtins.y2milestone(
-                    "Removing repo %1: Add-on found: %2, expected: %3",
-                    repo_id,
-                    Ops.get_string(prod2, "label", ""),
-                    prodname
-                  )
-                  Pkg.SourceDelete(repo_id)
-
-                  # ask for a different medium
-                  url = AskForCD(url, prodname)
-                  next false if url == nil
-                end
-              end
+          repo_id =
+            if CD_SCHEMES.include?(scheme)
+              add_product_from_cd(url, pth, priority, prodname, check_name)
             else
-              result = AskForCD(url, prodname)
-              next false if result == nil
-
-              repo_id = AddRepo(result, pth, priority)
-              next false if repo_id == nil
+              # a non CD/DVD repository
+              AddRepo(url, pth, priority)
             end
-          else
-            # a non CD/DVD repository
-            repo_id = AddRepo(url, pth, priority)
-            next false if repo_id == nil
-          end
+          next false unless repo_id
+
           if !AcceptedLicenseAndInfoFile(repo_id)
-            Builtins.y2warning("License not accepted, delete the repository")
+            log.warn "License not accepted, delete the repository"
             Pkg.SourceDelete(repo_id)
             next false
           end
           Integrate(repo_id)
           # adding the product to the list of products (BNC #269625)
           prod = Pkg.SourceProductData(repo_id)
-          Builtins.y2milestone(
-            "Repository (%1) product data: %2",
-            repo_id,
-            prod
-          )
-          InstallProductsFromRepository(
-            Ops.get_list(one_product, "install_products", []),
-            repo_id
-          )
+          log.info "Repository (#{repo_id} product data: #{prod})"
+          InstallProductsFromRepository(one_product.fetch("install_products", 
[]), repo_id)
           new_add_on_product = {
             "media"            => repo_id,
             "product"          => Ops.get_locale(
@@ -1789,9 +1741,7 @@
             "media_url"        => url,
             "product_dir"      => pth
           }
-          if Ops.greater_than(priority, -1)
-            Ops.set(new_add_on_product, "priority", priority)
-          end
+          new_add_on_product["priority"] = priority if priority > -1
           @add_on_products = Builtins.add(@add_on_products, new_add_on_product)
         end
       end
@@ -2256,6 +2206,63 @@
     publish :function => :ImportGpgKeyCallback, :type => "boolean (map 
<string, any>, integer)"
     publish :function => :AcceptNonTrustedGpgKeyCallback, :type => "boolean 
(map <string, any>)"
     publish :function => :SetSignatureCallbacks, :type => "void (string)"
+
+  private
+
+    # Adds a product from a CD/DVD
+    #
+    # To add the product, the name should match +prodname+ argument.
+    #
+    # @param url         [String]  Repository URL (cd: or dvd: schemas are 
expected)
+    # @param pth         [String]  Repository path (in the media)
+    # @param priority    [Integer] Repository priority
+    # @param prodname    [String]  Expected product's name
+    # @param check_name  [Boolean] Check product's name
+    # @return            [Integer,nil] Repository id; nil if product was not 
found and user cancelled.
+    #
+    # @see add_repo_from_cd
+    def add_product_from_cd(url, pth, priority, prodname, check_name)
+      current_url = url
+      loop do
+        # just try if it's there and ask if not
+        repo_id = AddRepo(current_url, pth, priority)
+        if repo_id
+          return repo_id if !check_name || prodname.empty?
+          found_product = Pkg.SourceProductData(repo_id)
+          return repo_id if found_product["label"] == prodname
+          log.info("Removing repo #{repo_id}: Add-on found 
#{found_product["label"]}, expected: #{prodname}")
+          Pkg.SourceDelete(repo_id)
+        end
+
+        # ask for a different medium
+        current_url = AskForCD(current_url, prodname)
+        return nil if current_url.nil?
+      end
+    end
+
+    # Parse a add-on products file
+    #
+    # @param filename [String] File path
+    # @param type     [String] File type ("xml" or "plain")
+    # @param base_url [String] Product's base URL
+    # @return [Hash] Add-on specification (allowed keys
+    #                are "name", "url", "path", "install_products",
+    #                "check_name", "ask_user", "selected" and "priority").
+    #
+    # @see ParseXMLBasedAddOnProductsFile
+    # @see ParsePlainAddOnProductsFile
+    # @see AddPreselectedAddOnProducts
+    def parse_add_on_products_file(filename, type, base_url)
+      case type.downcase
+      when "xml"
+        ParseXMLBasedAddOnProductsFile(filename, base_url)
+      when "plain"
+        ParsePlainAddOnProductsFile(filename, base_url)
+      else
+        log.error "Unsupported type: #{type}"
+        false
+      end
+    end
   end
 
   AddOnProduct = AddOnProductClass.new
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-packager-3.1.109/src/modules/Packages.rb 
new/yast2-packager-3.1.114/src/modules/Packages.rb
--- old/yast2-packager-3.1.109/src/modules/Packages.rb  2016-08-09 
20:55:14.000000000 +0200
+++ new/yast2-packager-3.1.114/src/modules/Packages.rb  2016-08-16 
16:51:47.000000000 +0200
@@ -16,6 +16,10 @@
 
     # All known types of resolvables
     RESOLVABLE_TYPES = [:product, :patch, :package, :pattern, :language]
+
+    # Key to sort by resolvable selection
+    RESOLVABLE_SORT_ORDER = { :product => "source", :pattern => "order" }
+
     # Minimum set of packages tags required to enable VNC server
     VNC_BASE_TAGS = ["xorg-x11", "xorg-x11-Xvnc", "xorg-x11-fonts", "xinetd"]
     # Additional packages tags needed to run second stage in graphical mode
@@ -157,40 +161,15 @@
     # @param [String] format string format string to print summaries in
     # @return a list of selected resolvables
     def ListSelected(what, format)
-      format = "%1" if format == "" || format == nil
       selected = Pkg.ResolvableProperties("", what, "")
 
-      # ignore hidden patterns
-      if what == :pattern
-        selected = Builtins.filter(selected) do |r|
-          Ops.get(r, "user_visible") == true
-        end
+      selected.select! {|r| r["status"] == :selected }
 
-        # order patterns according to "order" flag
-        selected = Builtins.sort(selected) do |x, y|
-          xo = Builtins.tointeger(Ops.get_string(x, "order", ""))
-          yo = Builtins.tointeger(Ops.get_string(y, "order", ""))
-          if xo == nil || yo == nil
-            # order is not an integer, compare as strings
-            next Ops.less_than(
-              Ops.get_string(x, "order", ""),
-              Ops.get_string(y, "order", "")
-            )
-          else
-            next Ops.less_than(xo, yo)
-          end
-        end
-      end
+      selected.select! {|r| r["user_visible"] } if what == :pattern
 
-      selected = Builtins.filter(selected) do |r|
-        Ops.get(r, "status") == :selected
-      end
+      sort_resolvable!(selected, what)
 
-      ret = Builtins.maplist(selected) do |r|
-        disp = Ops.get_string(r, "summary", Ops.get_string(r, "name", ""))
-        Builtins.sformat(format, disp)
-      end
-      deep_copy(ret)
+      formatted_resolvables(selected, format)
     end
 
     # Count the total size of packages to be installed
@@ -2599,6 +2578,33 @@
       end
     end
 
+    # Prepares a list of formatted selected resolvables
+    #
+    # @param [Array<Hash>] list of selected resolvables to format
+    # @param [String] string format to use
+    def formatted_resolvables(selected, format)
+      format = "%1" if format == "" || format == nil
+
+      Builtins.maplist(selected) do |r|
+        disp = Ops.get_string(r, "summary", Ops.get_string(r, "name", ""))
+        Builtins.sformat(format, disp)
+      end
+    end
+
+    # Sort selected resolvables of specified type
+    #
+    # :pattern resolvables are sorted by "order"
+    # :product resolvables are sorted by "source"
+    #
+    # @param [Array<Hash>] list of selected resolvables to sort
+    # @param [Symbol] what symbol specifying the type of resolvables to select
+    # @see RESOLVABLE_SORT_ORDER
+    def sort_resolvable!(selected, what)
+      order = RESOLVABLE_SORT_ORDER[what]
+
+      selected.sort_by! { |r| r[order].to_i } if order
+    end
+
     # Computes all patterns that are expected to be selected for default 
installation
     def patterns_to_install
       patterns = ComputeSystemPatternList().dup
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-packager-3.1.109/src/modules/ProductLicense.rb 
new/yast2-packager-3.1.114/src/modules/ProductLicense.rb
--- old/yast2-packager-3.1.109/src/modules/ProductLicense.rb    2016-08-09 
20:55:14.000000000 +0200
+++ new/yast2-packager-3.1.114/src/modules/ProductLicense.rb    2016-08-16 
16:51:47.000000000 +0200
@@ -52,6 +52,11 @@
       ]
       # no more wildcard patterns here, UI can display only html and txt anyway
 
+      initialize_default_values
+    end
+
+    # (Re)Initializes all internal caches
+    def initialize_default_values
       # All licenses have their own unique ID
       @license_ids = []
 
@@ -302,14 +307,56 @@
       )
     end
 
+    # Returns source ID of the base product - initial installation only!
+    # If no sources are found, returns 0.
+    # FIXME: Connected to bsc#993285, refactoring needed
+    #
+    # return [Integer] base_product_id or 0
+    def base_product_id
+      raise "Base product can be only found in installation" unless 
Stage.initial
+
+      # The first product in the list of known products
+      # 0 is the backward-compatible default value, first installation repo 
always
+      # gets this ID later
+      current_sources = Pkg.SourceGetCurrent(true)
+      current_sources.any? ? current_sources.first : 0
+    end
+
     # Returns whether accepting the license manually is requied.
     #
     # @see BNC #448598
+    # @param [Any] unique ID
     # @return [Boolean] if required
     def AcceptanceNeeded(id)
-      Ops.get(@license_acceptance_needed, id, true)
+      # FIXME: lazy loading of the info about licenses, bigger refactoring 
needed
+      # @see bsc#993285
+      #
+      # In the initial installation, for base product, acceptance_needed needs
+      # to be known before showing the license dialog (inst_complex_welcome).
+      # Loading the info is handled internally in other cases.
+      #
+      # id can be a string (currently is) when called from inst_complex_welcome
+      if !@license_acceptance_needed.key?(id) &&
+          Stage.initial &&
+          id.to_s == base_product_id.to_s
+        # Although we know the base product ID, the function below expects
+        # id to be nil for base product in inital installation
+        GetSourceLicenseDirectory(nil, "/")
+        cache_license_acceptance_needed(id, @license_dir)
+      end
+
+      if @license_acceptance_needed.key?(id)
+        @license_acceptance_needed[id]
+      else
+        log.warn "SetAcceptanceNeeded(#{id}) should be called first, using 
default 'true'"
+        true
+      end
     end
 
+    # Sets whether explicit acceptance of a license is needed
+    #
+    # @param [Any] unique ID (often a source ID)
+    # @param [Boolean] new_value if needed
     def SetAcceptanceNeeded(id, new_value)
       if new_value == nil
         Builtins.y2error(
@@ -320,15 +367,12 @@
         return
       end
 
-      Ops.set(@license_acceptance_needed, id, new_value)
+      @license_acceptance_needed[id] = new_value
 
       if new_value == true
-        Builtins.y2milestone("License agreement (ID %1) WILL be required", id)
+        log.info "License agreement (ID #{id}) WILL be required"
       else
-        Builtins.y2milestone(
-          "License agreement (ID %1) will NOT be required",
-          id
-        )
+        log.info "License agreement (ID #{id}) will NOT be required"
       end
 
       nil
@@ -751,12 +795,14 @@
       # Bugzilla #299732
       # Base Product - LiveCD installation
       if Mode.live_installation
+        log.info "LiveCD Installation"
         SearchForLicense_LiveCDInstallation(src_id, fallback_dir)
 
         # Base-product - license not in installation
         #   * Stage is not initial
         #   * source ID is not defined
       elsif !Stage.initial && src_id == nil
+        log.info "Base product, not in initial stage"
         SearchForLicense_NormalRunBaseProduct(src_id, fallback_dir)
 
         # Base-product - first-stage installation
@@ -764,14 +810,13 @@
         #   * Source ID is not set
         # bugzilla #298342
       elsif Stage.initial && src_id == nil
-        SearchForLicense_FirstStageBaseProduct(
-          src_id == nil ? Ops.get(Pkg.SourceGetCurrent(true), 0, 0) : src_id,
-          fallback_dir
-        )
+        log.info "Base product, initial stage"
+        SearchForLicense_FirstStageBaseProduct(base_product_id, fallback_dir)
 
         # Add-on-product license
         #   * Source ID is set
       elsif src_id != nil && Ops.greater_than(src_id, -1)
+        log.info "Add-On product"
         SearchForLicense_AddOnProduct(src_id, fallback_dir)
 
         # Fallback
@@ -793,20 +838,21 @@
       nil
     end
 
+    # Finds out whether user needs to 'Agree to the license coming from a 
given source_id'
+    #
+    # @param [Any] unique ID
+    # @param [String] path to directory with unpacked licenses (mandatory)
+    def cache_license_acceptance_needed(id, license_dir)
+      raise "Parameter 'license_dir' must not be nil" if license_dir.nil?
+
+      license_acceptance_needed = 
!FileUtils.Exists("#{license_dir}/no-acceptance-needed")
+      SetAcceptanceNeeded(id, license_acceptance_needed)
+    end
 
     def InitLicenseData(src_id, dir, licenses, available_langs, 
require_agreement, license_ident, id)
+      # Downloads and unpacks all licenses for a given source ID
       GetSourceLicenseDirectory(src_id, dir)
-
-      # License does not need to be accepted. Well, I mean, manually selected 
"Yes, of course, I agree..."
-      if FileUtils.Exists(
-          Builtins.sformat("%1/no-acceptance-needed", @license_dir)
-        )
-        if id == nil
-          Builtins.y2error("Parameter id not set")
-        else
-          SetAcceptanceNeeded(id, false)
-        end
-      end
+      cache_license_acceptance_needed(id, @license_dir)
 
       licenses.value = LicenseFiles(@license_dir, @license_patterns)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-packager-3.1.109/test/addon_product_test.rb 
new/yast2-packager-3.1.114/test/addon_product_test.rb
--- old/yast2-packager-3.1.109/test/addon_product_test.rb       2016-08-09 
20:55:14.000000000 +0200
+++ new/yast2-packager-3.1.114/test/addon_product_test.rb       2016-08-16 
16:51:47.000000000 +0200
@@ -5,6 +5,8 @@
 Yast.import "AddOnProduct"
 
 describe Yast::AddOnProduct do
+  subject { Yast::AddOnProduct }
+
   describe "#renamed?" do
     it "returns true if product has been renamed" do
       expect(Yast::AddOnProduct.renamed?("SUSE_SLES", "SLES")).to eq(true)
@@ -87,4 +89,197 @@
       end
     end
   end
+
+  describe "#AddPreselectedAddOnProducts" do
+    BASE_URL = "cd:/?devices=/dev/disk/by-id/ata-QEMU_DVD-ROM_QM00001".freeze
+    ADDON_REPO = {
+      "path" => "/foo", "priority" => 50, "url" => "cd:/?alias=Foo"
+    }.freeze
+
+    let(:repo) { ADDON_REPO }
+    let(:filelist) do
+      [{ "file" => "/add_on_products.xml", "type" => "xml" }]
+    end
+
+    before do
+      subject.SetBaseProductURL(BASE_URL)
+      allow(subject).to 
receive(:ParseXMLBasedAddOnProductsFile).and_return([repo])
+      subject.add_on_products = []
+    end
+
+    context "when filelist is empty" do
+      let(:filelist) { [] }
+
+      it "just returns true" do
+        expect(subject).to_not receive(:GetBaseProductURL)
+        subject.AddPreselectedAddOnProducts(filelist)
+      end
+    end
+
+    context "when filelist is nil" do
+      let(:filelist) { nil }
+
+      it "just returns true" do
+        expect(subject).to_not receive(:GetBaseProductURL)
+        subject.AddPreselectedAddOnProducts(filelist)
+      end
+    end
+
+    context "when filelist contains XML files" do
+      it "parses the XML file" do
+        expect(subject).to 
receive(:ParseXMLBasedAddOnProductsFile).and_return([])
+        subject.AddPreselectedAddOnProducts(filelist)
+      end
+    end
+
+    context "when filelist contains plain-text files" do
+      let(:filelist) do
+        [{ "file" => "/add_on_products.xml", "type" => "plain" }]
+      end
+
+      it "parses the plain file" do
+        expect(subject).to receive(:ParsePlainAddOnProductsFile).and_return([])
+        subject.AddPreselectedAddOnProducts(filelist)
+      end
+    end
+
+    context "when filelist contains unsupported file types" do
+      let(:filelist) do
+        [{ "file" => "/add_on_products.xml", "type" => "unsupported" }]
+      end
+
+      it "logs the error" do
+        expect(subject.log).to receive(:error).with(/Unsupported/)
+        subject.AddPreselectedAddOnProducts(filelist)
+      end
+    end
+
+    context "when the add-on is on a CD/DVD" do
+      let(:repo_id) { 1 }
+      let(:cd_url) { "cd:///?device=/dev/sr0" }
+
+      before do
+        allow(subject).to receive(:AcceptedLicenseAndInfoFile).and_return(true)
+        allow(Yast::Pkg). to receive(:SourceProductData).with(repo_id)
+        allow(subject).to receive(:InstallProductsFromRepository)
+        allow(subject).to receive(:ReIntegrateFromScratch)
+        allow(subject).to receive(:Integrate)
+      end
+
+      context "and no product name was given" do
+        let(:repo) { ADDON_REPO }
+
+        it "adds the repository" do
+          expect(subject).to receive(:AddRepo).with(repo["url"], repo["path"], 
repo["priority"])
+            .and_return(repo_id)
+          subject.AddPreselectedAddOnProducts(filelist)
+          expect(subject.add_on_products).to_not be_empty
+        end
+
+        it "asks for the CD/DVD if the repo could not be added" do
+          allow(subject).to receive(:AddRepo).with(repo["url"], repo["path"], 
repo["priority"])
+            .and_return(nil)
+          expect(subject).to receive(:AskForCD).and_return(cd_url)
+          expect(subject).to receive(:AddRepo).with(cd_url, repo["path"], 
repo["priority"])
+            .and_return(repo_id)
+          subject.AddPreselectedAddOnProducts(filelist)
+          expect(subject.add_on_products).to_not be_empty
+        end
+
+        it "does not add the repository if user cancels the dialog" do
+          allow(subject).to receive(:AddRepo).with(repo["url"], repo["path"], 
repo["priority"])
+            .and_return(nil)
+          allow(subject).to receive(:AskForCD).and_return(nil)
+
+          subject.AddPreselectedAddOnProducts(filelist)
+          expect(subject.add_on_products).to be_empty
+        end
+      end
+
+      context "and a network scheme is used" do
+        let(:repo) { ADDON_REPO.merge("url" => "http://example.net/repo";) }
+
+        it "checks whether the network is working" do
+          allow(subject).to receive(:AddRepo).and_return(nil)
+          expect(Yast::WFM).to 
receive(:CallFunction).with("inst_network_check", [])
+          subject.AddPreselectedAddOnProducts(filelist)
+        end
+      end
+
+      context "and a product name was given" do
+        let(:repo) { ADDON_REPO.merge("name" => "Foo") }
+        let(:matching_product) { { "label" => repo["name"] } }
+        let(:other_product) { { "label" => "other" } }
+        let(:other_repo_id) { 2 }
+
+        context "and the product is found in the CD/DVD" do
+          before do
+            allow(Yast::Pkg).to receive(:SourceProductData).with(repo_id)
+              .and_return(matching_product)
+          end
+
+          it "adds the product without asking" do
+            expect(subject).to_not receive(:AskForCD)
+            expect(subject).to receive(:AddRepo).with(repo["url"], anything, 
anything)
+              .and_return(repo_id)
+            subject.AddPreselectedAddOnProducts(filelist)
+          end
+        end
+
+        context "and the product is not found in the CD/DVD" do
+          before do
+            allow(Yast::Pkg).to receive(:SourceProductData).with(repo_id)
+              .and_return(matching_product)
+            allow(Yast::Pkg).to receive(:SourceProductData).with(other_repo_id)
+              .and_return(other_product)
+          end
+
+          it "does not add the repository if the user cancels the dialog" do
+            allow(subject).to receive(:AddRepo).with(repo["url"], anything, 
anything)
+              .and_return(other_repo_id)
+            allow(subject).to receive(:AskForCD).and_return(nil)
+
+            expect(Yast::Pkg).to receive(:SourceDelete).with(other_repo_id)
+            expect(subject).to_not receive(:Integrate).with(other_repo_id)
+            subject.AddPreselectedAddOnProducts(filelist)
+            expect(subject.add_on_products).to be_empty
+          end
+
+          it "adds the product if the user points to a valid CD/DVD" do
+            allow(subject).to receive(:AddRepo).with(repo["url"], 
repo["path"], repo["priority"])
+              .and_return(other_repo_id)
+            allow(subject).to receive(:AskForCD).and_return(cd_url)
+            allow(subject).to receive(:AddRepo).with(cd_url, repo["path"], 
repo["priority"])
+              .and_return(repo_id)
+
+            expect(Yast::Pkg).to receive(:SourceDelete).with(other_repo_id)
+            expect(Yast::Pkg).to_not receive(:SourceDelete).with(repo_id)
+            expect(subject).to receive(:Integrate).with(repo_id)
+            subject.AddPreselectedAddOnProducts(filelist)
+            expect(subject.add_on_products).to_not be_empty
+          end
+
+          context "and check_name option is disabled" do
+            let(:repo) { ADDON_REPO.merge("check_name" => true) }
+            it "adds the repository" do
+              allow(subject).to receive(:AddRepo).with(repo["url"], 
repo["path"], repo["priority"])
+                .and_return(other_repo_id)
+
+              subject.AddPreselectedAddOnProducts(filelist)
+              expect(subject.add_on_products).to_not be_empty
+            end
+          end
+        end
+      end
+
+      it "removes the product is the license is not accepted" do
+        allow(subject).to receive(:AddRepo).with(repo["url"], repo["path"], 
repo["priority"])
+          .and_return(repo_id)
+        expect(subject).to 
receive(:AcceptedLicenseAndInfoFile).and_return(false)
+        expect(Yast::Pkg).to receive(:SourceDelete).with(repo_id)
+        subject.AddPreselectedAddOnProducts(filelist)
+        expect(subject.add_on_products).to be_empty
+      end
+    end
+  end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-packager-3.1.109/test/packages_test.rb 
new/yast2-packager-3.1.114/test/packages_test.rb
--- old/yast2-packager-3.1.109/test/packages_test.rb    2016-08-09 
20:55:14.000000000 +0200
+++ new/yast2-packager-3.1.114/test/packages_test.rb    2016-08-16 
16:51:47.000000000 +0200
@@ -897,4 +897,92 @@
       Yast::Packages.Reset([:product])
     end
   end
+
+  describe "#ListSelected" do
+    let(:unordered_products) do
+      [
+        product("name" => "p3", "status" => :selected, "source" => 15),
+        product("name" => "p4", "status" => :available, "source" => 40),
+        product("name" => "p1", "status" => :selected, "source" => 10)
+      ]
+    end
+
+    let(:filtered_products) do
+      [
+        product("name" => "p3", "status" => :selected, "source" => 15),
+        product("name" => "p1", "status" => :selected, "source" => 10)
+      ]
+    end
+
+    let(:ordered_products) do
+      [
+        product("name" => "p1", "status" => :selected, "source" => 10),
+        product("name" => "p3", "status" => :selected, "source" => 15)
+      ]
+    end
+
+    let(:unordered_patterns) do
+      [
+        pattern("name" => "p3", "status" => :selected, "order" => "3", 
"user_visible" => true),
+        pattern("name" => "p1", "status" => :selected, "order" => "1", 
"user_visible" => false),
+        pattern("name" => "p2", "status" => :available, "order" => "2", 
"user_visible" => true)
+      ]
+    end
+
+    let(:filtered_patterns) do
+      [
+        pattern("name" => "p3", "status" => :selected, "order" => "3", 
"user_visible" => true)
+      ]
+    end
+
+    before do
+      allow(Yast::Pkg).to receive(:ResolvableProperties).with("", :product, "")
+        .and_return(unordered_products)
+    end
+
+    it "obtains a list of resolvables of the given type" do
+      expect(Yast::Pkg).to receive(:ResolvableProperties).with("", :product, 
"")
+
+      subject.ListSelected(:product, "")
+    end
+
+    it "filters not selected resolvables from the list" do
+      expect(subject).to receive(:sort_resolvable!)
+        .with(filtered_products, :product)
+
+      subject.ListSelected(:product, "")
+    end
+
+    it "filters not user visible resolvables from the list for type pattern" do
+      expect(Yast::Pkg).to receive(:ResolvableProperties).with("", :pattern, 
"")
+        .and_return(unordered_patterns)
+      expect(subject).to receive(:sort_resolvable!)
+        .with(filtered_patterns, :pattern)
+
+      subject.ListSelected(:pattern, "")
+    end
+
+    it "sorts resultant list depending on resolvable type" do
+      expect(subject).to 
receive(:formatted_resolvables).with(ordered_products, "")
+
+      subject.ListSelected(:product, "")
+    end
+
+    it "returns an empty list if no resolvables selected" do
+      allow(Yast::Pkg).to receive(:ResolvableProperties).with("", :product, "")
+        .and_return([])
+
+      expect(subject.ListSelected(:product, "Product: %1")).to eql([])
+    end
+
+    it "returns a list with each resultant resolvable formatted as the format 
given" do
+      expect(subject.ListSelected(:product, "Product: %1")).to eql(
+        [
+          "Product: p1",
+          "Product: p3"
+        ]
+      )
+    end
+  end
+
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/yast2-packager-3.1.109/test/product_license_test.rb 
new/yast2-packager-3.1.114/test/product_license_test.rb
--- old/yast2-packager-3.1.109/test/product_license_test.rb     2016-08-09 
20:55:14.000000000 +0200
+++ new/yast2-packager-3.1.114/test/product_license_test.rb     2016-08-16 
16:51:47.000000000 +0200
@@ -147,4 +147,101 @@
     end
 
   end
+
+  describe "#AcceptanceNeeded" do
+    let(:base_product_id) { 0 }
+    let(:add_on_product_id) { 1 }
+
+    before do
+      # Downloading and unpacking licenses is expensive, so we cache all values
+      # and thus we need to reinit all caches for testing with different values
+      Yast::ProductLicense.initialize_default_values
+
+      allow(Yast::ProductLicense).to 
receive(:base_product_id).and_return(base_product_id)
+    end
+
+    context "when called in the initial stage of installation" do
+      before do
+        # Initial installation
+        allow(Yast::Stage).to receive(:initial).and_return(true)
+        allow(Yast::Mode).to receive(:installation).and_return(true)
+
+        # Tarball with licenses exists
+        allow(Yast::FileUtils).to 
receive(:Exists).with(/license.tar.gz/).and_return(true)
+        # Info file exists
+        allow(Yast::FileUtils).to 
receive(:Exists).with(/info.txt/).and_return(true)
+      end
+
+      context "when called for base-product" do
+        before do
+          expect(Yast::ProductLicense).to 
receive(:GetSourceLicenseDirectory).and_call_original
+          expect(Yast::ProductLicense).to 
receive(:SetAcceptanceNeeded).and_call_original
+          expect(Yast::ProductLicense).to 
receive(:UnpackLicenseTgzFileToDirectory).and_return(true)
+        end
+
+        it "returns that acceptance is needed if no-acceptance-needed file is 
not found" do
+          expect(Yast::FileUtils).to 
receive(:Exists).with(/no-acceptance-needed/).and_return(false)
+          expect(Yast::ProductLicense.AcceptanceNeeded(base_product_id)).to 
eq(true)
+        end
+
+        it "returns that acceptance is not needed if the no-acceptance-needed 
file is found" do
+          expect(Yast::FileUtils).to 
receive(:Exists).with(/no-acceptance-needed/).and_return(true)
+          expect(Yast::ProductLicense.AcceptanceNeeded(base_product_id)).to 
eq(false)
+        end
+      end
+
+      context "when called for add-on product" do
+        context "when value has not been stored yet" do
+          it "returns the safe default true" do
+            
expect(Yast::ProductLicense.AcceptanceNeeded(add_on_product_id)).to eq(true)
+          end
+        end
+
+        context "when value has been already stored" do
+          it "returns the stored value" do
+            Yast::ProductLicense.SetAcceptanceNeeded(add_on_product_id, false)
+            
expect(Yast::ProductLicense.AcceptanceNeeded(add_on_product_id)).to eq(false)
+          end
+        end
+      end
+    end
+
+    context "when not called in initial installation" do
+      before do
+        allow(Yast::Stage).to receive(:initial).and_return(false)
+        allow(Yast::Mode).to receive(:installation).and_return(false)
+      end
+
+      context "when called for base-product" do
+        context "returns the safe default true" do
+          it "throws an error" do
+            expect(Yast::ProductLicense.AcceptanceNeeded(base_product_id)).to 
eq(true)
+          end
+        end
+
+        context "when value has been already stored" do
+          it "returns the stored value" do
+            Yast::ProductLicense.SetAcceptanceNeeded(base_product_id, false)
+            expect(Yast::ProductLicense.AcceptanceNeeded(base_product_id)).to 
eq(false)
+          end
+        end
+      end
+
+      context "when called for add-on product" do
+        context "when value has not been stored yet" do
+          it "returns the safe default true" do
+            
expect(Yast::ProductLicense.AcceptanceNeeded(add_on_product_id)).to eq(true)
+          end
+        end
+
+        context "when value has been already stored" do
+          it "returns the stored value" do
+            Yast::ProductLicense.SetAcceptanceNeeded(add_on_product_id, true)
+            
expect(Yast::ProductLicense.AcceptanceNeeded(add_on_product_id)).to eq(true)
+          end
+        end
+      end
+    end
+  end
+
 end


Reply via email to