Wednesday, December 5, 2007

SWTBot

For those who have tried to test GUI-based Eclipse plug-ins - i.e., Wizards, Dialogs, Editors, and a lot of other useful things in Eclipse, you know that there is a scarcity of options - and especially non-commercial ones. So, when it came time to write functional tests for the Eclipse-based toolset that the team I am part of is developing, it was really a matter of choosing one among a few frameworks that are still in the early stages - meaning minimal documentation, and constant changes.

Though the situation was less than ideal, I decided to go with one that looked promising - SWTBot. Although the documentation is almost non-existent as of yet, you will still be able to pick it up quickly after going through the samples and the code. You start with a SWTBot and use its various methods to locate the controls you want to test, and then use custom bots to manipulate (simulating clicks, key-presses, etc) and verify (testing value of label, etc.) aspects of those controls. Its trivial to include the SWTBot testing code within JUnit test cases and run it using the Eclipse JUnit runners. By the end of day one with SWTBot, I had tests for some dialogs and preference pages, and was well on my way to satisfying my testing requirements.

Now this is great and all but be aware that this framework is still in a relatively early stage of development: expect to go through the framework code to fix issues you encounter. In fact, I had several issues related to getting the appropriate timeouts (especially with the editor content-assist tests), making sure my event listeners were fired, etc. But the guy behind the framework - Ketan Padegaonkar is very committed to the project and is very approachable - he has already addressed the issues I have brought up. If you need to do functional testing of your SWT and Eclipse applications, SWTBot is definitely worth considering.

Tuesday, July 3, 2007

Mechanizations - Towards an ActiveRecord for Gears

With the recent hoopla over ECMA/JavaScript on Rails, and the advent of Google Gears (albeit beta), there is definitely a potential bubble waiting to happen. It's reasonable to expect a lot of APIs and frameworks to appear around Gears, and around the idea of making the browser an even thicker client in general.

Towards that end, I started Mechanizations to ease development of Gears, and JavaScript applications in general. While it is very rudimentary at the moment, supporting only a basic RoR ActiveRecord's ConnectionAdapter-style API, I think it can still help others working with Gears. Eventually, I hope to evolve it into an ActiveRecord for Gears (who didn't think of how great it would be to have a JavaScript ActiveRecord when they first saw Gears?), and a more generic framework beyond that.

As an introduction, I think the following snippet from the tests for the GearsConnector object is useful:


testMigrate: function() {
var migrations = [
{ // version 0
up: function(c) {
c.createTable('test_table', {id: 'INTEGER', name: 'TEXT'});
},
down: function(c) {
c.dropTable('test_table');
}
},
{ // version 1
up: function(c) {
c.createTable('test_table1', {id: 'INTEGER'});
},
down: function(c) {
c.dropTable('test_table1');
}
},
{ // version 2
up: function(c) {
c.dropTable('test_table');
c.dropTable('test_table1');
},
down: function(c) {
c.createTable('test_table', {id: 'INTEGER', name: 'TEXT'});
c.createTable('test_table1', {id: 'INTEGER'});
}
},
{ // version 3
up: function(c) {
c.renameTable('test_table', 'test_table1');
},
down: function(c) {
c.renameTable('test_table1', 'test_table');
}
}
];

this.connector.migrate(migrations, 0);
assertEquals(this.connector.tables(), ['_migrations','test_table']);

this.connector.migrate(migrations, 2);
assertEquals(this.connector.tables(), ['_migrations']);

this.connector.migrate(migrations, -1);
assertEquals(this.connector.tables(), ['_migrations']);

// This migrations fails, so we remain at version -1
this.connector.migrate(migrations, 3);
assertEquals(this.connector.tables(), ['_migrations']);
}

Just a bit more from the test for select:

testSelect: function() {
this.connector.createTable('test_table', {id: 'INTEGER', name: 'TEXT'});
this.connector.insert('test_table', [0, 'R1']);
this.connector.insert('test_table', [1, 'R2']);
this.connector.insert('test_table', [2, 'R3']);
assertEquals(this.connector.select('test_table').length, 3);
assertEquals($.keys(this.connector.select('test_table', [])[0]).length, 2);
assertEquals($.keys(this.connector.select('test_table', ['*'])[0]).length, 2);
assertEquals(this.connector.select('test_table', {headers: true}).length, 4);
assertEquals(this.connector.select('test_table', {where: ['id=?', 0]}).length, 1);
assertEquals(this.connector.select(
'test_table', ['id'], {where: ['id>?', 0], limit: 1})[0]['test_table_id'], 1);
assertEquals(this.connector.select(
'test_table', {where: ['id>?', 0], limit: 1, offset: 1})[0]['id'], 2);
assertEquals(this.connector.select(
'test_table', ['id'], {orderBy: 'id DESC'})[0]['test_table_id'], 2);
this.connector.dropTable('test_table');
}

Thursday, June 14, 2007

Getting through that damn proxy! (using Java)

If you are on a corporate network, you know how annoying it can be to try and get through the proxy. And if you are a developer, it can be even more frustrating because you can't even test your networked application until you first get through the proxy.

Sun has a great article on what is built in to Java to handle network proxies. This one covers the latest features available since Java 1.5 that let you control use of a proxy at a per-socket and per-connection level. Check it out first:

Java Networking and Proxies

Now, that article is pretty good but it doesn't cover how to do proxy authentication. Thankfully, there is another article from Sun which covers how to use java.net.Authenticator. This class, and its related classes, is how you will be doing authentication. How you go about doing authentication with these classes is covered here:

Http Authentication

But wait! There is one prominent piece missing from what's built in to Java for dealing with proxies. And this is that it cannot make regular TCP tunnels through a HTTP proxy (using a Proxy.Type.HTTP Proxy with a Socket won't work). This support is provided in HTTP & HTTP proxies through the CONNECT method. Before getting into how to handle this in Java, let me just give a quick overview of how the process works:


  • First, the client starts a socket and sends out a HTTP CONNECT request to the HTTP proxy. This request indicates what host and port we are actually interested in connecting to through the proxy.

  • The proxy establishes a connection to the host and port we request.

  • The proxy then sets up a tunnel which allows data we send to the proxy to pass through to the actual host, and vice-versa.

  • Now, the proxy sends back a HTTP OK response on the socket we used to send it the request (if everything goes right - if not, you'll get a response with a HTTP error code).

  • Right after the HTTP OK response, the same socket is now what we use to do all communications with the actual host we wanted to connect to. The proxy will tunnel data between us and that remote host.



Now, this HTTP CONNECT method for establishing a tunnel through a HTTP proxy is typically used for establishing SSL connections through the proxy. This means the proxy is usually not going to monitor the actual traffic that goes out once the tunnel is established (if it were SSL traffic, it would be encrypted anyway). But the typical use of this method for SSL tunnels means that HTTP proxies are most likely going to have the following restriction: If you try to establish a tunnel through the proxy to a remote port other than 443 (the SSL port), the proxy is probably going to reject your request. That is, you couldn't use the CONNECT method to connect to somehost.com:3001 because 3001 is not the SSL port.

So, keeping all that in mind, do you have to write your own implementation for dealing with HTTP proxy tunneling? Nope - luckily, there is the Jakarta Commons HttpClient. It has a ProxyClient class which can set all this up for you and return the socket which is ready to have actual data sent over it (that is, after the HTTP OK response has been received from the proxy). For a quick start, check out the ProxyTunnelDemo sample.