Wednesday, May 28, 2008

Since the Compact Framework doesn't have support for App.Config file, we created our own implementation that follows the full framework model.  It requires that the config file be named “MyApp.exe.config” (which is how it works on the desktop) with a subset of the desktop functionality and it's worked well for some time.  Until recently that is.

Yesterday I set out to create some unit tests for some of our OpenNETCF.Rss namespace objects.  Well the FeedEngine object requires information from an app.config file on construction, so I figured it would be simple - I'd just add an app.config file to the test project and mark it as a DeploymentItem.  After a little investigation I found that the calling assembly for a device unit test is SmartDeviceTestHost.exe, which by default runs out of \Program Files\SmartDeviceTest on the target device.  That means that to use your own app config file from a unit test it would need to be named SmartDeviceTestHost.exe.config. 

Interestingly (or frustratingly, depending on when you asked me yesterday), this test host deploys *its own* version of a config file with the same name with some info on what framework it’s running against.  The test framework just heavy-handedly overwrites any existing file rather than merging its contents into the existing one, and it overwrites *after* it deploys all of the test pieces, so you can’t just merge its contents into your own app config and use it.

As a workaround I actually modified the OpenNETCF implementation for app config files.  I didn't really want to, but the only other solution I could think of was to write code that would open the MS-deployed version and do a manual merge in the unit  test code before the test is run, and that seemed like a much uglier route. The OpenNETCF Configuration implementation now looks for a file named MyApp.exe.config.unittest before looking for MyApp.exe.config and uses the "unittest"-suffixed version if it’s there.  I then modified my TestBase class (from which all of my unit tests derive) to add this:

 

 

        [TestInitialize]

        public virtual void TestInitialize()

        {

            CopyTestConfigFile();

        }

 

        private void CopyTestConfigFile()

        {

            // copy the config file to the test host folder

            string src = Path.Combine(TestContext.TestDeploymentDir, "SmartDeviceTestHost.exe.config");

            string dest = Path.Combine(TestHostFolder, "SmartDeviceTestHost.exe.config.unittest");

            if ((File.Exists(src)) && (!File.Exists(dest)))

            {

                File.Copy(src, dest);

            }

        }

 

        public string TestHostFolder

        {

            get

            {

                return  Path.GetDirectoryName(

                        Path.GetDirectoryName(

                        Path.GetDirectoryName(

                        Assembly.GetCallingAssembly().GetName().CodeBase)));

            }

        }

 

Now I simply add SmartDeviceTestHost.exe.config to the unit test project, mark it as a deployment item and voila - it works as expected.  Just how it should have yesterday morning when I set out to write a couple simple tests.

And for the record - the current "solution" for debugging device unit tests (which involves putting in a Debugger.Break() call in the unit test and then doing an "attach to process" from another instance of Studio) is an unweildy pain in the ass.  It takes no less than a minute just te get a unit test running and in a state that you can step through code.  That might not sound like a lot, but try this: put a breakpoint in your code and when the debugger hit is, wait a full minute before you step or look at the Locals window.  Now do this every time you want to debug.

 

5/28/2008 12:46:42 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2]  | 
 Tuesday, May 27, 2008

I don't use LINQ terribly often because it seems that most of the work I do is pre-CF 3.5.  It only creeps into my code in things like unit tests or test projects. 

This weekend I spent a little time updating my AudioTagR project and adding some tools and utilities to it.  One of the challenges I faced was let's assume I have two root directories that contain music files (they really can be any file, but this is a real-world example, so we'll use the exact scenario I had).  In each of these roots are folders for Artists.  In each of those are one or more folders that represent albums by that artist.

Now let's assume one of those root folders (Root A) is my "root" music collection and it contains a few thousand artists.  The other root folder (Root B) is a collection of music off my old hard drives, CDs, etc.  I know that there are duplicates between these folders - artists that exist in both as well as albums that exist in both.  I want to go through Root B and move only those albums to Root A if they don't already exist.  That means that I want to move all albums that aren't already in Root A, creating the containing Artist folder if it's there.  If the folder is there, I want to skip it and I'll manually check the list of songs later.

The traditional way to do this would be to write a recursive method that would look something like this pseudocode:

