Saturday, December 19, 2009

XPCShell Testing for Firefox Addons (Extensions)

A quick bit of info on what I've done to get xpcshell unit tests working for my add-on.
Here's my folder structure:To the .mozconfig (not in the above diagram, as mine is located in my home folder), I add the following to enable tests:
ac_add_options --enable-tests

To the Makefile.in in the bittorrent folder, I take the existing lines:
# list of subfolders to process
DIRS = public src

And add the following (based on http://zenit.senecac.on.ca/wiki/index.php/Dive_into_Mozilla_Unit_Testing_Lab)
# Adding xpcshell unit tests
ifdef ENABLE_TESTS
DIRS += test
endif

This means in addition to Make processing the public & src subfolders, it should also process the test folder, if tests are enabled.
The Makefile.in in the test folder is terribly boring. My Add-ons module name is bittorrentwikiscraps, I can perhaps specify the module for this Makefile as that also, however the pattern seems to be test_modulename. (My notes say I was basing this on this Makefile.in, take a look if you need platform specific tests)
DEPTH        = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@

include $(DEPTH)/config/autoconf.mk

MODULE = test_bittorrentwikiscraps

XPCSHELL_TESTS = \
unit \
$(NULL)

include $(topsrcdir)/config/rules.mk

The XPCSHELL_TESTS defines the folders where the tests are stored, and MODULE defines where the tests will be copied, and run from. The tests will be copied to OBJDIR/_tests/xpcshell/test_bittorrentwikiscraps/unit/ when running 'make', and when running 'make xpcshell-tests' the tests in these folders will be executed.
The above is pretty normal for xpcshell tests. The next bit is what's been described to me by ted a couple of times, it's what crashreporter does to circumvent an issue with xpcshell (with a little hack of it's own^).
xpcshell doesn't know about add-ons, so the components I want to test won't be available when the tests run. So, I copy the components (JS files, in bittorrent/src/) to the testing area, by adding the following to bittorrent/src/Makefile.in (at the end of the file):
libs:: 
$(NSINSTALL) -D $(DEPTH)/_tests/xpcshell/test_$(MODULE)/unit/components

libs:: $(EXTRA_COMPONENTS)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/xpcshell/test_$(MODULE)/unit/components

With JavaScript components, the files are listed in EXTRA_COMPONENTS (you list them! It's not automatic!). What we're doing here is first creating the components subfolder in the testing area (the first use of libs::) and then copying every item in EXTRA_COMPONENTS to this new folder (second use of libs::). Yay! Now the component files are there. But how will xpcshell know about them when the tests are run?
This is acheived by using nsIComponentRegistrar to register the components during the test. Yes, every time a test is run the components are registered first. We do this by creating head_regComps.js in the unit folder. When tests are run, for example test_sample.js, all the head_*.js files are executed before the contents of test_sample.js to do any setup. So, head_regComps.js will be run first, which ensures the components are loaded before the testing code. The contents of head_regComps.js are from the crashreporter code:
// load components from test subfolder
// Get current working directory
let cwd = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("CurWorkD", Components.interfaces.nsILocalFile);
// load subfolder
let cd = cwd.clone();
cd.append("components");
Components.manager instanceof Components.interfaces.nsIComponentRegistrar;
Components.manager.autoRegister(cd);

The code gets the current working directory (the unit folder in the testing area) and then registers all the components in the components subfolder.
For tests that just involve new components, this is enough. My tests can now see the components (in test_sample.js):
function run_test() {
var btPresent = ("@wikiscraps.com/transport/wireInStream;1" in Components.classes);
do_check_true(btPresent);
}

However, my Add-on also has new component interfaces (see the IDL files in the public folder). This issue isn't tackled in the current Mozilla build setup, and the above approach based on crashreporter can't handle interfaces. So, to recap: the issue of xpcshell not being aware of Add-ons prevents the interface being loaded; and unlike components, interfaces can't be loaded dynamically. My hack is just to copy the interfaces to OBJDIR/dist/bin/components/. I'm fine with this, my Firefox build has an interface that belongs in the extension folder. This will probably cause an issue when the Add-on interface in a profile and this components folder are out of sync, but oh well. This build is fine for testing and generating the Add-on's XPI file.
Anyway, the copying of the XPT file is achieved by adding the following in the bittorrent/public/Makefile.in (at the end)
libs:: 
$(INSTALL) $(XPIDL_GEN_DIR)/$(XPIDL_MODULE).xpt $(DIST)/bin/components

Usually the xpt files are copied in a similar manner to the folder '${FINAL_TARGET}/components', however because Add-ons are built for an XPI that will mean FINAL_TARGET will point to the xpi-staging area, so instead '$(DIST)/bin/components' must be used. Now, when xpcshell is run the interface will be ready to be loaded.
Just a note, I had removed OBJDIR/dist/bin/components/components.list, OBJDIR/dist/bin/components/compreg.dat & OBJDIR/dist/bin/components/xpti.dat earlier during my development; I'm unsure if I hadn't done this if the interfaces would be loaded.
Anyway, we can now test and see the interface has been loaded, by adjusting test_sample.js:
function run_test() {
var btPresent = ("@wikiscraps.com/transport/wireInStream;1" in Components.classes);
do_check_true(btPresent);
var btiPresent = ("btIWireInputStream" in Components.interfaces);
do_check_true(btiPresent);
}

Yay. I can test my Add-on components using xpcshell unit tests.

Friday, November 27, 2009

Bah, 4 year old fixed chrome bug still an issue for TB developers

I idle in many Mozilla's IRC channels, and happened to see some chrome registration lines pass by on #extdev. The developer was having an issue with their chrome files not being found, with an error similar to:
no chrome package registered for chrome://extensionname/content/somefile.xul
The immediate thing I noticed was their chrome registration lines were describing extensionName rather than extensionname. Having been in the process of rewriting my extension to do things Mozilla Build way, I had been reading Chrome Registration a day or so earlier, and saw that it listed FireFox 2 as not handling mixed case package names as part of registration. Bug 132183 (thankfully listed on MDC article) seems to describe the issue, first reported in 2002 and resolved in 2005. A suggestion to use lower case for the package name fixed the bug for the #extdev developer, and a few hours later I've tried figuring out if this really was the cause.

I needed to find what code the latest release of ThunderBird was using. The version is 2.0.0.23 at this time, found here http://www.mozillamessaging.com/en-US/thunderbird/ . According to MDC, ThunderBird 2 uses the Gecko 1.8.1 branch. The bug report says the it was fixed on trunk in December 2005, somewhere around the 13th or 14th. After spending some time trying to work out what version was trunk at that time (I don't figure it out), I try to find the commit related to the bug. There isn't a link to the commit from the bug report. I look at the patches submitted on the bug report to find out the file most likely to change by the fix is nsChromeRegistry.cpp. Using humph's advice from the IRC channel #Education I use bonsai to find commits affecting this file between 2005-12-13 and 2005-12-15 (just in case the timezones are different between cvs commits and bugzilla).
Once the differences are found, and I still have no idea what gecko version this is, I compare the changes made to the source tar.bz2 - why not just check with the source repository for ThunderBird 2 releases? Well, that would assume it's documented somewhere, and in a place that could be found. Under the release notes for TB 2.0.0.23, the FAQ says the release code is in CVS, and to follow the build intructions. The 'TB simple build' on this page is for the coming-soon TB 3, and TB 2 just isn't mentioned. Since the release notes mention it's CVS, maybe the only cvs titled link works: 'Getting Mozilla Source Code Using CVS'. I can see a mention of how to get the TB 2 development branch, but no mention of how to get TB 2 releases. I follow over to CVS Tags and find that TB 2 was indeed using 1.8 branch, and it was made in September 2005 so a trunk commit in December 2005 won't help ThunderBird 2.
The checking of the source.tar.bz2 of the 2.0.0.23 confirms, this bug fix never came to ThunderBird 2. So what to do? The bug was fixed 4 years ago on a later code base; it's not a show stopper but extension developers involved or introduced to ThunderBird 2 have a fair possibility of running into it. So, I've done the only thing I can think of, and documented it on MDC that chrome registrations with mixed case won't work for ThunderBird 2 or SeaMonkey 1.1, in addition to the existing comment about FireFox 2, and used the warning template so people won't miss it. Unfortunately, the templates strip any formatting or links, which makes a link to the bug impossible, but at least there's a place to point for the next ThunderBird extension developer butting their heads against this.

Sunday, November 8, 2009

Forced fsck on Ubuntu 8.10 boot

During the boot I got one of those "filesystem has been mounted X times, time for a checkup" messages. I didn't particularly want that, I had other things to do, so pressed Esc to skip. It kinda hanged, and I may have pressed Esc again. Messages spewed across the screen, and Iwas forced to a shell prompt with an instruction to run fsck. Didn't want to do that, so rebooted. Dumped to shell prompt again with an instruction to run fsck. On running fsck, I get many screens of not-very-nice looking messages, and then this:
Buffer I/O error on device sda1, logical block 16777358
Error reading block 16777358 (Attempt to read block from filesystem resulted in short read) while getting next inode from scan. Ignore error? yes
Force rewrite?

Scary. Similar messages have been seen by others, and they've succeeded to get their system back by answering 'yes' to the above questions. Personally, this looks like the point where you will likely lose everything, so if there's anything you value, seek professional help. Answering 'yes' worked for me, but I have no idea what damage has been done. Time for a new hard drive.

Saturday, November 7, 2009

Building Firefox Round 2

Things are better the second time around? Rewind 8 months, I was struggling with a debug build of Firefox, nothing was working right. Well, the hint may have been in what I was compiling. I eventually got a build working, but looking back at that, I was building the wrong thing, I was building from cvs when it seems I should have been using Mercurial. Ugh. Makes some amount of sense, but looking at MDC I can see why I thought I was doing the right thing:
"Those doing active development can check out the latest source using CVS"
Which seems to contradict other information on the page. I'm always hesitant with editing this stuff; there are smarter people than me working on this, maybe it's right. I'm pretty darn sure it's wrong.

So, I've bought a new hard drive, installed the latest OS I favour, and starting fresh. I'm building from Mercurial looking at jbostons' wiki content on the build process for Ubuntu. and checking against MDC's build options info. It's been lovely. Here's some feedback I certainly didn't get last time:
checking for stdint.h... yes
checking for inttypes.h... yes
checking for sys/int_types.h... no
checking for GL/glx.h... no
configure: error: Can't find header GL/glx.h for WebGL (install mesa-common-dev (Ubuntu), mesa-libGL-devel (Fedora), or Mesa (SuSE))
*** Fix above errors and then restart with "make -f client.mk build"
make[1]: *** [configure] Error 1
make[1]: Leaving directory `/home/matt/Projects/mozilla/src'
make: *** [/home/matt/Projects/mozilla/src/ff-debug/Makefile] Error 2
What? It couldn't find a header and told me what to install? We are certainly living in the future.
sudo apt-get install mesa-common-dev
Running the debug build has also been a joy. I don't remember getting this much feedback at the console last time, I'm not entirely sure it's all good news:
++DOCSHELL 0xb11bbac0 == 5
WARNING: NS_ENSURE_TRUE(browserChrome) failed: file /home/matt/Projects/mozilla/src/docshell/base/nsDocShell.cpp, line 9888
WARNING: Something wrong when creating the docshell for a frameloader!: file /home/matt/Projects/mozilla/src/content/base/src/nsFrameLoader.cpp, line 1017
WARNING: NS_ENSURE_SUCCESS(rv, rv) failed with result 0x80004005: file /home/matt/Projects/mozilla/src/content/base/src/nsFrameLoader.cpp, line 1041
WARNING: NS_ENSURE_SUCCESS(rv, rv) failed with result 0x80004005: file /home/matt/Projects/mozilla/src/content/base/src/nsFrameLoader.cpp, line 197
pldhash: for the table at address 0xb11bbee8, the given entrySize of 48 probably favors chaining over double hashing.
++DOCSHELL 0xb11bbe80 == 6
But it's better the second time around.

Friday, November 6, 2009

Burying The Dead

Sigh. The time has come. I'll try to develop a more detailed post mortem, might create some content for a small bit of a thesis.
Partly in an effort to find a memory leak, way back in March I went about splitting the Bittorrent XPCOM work into separate reusable components. With hope, things like BirdTorrent could make use of these components, as the code is reliant on Mozilla's platform rather than any direct system calls.
Things attempted:
  • Splitting the work into parts eg. Server, PeerFactories, Torrents, Bencoding library, BitTorrent Wire library, BitTorrent Utility library
  • One step build
  • Testing of individual components
These didn't go well. Things didn't go well at all. The fact that the last release of something usable was February, and it's now November, isn't a particularly thrilling fact. Pretty much each task I've gone along with in that time has stalled at some point, so now my project has more loose ends than functioning ones. There's been times when I've thought it might get better, but I don't really have the support needed for this development (or not willing to ask for it, because it needs too much). It's damn irritating, but at this point I think fixing what's broken would take more time than just starting again.
My aim is to let the current work die. Start afresh, and as it goes I hope to pick through the bones for what may be used, without inheriting the bits that caused this collapse.

Wednesday, September 23, 2009

Wasted Time

I have no sympathy for plagiarists in education, they have literally wasted weeks of my life this year.

If you haven't learnt any damn thing well enough to do the work yourself, for what work will the degree aid you?

Wednesday, June 10, 2009

XPCOM Bittorrent Tracker IDL

Here's the working copy of the IDL I'm working on for the Bittorrent tracker component.


#include "nsISupports.idl"
#include "nsIVariant.idl"

[scriptable, function, uuid(a2a97e13-ddab-4a8c-a72e-45f3c26e5dce)]
interface nsIBittorrentInformation : nsISupports
{
nsIVariant get(in ACString key);
};

[scriptable, function, uuid(8bd92751-6566-4e8c-9d65-9a84195876d9)]
interface nsIBittorrentTrackerListener : nsISupports
{
void update(in nsIBittorrentTracker tracker, in nsIVariant information);
void onError(in nsIBittorrentTracker tracker, in nsIVariant information);
};



[scriptable, uuid(eb4f9895-e19a-4655-95d6-de801075e303)]
interface nsIBittorrentTracker : nsISupports
{
void init(in ACString announceURI, in nsIBittorrentInformation infoSource);
void start();
void stop();
// boolean forceUpdate(in boolean ignoreInterval);
void addTrackerListener(in nsIBittorrentTrackerListener listener);
void removeTrackerListener(in nsIBittorrentTrackerListener listener);
};


BittorrentInformation is just a simple map, the source for finding basic information about a torrent: info_hash, peer_id, port, uploaded bytes, etc. Perhaps this should be an interface with those attributes? At the moment, I find the thought of adding more and more interfaces for simple information a waste, but would be glad to hear differing opinions. I expect if the key isn't found, then an exception would be thrown.

The listener is for receiving the tracker updates, or errors if the tracker has issues. For example: a PeerManager may listen for tracker updates to record new peers.

I've thought about adding fancy extras to the Tracker, (eg. force updating, setting auto-retries) but this seems like predicting usage of the component before there's actually usage. Hoping to keep it simple.

I expect that the components implementing these interfaces will be completed this week. I'm also expecting to migrate new code from the current firewalled SVN repository to Google Code (Bittorrent4XPCOM).

Nice to be back at work.