Tag Archives: testing

Test driving Enigmail’s permanent decryption function

Last few weeks my teammates and I have been writing some code for the Enigmail project  and experienced first-hand perks and quirks of  the Mozilla Thunderbird platform. More on the work we did there is here: Lessons learned working on Enigmail.

During the time we spent doing this we found that a few basic things you would do with an email are not so obvious and finding an example to guide your way is not a trivial exercise. This post is to share with you a few things we learned after expending some time reading docs, source code and discussions threads which enabled us to test drive our code.

Decrypting permanently.

The EnigmailDecryptPermanently.jsm is file in charge of copying/moving an encrypted email message. The copied/moved email message will be decrypted and stored permanently in the target email folder while the original might remain in the source folder or might be deleted depending if it’s a copy or a move.

Arranging the test scenario.

To test an email message has been decrypted permanently we need the email message in an email folder, the Enigmail extension ready to take over the control of the moving and a PGP implementation to do the actual decrypting work.

We will begin by setting the path to PGP implementation, which in our case is the Gnu Privacy Gard set of tools. In order to run this test it is assumed you have this piece of software installed, something we provision as soon as the development machine starts.

After some directory and files work you configure pinentry and set a development GNUPGHOME so you don’t modify the real one when running the test. More details can be found in the withTestGpgHome function within this test helper.

var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
.createInstance(Components.interfaces.nsIConverterOutputStream);
converter.init(foStream, "UTF-8", 0, 0);
converter.writeString("pinentry-program "+do_get_cwd().path+"/pinentry-auto");
converter.close();

var environment = Components.classes["@mozilla.org/process/environment;1"]
.getService(Components.interfaces.nsIEnvironment);
environment.set("GNUPGHOME", workingDirectory.path);

Then the setup of the Enigmail extension consists of the initialization of the Enigmail service and making it available for the the test scope, which is done in the withEnigmail function in the same test helper.

const enigmail = Components.classes["@mozdev.org/enigmail/enigmail;1"]
.createInstance(Components.interfaces.nsIEnigmail);
const window = JSUnit.createStubWindow();
enigmail.initialize(window, "");

Finally, when you build an extension for Thunderbird an email message is something you’ll  see a lot. And to be able to test things your extension does on an email message you will need to be able to stub it.

Stub an email message.

An email message, within the Thunderbird space, is a record inside a nsIMsgDatabase. The way to access the message’s database is through a nsIMsgLocalMailFolder which is one of the many email folders you may have in your root messages folder. This root folder is associated to an account.

Let’s begin by creating the account and associating an identity to it, so we get ready the scaffolding for any work with messages.

let localAccount = MailServices.accounts
.FindAccountForServer(MailServices.accounts.localFoldersServer);
let identity = MailServices.accounts.createIdentity();
identity.email = "tester@enigmail.org";
localAccount.addIdentity(identity);
localAccount.defaultIdentity = identity;

MailServices is just an entry point to many services around accounts, messages, and more.

Next, we need to create an email folder in the root folder

MailHelper.incomingServer = MailServices.accounts.localFoldersServer;
MailHelper.rootFolder = MailHelper.incomingServer.rootMsgFolder;
let localRoot = MailHelper.rootFolder
.QueryInterface(Components.interfaces.nsIMsgLocalMailFolder);
let mailFolder = localRoot.createLocalSubfolder(name);
mailFolder.setFlag(Components.interfaces.nsMsgFolderFlags.Mail);

And were ready to copy a message from a file to the email folder. do_get_file is one of the utilities available in the Mozilla JSUnit extension.

let emailFile = do_get_file(emailFilePath, false);
MailServices.copy
.CopyFileMessage(emailFile, mailFolder, null, false, 0, null, null, null);

Then, you are ready to retrieve the email message and check your extension does what it’s meant to. When you retrieve the message, you don’t get the actual message but a nsIMsgDBHdr which contains basic information like headers and and id information. As you would expect, the message header is accessed through the messages database.

let msgDb = mailFolder.msgDatabase;
let enumerator = msgDb.EnumerateMessages();
let header = enumerator.getNext()
.QueryInterface(Components.interfaces.nsIMsgDBHdr);

Here are some utilities functions where you can see all this code in context.

Acting on the permanent decryption function.

