Tuesday, March 27, 2012

How to install msi package using ant


Problem:
to install msi package in silent mode using ANT.

Constraints:
1. some prodcuts require system rebooting after installer finishes working. That is represented in some exit code (or a set of possible codes) meaning approximately the following: "It is all okay but still user actions required". So you never get 0 exit code meaning regulary success operation completion.
2. sometimes we need not just click Next> while installing the package but rather specify some custom specifications on installation phase. This is sure our case :) Assume we need to override some public property of msi package. Let it be named SOMEPROPERTY and the value we'd like to assign is SOMEVALUE

Solution:
Actually several solutions could be applied to solve this problem. The simplest one is to set failonerror attribute of ant execute task to false. Such the solution has quite big and quite obviouse disadvantage: your automated procedure will never know any problem has happened. But we would actually like to skip the only one exit code to consider it successfull.
To achieve that we should approach with batch scripting (remember we're acting under windows as we're trying to imstall msi package which is true windows stuff)

so lets introduce the following batch script

msiexec /i %1 /qn SOMEPROPERTY=%2

IF ERRORLEVEL 1642 GOTO ERR

IF ERRORLEVEL 1641 GOTO 1641

IF ERRORLEVEL 1 GOTO ERR


:1641
ECHO.Returned expected error code. System is getting to be rebooted...
EXIT 0

:ERR
ECHO.Uexpected error code: %ERRORLEVEL%
ECHO.failing..
EXIT %ERRORLEVEL%

So, what we expect from this script? First of all we expect it to install the package we propagate via script argument %1 and set the property SOMEPROPERTY to the value we specify in script argument %2. However we would like our script behave in certain way to address the constraint #1 (see above)

In the script we call msiexec tool with the keys /i (install the package) and /qn meaning silent non-gui installation. The lines below address the problem of exit codes. We should catch the code meaning "Its okay but the system's going to be rebooted now". This is encoded with the exit code sequence 1641. However the called process may return some other code which we should consider as not successful. This is achieved by "IF ERRORLEVEL" and "GOTO" batch language construction.

IF ERRORLEVEL N returns true if the returned exit code equals or grated than N. That is why we try to catch all errors higher than N (=1641)


IF ERRORLEVEL 1642 GOTO ERR


... and all errors lower than N

IF ERRORLEVEL 1 GOTO ERR
The order of instructions invocation makes us sure we'll get to :1641 label if only the expected installation state will take place
So now we just should call the batch from ANT passing the corresponding cmd arguments like this:
<exec executable="install-msi.bat" failonerror="true" >
 <arg value="&quot;PACKAGETOINSTALL.msi&quot;"/>
 <arg value="&quot;SOMEVALUE&quot;"/>
</exec> 

Wednesday, March 21, 2012

Ant practice on how to build your file with substituing the @tokens@

The usual need of a person going to build the code is to keep the certain properties in separate file and move the property values to certain files on build stage. This is what the article about. Let's look and the very small example.

Assume we have the following property file (let's name it "configuration properties")
user.role=admin
user.login=superuser
user.password=whoifnotme

and the following file we're going to build (settings.xml)
<settings>
 <users>
  <user id="@user.login@">
   <role>@user.role@</role>
   <password>@user.password@</password>
  </user>
 </users>
</settings>

Let's place the files above to d:/temp/props_src and would like to build the file settings.xml to the folder d:/temp/props_build.

Here is how the ant script should look like

<project name="testBuildCodeWithProperties" default="start" basedir=".">

 <property name="sourceDir" value="d:/temp/props_src"/>
 <property name="targetDir" value="d:/temp/props_build"/>
 
 <target name="start">
  <copy todir="${targetDir}" overwrite="true">
   <fileset dir="${sourceDir}" includes="*.xml"/>
   <filterset filtersfile="${sourceDir}/configuration.properties"/>
  </copy>
 </target>
 
</project>

Note that we should not write any specific script to substitute the values. The only inbound data we need to have to build the file is actually the template of a file to build and the property file to be used in filter set. The @ symbol is used by default to point out the tokens in the template file. However you may change it by specifying begintoken and endtoken attributes of filterset.

Sunday, March 04, 2012

When WebElement.click() doesn't work in Selenium2 WebDriver

Sometimes I face the problems when regular WebElement click() method does not work. For example I met such the problem when tried to locate the link under H2 like the following snippet:

<h2>
 <a href="something" title="something">Some text</a>
</h2>

The regular method of locating like driver.findElement(By.xpath("somexpath")).click() led to the exception saying some other element will get the click instead of one located by me. So I applied the following work-around:

 public void safeClick(WebElement element){
  Actions builder = new Actions(getDriver());
  builder.moveToElement(element).click().build().perform();
 }
where getDriver() just means your current WebDriver implementation. This made me capable to work-around the problem from one hand and even made click action closer to the actual action sequence performed by the user clicking anything on the page.