Using UIAutomation for Multilanguage iOS Applications

With the appearance of iOS 4.0 Apple introduced a new Test-Framework for automatically UI Testing: UI Automation. Based on Javascript and build-in into Instruments, UI Automation is a very useful tool during the Developing of iOS Application.
A very good introduction in UIAutomation is here and here.
During the development of a iOS Application, we decided to port it to iOS 4.0 and therefor use also UIAutomation for regression testing (before that we used GHUnit Tests for Component Testing – but thats another story).
As we are primarily a company dealing with web-based application, we had almost zero afford to deal with the Javascript syntax of UI Automation. But we had to deal with the fact, that we developing a dual language Application (de and en), and therefore need a possibility to test the whole UI in both languages.
If you are familiar with UI Automation, you probably know that the Framework uses the accessibility labels of your UI and also often Button Labels. So you have to deal with the actual language of the current UI Setting. But wait. There is already a valid mapping of different language to a given key. If you internationalize your application you will use so called Localizable.strings to do your language Mapping (more here).
So we just need a way to move our already existing Mapping into our UI Automation world. UI Automation supports the import of separate JavaScript Files to use your own Libraries and Settings. So i build a conversation script to translate your different Localizable.strings to JavaScript and moving all languages into one big collection.
So for example a String like this:

    "Library" = "Bibliothek";
    "Shop" = "Kiosk";

Will be converted to:

UIA.Localizables = {
    "de":{
        ...
        "Library" : "Bibliothek",
        "Shop" : "Kiosk",
        ...
    },
    "English":{
    }
    ...
}

The next step is to determine during your UIAutomation Test which language Setting you need to Load from your Localization File.
It is possible to readout some System Settings during an UIAutomation Test. The basic functions to find your current language and to read the correct language Array look like this:

UIA.getCurrentLang = function(){
    if(application.preferencesValueForKey("AppleLanguages")[0]  == "en")
        return "English";
    else
        return application.preferencesValueForKey("AppleLanguages")[0];
}
UIA.getCurrentLocalizables = function(){
    return UIA.Localizables[UIA.getCurrentLang()];
}
var Localizable = UIA.getCurrentLocalizables();

The first function is necessary to capture a quirk of the recent Xcode Versions (some people calling it a bug 🙂 ).
So now we can just use our String within our Test-Cases.

#import "lib/Localizables.js"
function delay(seconds){
    UIATarget.localTarget().delay(seconds);
}
function tapTab(name){
    var window = UIATarget.localTarget().frontMostApp().mainWindow();
    window.tabBar().buttons()[name].tap();
}
var window = UIATarget.localTarget().frontMostApp().mainWindow();
tapTab(Localizable['Library']);
delay(1);
tapTab(Localizable['Shop']);
delay(7);

I attached the conversion script to this post.
You just need to alter the source and destination folders of your i18n files and the UIAutomation-Tests directory.
Download file

Philipps 5 mins: Graph-Fun with AJAX and Canvas

I always searched for an efficient way add dynamic diagrams to a web-project without using flash or other plugin-based magic.
With the support of the canvas tag element in almost all mainstream browser, i thought it would be a good time for creating a short demo how things workout.
You will need at least two Parts for this demo. First of all you will need a Source JSON feed. For this demo i just hacked together a very basis PHP script:

<?php
header('Content-type: application/json');
echo'{';
echo '"value":"' . rand(0, 60) . '"';
echo '}';
?>

The result is something like:

{"value":"34"}

Secondly you need a Webpage, where you want to insert your canvas element, load the data from the json feed and draw the changing values to the canvas element.
For a better performance, we will implementing pulling the data and drawing the data within two parallel cycles. The Common data Storage will be an array of 300 value (for our diagram with a width of 300px).
We are using two additional JS Files. The first we need for creating our XHTTPRequest Object and handling the response within a callback method. The second script is for parsing the JSON Feed as a Javascript Object in a safe way (an ordinary eval works, but is to unsecury).
Our main-script works in several steps:
First we initialize an array with empty elements:

function init(){
    for(var i=0; i < 300; i++){
        randomValues[i] = 0;
    }
}

 


This step is optional, but then you have a nice “zero line” at the beginning.

Secondly we have a method, that pushes a new value to the existing array, and drops the first entry, if the length of the array is greater than 300.

function addValue(arr, value){
    if(arr.push(value) > 300){
        arr.shift();
    }
}

 


