Monday, October 6, 2008

Testing LWUIT Applications

Its been some time since I last blogged, mostly due to the holidays around here so I'm only partially available and just remarkably busy.
Last time I blogged about one of the cooler features I've been working on, the automatic testing framework to which I just committed some technical changes. This is still work in progress but the general direction is pretty solid and I think we can start discussing it.

The general idea behind these features is to enable debugging using very little code, so we can build a debug version of the application without changing application code and thus automate testing of this application. The code exists in DebugController and it essentially replaces the underlying implementation class with an internal DebugImplementation, the controller is far more capable as an API than a simple recorder, it allows Java based automation and testing allowing you to write unit tests in Java for your applications. To use the debug controller you must avoid Display.init(this) and instead call DebugController.init and everything should work as expected.... Sort of...

If you use the standard DebugController.init(this) method you won't get the automated script recording feature demoed previously, unlike Display's init method the debug controller accepts another argument: ScriptStore.

A ScriptStore is a model for storing recorded scripts thus allowing the developer (you) to store your scripts anywhere you want. Why didn't we provide a default implementation of this???

Well:
1. RMS exists only in MIDP (not in CDC).
2. GCF isn't always supported in the same way and might require permissions.
3. Networking needs a server component or storage space.

However, for you this should be pretty easy since it allows you to store your scripts on the server side for sharing between phones or on the device itself for fast access (just like you would expect from MVC!). To ease your life a bit here is a simple RMS based implementation of such a store use with caution since the format of the underlying scripts is sure to change in the future so don't grow too invested.
class RMSStore implements DebugController.ScriptStore {
private ListModel model;
public ListModel getStoredScriptNameList() throws IOException {
if(model == null) {
try {
RecordStore r = RecordStore.openRecordStore("scriptNames", true);
RecordEnumeration e = r.enumerateRecords(null, null, false);
Vector result = new Vector();
while (e.hasNextElement()) {
String s = new String(e.nextRecord());
result.addElement(s);
}
e.destroy();
r.closeRecordStore();
model = new DefaultListModel(result);
} catch (RecordStoreException ex) {
ex.printStackTrace();
throw new IOException(ex.getMessage());
}
}
return model;
}

public void storeScript(Script s) throws IOException {
try {
if(model != null) {
model.addItem(s.getTitle());
}
RecordStore r = RecordStore.openRecordStore("scriptNames", true);
byte[] b = s.getTitle().getBytes();
r.addRecord(b, 0, b.length);
r.closeRecordStore();
r = RecordStore.openRecordStore("script-" + s.getTitle(), true);
b = DebugController.scriptToBytes(s);
r.addRecord(b, 0, b.length);
r.closeRecordStore();
} catch (RecordStoreException ex) {
ex.printStackTrace();
throw new IOException(ex.getMessage());
}
}

public Script loadScript(String name) throws IOException {
try {
RecordStore r = RecordStore.openRecordStore("script-" + name, true);
RecordEnumeration e = r.enumerateRecords(null, null, false);
Script s = DebugController.scriptFromBytes(e.nextRecord());
e.destroy();
r.closeRecordStore();
return s;
} catch (RecordStoreException ex) {
ex.printStackTrace();
throw new IOException(ex.getMessage());
}
}

public void deleteScript(String name) throws IOException {
try {
if(model != null) {
for(int iter = 0 ; iter < model.getSize() ; iter++) {
if(model.getItemAt(iter).equals(name)) {
model.removeItem(iter);
}
}
}
RecordStore.deleteRecordStore("script-" + name);
RecordStore r = RecordStore.openRecordStore("scriptNames", true);
RecordEnumeration e = r.enumerateRecords(null, null, false);
while(e.hasNextElement()) {
int id = e.nextRecordId();
String entry = new String(r.getRecord(id));
if(entry.equals(name)) {
r.deleteRecord(id);
break;
}
}
e.destroy();
r.closeRecordStore();
} catch (RecordStoreException ex) {
ex.printStackTrace();
throw new IOException(ex.getMessage());
}
}
}

12 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Hello!
    First, sorry for my bad english)

    LWUIT is a great ui, and i was shocked that it's very nice ui have very nice speed on my SE k810i)

    But i want to mix lwuit and lcdui (i want to use GameCanvas), and there is some problem i have...

    When I do javax.microedition.lcdui.Display.getDisplay(midlet).setCurrent(Canvas);

    my Canvas show and then disappear... And the Form is shown..

    Can you post full example of switching between lwuit and lcdui?
    Or send me this example to mail nikit87@list.ru...
    I would be very glad =))

    ReplyDelete
  4. The difference between invokeAndBlock and opening a new thread is that invoke and block would block the current thread and return when completed. A new thread would return immediately, so action performed in your example would complete immediately. Try adding printouts to actionPerformed in your example to see what I mean.

    ReplyDelete
  5. I have zero background on lwuit so excuse me if this will sound stupid/silly. Is lwuit capable to get systems calls and therefore re-do interface of underlying phone system? Something along the line of PIM Optional Package (add-on of JSR-75). However this package can do just small amount of data mining. I looking for some package that can grab whole displayed content by device and pass relevant data to text-to-speech (attempt on Nuance Talk or Mobile Speak for Java phone device for visually impaired users)

    ReplyDelete
  6. Hi Peter,
    I don't think LWUIT or any Java library can help you with what you are looking for. Things of that nature usually should be done in the system layer.

    ReplyDelete
  7. I think that eventually such thing will be done if Java does not want to stay out of loop.

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. Microsoft PStreets on J2ME (LWUIT appliation :)

    http://www.youtube.com/watch?v=mBKfDw40pRw&feature=related

    ReplyDelete
  10. 3 is the best answer

    TextField keyWord = new TextField(){
    protected void commitChange(){
    super.commitChange();
    Console.out(super.getText(), 1);
    }
    };

    works very well ,thanks shai

    ReplyDelete
  11. Hello shai,
    I would like to know about Textfield in LWUIT.I dont know how to get input from the user through Textfield and TextArea..pls help me ..Thanks in advance...Have a good day..My gtalk id is teknoturfian@gmail.com

    Thanks,
    P.Marimuthu
    teknoturfian.blogspot.com

    ReplyDelete