Showing posts with label LWUIT J2ME JavaME Testing Scripting Automation. Show all posts
Showing posts with label LWUIT J2ME JavaME Testing Scripting Automation. Show all posts

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());
}
}
}

Tuesday, September 23, 2008

Exciting Changes To LWUIT

A super geek like myself is excited about game development as much as he is excited about accounting software (I know I wrote both), in my case the new testing and recording framework I just committed to LWUIT are remarkably exciting and I don't think you need to be a hardcore geek like myself to get excited (am I delusional?).
I will try to write additional posts explaining what is going on and how you can use this in your applications but since this is all under heavy development and WILL CHANGE... SIGNIFICANTLY! I don't want to go into too many details.
Generally what you see here is the LWUIT demo mostly unchanged, you can adapt any application to support this and when you remove those lines of code the application doesn't have a trace of that functionality. Currently long clicks on the numbers 1,2,3 & 4 are assigned to the testing framework:
1: starts recording every operation I perform.
2: opens the assert screen where I can assert various states of the UI such as the label of the focused component or whether an exception was thrown.
3: Stops recording and names a script.
4: Opens the script manager where I can rename, delete & play my scripts (hopefully edit too in the future).

Scripts are stored using a model of their own so they can be stored anywhere the user wants to e.g. RMS, filesystem or even the network (in this example in RMS). Scripts can theoretically run on every device or simulator unchanged but this has some issues such as applications behaving differently in different resolutions might not be a bug...

I will blog more about this in the coming weeks as the code and logic finalizes and becomes more concrete.