The next two methods are necessary for sending our ajax-request and for handling the response in a callback method.
Basically the callback method just calls the addValue method.

The timeout variable is set to 200 ms. So the script calls our backend periodically every 200 ms and then adds a new value to our array.

function pullValue(){
    sendRequest('random.php',handleRandomRequest);
    setTimeout(pullValue, timeout);
}

function handleRandomRequest(req) {
    var text = JSON.parse(req.responseText);
    addValue(randomValues, text.value);
}

The last method is for the drawing functionality:

function draw(){
    ctx.clearRect(0, 0, 300, 60);
    ctx.fillStyle = "rgba(101,101,101, 0.5)";
    ctx.fillRect (0, 0, 300, 60);
    ctx.lineWidth = 1;
    ctx.strokeStyle = 'blue';
    ctx.beginPath();
    ctx.moveTo(1, 60-parseInt(randomValues[0]));
    for (var i=1; i<randomValues.length; i++){
        value = 60-parseInt(randomValues[i]);
        ctx.lineTo(i,value);
    }
    ctx.stroke();
    setTimeout(draw, timeout);
}

ctx is a 2d context of the canvas element.
On every call of the draw method, all elements of the array are painted. The first element is always the start point.
Because the canvas coordinate system has the point 0,0 in the upper left corner but the 0,0 point of our diagram should be in the lower left corner, you have to subtract the array-values from 60 to get the right drawing coordinate.
This method also runs periodically every 200 ms. But it also works for two times for pulling the data an drawing it.

Here you can see the script in action

creating JNI with Swig

