Hello community,

here is the log from the commit of package autoyast2 for openSUSE:Factory 
checked in at 2015-04-27 22:07:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/autoyast2 (Old)
 and      /work/SRC/openSUSE:Factory/.autoyast2.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "autoyast2"

Changes:
--------
--- /work/SRC/openSUSE:Factory/autoyast2/autoyast2.changes      2015-04-02 
16:01:23.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.autoyast2.new/autoyast2.changes 2015-04-27 
22:08:56.000000000 +0200
@@ -1,0 +2,19 @@
+Fri Apr 10 10:48:04 CEST 2015 - sch...@suse.de
+
+- New autoinst flag in general/mode section:
+  activate_systemd_default_target.
+  The default target of systemd will be activated in the second
+  stage of autoyast installation.
+  The default is "true" which is a backward compatible value.
+  (bnc#923992)
+- 3.1.74
+
+-------------------------------------------------------------------
+Tue Apr  7 15:26:44 UTC 2015 - igonzalezs...@suse.com
+
+- Avoid ayast_probe module crashing when called from an installed
+  system.
+  (bnc#926241)
+- 3.1.73
+
+-------------------------------------------------------------------

Old:
----
  autoyast2-3.1.72.tar.bz2

New:
----
  autoyast2-3.1.74.tar.bz2

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

Other differences:
------------------
++++++ autoyast2.spec ++++++
--- /var/tmp/diff_new_pack.MqxT48/_old  2015-04-27 22:08:57.000000000 +0200
+++ /var/tmp/diff_new_pack.MqxT48/_new  2015-04-27 22:08:57.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           autoyast2
-Version:        3.1.72
+Version:        3.1.74
 Release:        0
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build

++++++ autoyast2-3.1.72.tar.bz2 -> autoyast2-3.1.74.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/CONTRIBUTING.md 
new/autoyast2-3.1.74/CONTRIBUTING.md
--- old/autoyast2-3.1.72/CONTRIBUTING.md        2015-03-27 09:54:14.000000000 
+0100
+++ new/autoyast2-3.1.74/CONTRIBUTING.md        2015-04-27 10:29:12.000000000 
+0200
@@ -12,13 +12,13 @@
 -----------
 
 If you find a problem, please report it either using
-[Bugzilla](https://bugzilla.novell.com/enter_bug.cgi?format=guided&product=openSUSE+Factory&component=YaST2)
+[Bugzilla](https://bugzilla.suse.com/enter_bug.cgi?format=guided&product=openSUSE+Factory&component=YaST2)
 or [GitHub issues](../../issues). (For Bugzilla, use the [simplified
 
registration](https://secure-www.novell.com/selfreg/jsp/createSimpleAccount.jsp)
 if you don't have an account yet.)
 
 If you find a problem, please report it either using
-[Bugzilla](https://bugzilla.novell.com/) or GitHub issues. We can't guarantee
+[Bugzilla](https://bugzilla.suse.com/) or GitHub issues. We can't guarantee
 that every bug will be fixed, but we'll try.
 
 When creating a bug report, please follow our [bug reporting
@@ -71,7 +71,7 @@
 [widely used
 
conventions](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
 
-If your commit is related to a bug in Buzgilla or an issue on GitHub, make sure
+If your commit is related to a bug in Bugzilla or an issue on GitHub, make sure
 you mention it in the commit message for cross-reference. Use format like
 bnc#775814 or gh#yast/yast-foo#42. See also [GitHub
 
autolinking](https://help.github.com/articles/github-flavored-markdown#references)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/README.md 
new/autoyast2-3.1.74/README.md
--- old/autoyast2-3.1.72/README.md      2015-03-27 09:54:14.000000000 +0100
+++ new/autoyast2-3.1.74/README.md      2015-04-27 10:29:12.000000000 +0200
@@ -1,5 +1,66 @@
-# YaST - The AutoYaST Framework #
+YaST - The AutoYaST Framework
+=============================
 
 [![Travis 
Build](https://travis-ci.org/yast/yast-autoinstallation.svg?branch=master)](https://travis-ci.org/yast/yast-autoinstallation)
 [![Jenkins 
Build](http://img.shields.io/jenkins/s/https/ci.opensuse.org/yast-autoinstallation-master.svg)](https://ci.opensuse.org/view/Yast/job/yast-autoinstallation-master/)
 
+
+Development
+===========
+
+This module is developed as part of YaST. See the
+[development 
documentation](http://yastgithubio.readthedocs.org/en/latest/development/).
+
+
+Getting the Sources
+===================
+
+To get the source code, clone the GitHub repository:
+
+    $ git clone https://github.com/yast/yast-autoinstallation.git
+
+If you want to contribute into the project you can
+[fork](https://help.github.com/articles/fork-a-repo/) the repository and clone 
your fork.
+
+
+Testing Environment
+===================
+
+There are several possibilities how test an updated code. It also depends on
+the fact in which stage of installation it comes into effect. First stage runs
+between the start from installation media to reboot (or kexec), then continues
+second stage.
+
+### First Stage ###
+
+Either use *StartShell=1* option in 
[Linuxrc](https://en.opensuse.org/SDB:Linuxrc),
+edit the installation system and run *yast* manually or use
+a [DriverUpdate](https://en.opensuse.org/SDB:Linuxrc#p_dud) via Linuxrc.
+
+### Second Stage ###
+
+There are two possible ways how to rerun this stage, just keep in mind that
+the system might be already configured and thus it might behave
+a bit differently:
+
+  ```
+  cp /var/lib/YaST2/install.inf /etc/
+  touch /var/lib/YaST2/runme_at_boot
+  cp /var/adm/autoinstall/cache/installedSystem.xml \
+    /var/lib/autoinstall/autoconf/autoconf.xml
+  reboot
+  ```
+
+or faster without rebooting but with possible side-effects:
+
+  ```
+  yast ayast_setup setup 
filename=/var/adm/autoinstall/cache/installedSystem.xml
+  ```
+
+
+Contact
+=======
+
+If you have any question, feel free to ask at the [development mailing
+list](http://lists.opensuse.org/yast-devel/) or at the
+[#yast](https://webchat.freenode.net/?channels=%23yast) IRC channel on 
freenode.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/package/autoyast2.changes 
new/autoyast2-3.1.74/package/autoyast2.changes
--- old/autoyast2-3.1.72/package/autoyast2.changes      2015-03-27 
09:54:14.000000000 +0100
+++ new/autoyast2-3.1.74/package/autoyast2.changes      2015-04-27 
10:29:12.000000000 +0200
@@ -1,4 +1,23 @@
 -------------------------------------------------------------------
+Fri Apr 10 10:48:04 CEST 2015 - sch...@suse.de
+
+- New autoinst flag in general/mode section:
+  activate_systemd_default_target.
+  The default target of systemd will be activated in the second
+  stage of autoyast installation.
+  The default is "true" which is a backward compatible value.
+  (bnc#923992)
+- 3.1.74
+
+-------------------------------------------------------------------
+Tue Apr  7 15:26:44 UTC 2015 - igonzalezs...@suse.com
+
+- Avoid ayast_probe module crashing when called from an installed
+  system.
+  (bnc#926241)
+- 3.1.73
+
+-------------------------------------------------------------------
 Thu Mar 26 17:00:32 CET 2015 - sch...@suse.de
 
 - New autoinst flag in general/mode section: final_restart_services.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/package/autoyast2.spec 
new/autoyast2-3.1.74/package/autoyast2.spec
--- old/autoyast2-3.1.72/package/autoyast2.spec 2015-03-27 09:54:14.000000000 
+0100
+++ new/autoyast2-3.1.74/package/autoyast2.spec 2015-04-27 10:29:12.000000000 
+0200
@@ -17,7 +17,7 @@
 
 
 Name:           autoyast2
-Version:        3.1.72
+Version:        3.1.74
 Release:        0
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/src/clients/inst_autoconfigure.rb 
new/autoyast2-3.1.74/src/clients/inst_autoconfigure.rb
--- old/autoyast2-3.1.72/src/clients/inst_autoconfigure.rb      2015-03-27 
09:54:15.000000000 +0100
+++ new/autoyast2-3.1.74/src/clients/inst_autoconfigure.rb      2015-04-27 
10:29:12.000000000 +0200
@@ -39,7 +39,8 @@
         "Profile general,mode:%1",
         Ops.get_map(Profile.current, ["general", "mode"], {})
       )
-      @need_systemd_isolate = true
+      @need_systemd_isolate = Ops.get_boolean(
+        Profile.current,["general", "mode", 
"activate_systemd_default_target"], true)
       final_restart_services = Ops.get_boolean(
         Profile.current,["general", "mode", "final_restart_services"], true)
       @max_steps = Y2ModuleConfig.ModuleMap.size + 3 # additional for 
scripting and finished message
@@ -299,6 +300,8 @@
         Builtins.y2milestone("after  calling \"%1\"", @cmd)
         Builtins.y2milestone("ret=%1", @out)
         wait_systemd_finished(@max_wait, @ser_ignore)
+      else
+        Builtins.y2milestone("Do not activate systemd default target (defined 
in autoyast.xml)")
       end
 
       # Just in case, remove this file to avoid reconfiguring...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/src/include/autoinstall/ask.rb 
new/autoyast2-3.1.74/src/include/autoinstall/ask.rb
--- old/autoyast2-3.1.72/src/include/autoinstall/ask.rb 2015-03-27 
09:54:15.000000000 +0100
+++ new/autoyast2-3.1.74/src/include/autoinstall/ask.rb 2015-04-27 
10:29:12.000000000 +0200
@@ -144,6 +144,10 @@
           if Ops.greater_than(Ops.get_integer(ask, "height", 0), min_height)
             min_height = Ops.get_integer(ask, "height", 0)
           end
+
+          #
+          # Script to calculate the default value (optional)
+          #
           if Builtins.haskey(ask, "default_value_script")
             interpreter = Ops.get_string(
               ask,
@@ -192,6 +196,10 @@
               Ops.get_string(out, "stderr", "")
             )
           end
+
+          #
+          # Start to handle questions: widgets creation (checkbox, combobox)
+          #
           dlg = Dummy()
           if type == "boolean"
             on = Ops.get(ask, "default") == "true" ? true : false
@@ -216,7 +224,7 @@
           elsif type == "static_text"
             widget = Label(Id(entry_id), Ops.get_string(ask, "default", ""))
             dlg = createWidget(widget, frametitle)
-          else
+          else # integer or string
             if Ops.get_boolean(ask, "password", false) == true
               widget1 = Password(
                 Id(entry_id),
@@ -225,7 +233,7 @@
                 Ops.get_string(ask, "default", "")
               )
               widget2 = Password(
-                Id(:pass2),
+                Id("#{entry_id}_pass2"),
                 Opt(:notify),
                 "",
                 Ops.get_string(ask, "default", "")
@@ -261,35 +269,47 @@
               end
             end
           end
-          if frametitle != ""
-            if frameBuffer == nil
-              frameBufferVBox = VBox(dlg)
-            else
-              if frametitle == frameBufferTitle
-                frameBufferVBox = Builtins.add(frameBufferVBox, dlg)
-              else
-                dialog_term = Builtins.add(dialog_term, frameBuffer)
+          #
+          # At this time, widgets are created.
+          #
+
+          #
+          # Add the widget to the correct frame (using the frametitle).
+          #
+          if frametitle != "" # some frametitle is set
+            if frameBuffer == nil # and no frameBuffer exists
+              frameBufferVBox = VBox(dlg) # create a new frameBuffer
+            else # a frameBuffer exists
+              if frametitle == frameBufferTitle # with same title
+                frameBufferVBox = Builtins.add(frameBufferVBox, dlg) # add 
current to frameBuffer
+              else # with different title
+                dialog_term = Builtins.add(dialog_term, frameBuffer) # add 
frameBuffer to dialog
                 dialog_term = Builtins.add(dialog_term, VSpacing(1))
-                frameBufferVBox = VBox(dlg)
+                frameBufferVBox = VBox(dlg) # populate the frameBuffer with 
the new frame
               end
             end
-            frameBuffer = Frame(frametitle, frameBufferVBox)
+            frameBuffer = Frame(frametitle, frameBufferVBox) # set frameBuffer 
values for the next iteration
             frameBufferTitle = frametitle
-          else
-            if frameBuffer != nil
-              dialog_term = Builtins.add(dialog_term, frameBuffer)
+          else # frametitle is in blank
+            if frameBuffer != nil # a previous frameBuffer exists
+              dialog_term = Builtins.add(dialog_term, frameBuffer) # add 
frameBuffer to dialog
               dialog_term = Builtins.add(dialog_term, VSpacing(1))
               frameBuffer = nil
               frameBufferVBox = nil
             end
-            dialog_term = Builtins.add(dialog_term, dlg)
+            dialog_term = Builtins.add(dialog_term, dlg) # add new element 
(with no frame) to dialog
             dialog_term = Builtins.add(dialog_term, VSpacing(1))
           end
         end
+
+        # If some frameBuffer left after iterations, it's added to the dialog.
         if frameBuffer != nil
           dialog_term = Builtins.add(dialog_term, frameBuffer)
         end
 
+        #
+        # Let's build the dialog
+        #
         help_term = HWeight(30, RichText(helptext)) if helptext != ""
         title_term = Heading(title) if title != ""
         backButton = PushButton(Id(:back), back_label)
@@ -316,6 +336,10 @@
         if Ops.less_than(Builtins.size(history), 2)
           UI.ChangeWidget(Id(:back), :Enabled, false)
         end
+
+        #
+        # Wait for user input
+        #
         while true
           ret = nil
           if timeout == 0
@@ -324,13 +348,13 @@
             ret = UI.TimeoutUserInput(Ops.multiply(timeout, 1000))
           end
           timeout = 0
-          if ret == :ok || ret == :timeout
+          if ret == :ok || ret == :timeout # Process users' input and save 
asks into dialogs hash.
             runAgain = 0
             element_cnt2 = 0
             Ops.set(
               dialogs,
               dialog_nr,
-              Builtins.maplist(
+              Builtins.maplist( # Iterate through widgets building a list of 
asks (containing responses)
                 Convert.convert(
                   Ops.get(dialogs, dialog_nr, []),
                   :from => "list",
@@ -350,10 +374,10 @@
                   val = Builtins.tointeger(Convert.to_string(val))
                 end
                 if Ops.get_boolean(ask, "password", false) == true
-                  pass2 = Convert.to_string(UI.QueryWidget(Id(:pass2), :Value))
+                  pass2 = 
Convert.to_string(UI.QueryWidget(Id("#{entry_id}_pass2"), :Value))
                   if pass2 != Convert.to_string(val)
                     Popup.Error("The two passwords mismatch.")
-                    runAgain = 1
+                    runAgain = 1 # Run the same dialog again.
                   end
                 end
                 Builtins.y2debug(
@@ -361,9 +385,11 @@
                   Ops.get_string(ask, "question", ""),
                   val
                 )
+
+                # Save the value in the profile (and also as default value)
                 Ops.set(ask, "default", val)
                 pos = path2pos(Ops.get_string(ask, "path", ""))
-                if Ops.get_string(ask, "path", "") != ""
+                if Ops.get_string(ask, "path", "") != "" # Set value in the 
profile
                   Profile.current = Profile.setElementByList(
                     pos,
                     val,
@@ -378,6 +404,8 @@
                     Profile.current
                   )
                 end
+
+                # Save the value into a file (if 'file' property was set)
                 if file != ""
                   if Ops.get_string(ask, "type", "string") == "boolean"
                     if !SCR.Write(
@@ -400,6 +428,8 @@
                     end
                   end
                 end
+
+                # If ask must run an script, let's run it
                 if script != {}
                   scriptName = Ops.get_string(
                     script,
@@ -426,7 +456,7 @@
                     SCR.Execute(path(".target.mkdir"), current_logdir)
                   end
                   executionString = ""
-                  if Ops.get_boolean(script, "environment", false)
+                  if Ops.get_boolean(script, "environment", false) # FIXME: 
why not pass the variable always?
                     if Ops.get_string(ask, "type", "string") == "boolean"
                       val = Builtins.sformat(
                         "%1",
@@ -461,15 +491,19 @@
                       executionString
                     )
                   end
-                  runAgain = Ops.add(
-                    runAgain,
-                    Convert.to_integer(
-                      SCR.Execute(path(".target.bash"), executionString)
+                  Popup.Feedback("", _("A user defined script is running. This 
may take a while.")) do
+                    runAgain = Ops.add(
+                      runAgain,
+                      Convert.to_integer(
+                        SCR.Execute(path(".target.bash"), executionString)
+                      )
                     )
-                  )
+                  end
                   if Ops.get_boolean(script, "rerun_on_error", false) == false
                     runAgain = 0
                   end
+
+                  # Show feedback in a dialog
                   showFeedback = Ops.get_boolean(script, "feedback", false)
                   feedback = ""
                   if showFeedback
@@ -487,10 +521,12 @@
                     Popup.LongText(
                       "",
                       RichText(Opt(:plainText), feedback),
-                      40,
+                      70,
                       15
                     )
                   end
+
+                  # Save the next_dialog
                   if Ops.greater_than(
                       SCR.Read(path(".target.size"), "/tmp/next_dialog"),
                       0
@@ -511,7 +547,7 @@
               end
             )
             break if runAgain == 0
-          elsif ret == :back
+          elsif ret == :back # back button pressed: one step back on history.
             jumpToDialog = Ops.get(
               history,
               Ops.subtract(Builtins.size(history), 2),
@@ -529,7 +565,7 @@
           end
         end
         UI.CloseDialog
-        if jumpToDialog != -2
+        if jumpToDialog != -2 # If we must jump to another dialog (as read on 
/tmp/next_dialog)
           dialog_nr = jumpToDialog
           jumpToDialog = -2
           i = 0
@@ -540,7 +576,7 @@
             end
             i = Ops.add(i, 1)
           end
-        else
+        else # Next dialog
           dialogCounter = Ops.add(dialogCounter, 1)
           dialog_nr = Ops.get(keys, dialogCounter, -1)
         end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/src/modules/AutoInstallRules.rb 
new/autoyast2-3.1.74/src/modules/AutoInstallRules.rb
--- old/autoyast2-3.1.72/src/modules/AutoInstallRules.rb        2015-03-27 
09:54:15.000000000 +0100
+++ new/autoyast2-3.1.74/src/modules/AutoInstallRules.rb        2015-04-27 
10:29:12.000000000 +0200
@@ -36,6 +36,15 @@
 
       Yast.include self, "autoinstall/io.rb"
 
+      reset
+    end
+
+    # Reset the module's state
+    #
+    # @return nil
+    #
+    # @see #AutoInstallRules
+    def reset
       @userrules = false
       @dontmergeIsDefault = true
       @dontmergeBackup = []
@@ -50,19 +59,12 @@
       @ATTR = {}
 
       @installed_product = ""
-
       @installed_product_version = ""
-
       @hostname = ""
-
       @hostaddress = ""
-
       @network = ""
-
       @domain = ""
-
       @arch = ""
-
       @karch = ""
 
       # Taken from smbios
@@ -78,31 +80,19 @@
       @board = ""
 
       @memsize = 0
-
       @disksize = []
-
       @totaldisk = 0
-
       @hostid = ""
-
       @mac = ""
-
       @linux = 0
-
       @others = 0
-
       @xserver = ""
-
       @haspcmcia = "0"
 
       #///////////////////////////////////////////
       #///////////////////////////////////////////
-
       @NonLinuxPartitions = []
-
       @LinuxPartitions = []
-
-
       @UserRules = {}
 
       # Local Variables
@@ -110,7 +100,6 @@
       @env = {}
 
       @tomerge = []
-
       @element2file = {}
       AutoInstallRules()
     end
@@ -322,8 +311,8 @@
       distro_str = SCR.Read(path(".content.DISTRO"))
       log.info "DISTRO: #{distro_str}"
 
-      distro = distro_map(distro_str)
-      cpe = distro ? cpeid_map(distro["cpeid"]) : {}
+      distro = distro_map(distro_str) || {}
+      cpe = cpeid_map(distro["cpeid"]) || {}
 
       @installed_product = distro["name"] || ""
       @installed_product_version = cpe["version"] || ""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/src/modules/AutoinstClass.rb 
new/autoyast2-3.1.74/src/modules/AutoinstClass.rb
--- old/autoyast2-3.1.72/src/modules/AutoinstClass.rb   2015-03-27 
09:54:15.000000000 +0100
+++ new/autoyast2-3.1.74/src/modules/AutoinstClass.rb   2015-04-27 
10:29:12.000000000 +0200
@@ -19,7 +19,10 @@
   class AutoinstClassClass < Module
     include Yast::Logger
 
+    MERGE_XSLT_PATH = "/usr/share/autoinstall/xslt/merge.xslt"
+
     def main
+
       Yast.import "AutoinstConfig"
       Yast.import "XML"
       Yast.import "Summary"
@@ -38,21 +41,21 @@
       AutoinstClass()
     end
 
-    # find a profile path
+    # Finds a profile path
     # @param string profile name
     # @return [String] profile Path
     def findPath(name, class_)
-      result = @confs.find { |c| c['name'] == name && c['class'] == class_ }
-      result ||= { 'class' => '', 'name' => 'default' }
-      File.join(@classDir, result['class'], result['name'])
+      result = @confs.find { |c| c["name"] == name && c["class"] == class_ }
+      result ||= { "class" => "", "name" => "default" }
+      File.join(@classDir, result["class"], result["name"])
     end
 
-    # Read classes
+    # Reads classes
     def Read
       if SCR.Read(path(".target.size"), @classPath) != -1
         # TODO: use XML module
         classes_map = Convert.to_map(SCR.Read(path(".xml"), @classPath))
-        @Classes = (classes_map && classes_map['classes']) || []
+        @Classes = (classes_map && classes_map["classes"]) || []
       else
         @Classes = []
       end
@@ -64,15 +67,15 @@
     #     classes.xml files, one for each repository
     def Compat
       if !class_file_exists? && compat_class_file_exists?
-        log.info "Compat: #{@classPath} no found but #{compat_class_file} 
exists"
-        new_classes_map = { 'classes' => read_old_classes }
+        log.info "Compat: #{@classPath} not found but #{compat_class_file} 
exists"
+        new_classes_map = { "classes" => read_old_classes }
         log.info "creating #{new_classes_map}"
         XML.YCPToXMLFile(:class, new_classes_map, @classPath)
       end
       nil
     end
 
-    # Change the directory and read the class definitions
+    # Changes the directory and reads the class definitions
     #
     # @param [String] Path of the new directory
     # @return nil
@@ -84,7 +87,7 @@
       nil
     end
 
-    # Change the directory of classes definitions.
+    # Changes the directory of classes definitions.
     #
     # AutoinstConfig#classDir= is called to set the new value
     # in the configuration. It does not check if the directory
@@ -108,76 +111,39 @@
       nil
     end
 
-    # Merge Classes
+    # Merge classes
     #
     def MergeClasses(configuration, base_profile, resultFileName)
-      configuration = deep_copy(configuration)
       dontmerge_str = ""
-      i = 1
-      Builtins.foreach(AutoinstConfig.dontmerge) do |dm|
-        dontmerge_str = Ops.add(
-          dontmerge_str,
-          Builtins.sformat(" --param dontmerge%1 \"'%2'\" ", i, dm)
-        )
-        i = Ops.add(i, 1)
+      AutoinstConfig.dontmerge.each_with_index do |dm, i|
+        dontmerge_str << " --param dontmerge#{i+1} \"'#{dm}'\" "
       end
-      tmpdir = AutoinstConfig.tmpDir
-      _MergeCommand = Builtins.sformat(
-        "/usr/bin/xsltproc --novalid --param replace \"'false'\" %1 --param 
with ",
-        dontmerge_str
-      )
-
-      _MergeCommand = Ops.add(
-        Ops.add(
-          Ops.add(_MergeCommand, "\"'"),
-          findPath(
-            Ops.get_string(configuration, "name", ""),
-            Ops.get_string(configuration, "class", "")
-          )
-        ),
-        "'\"  "
-      )
-      _MergeCommand = Ops.add(
-        Ops.add(
-          Ops.add(Ops.add(Ops.add(_MergeCommand, "--output "), tmpdir), "/"),
-          resultFileName
-        ),
-        " "
-      )
-      _MergeCommand = Ops.add(
-        _MergeCommand,
-        " /usr/share/autoinstall/xslt/merge.xslt "
-      )
-      _MergeCommand = Ops.add(Ops.add(_MergeCommand, base_profile), " ")
-
-
-      Builtins.y2milestone("Merge command: %1", _MergeCommand)
-
-      out = Convert.to_map(
-        SCR.Execute(path(".target.bash_output"), _MergeCommand, {})
-      )
-      Builtins.y2milestone(
-        "Merge stdout: %1, stderr: %2",
-        Ops.get_string(out, "stdout", ""),
-        Ops.get_string(out, "stderr", "")
-      )
-      deep_copy(out)
+      merge_command =
+        "/usr/bin/xsltproc --novalid --param replace \"'false'\" 
#{dontmerge_str} --param with " \
+        "\"'#{findPath(configuration["name"], configuration["class"])}'\"  " \
+        "--output #{File.join(AutoinstConfig.tmpDir, resultFileName)}  " \
+        "#{MERGE_XSLT_PATH} #{base_profile} "
+
+      out = SCR.Execute(path(".target.bash_output"), merge_command, {})
+      log.info "Merge command: #{merge_command}"
+      log.info "Merge stdout: #{out["stdout"]}, stderr: #{out["stderr"]}"
+      out
     end
 
-    # Read files from class directories
+    # Reads files from class directories
     # @return [void]
     def Files
       @confs = []
       @Classes.each do |class_|
-        class_name_ = class_['name'] || 'xxx'
+        class_name_ = class_["name"] || "xxx"
         files_path = File.join(@classDir, class_name_)
-        files = Convert.convert(SCR.Read(path('.target.dir'), files_path),
+        files = Convert.convert(SCR.Read(path(".target.dir"), files_path),
           :from => "any", :to   => "list <string>")
 
         next if files.nil?
 
         log.info "Files in class #{class_name_}: #{files}"
-        new_confs = files.map { |f| { 'class' => class_name_, 'name' => f  }  }
+        new_confs = files.map { |f| { "class" => class_name_, "name" => f  }  }
         log.info "Configurations: #{new_confs}"
         @confs.concat(new_confs)
       end
@@ -185,15 +151,11 @@
       nil
     end
 
-    # Save Class definitions
+    # Saves classes definitions
     def Save
-      Builtins.foreach(@deletedClasses) do |c|
-        toDel = Builtins.sformat(
-          "/bin/rm -rf %1/%2",
-          AutoinstConfig.classDir,
-          c
-        )
-        SCR.Execute(path(".target.bash"), toDel)
+      @deletedClasses.each do |c|
+        to_del = "/bin/rm -rf #{File.join(AutoinstConfig.classDir, c)}"
+        SCR.Execute(path(".target.bash"), to_del)
       end
       @deletedClasses = []
       tmp = { "classes" => @Classes }
@@ -202,34 +164,29 @@
     end
 
 
-    # Import configuration
+    # Imports configuration
+    # @params [Array<Hash>] settings Configuration
+    # @return true
     def Import(settings)
-      settings = deep_copy(settings)
       @profile_conf = deep_copy(settings)
       true
     end
 
-    # Export configuration
+    # Exports configuration
+    # @return [Array<Hash>] Copy of the configuration
     def Export
       deep_copy(@profile_conf)
     end
 
-    # Configuration Summary
+    # Builds the configuration summary
+    # @return [String] Configuration summary
     def Summary
       summary = ""
-
-      Builtins.foreach(@profile_conf) do |c|
-        summary = Summary.AddHeader(
-          summary,
-          Ops.get_string(c, "class_name", "None")
-        )
-        summary = Summary.AddLine(
-          summary,
-          Ops.get_string(c, "configuration", "None")
-        )
+      @profile_conf.each do |conf|
+        summary = Summary.AddHeader(summary, conf["class_name"] || "None")
+        summary = Summary.AddLine(summary, conf["configuration"] || "None")
       end
-      return Summary.NotConfigured if Builtins.size(summary) == 0
-      summary
+      summary.empty? ? Summary.NotConfigured : summary
     end
 
     publish :variable => :classDir, :type => "string"
@@ -276,10 +233,10 @@
     # Builds a map of classes to import from /etc/autoinstall/classes.xml
     # @return [Array<Hash>] Classes defined in the file.
     def read_old_classes
-      old_classes_map = Convert.to_map(SCR.Read(path('.xml'), 
compat_class_file))
-      old_classes = old_classes_map['classes'] || []
+      old_classes_map = Convert.to_map(SCR.Read(path(".xml"), 
compat_class_file))
+      old_classes = (old_classes_map && old_classes_map["classes"]) || []
       old_classes.each_with_object([]) do |class_, new_classes|
-        class_path_ = File.join(@classDir, class_['name'] || '')
+        class_path_ = File.join(@classDir, class_["name"] || "")
         log.info "looking for #{class_path_}"
         new_classes << class_ unless SCR.Read(path(".target.dir"), 
class_path_).nil?
       end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/test/AutoInstallRules_test.rb 
new/autoyast2-3.1.74/test/AutoInstallRules_test.rb
--- old/autoyast2-3.1.72/test/AutoInstallRules_test.rb  2015-03-27 
09:54:15.000000000 +0100
+++ new/autoyast2-3.1.74/test/AutoInstallRules_test.rb  2015-04-27 
10:29:12.000000000 +0200
@@ -1,12 +1,10 @@
 #!/usr/bin/env rspec
 
-ENV["Y2DIR"] = File.expand_path("../../src", __FILE__)
-
-require "yast"
+require_relative "test_helper"
 
 Yast.import "AutoInstallRules"
 
-describe "Yast::AutoInstallRules" do
+describe Yast::AutoInstallRules do
   subject { Yast::AutoInstallRules }
 
   describe "#cpeid_map" do
@@ -96,6 +94,22 @@
       expect(Yast::AutoInstallRules.installed_product).to eq("SUSE Linux 
Enterprise Server 12")
       expect(Yast::AutoInstallRules.installed_product_version).to eq("12")
     end
+
+    context "when .content.DISTRO is not found" do
+      before(:each) do
+        subject.reset
+        allow(Yast::SCR).to receive(:Read).with(any_args)
+        allow(Yast::Arch).to receive(:architecture).and_return("x86_64")
+      end
+
+      it 'set installed_product and installed_product_version to blank string' 
do
+        expect(Yast::SCR).to 
receive(:Read).with(Yast::Path.new(".content.DISTRO")).
+          and_return(nil)
+        subject.ProbeRules
+        expect(Yast::AutoInstallRules.installed_product).to eq('')
+        expect(Yast::AutoInstallRules.installed_product_version).to eq('')
+      end
+    end
   end
 
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/test/AutoinstClass_test.rb 
new/autoyast2-3.1.74/test/AutoinstClass_test.rb
--- old/autoyast2-3.1.72/test/AutoinstClass_test.rb     2015-03-27 
09:54:15.000000000 +0100
+++ new/autoyast2-3.1.74/test/AutoinstClass_test.rb     2015-04-27 
10:29:12.000000000 +0200
@@ -1,19 +1,17 @@
 #!/usr/bin/env rspec
 
-root_path = File.expand_path('../..', __FILE__)
-ENV["Y2DIR"] = File.join(root_path, 'src')
-
-require "yast"
+require_relative "test_helper"
 
 Yast.import "AutoinstClass"
 
 describe Yast::AutoinstClass do
   subject { Yast::AutoinstClass }
 
+  let(:root_path) { File.expand_path('../..', __FILE__) }
   let(:test_xml_dir) { File.join(root_path, 'test', 'fixtures')  }
   let(:class_dir) { File.join(test_xml_dir, 'classes') }
   let(:class_path) { File.join(class_dir, 'classes.xml') }
-  let(:faked_autoinstall_dir) { File.join(test_xml_dir, 'etc', 'autoinstall') }
+  let(:settings) { [ { 'class_name' => 'swap', 'configuration' => 
'largeswap.xml' } ] }
 
   before(:each) do
     subject.class_dir = class_dir
@@ -159,20 +157,27 @@
   describe '#Compat' do
     let(:faked_autoinstall_dir) { File.join(test_xml_dir, 'etc', 
'autoinstall') }
 
-    context 'when /etc/autoinstall/classes.xml exists' do
-      around(:each) do |example|
-        subject.ClassConf = faked_autoinstall_dir
-        example.call
-        subject.ClassConf = '/etc/autoinstall'
+    around(:each) do |example|
+      subject.ClassConf = faked_autoinstall_dir
+      example.call
+      subject.ClassConf = '/etc/autoinstall'
+    end
+
+    context 'when a classes.xml file exists in the new location' do
+      it 'does not overwrite classes.xml file' do
+        expect(Yast::XML).to_not receive(:YCPToXMLFile)
+        subject.Compat
       end
+    end
 
-      context 'and a classes.xml file does not exist in the new location' do
-        before(:each) do
-          allow(Yast::SCR).to receive(:Read).and_call_original
-          allow(Yast::SCR).to receive(:Read).
-            with(Yast::Path.new('.target.size'), class_path).and_return(-1)
-        end
+    context 'when a classes.xml file does not exist in the new location' do
+      before(:each) do
+        allow(Yast::SCR).to receive(:Read).and_call_original
+        allow(Yast::SCR).to receive(:Read).
+          with(Yast::Path.new('.target.size'), class_path).and_return(-1)
+      end
 
+      context 'and /etc/autoinstall/classes.xml exists' do
         it 'creates a classes.xml file in the new location' do
           expect(Yast::XML).to receive(:YCPToXMLFile) do |type, data, path|
             expect(type).to eq(:class)
@@ -183,9 +188,16 @@
         end
       end
 
-      context 'and a classes.xml file exists in the new location' do
-        it 'does not create a classes.xml file' do
-          expect(Yast::XML).to_not receive(:YCPToXMLFile)
+      context 'and /etc/autoinstall/classes.xml is empty or not valid XML' do
+        before(:each) do
+          allow(Yast::SCR).to receive(:Read).
+            with(Yast::Path.new('.xml'), File.join(faked_autoinstall_dir, 
'classes.xml')).
+            and_return(nil)
+        end
+
+        it 'creates a classes.xmlfile in the new location with no classes' do
+          expect(Yast::XML).to receive(:YCPToXMLFile).
+            with(:class, {"classes" => []}, File.join(class_dir, 
'classes.xml'))
           subject.Compat
         end
       end
@@ -198,4 +210,176 @@
       expect(subject.classDir).to eq(test_xml_dir)
     end
   end
+
+  describe '#MergeClasses' do
+    let(:base_profile_path) { File.join('test', 'fixtures', 'profiles', 
'partitions.xml') }
+    let(:tmp_dir) { File.join(root_path, 'tmp') }
+    let(:expected_xml) { File.read(expected_xml_path) }
+    let(:output_path) { File.join(tmp_dir, 'output.xml') }
+    let(:output_xml) { File.read(output_path) }
+    let(:dontmerge) { [] }
+    let(:merge_xslt_path) { File.join('xslt', 'merge.xslt') }
+    let(:xsltproc_command) {
+      "/usr/bin/xsltproc --novalid --param replace \"'false'\"  " \
+      "--param with \"'#{subject.findPath("largeswap.xml", "swap")}'\"  "\
+      "--output #{File.join(tmp_dir, "output.xml")}  " \
+      "#{merge_xslt_path} test/fixtures/profiles/partitions.xml "
+    }
+
+    before(:each) do
+      stub_const("Yast::AutoinstClassClass::MERGE_XSLT_PATH", merge_xslt_path)
+    end
+
+    around(:each) do |example|
+      FileUtils.rm_rf(tmp_dir) if Dir.exist?(tmp_dir)
+      FileUtils.mkdir(tmp_dir)
+      example.run
+      FileUtils.rm_rf(tmp_dir)
+    end
+
+    before(:each) do
+      allow(Yast::AutoinstConfig).to receive(:tmpDir).and_return(tmp_dir)
+      allow(Yast::AutoinstConfig).to receive(:dontmerge).and_return(dontmerge)
+      subject.Files
+    end
+
+    it 'executes xsltproc and returns a hash with info about the result' do
+      expect(Yast::SCR).to receive(:Execute).
+        with(Yast::Path.new(".target.bash_output"), xsltproc_command, 
{}).and_call_original
+      out = subject.MergeClasses(subject.confs[0], base_profile_path, 
'output.xml')
+      expect(out).to eq({ 'exit' => 0, 'stderr' => '', 'stdout' => '' })
+    end
+
+    context 'when all elements must be merged' do
+      let(:expected_xml_path) { File.join(root_path, 'test', 'fixtures', 
'output', 'partitions-merged.xml')  }
+
+      it 'merges elements from profile and configuration' do
+        expect(Yast::SCR).to receive(:Execute).
+          with(Yast::Path.new(".target.bash_output"), xsltproc_command, 
{}).and_call_original
+        subject.MergeClasses(subject.confs[0], base_profile_path, 'output.xml')
+        expect(output_xml).to eq(expected_xml)
+      end
+    end
+
+    context 'when some elements are not intended to be merged' do
+      let(:expected_xml_path) { File.join(root_path, 'test', 'fixtures', 
'output', 'partitions-dontmerge.xml')  }
+      let(:dontmerge) { ['partition'] }
+      let(:xsltproc_command) {
+        "/usr/bin/xsltproc --novalid --param replace \"'false'\"  " \
+        "--param dontmerge1 \"'partition'\"  " \
+        "--param with \"'#{subject.findPath("largeswap.xml", "swap")}'\"  "\
+        "--output #{File.join(tmp_dir, "output.xml")}  " \
+        "#{merge_xslt_path} test/fixtures/profiles/partitions.xml "
+      }
+
+      it 'does not merge those elements' do
+        expect(Yast::SCR).to receive(:Execute).
+          with(Yast::Path.new(".target.bash_output"), xsltproc_command, 
{}).and_call_original
+        subject.MergeClasses(subject.confs[0], base_profile_path, 'output.xml')
+        expect(output_xml).to eq(expected_xml)
+      end
+    end
+  end
+
+  describe '#Import' do
+    it 'sets profile_conf variable as a copy of the given settings' do
+      subject.Import(settings)
+      expect(subject.profile_conf).to eq(settings)
+      expect(subject.profile_conf).to_not equal(settings)
+    end
+
+    after(:each) do
+      subject.Import([])
+    end
+  end
+
+  describe '#Export' do
+    around(:each) do |example|
+      subject.Import(settings)
+      example.call
+      subject.Import([]) # reset settings
+    end
+
+    it 'returns a copy of profile_conf' do
+      exported = subject.Export
+      expect(exported).to eq(settings)
+      expect(exported).to_not equal(settings)
+    end
+  end
+
+  describe '#Summary' do
+    context 'when some settings are given' do
+      around(:each) do |example|
+        subject.Import(settings)
+        example.call
+        subject.Import([]) # reset settings
+      end
+
+      it 'returns a summary containing class names and configurations' do
+        expect(Yast::Summary).to receive(:AddHeader).with(anything, 'swap').
+          and_return('<h3>swap</h3>')
+        expect(Yast::Summary).to receive(:AddLine).with(anything, 
'largeswap.xml').
+          and_return('<h3>swap</h3><p>largeswap.xml</p>')
+        expect(subject.Summary).to eq('<h3>swap</h3><p>largeswap.xml</p>')
+      end
+
+      context 'when no class name is given' do
+        let(:settings) { [ { 'configuration' => 'largeswap.xml' } ] }
+
+        it "'None' is used instead" do
+          expect(Yast::Summary).to receive(:AddHeader).with(anything, 'None').
+            and_return('<h3>None</h3>')
+          subject.Summary
+        end
+      end
+
+      context 'when no configuration is given' do
+        let(:settings) { [ { 'class_name' => 'swap' } ] }
+
+        it "'None' is used instead" do
+          expect(Yast::Summary).to receive(:AddLine).with(anything, 'None').
+            and_return('<h3>None</h3><p>largeswap.xml</p>')
+          subject.Summary
+        end
+      end
+    end
+
+    context 'when no settings are given' do
+      it 'returns an empty summary' do
+        expect(subject.Summary).to eq(Yast::Summary.NotConfigured)
+      end
+    end
+  end
+
+  describe '#Save' do
+    before(:each) do
+      subject.Read
+    end
+
+    it 'creates a classes.xml file in the new location' do
+      expect(Yast::XML).to receive(:YCPToXMLFile) do |type, data, path|
+        expect(type).to eq(:class)
+        expect(data['classes']).to be_kind_of(Array)
+        expect(path).to eq(File.join(class_dir, 'classes.xml'))
+      end
+      subject.Save
+    end
+
+    context 'when classes are marked for deletion' do
+      around(:each) do |example|
+        subject.deletedClasses = ['swap']
+        example.call
+        subject.deletedClasses = []
+      end
+
+      it 'deletes classes files' do
+        allow(Yast::XML).to receive(:YCPToXMLFile).with(any_args)
+        expect(Yast::SCR).to receive(:Execute).with(
+          Yast::Path.new('.target.bash'),
+          "/bin/rm -rf #{class_dir}/swap")
+
+        subject.Save
+      end
+    end
+  end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/autoyast2-3.1.72/test/fixtures/output/partitions-dontmerge.xml 
new/autoyast2-3.1.74/test/fixtures/output/partitions-dontmerge.xml
--- old/autoyast2-3.1.72/test/fixtures/output/partitions-dontmerge.xml  
1970-01-01 01:00:00.000000000 +0100
+++ new/autoyast2-3.1.74/test/fixtures/output/partitions-dontmerge.xml  
2015-04-27 10:29:12.000000000 +0200
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<profile xmlns="http://www.suse.com/1.0/yast2ns"; 
xmlns:config="http://www.suse.com/1.0/configns";>
+  <partitioning config:type="list">
+    <drive>
+      <device>/dev/sda</device><disklabel>msdos</disklabel><enable_snapshots 
config:type="boolean">true</enable_snapshots><initialize 
config:type="boolean">true</initialize><partitions config:type="list">
+        <partition>
+          <create config:type="boolean">true</create>
+          <crypt_fs config:type="boolean">false</crypt_fs>
+          <filesystem config:type="symbol">swap</filesystem>
+          <format config:type="boolean">true</format>
+          <loop_fs config:type="boolean">false</loop_fs>
+          <mount>swap</mount>
+          <mountby config:type="symbol">uuid</mountby>
+          <partition_id config:type="integer">130</partition_id>
+          <partition_nr config:type="integer">1</partition_nr>
+          <resize config:type="boolean">false</resize>
+          <size>1043496448</size>
+        </partition><partition>
+          <create config:type="boolean">true</create>
+          <crypt_fs config:type="boolean">false</crypt_fs>
+          <filesystem config:type="symbol">ext4</filesystem>
+          <format config:type="boolean">true</format>
+          <fstopt>acl,user_xattr</fstopt>
+          <loop_fs config:type="boolean">false</loop_fs>
+          <mount>/</mount>
+          <mountby config:type="symbol">uuid</mountby>
+          <partition_id config:type="integer">131</partition_id>
+          <partition_nr config:type="integer">2</partition_nr>
+          <resize config:type="boolean">false</resize>
+          <size>5381455360</size>
+        </partition><partition>
+                        <filesystem config:type="symbol">swap</filesystem>
+                        <format config:type="boolean">true</format>
+                        <mount>swap</mount>
+                        <partition_id config:type="integer">130</partition_id>
+                        <size>2000mb</size>
+                    </partition>
+                    <partition>
+                        <filesystem config:type="symbol">ext3</filesystem>
+                        <partition_type>primary</partition_type>
+                        <size>4Gb</size>
+                        <mount>/</mount>
+                    </partition>
+                </partitions>
+      <pesize/><type config:type="symbol">CT_DISK</type><use>all</use>
+    </drive>
+  </partitioning>
+</profile>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/autoyast2-3.1.72/test/fixtures/output/partitions-merged.xml 
new/autoyast2-3.1.74/test/fixtures/output/partitions-merged.xml
--- old/autoyast2-3.1.72/test/fixtures/output/partitions-merged.xml     
1970-01-01 01:00:00.000000000 +0100
+++ new/autoyast2-3.1.74/test/fixtures/output/partitions-merged.xml     
2015-04-27 10:29:12.000000000 +0200
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<profile xmlns="http://www.suse.com/1.0/yast2ns"; 
xmlns:config="http://www.suse.com/1.0/configns";>
+  <partitioning config:type="list">
+    <drive>
+      <device>/dev/sda</device><disklabel>msdos</disklabel><enable_snapshots 
config:type="boolean">true</enable_snapshots><initialize 
config:type="boolean">true</initialize><partitions config:type="list">
+        <partition>
+          <create config:type="boolean">true</create><crypt_fs 
config:type="boolean">false</crypt_fs><filesystem 
config:type="symbol">swap</filesystem>
+          <format config:type="boolean">true</format>
+          <loop_fs config:type="boolean">false</loop_fs><mount>swap</mount>
+          <mountby config:type="symbol">uuid</mountby><partition_id 
config:type="integer">130</partition_id>
+          <partition_nr config:type="integer">1</partition_nr><resize 
config:type="boolean">false</resize><size>2000mb</size>
+        </partition>
+        <partition>
+          <create config:type="boolean">true</create><crypt_fs 
config:type="boolean">false</crypt_fs><filesystem 
config:type="symbol">ext3</filesystem>
+          <format 
config:type="boolean">true</format><fstopt>acl,user_xattr</fstopt><loop_fs 
config:type="boolean">false</loop_fs><partition_type>primary</partition_type>
+                        <size>4Gb</size>
+                        <mount>/</mount>
+          <mountby config:type="symbol">uuid</mountby>
+          <partition_id config:type="integer">131</partition_id>
+          <partition_nr config:type="integer">2</partition_nr>
+          <resize config:type="boolean">false</resize>
+          <size>5381455360</size>
+        </partition>
+      </partitions>
+      <pesize/><type config:type="symbol">CT_DISK</type><use>all</use>
+    </drive>
+  </partitioning>
+</profile>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/autoyast2-3.1.72/test/fixtures/profiles/partitions.xml 
new/autoyast2-3.1.74/test/fixtures/profiles/partitions.xml
--- old/autoyast2-3.1.72/test/fixtures/profiles/partitions.xml  1970-01-01 
01:00:00.000000000 +0100
+++ new/autoyast2-3.1.74/test/fixtures/profiles/partitions.xml  2015-04-27 
10:29:12.000000000 +0200
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!DOCTYPE profile>
+<profile xmlns="http://www.suse.com/1.0/yast2ns"; 
xmlns:config="http://www.suse.com/1.0/configns";>
+  <partitioning config:type="list">
+    <drive>
+      <device>/dev/sda</device>
+      <disklabel>msdos</disklabel>
+      <enable_snapshots config:type="boolean">true</enable_snapshots>
+      <initialize config:type="boolean">true</initialize>
+      <partitions config:type="list">
+        <partition>
+          <create config:type="boolean">true</create>
+          <crypt_fs config:type="boolean">false</crypt_fs>
+          <filesystem config:type="symbol">swap</filesystem>
+          <format config:type="boolean">true</format>
+          <loop_fs config:type="boolean">false</loop_fs>
+          <mount>swap</mount>
+          <mountby config:type="symbol">uuid</mountby>
+          <partition_id config:type="integer">130</partition_id>
+          <partition_nr config:type="integer">1</partition_nr>
+          <resize config:type="boolean">false</resize>
+          <size>1043496448</size>
+        </partition>
+        <partition>
+          <create config:type="boolean">true</create>
+          <crypt_fs config:type="boolean">false</crypt_fs>
+          <filesystem config:type="symbol">ext4</filesystem>
+          <format config:type="boolean">true</format>
+          <fstopt>acl,user_xattr</fstopt>
+          <loop_fs config:type="boolean">false</loop_fs>
+          <mount>/</mount>
+          <mountby config:type="symbol">uuid</mountby>
+          <partition_id config:type="integer">131</partition_id>
+          <partition_nr config:type="integer">2</partition_nr>
+          <resize config:type="boolean">false</resize>
+          <size>5381455360</size>
+        </partition>
+      </partitions>
+      <pesize/>
+      <type config:type="symbol">CT_DISK</type>
+      <use>all</use>
+    </drive>
+  </partitioning>
+</profile>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/test/include/ask_test.rb 
new/autoyast2-3.1.74/test/include/ask_test.rb
--- old/autoyast2-3.1.72/test/include/ask_test.rb       1970-01-01 
01:00:00.000000000 +0100
+++ new/autoyast2-3.1.74/test/include/ask_test.rb       2015-04-27 
10:29:12.000000000 +0200
@@ -0,0 +1,337 @@
+#!/usr/bin/env rspec
+
+require_relative "../test_helper"
+
+require "yast"
+
+Yast.import "Profile"
+Yast.import "Stage"
+Yast.import "UI"
+
+describe "Yast::AutoinstallAskInclude" do
+  module DummyYast
+    class AutoinstallAskClient < Yast::Client
+      def main
+        Yast.include self, "autoinstall/ask.rb"
+      end
+
+      def initialize
+        main
+      end
+    end
+  end
+
+  BASE_ASK = {
+    "type" => "string", "question" => "hostname?", "default" => "my.site.de",
+    "help" => "Some help", "dialog" => 0, "element" => 0,
+    "stage" => "initial" }
+
+  subject(:client) { DummyYast::AutoinstallAskClient.new }
+  let(:profile) { { "general" => { "ask-list" => ask_list } } }
+  let(:pressed_button) { :ok }
+
+  describe "#askDialog" do
+    let(:ask_list) { [ask] }
+
+    before do
+      allow(Yast::Profile).to receive(:current).and_return(profile)
+      allow(Yast::Stage).to receive(:initial).and_return(true)
+      allow(Yast::UI).to receive(:UserInput).and_return(pressed_button)
+    end
+
+    context "when ask-list is empty" do
+      let(:ask_list) { [] }
+
+      it "no dialog is shown" do
+        expect(Yast::UI).to_not receive(:OpenDialog)
+        client.askDialog
+      end
+    end
+
+    describe "dialogs creation" do
+      context "when the ask-list contains a question with type 'string'" do
+        let(:ask) { BASE_ASK }
+
+        it "creates a TextEntry widget" do
+          expect(Yast::UI).to receive(:OpenDialog)
+          expect(client).to receive(:TextEntry).
+            with(Id("0_0"), Opt(:notify), ask["question"], ask["default"]).
+            and_call_original
+          client.askDialog
+        end
+      end
+
+      context "when ask-list contains a question with type 'selection'" do
+        let(:ask) { BASE_ASK.merge("selection" => items, "default" => 
"desktop") }
+        let(:items) {
+          %w(desktop server).map { |i| { "value" => i, "label" => i.capitalize 
} }
+        }
+
+        it "creates a ComboBox widget" do
+          expect(Yast::UI).to receive(:OpenDialog)
+          expected_options = [
+            Item(Id("desktop"), "Desktop", true),
+            Item(Id("server"), "Server", false)
+          ]
+          expect(client).to receive(:ComboBox).
+            with(Id("0_0"), Opt(:notify), ask["question"], expected_options).
+            and_call_original
+          client.askDialog
+        end
+      end
+
+      context "when ask-list contains a question with type 'password'" do
+        let(:ask) { BASE_ASK.merge("password" => true) }
+
+        it "creates two Password widgets" do
+          expect(Yast::UI).to receive(:OpenDialog)
+          expect(client).to receive(:Password).
+            with(Id("0_0"), Opt(:notify), ask["question"], ask["default"]).
+            and_call_original
+          expect(client).to receive(:Password).
+            with(Id("0_0_pass2"), Opt(:notify), "", ask["default"]).
+            and_call_original
+          client.askDialog
+        end
+      end
+
+      context "when ask-list contains a question with type 'static_text'" do
+        let(:ask) { BASE_ASK.merge("type" => "static_text") }
+
+        it "creates a Label widget" do
+          expect(Yast::UI).to receive(:OpenDialog)
+          expect(client).to receive(:Label).
+            with(Id("0_0"), ask["default"]).
+            and_call_original
+          client.askDialog
+        end
+      end
+
+      context "when ask-list contains question with type 'symbol'" do
+        let(:ask) {
+          BASE_ASK.merge("type" => "symbol", "default" => :desktop, 
"selection" => items)
+        }
+        let(:items) {
+          %w(desktop server).map { |i| { "value" => i.to_sym, "label" => 
i.capitalize } }
+        }
+
+        it "creates a ComboBox widget" do
+          expect(Yast::UI).to receive(:OpenDialog)
+          expected_options = [
+            Item(Id(:desktop), "Desktop", true),
+            Item(Id(:server), "Server", false)
+          ]
+          expect(client).to receive(:ComboBox).
+            with(Id("0_0"), Opt(:notify), ask["question"], expected_options).
+            and_call_original
+          client.askDialog
+        end
+      end
+
+      context "when ask-list contains a question with type 'boolean'" do
+        let(:ask) do
+          BASE_ASK.merge("type" => "boolean", "question" => "Register 
system?", "default" => "true")
+        end
+
+        it "creates a CheckBox widget" do
+          expect(Yast::UI).to receive(:OpenDialog)
+          expect(client).to receive(:CheckBox).
+            with(Id("0_0"), Opt(:notify), ask["question"], true).
+            and_call_original
+          client.askDialog
+        end
+      end
+
+      context "when ask-list contains more than one question" do
+        let(:string_ask) { BASE_ASK }
+        let(:boolean_ask) do
+          BASE_ASK.merge("type" => "boolean", "element" => 1, "default" => 
"true")
+        end
+        let(:ask_list) { [string_ask, boolean_ask] }
+
+        it "creates one widget for each one of them" do
+          expect(Yast::UI).to receive(:OpenDialog)
+          expect(client).to receive(:TextEntry).
+            with(Id("0_0"), Opt(:notify), string_ask["question"], 
string_ask["default"]).
+            and_call_original
+          expect(client).to receive(:CheckBox).
+            with(Id("0_1"), Opt(:notify), boolean_ask["question"], true).
+            and_call_original
+          client.askDialog
+        end
+      end
+
+      context "when ask-list contains more than one password question" do
+        let(:first_pass_ask) { BASE_ASK.merge("password" => true)}
+        let(:second_pass_ask) { BASE_ASK.merge("password" => true, "element" 
=> 1)}
+        let(:ask_list) { [first_pass_ask, second_pass_ask] }
+
+        it "creates two password widgets for each question without repeating 
the Ids" do
+          expect(Yast::UI).to receive(:OpenDialog)
+          ["0_0", "0_0_pass2", "0_1", "0_1_pass2"].each do |wid|
+            expect(client).to receive(:Password).
+              with(Id(wid), anything, anything, anything).and_call_original
+          end
+          client.askDialog
+        end
+      end
+    end
+
+    describe "dialogs actions" do
+
+      context "when ok button is pressed" do
+        let(:pressed_button) { :ok }
+        let(:response) { "some-user-response" }
+
+        before do
+          allow(Yast::UI).to receive(:QueryWidget).
+            with(Id("0_0"), :Value).and_return(response)
+        end
+
+        context "when a path was specified" do
+          let(:ask) { BASE_ASK.merge("path" => "users,0,gecos") }
+
+          it "response is saved into the Profile at the specified path" do
+            expect(Yast::Profile).to receive(:setElementByList).
+              with(["users", 0, "gecos"], response, profile)
+            client.askDialog
+          end
+        end
+
+        context "when a pathlist was specified" do
+          let(:ask) { BASE_ASK.merge("pathlist" => ["users,1000,login", 
"groups,1000,name"]) }
+
+          it "saves response into Profile at the paths specified in the 
pathlist" do
+            expect(Yast::Profile).to receive(:setElementByList).
+              with(["users", 1000, "login"], response, profile)
+            expect(Yast::Profile).to receive(:setElementByList).
+              with(["groups", 1000, "name"], response, profile)
+            client.askDialog
+          end
+        end
+
+        context "when a file was specified" do
+          let(:file_path) { "/tmp/response" }
+          let(:ask) { BASE_ASK.merge("file" => file_path) }
+
+          it "saves the user answer in the file" do
+            expect(Yast::SCR).to receive(:Write).
+              with(Yast::Path.new(".target.string"), file_path, response).
+              and_return(true)
+            client.askDialog
+          end
+
+          context "when response is a boolean" do
+            let(:response) { true }
+            let(:ask) { BASE_ASK.merge("type" => "boolean", "file" => 
file_path)}
+
+            it "converts the answer to a string and saves it in the file" do
+              expect(Yast::SCR).to receive(:Write).
+                with(Yast::Path.new(".target.string"), file_path, "true").
+                and_return(true)
+              client.askDialog
+            end
+          end
+
+          context "when asking for a 'password' and values don't match" do
+            let(:ask) { BASE_ASK.merge("password" => true) }
+
+            it "shows an error message and try run the dialog again" do
+              expect(Yast::UI).to receive(:QueryWidget).
+                with(Id("0_0_pass2"), :Value).and_return("some-other-thing", 
response)
+              expect(Yast::Popup).to receive(:Error).with("The two passwords 
mismatch.")
+              client.askDialog
+            end
+          end
+        end
+
+        context "when a script was specified" do
+          let(:script) { { "source" => "echo", "filename" => "test.sh" } }
+          let(:ask) { BASE_ASK.merge("script" => script) }
+          let(:tmp_dir) { "/tmp" }
+          let(:log_dir) { "/var/log/YaST2" }
+
+          before do
+            allow(Yast::AutoinstConfig).to receive(:tmpDir).and_return(tmp_dir)
+            allow(Yast::AutoinstConfig).to receive(:logs_dir).
+              and_return(log_dir)
+            script_path = "#{tmp_dir}/#{script["filename"]}"
+            allow(Yast::SCR).to receive(:Write).
+              with(Yast::Path.new(".target.string"), script_path, 
script["source"]).
+              and_return(true)
+            allow(Yast::SCR).to receive(:Execute).
+              with(Yast::Path.new(".target.mkdir"), File.join(tmp_dir, 
"ask_scripts_log"))
+          end
+
+          context "when 'environment' property is not set" do
+            it "runs the script without passing the ask response" do
+              expect(Yast::SCR).to receive(:Execute).
+                with(Yast::Path.new(".target.bash"),
+                     "/bin/sh -x /tmp/test.sh 2&> 
/tmp/ask_scripts_log/test.sh.log ")
+              client.askDialog
+            end
+          end
+
+          context "when 'environment' property is set" do
+            let(:script) { { "source" => "echo", "filename" => "test.sh", 
"environment" => true } }
+
+            it "runs the script passing the ask response" do
+              expect(Yast::SCR).to receive(:Execute).
+                with(Yast::Path.new(".target.bash"),
+                     "VAL=\"some-user-response\" /bin/sh -x /tmp/test.sh 2&> 
/tmp/ask_scripts_log/test.sh.log ")
+              client.askDialog
+            end
+          end
+
+          it "shows some feedback while script is running" do
+            message = "A user defined script is running. This may take a 
while."
+            allow(Yast::SCR).to receive(:Execute)
+            expect(Yast::Popup).to receive(:Feedback).
+              with("", client._(message))
+            client.askDialog
+          end
+        end
+
+        context "when more dialogs left" do
+          let(:ask_list) { [BASE_ASK, BASE_ASK.merge("dialog" => "1")] }
+
+          it "next dialog is shown" do
+            expect(Yast::UI).to receive(:UserInput).twice
+            client.askDialog
+          end
+        end
+
+        context "when no more dialogs left" do
+          let(:ask_list) { [BASE_ASK] }
+
+          it "finishes dialog processing and returns" do
+            expect(Yast::UI).to receive(:UserInput).once
+            client.askDialog
+          end
+        end
+
+        context "when a value for 'next dialog' is set" do
+          let(:ask_list) { [BASE_ASK, BASE_ASK.merge("dialog" => 1), 
BASE_ASK.merge("dialog" => 2)] }
+
+          before do
+            expect(Yast::SCR).to receive(:Read).
+              with(Yast::Path.new(".target.size"), "/tmp/next_dialog").
+              and_return(1)
+            expect(Yast::SCR).to receive(:Read).
+              with(Yast::Path.new(".target.string"), "/tmp/next_dialog").
+              and_return("2")
+          end
+
+          it "jumps to that dialog" do
+            expect(Yast::UI).to_not receive(:QueryWidget).
+              with(Id("1_0"), :Value) # Skips dialog 1.
+            expect(Yast::UI).to receive(:QueryWidget).
+              with(Id("2_0"), :Value)
+            expect(Yast::UI).to receive(:UserInput).twice.and_return(:ok)
+            client.askDialog
+          end
+        end
+      end
+    end
+  end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/autoyast2-3.1.72/test/test_helper.rb 
new/autoyast2-3.1.74/test/test_helper.rb
--- old/autoyast2-3.1.72/test/test_helper.rb    1970-01-01 01:00:00.000000000 
+0100
+++ new/autoyast2-3.1.74/test/test_helper.rb    2015-04-27 10:29:12.000000000 
+0200
@@ -0,0 +1,12 @@
+root_location = File.expand_path("../../", __FILE__)
+ENV["Y2DIR"] = File.expand_path("../../src", __FILE__)
+
+require "yast"
+require "yast/rspec"
+require "fileutils"
+
+if ENV["COVERAGE"]
+  STDERR.puts "COVERAGE is disabled because when requiring some modules (like 
AutoinstPartition) "\
+    "errors are raised in other YaST components."
+end
+


Reply via email to