void Parse(sourcefolder, destfolder)
{
   foreach(directory in sourcefolder.directories)
   {
       if(! exists(destfolder[folder]))
       {
          Copy(sourcefolder, destfolder);
       }
       else
       {
           Parse(directory, destfolder + directory);
       }
   }
}

Well since this code really is nothing but a series of operations on a collection, LINQ immediately comes to mind as a good fit.

The first challenge I ran into is that my algorithm requires a list of directories contained in a parent directory.  Unfortunately Directory.GetDirectories returns a list with the fully qualified path and I just needed the name of the folder itself.  So I wrote a couple of extension methods that get purely a list of the directory names, not the paths.  I also added one for checking to see if a directory was empty (to be used later for cleanliness):


public static class IO
{
  public static bool IsEmptyDirectory(this string path)
  {
    return (Directory.GetDirectories(path).Count() + Directory.GetFiles(path).Count()) == 0;
  }

  public static string FolderNameWithoutPath(this string path)
  {
    return Path.GetFileName(path);
  }

  public static string[] GetFolderNamesWithoutPath(this string path)
  {
    return (from folder in Directory.GetDirectories(path)
        select folder.FolderNameWithoutPath()).ToArray();
  }
}

Once I had those, the resulting merge method was a simple iteration across a couple LINQ queries.

private void MergeFolders(string sourceFolder, string destinationFolder)
{
  // copy those in A but not in B
  foreach(string folder in
      sourceFolder.GetFolderNamesWithoutPath().Except(
      destinationFolder.GetFolderNamesWithoutPath(), StringComparer.InvariantCultureIgnoreCase))
  {
    Directory.Move(Path.Combine(sourceFolder, folder), Path.Combine(destinationFolder, folder));
  }

  // now go through those in both and repeat
  foreach (string folder in
      sourceFolder.GetFolderNamesWithoutPath().Intersect(
      destinationFolder.GetFolderNamesWithoutPath(), StringComparer.InvariantCultureIgnoreCase))
  {
    string src = Path.Combine(sourceFolder, folder);
    MergeFolders(src, Path.Combine(destinationFolder, folder));

    if (src.IsEmptyDirectory()) Directory.Delete(src);
  }
}

 

5/27/2008 10:33:04 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Monday, May 19, 2008
We've published a new article on the OpenNETCF Community Site titled "Native vs. Managed Code: GDI Performance"
 
 
In it, I look at the performance differences between native and managed code making GDI calls.
 
In case you missed them, our other recently published articles include:
 
- Performance Implications of Crossing the P/Invoke Boundary
- An Introduction to WCF for Device Developers
- Getting a Millisecond-Resolution DateTime under Windows CE
- Using GDI+ on Windows Mobile
- Sharing Windows Mobile Ink with the Desktop
- OpenNETCF Mobile Ink Library for Windows Mobile 6
- Improving Data Access Performance with Data Caching
- Developing Connected Smart Device Applications with sqlClient
- Debugging Without ActiveSync
- Image Manipulation in Windows Mobile 5.0
- Don't Fear the Garbage Collector
 
All of our articles are available online at:
http://community.OpenNETCF.com/articles
 
5/19/2008 12:27:16 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Friday, May 09, 2008
OpenNETCF is in the midst of some major infrastructure updates to get automated unit testing of device assemblies working with Team Foundation Server.  Let's just say that it's nowhere near a simple task.

As part of the work, we needed to automate access to the Device Emulator using the Microsoft-supplied Automation Interfaces.  Their C++ and WScript samples for usage leave a bit to be desired, so we wrapped them in an object-oriented model to provide at least a skeletal framework for launching, resetting, saving state etc.  The classes haven't been heavily tested and the test harness for the classes is very basic, but it's a good starting point, and the download (below) is what we're using in our continuous integration process, so it's definitely functional.



We're releasing this source under the MIT X-11 license.  If you make updates and want to submit them back to us, we'll be happy to integrate them, but it's not required.

Download the source and test harness here

5/9/2008 3:04:11 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1]  | 
 Monday, May 05, 2008

Finally!  Someone has created a code profiler for the .NET Compact Framework.  And what's even more exciting is that it's free.  I've not used it, so I can't vouch for how well it works, or how easy it is to use, but the fact it exists is quite promising.

5/5/2008 8:14:17 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [1]  |