Here’s the first of many posts to come out of what I’ve learned and experienced attempting to automate Acrylic.
As mentioned before and elsewhere, Microsoft acquired Creature House a few years ago. With this acquisition came the creative design toolset Creature House Expression. We’ve been working on a new version of the application and one of the challenges I faced in testing the application was finding a way to automate it.
Acrylic is a pretty large unmanaged codebase (the executable is about 10mb) with a wide variety of features. When I started working on it the application did not have automated tests running against it or an api to write tests against. We intended to change this, but the route to take took some effort to discern. Acrylic has a number of custom controls that aren’t actual Windows controls (no HWND of their own). It also has standard Windows controls (in dialogs, mostly). To complicate matters, the application swallows modifier keys in mouse and keyboard messages. We really couldn’t use any “off the shelf” solutions.
An api was added to the application to load & run automation dlls and expose internal state (through interface classes) and provide a method to send modifier keys with input messages. The question as to whether to write unmanaged C++ code directly against this api or put some sort of wrapper layer in to let most of our test code be written in managed code.
I pushed very hard to write as much of the test automation as possible in managed code. Why? Well, reliable code would be easier to write with the managed safety net. I didn’t want to be spending too much time worrying about things like pointer “accidents” in the test code. In addition, there is a great deal of effort and experience being built up with managed tools that take advantage of things like reflection, generics, etc. Another very important thing to me was to be able to easily utilize help from others that could whenever the opportunity arose.
We did ultimately go with pushing towards mostly managed code. Visual Studio 2005 made this very possible. Here’s how we did it:
We wrote a Managed C++ dll to interface with the internal test interfaces and expose those interfaces in a managed way. Our test libraries and test cases are in seperate dlls (C# mostly, some VB.Net tools). A managed harness kicks off the application with automation enabled (standard shell execute pretty much). The application loads the managed C++ dll (not knowing it is managed) and tells it to kick off tests. The dll tells the test harness to kick off tests. The test harness then starts executing attributed test methods as specified in a configuration file.
Works like a charm. Writing the interop code in the new managed C++ is incredibly straight forward. It’s so straight-forward that once I learned how to really use it I threw out some 14,000 lines of Win32 interop code I had written in C# and rewrote the entire thing in managed C++ (made it much better and it took about a week and a half or so). P/Invoke is just way too painful by comparison once you’ve seen how well the new C++ interops.
I plan to spend some time here writing about utilizing the new C++ with a particular bent towards interoping with Win32. I’ll also talk about other things that I’ve had to deal with that don’t seem too particularly well documented yet (such as the new generic collections and anonymous delegates). If there is anything you’re aching to see I’m open to feedback and will direct my efforts towards hitting the things people are interested in.