I am currently playing around with JNI and Java due the colleagues question to make the connect features of jack-audio (http://jackaudio.org) accessible to java.
There is already a javalib (http://jjack.berlios.de) with some features, there seems still some needes ones missing.
So i started today to have a look into SWIG (http://swig.org).
“SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages.”
After some hours of research i ended up with some facts:
To created yourself a Java binding to a given c/c++ Program or Library you need one or more Interface files (*.I) and swig file with all the necessary swig module descriptions.
There is an example on the swig homepage ( http://www.swig.org/Doc1.3/SWIGDocumentation.html#Introduction) to explain the workflow of SWIG.
There is a c file exmple.c:

/* File : example.c */
double  My_variable  = 3.0;

/* Compute factorial of n */
int  fact(int n) {
    if (n <= 1) 
        return 1;
    else 
        return n*fact(n-1);
}

/* Compute n mod m */
int my_mod(int n, int m) {
    return(n % m);
}

The mapping example.i files looks as the following:

/* File : example.i */
%module example
%{
/* Put headers and other declarations here */
    extern double My_variable;
    extern int    fact(int);
    extern int    my_mod(int n, int m);
%}
extern double My_variable;
extern int    fact(int);
extern int    my_mod(int n, int m);

As you can see, the Interface file has a similar syntax with some additional meta information.
You can now create your JNI bindings:

swig -java example.i
There are also flags for different other languages:
-allegrocl - Generate ALLEGROCL wrappers
-chicken - Generate CHICKEN wrappers
-clisp - Generate CLISP wrappers
-cffi - Generate CFFI wrappers
-csharp - Generate C# wrappers
-guile - Generate Guile wrappers
-java - Generate Java wrappers
-lua - Generate Lua wrappers
-modula3 - Generate Modula 3 wrappers
-mzscheme - Generate Mzscheme wrappers
-ocaml - Generate Ocaml wrappers
-octave - Generate Octave wrappers
-perl - Generate Perl wrappers
-php - Generate PHP wrappers
-pike - Generate Pike wrappers
-python - Generate Python wrappers
-r - Generate R (aka GNU S) wrappers
-ruby - Generate Ruby wrappers
-sexp - Generate Lisp S-Expressions wrappers
-tcl - Generate Tcl wrappers
-uffi - Generate Common Lisp / UFFI wrappers
-xml - Generate XML wrappers

As a result you get three new files:

  • example.java
  • exampleJNI.java
  • example_wrap.c

The example_wrap.c can be used to compile the needed library file for your JNI access.
The two java Files are the basic JNI implementation:

    class exampleJNI {
        public final static native void My_variable_set(double jarg1);
        public final static native double My_variable_get();
        public final static native int fact(int jarg1);
        public final static native int my_mod(int jarg1, int jarg2);
    }

And a basic java example how to access these functions:

public class example {
    public static void setMy_variable(double value) {
        exampleJNI.My_variable_set(value);
    }
    public static double getMy_variable() {
        return exampleJNI.My_variable_get();
    }
    public static int fact(int arg0) {
        return exampleJNI.fact(arg0);
    }
    public static int my_mod(int n, int m) {
        return exampleJNI.my_mod(n, m);
    }
}

To get into working with SWIG i can advise the sources of the G4Java Project.
There is also a maven plugin to use SWIG from within your maven build: http://java.freehep.org/freehep-swig-plugin.
I am currently trying to create the necessary Interface files from the jack-audio sources to use them for a first run of SWIG. For python and tck you can use cmake to create these files.

Preparation for counter update

So after a long time i plan to give my counter-engine (^^) an update.
The main Problem is, that the counter itself was perfect for ~2000 counts. But now with 5000 count, the presentation of the results is just not readable anymore.
So i plan the following things to do during the next days (i hope i will finish this during tomorrow):

  • using a new drawing method: instead of drawing the whole graph, i will draw just time-slices of a particular periode. So e.g. if you want to plot the current graph with this method, it would be necessary to draw 24 slices with a month periode.
  • to get read of lots of code within the counter/presentation script, i want to use the build-in date-calculation functions of mysql.
    So e.g.
    SELECT something FROM `counts` WHERE DATE_SUB(CURDATE(),INTERVAL 30 DAY) <= date_col; Should be perfect :-).
  • To use this functions, i have to use the build-in datetime Datatype. At the moment i am storring all counts with a unix timestamp. To make the move, i have to do three steps:
    1. creating a new field: ALTER TABLE `counts` ADD `created` DATETIME NOT NULL;
    2. migrate the old dates: UPDATE `counts` SET created = from_unixtime(unixtime);
    3. finally i had to change to insert statement within the counter script:
      “INSERT INTO `counts` ( … user_string, created) VALUES … ‘”.$user_string.”‘, NOW()) “;

More next time…

GnuPG Java Wrapper API

Yaniv Yemini wrote a small GnuPG Java Wrapper API.
Just had a small look over it.
So to get it your version from here
Here is just a small demo:

import javax.swing.JOptionPane;
import org.gpg.java.GnuPG;
public class Loader {
    public static void main (String args[]){
        GnuPG pgp = new GnuPG ();
        String toolChain[] = {"sign", "clearsign", "signAndEncrypt", "encrypt", "decrypt"};
        String message = JOptionPane.showInputDialog(null, 
                                "Message you want to encrypt?", 
                                "Enter your message", 
                                JOptionPane.QUESTION_MESSAGE);
        String keyID = "0x56B69D6B";
        System.out.println("using message: "+message);
        System.out.println("using key ID: "+keyID);
        for(String tool : toolChain){
            System.out.println("running: "+tool);
            if(tool.equals("sign")){
                String passPhrase = enterPassPhrase(tool);
                pgp.sign (message, passPhrase);
            }
            if(tool.equals("clearsign")){
                String passPhrase = enterPassPhrase(tool);
                pgp.clearSign (message, passPhrase);
            }
            if(tool.equals("signAndEncrypt")){
                String passPhrase = enterPassPhrase(tool);
                pgp.signAndEncrypt (message, keyID, passPhrase);
            }
            if(tool.equals("encrypt")){
                pgp.encrypt (message, keyID);
            }
            if(tool.equals("decrypt")){
                String passPhrase = enterPassPhrase(tool);
                pgp.decrypt (message, passPhrase);
            }
            System.out.println("result: " + pgp.getGpg_result() + "nn");
            System.out.println("error: " + pgp.getGpg_err() + "nn");
            System.out.println("exit: " + pgp.getGpg_exitCode() + "nn");
        }
    }
    public static String enterPassPhrase(String usage){
        return JOptionPane.showInputDialog(null, 
                                "Please enter the Passphrase of your private Key for "+usage, 
                                "Passphrase", 
                                JOptionPane.QUESTION_MESSAGE);
    }
}

Unforntunetally there is a Problem with decrypting a message. It is possible to decrypt the String with the gpg CI Version, but within Java it does not work. So maybe the error is on my site :-).

SortedProperties

sorted white and brown pebbles

Angenommen, man braucht für ein Java Property Set ein geordnete Ausgabe – zum Beispiel um einem Übersetzer eine sortierte Liste mit zu übersetzenden String zu liefern.
Man erstellt eine Klasse (zum Beispiel SortedProperties) und lässt diese von Properties erben.
Bedingt durch die Kapselung ist es notwendig, dass die Methoden
private static char toHex(int nibble) ;
private String saveConvert(String theString, boolean escapeSpace);
private static void writeln(BufferedWriter bw, String s);
und Attribute
private static final char[] hexDigit;
in die neue Klasse kopiert werden müssen.
Wir überschreiben die Methode

public synchronized void store(OutputStream out, String comments)

Diese Methode ist für das eigentliche Speichern in eine Datei verantwortlich.
Der neue Inhalt entspricht bist auf eine zusätzliche Sortierung dem alten:

public synchronized void store(OutputStream out, String comments) throws IOException {
    TreeMap propTree = new TreeMap();
    for (Enumeration e = keys(); e.hasMoreElements();) {
        String key = (String) e.nextElement();
        String value = (String) get(key);
        key = saveConvert(key, true);
        value = saveConvert(value, false);
        propTree.put(key, value);
    }
    BufferedWriter awriter;
    awriter = new BufferedWriter(new OutputStreamWriter(out, "8859_1"));
    if (comments != null)
        writeln(awriter, "#" + comments);
        writeln(awriter, "#" + new Date().toString());
        Set keys = propTree.keySet();
        for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
            String key = (String) iterator.next();
            writeln(awriter, key + "=" + propTree.get(key));
        }
        awriter.flush();
    }