The decrypt permanently function receives as parameters an array of the headers for the messages to move, the URI for the email folder you want the messages to end up, a boolean describing if you want to copy or move and a flag to specify if you want this function to behave synchronously or asynchronously.

const header = MailHelper.fetchFirstMessageHeaderIn(sourceFolder);
const targetFolder = MailHelper.createMailFolder("target-box");
const move = true;
const reqSync = true;
EnigmailDecryptPermanently.dispatchMessages([header], targetFolder.URI, move, reqSync);

Asserting on the content of the message header.

To verify the result produced by the permanent decryption function we need to access the message from the only information we have available: the header of the dispatched message. In order to do this we use the MsgHrdToMimeMessage and pass as the callback the assertion function. Also notice, that due to the callback function we need to use the do_test_pending and do test_finished pair of function to synchronize the assertion.

const dispatchedHeader = MailHelper.fetchFirstMessageHeaderIn(targetFolder);
do_test_pending();
msgHdrToMimeMessage(
dispatchedHeader,
null,
function(header, mime) {
Assert.ok(!mime.isEncrypted);
Assert.assertContains(mime.parts[0].body, "This is encrypted");
do_test_finished();
},
false
);

And this is it. You can see how this test looks by browsing to the messageIsMovedAndDecrypted() function in the decryptPermanently-test.js file.

 

The many tools for testing

As Arquillian has become steady at the programmer’s tool belt, its action scope has entered debate as some feel it’s an integration testing specific tool and some others see it as the long awaited tool which will bring end to end testing as the only testing a programmer needs from now on. The latter is the motivation of this post as it forgets about the power of unit testing and might not see that Arquillian’s purpose may change depending on the extension you use. Continue reading

The fruits of working out

This a little tale about some code which didn’t exercise in order to work on the really important.

A couple of months ago our project entered in total-madness mode. We were weeks away from the release and some bugs were still pending – a few critical. So, the management solution was dictated: All of the resources (meaning the members of the development team) will work as long as 24 hours per day if necessary until the bugs list is reduced to zero. Continue reading

To consider when arquilling

After a few weeks of real world testing with Arquillian I’ve noticed a few things to consider when building integration tests. These items should grow or evolve, here or in other post, as experience is gained.

To manage provided dependencies. The building of a project most of the time considers dependencies to libraries, toolkits, frameworks, etc., and for each of them you define a scope. You find some to be provided-scoped as your company’s login jar, the jee6-spec.jar library, etc. What’s to consider here is that these libraries are provided by the environment, so they don’t need to be in your deployment file. If you just drop them in your deploy or lib folders when you start the container, without your test file yet, you can make sure the can actually deploy by them selves. Then, if your test-deployment-file fails to deploy it’s less likely because of the provided dependencies. Continue reading

Code coverage with Maven and Cobertura

Cobertura‘s usage is covered in its page so that you can install the plugin and run it separately from the build lifecycle. This how-to integrates it with maven3 to run as part of the site lifecycle. Continue reading

Having richer tests with Arquillian

Let’s add an auditing feature to the previous post‘s example. As simple as writing into a log file everything that went through the save business method in the employees’ service. To do so, we need to add an appender to the jboss-log4j.xml file, code an interceptor and register it with the method.

To test this interceptor class is doing its job you need to access the audit.log file, something inside the application server. I t means it’s necessary to enrich the test case with a reference to this file. In Arquillian, test enrichmentmeans hooking the test class to the container environment by satisfying its injection points“. What we are going to inject into the test class is the log file so we can check it’s growing up with the logs from the interceptor.

Code

log-enricher.tar and lunch-ejb-enriched.tar

Continue reading

Testing resource injection with Arquillian

After coding a simple component that uses an injected datasource to get a connection, we need to test it. To do so we should choose a testing tool  like JUnit and prepare some mock classes, run the tests, pass them and fail on production because running over the actual application server has some big differences.

This time it’s gonna be different.  From our toolbox we’ll take for a spin our brand new testing tool: the Arquillian framework. So, we’ll be running our test right to a JBoss AS 5.1 managed by the testing framework.

The application we’ll use is quite simple. An EJB component that saves an employee to the database. It doesn’t use JPA so we get a chance to inject a datasource and use it to get a connection. You can download the code from here.

Continue reading