Dies ist tatsächlich ein sehr einfacher Weg, um vorhandene Java-Methoden für eigene Zwecke anzupassen.

Nexenta CP 2 RC1 & Java 6

numbers on monitor

root@sunny:/tank/home# apt-get install sun-java6-jre

root@sunny:/tank/home# java -version
dl failure on line 685Error: failed /usr/lib/jvm/java-6-sun-1.6.0.10/jre/lib/i386/client/libjvm.so, because ld.so.1: java: fatal: libCrun.so.1: open failed: No such file or directory

root@sunny:/tank/home# apt-get install sunwlibc

root@sunny:/tank/home# java -version
java version “1.6.0_10”
Java(TM) SE Runtime Environment (build 1.6.0_10-b33)
Java HotSpot(TM) Client VM (build 11.0-b15, mixed mode)

Good signature, but could not determine key fingerprint?!

root@sunny:/tank/home# apt-get update
Get:1 http://apt.nexenta.org hardy-unstable Release.gpg [185B]
Hit http://apt.nexenta.org hardy-unstable Release
Ign http://apt.nexenta.org hardy-unstable Release
Hit http://apt.nexenta.org hardy-unstable/main Packages
Hit http://apt.nexenta.org hardy-unstable/contrib Packages
Hit http://apt.nexenta.org hardy-unstable/non-free Packages
Hit http://apt.nexenta.org hardy-unstable/main Sources
Hit http://apt.nexenta.org hardy-unstable/contrib Sources
Hit http://apt.nexenta.org hardy-unstable/non-free Sources
Fetched 185B in 1s (151B/s)
Reading package lists… Done
W: GPG error: http://apt.nexenta.org hardy-unstable Release: Internal error: Good signature, but could not determine key fingerprint?!
W: You may want to run apt-get update to correct these problems

root@sunny:/tank/home# apt-get install gnupg add-apt-key
Reading package lists… Done
Building dependency tree
Reading state information… Done
The following extra packages will be installed:
gpgv makedev
Suggested packages:
gnupg-doc xloadimage
The following NEW packages will be installed:
gpgv makedev
The following packages will be upgraded:
gnupg add-apt-key
1 upgraded, 2 newly installed, 0 to remove and 1 not upgraded.
Need to get 1957kB of archives.
After this operation, 5050kB of additional disk space will be used.
Do you want to continue [Y/n]? y
WARNING: The following packages cannot be authenticated!
gpgv makedev gnupg
Install these packages without verification [y/N]? y

root@sunny:/tank/home# apt-get update
Hit http://apt.nexenta.org hardy-unstable Release.gpg
Hit http://apt.nexenta.org hardy-unstable Release
Hit http://apt.nexenta.org hardy-unstable/main Packages
Hit http://apt.nexenta.org hardy-unstable/contrib Packages
Hit http://apt.nexenta.org hardy-unstable/non-free Packages
Hit http://apt.nexenta.org hardy-unstable/main Sources
Hit http://apt.nexenta.org hardy-unstable/contrib Sources
Hit http://apt.nexenta.org hardy-unstable/non-free Sources
Reading package lists… Done