www.BrettDaniel.com

Notebook Doodle: Grad School Edition

Now that I am back to taking classes, I would like to start posting my notebook doodles again.

Today's comes from my PhD. seminar:

As with the old notebook doodles, please feel free to suggest captions in the comments.

Building Architecture vs. Software Architecture

Yesterday's software engineering class asked the question, "What is a software architect?" The class offered all sorts of answers, most involving some type of technical leadership. To me, the architect sets up the framework in which the other developers work. He or she makes the module- and application-spanning decisions that guide— and in some cases limit— the decisions of other developers.

The architect's main goal when making these decisions is to ensure the overall quality of the final product. However "quality" can be defined using any number of criteria: maintainability, security, speed, flexibility, reliability, etc. Professor Johnson refers to these as the "-ity" words. All involve tradeoffs and may indeed be mutually exclusive. The architect must decide which of the particular "-ities" to focus on and control the tradeoffs based on the client, business, or product needs.

We also discussed some of the many differences between architects of buildings and architects of software. The point that stuck in my mind was this: many years ago, a building architect used to be the designer, engineer, and on-site technical lead of a project. Cristopher Wren (who rebuilt the churches of London after the fire of 1666) and Washington Roebling (who directed the construction of the Brooklyn Bridge from his sickbed via his wife) both seem to meet this criteria. This description sounds very similar to how we describe a software architect today.

As time went by, the roles seemed to diverge. An "architect" became responsible for the aesthetic design of a building, the "engineer" became responsible for making the design work, and the on-site technical lead became any number of contractors and subcontractors. Of course there will always be a great deal of overlap and interaction between these roles, but the differences certainly exist.

Software architecture is still such a young field that I cannot help speculating that it will one day undergo a similar divergence. Professional software architects love trying to make programming “more like engineering”, but I think many changes need to occur before that can happen. To illustrate, a building architectural firm can easily send a design to an architectural engineering firm to prepare technical blueprints, and the engineering firm can easily send the blueprints to any number of contractors. (I realize this is a gross simplification; bear with me.) In software architecture, however, it is very difficult to create a transferable, adaptable “blueprint” of a software system for others to implement. Formal specifications, UML, design/architectural patterns, and other tools try to meet this need, but they still have a long way to go.

Despite this speculation, I believe that a software architect will always remain closer to code (or some other problem-solving abstraction) than a building architect is to a hammer. Certainly part of this belief is personal in that I really like code. However the larger part is based on the observation that software architecture is fundamentally different from any other constructive profession:

  • The final product can be duplicated infinitely. This means that there is no need to “mail the blueprint to all the contractors”. One can just email the executable.
  • Code is the current “top” of many layers of abstractions. Software architecture can grow (and is growing) to accommodate new, more powerful layers of abstractions such as architectural patters or large, interconnected modules.
  • The tools are constantly changing. Software architecture will use different tools, but these tools will still require a technical lead for the same reasons projects currently need a technical lead when writing code.

For these reasons, I believe that software architecture will evolve not toward greater speciality—like what happened in building architecture— but toward greater generality to encompass new abstractions and tools.

An NUnit Test Suite Implementation

During my last two programming jobs and my senior year software engineering project, I became the test-driven development evangelist on my development teams. I designed or helped design three unit test suite architectures and wrote many of the actual unit tests as I developed production code. The following article outlines the implementation of a hypothetical test suite with some of the best features of the test suite architectures that I used.

Update: I submitted this article to The Code Project. It is a bit easier to read over there since the site has automatic code coloring.

Introduction

In this article I will describe a scalable NUnit unit test suite for use on a tiered, database-driven .NET application. The suite will define sample generators used to easily create dummy data for tests, and it will use test fixture inheritance for increased scalability and to allow for easy testing of common functionality.

I will focus on testing of the domain model of a sample application. This is usually located in the "middle" of an application and is often called the business logic layer (BLL). It uses the data access layer (DAL) to mediate data transfer to and from the database and drives the behavior of one or more user interfaces (UI) with which the user interacts or in which data is displayed.

This article assumes knowledge of .NET and C#, but does not require experience with unit testing or NUnit in particular.

BLL Implementation

In this sample application, classes in the BLL that implement database operations inherit from a base class called PersistentObject. This class defines the following interface[1]:

public abstract class PersistentObject {
    protected long _uid = long.MinValue;

    /// <summary>
    /// The unique identifier of this object 
    ///  in the database
    /// </summary>
    /// <remarks>
    /// Set when Fill() is called
    /// </remarks>
    public long UID {
        get { return _uid; }
    }

    /// <summary>
    /// Save this object's data to the database. 
    /// </summary>
    public abstract void Save();

    /// <summary>
    /// Fill this object with data fetched from the
    ///  database for the given UID
    /// </summary>
    /// <param name="uid">The unique identifier of
    /// the record to fetch from the database</param>
    public abstract void Fill(long uid);

    /// <summary>
    /// Remove this object from the database
    /// </summary>
    public abstract void Delete();

    /// <summary>
    /// Fetches an object of the given type and with the
    ///  given UID from the database
    /// </summary>
    /// <typeparam name="ConcreteType">
    /// The type of object to fetch
    /// </typeparam>
    /// <param name="uid">
    /// The unique identifier of the object in the database
    /// </param>
    public static ConcreteType Fetch<ConcreteType>(long uid) 
      where ConcreteType : PersistentObject, new() {
        ConcreteType toReturn = new ConcreteType();
        toReturn.Fill(uid);
        return toReturn;
    }
}

Say, for example, the application must save some client data and a client address that can be used elsewhere in the application. The BLL would therefore need to contain Address and Client classes derived from PersistentObject.

public class Address : PersistentObject {
    private string _streetAddress = null;
    private string _city = null;
    private string _state = null;
    private string _zip = null;

    public string StreetAddress {
        get { return _streetAddress; }
        set { _streetAddress = value; }
    }

    public string City {
        get { return _city; }
        set { _city = value; }
    }

    public string State {
        get { return _state; }
        set { _state = value; }
    }

    public string Zip {
        get { return _zip; }
        set { _zip = value; }
    }

    public override void Save() {
        // Call DAL to save fields
        // ...
    }
    public override void Fill(long uid) {
        // Call DAL to fill fields
        // ...
    }
    public override void Delete() {
        // Call DAL to delete object
        // ...
    }    

    /// <summary>
    /// Utility function that returns the Address with 
    ///  the given UID
    /// </summary>
    public static Address Fetch(long addressUID) {
        return PersistentObject.Fetch<Address>(addressUID);
    }    
}

Client is similar, except it contains a property that returns the Client's Address object.

public class Client : PersistentObject {
    private string _firstName = null;
    private string _lastName = null;
    private string _middleName = null;
    private long _addressUID = long.MinValue;

    private Address _addressObject;

    // ...

    public long AddressUID {
        get { return _addressUID; }
        set { _addressUID = value; }
    }

    /// <summary>
    /// On-demand property that returns this Client's 
    ///  Address based on the current value of AddressUID
    /// </summary>
    public Address Address {
        get {
            if (AddressUID == long.MinValue) {
                _addressObject = null;
            }
            else if (_addressObject == null 
              || AddressUID != _addressObject.UID) {
                _addressObject = new Address();
                _addressObject.Fill(AddressUID);
            }
            return _addressObject;
        }
    }

    // ...

}

To save new client data, the user would do something like the following:

// Create the address that the client will link to
Address newAddress = new Address();
newAddress.StreetAddress = StreetAddressInput.Text;
newAddress.City = CityInput.Text;
newAddress.State = StateInput.Text;
newAddress.Zip = ZipInput.Text;
// Save the address to the database
newAddress.Save();

// Create the client
Client newClient = new Client();
newClient.FirstName = FirstNameInput.Text;
newClient.MiddleName = MiddleNameInput.Text;
newClient.LastName = LastNameInput.Text;
// Link to the address
newClient.AddressUID = newAddress.UID;
// Save the client to the database
newClient.Save();

And to retrieve client data elsewhere in the application, the user would do something like the following:

Client existingClient = Client.Fetch(clientUID);
Address clientAddress = existingClient.Address;

Unit Testing Background

The BLL implementation outlined above is relatively standard. One can verify its behavior in any number of ways. The simplest but least robust is to test the UI. Since the UI depends on the BLL, one could conceivably verify the application by running through web pages or dialog boxes by hand. But what if the application has multiple UIs? Obviously, this method is slow, difficult to repeat, prone to human error, and may miss bugs. Also, it may promote bad programming practice in that a naïve coder may fix a symptom in the UI rather than the base cause in the BLL. This is not to say that we should omit UI testing, just that we should not rely on it to verify business logic.

A better option would be to create a simple driver program that calls the BLL method under development. This option would certainly be easier to repeat, but it may be difficult to save drivers for later or run all existing drivers to verify that nothing is broken.

This is where unit tests come in. One can think of a unit test as a simple driver program that one would probably write anyway. The unit testing framework organizes the tests, provides tools to make writing tests easier, and allows one to run tests in aggregate.

Test Suite Implementation

Since this article discusses a .NET application, I will use the NUnit testing framework in the example test suite. NUnit provides several features such as a test execution GUI, built-in assertions, and test attributes that make writing and running tests very easy.

It is most intuitive to create a test fixture (that is, a class containing a series of tests) for each class in the BLL. So, in keeping with the example, we will have ClientTest and AddressTest classes in the example test suite. These basic test fixtures will need to verify that data is added to the database, retrieved, edited, and deleted correctly. We often need to create dummy objects, so these test fixtures will also include some sample generators. Finally, we do not want to have to repeat common test code across many different test fixtures, so we will test the common database operations in a PersistentObjectTest class from which ClientTest and AddressTest both inherit.

I will explain the construction of PersistentObjectTest in parts. First, the class declaration:

/// <summary>
/// Abstract base class for test fixtures that test 
///  classes derived from BLL.PersistentObject
/// </summary>
/// <typeparam name="PersistentObjectType">
/// The type of BLL.PersistentObject that the derived 
///  class tests
/// </typeparam>
public abstract class PersistentObjectTest<PersistentObjectType> 
  where PersistentObjectType : PersistentObject, new() {

This shows that PersistentObjectTest is a generic type that accepts the type of the object that its derived class tests. This type derives from PersistentObject and has an empty constructor. This lets us create sample generators and other utilities in a type-safe, generic manner:

#region Sample Generators

/// <summary>
/// Returns a dummy object
/// </summary>
/// <param name="type">
/// Indicates whether the returned dummy object should
///  be saved to the database or not
/// </param>
public PersistentObjectType GetSample(SampleSaveStatus saveStatus) {
    PersistentObjectType toReturn = new PersistentObjectType();
    FillSample(toReturn);
    if (saveStatus == SampleSaveStatus.SAVED_SAMPLE) {
        toReturn.Save();
        // Check Save() postconditions...
    }
    return toReturn;
}

/// <summary>
/// Fills the given object with random data
/// </summary>
/// <param name="sample">
/// The sample object whose fields to fill
/// </param>
/// <remarks>
/// Should be overridden and extended in 
///  derived classes
/// </remarks>
public virtual void FillSample(PersistentObjectType sample) {
    // nothing to fill in the base class
}

/// <summary>
/// Asserts that all fields in the given objects match
/// </summary>
/// <param name="expected">
///  The object whose data to check against
/// </param>
/// <param name="actual">
/// The object whose fields to test
/// </param>
/// <remarks>
/// Should be overridden and extended in 
///  derived classes
/// </remarks>
public virtual void AssertIdentical
 (PersistentObjectType expected, PersistentObjectType actual) {
    Assert.AreEqual(expected.UID, actual.UID, 
      "UID does not match");
}

#endregion

GetSample() simply returns a dummy object. The implementations of FillSample() and AssertIdentical() are delegated to the derived classes. These three methods are used by other test fixtures to create and test sample objects. The base class uses them to verify the basic database operations in the following test methods:

#region Data Tests

/// <summary>
/// Tests that data is sent to and retrieved from 
///  the database correctly
/// </summary>
[Test()]
public virtual void SaveAndFetch() {
    PersistentObjectType original = 
      GetSample(SampleSaveStatus.SAVED_SAMPLE);
    PersistentObjectType fetched = 
      PersistentObject.Fetch<PersistentObjectType>(original.UID);
    // verify that the objects are identical
    AssertIdentical(original, fetched);
}

/// <summary>
/// Tests that editing an existing object works correctly
/// </summary>
[Test()]
public virtual void EditAndFetch() {
    PersistentObjectType modified = 
      GetSample(SampleSaveStatus.SAVED_SAMPLE);
    // edit fields 
    FillSample(modified);
    // save edits
    modified.Save();
    // make sure edits were reflected in the database
    PersistentObjectType fetched = 
      PersistentObject.Fetch<PersistentObjectType>(modified.UID);
    AssertIdentical(modified, fetched);
}

/// <summary>
/// Tests that deletion works correctly.
/// </summary>
/// <remarks>
/// Expects data retrieval to fail
/// </remarks>
[Test(), 
ExpectedException(typeof(DataNotFoundException))]
public virtual void Delete() {
    PersistentObjectType toDelete = 
      GetSample(SampleSaveStatus.SAVED_SAMPLE);
    long originalUID = toDelete.UID;
    toDelete.Delete();
    // expect failure because the object does not exist
    PersistentObject.Fetch<PersistentObjectType>(originalUID);
}

#endregion

With PersistentObjectTest doing the heavy lifting, the concrete test classes need only define how to fill a sample object and how to check if two sample objects are identical. They can also define additional sample generators, utility functions, and test methods as needed.

[TestFixture()]
public class AddressTest : PersistentObjectTest<Address> {

    public override void FillSample(Address sample) {
        base.FillSample(sample);
        Random r = new Random();
        string[] states = {"IL", "IN", "KY", "MI"};

        sample.City = "CITY" + DateTime.Now.Ticks.ToString();
        sample.State = states[r.Next(0, states.Length)];
        sample.StreetAddress = r.Next().ToString() + " Anywhere Street";
        sample.Zip = r.Next(0, 100000).ToString("00000");
    }

    public override void AssertIdentical(Address expected, Address actual) {
        base.AssertIdentical(expected, actual);
        Assert.AreEqual(expected.City, actual.City, 
		  "City does not match");
        Assert.AreEqual(expected.State, actual.State,
		  "State does not match");
        Assert.AreEqual(expected.StreetAddress, actual.StreetAddress, 
		  "StreetAddress does not match");
        Assert.AreEqual(expected.Zip, actual.Zip, 
          "Zip does not match");
    }

}
[TestFixture()]
public class ClientTest : PersistentObjectTest<Client> {

    public override void FillSample(Client sample) {
        base.FillSample(sample);
        sample.FirstName = "FIRST" + DateTime.Now.Ticks.ToString();
        sample.MiddleName = "MIDDLE" + DateTime.Now.Ticks.ToString();
        sample.LastName = "LAST" + DateTime.Now.Ticks.ToString();
        sample.AddressUID = new AddressTest().GetSample
         (SampleSaveStatus.SAVED_SAMPLE).UID;
    }

    public override void AssertIdentical(Client expected, Client actual) {
        base.AssertIdentical(expected, actual);
        Assert.AreEqual(expected.FirstName, actual.FirstName, 
          "FirstName does not match");
        Assert.AreEqual(expected.MiddleName, actual.MiddleName, 
          "MiddleName does not match");
        Assert.AreEqual(expected.LastName, actual.LastName,
          "LastName does not match");
        Assert.AreEqual(expected.AddressUID, actual.AddressUID, 
          "AddressUID does not match");
    }
}

ClientTest’s sample generator uses AddressTest.GetSample() to create a dummy Address when filling a dummy sample Client. This general pattern is used often in this type of test suite. Any test that needs a dummy object simply calls the appropriate sample generator.

When running tests, NUnit looks for any classes marked with the attribute [TestFixture()]. It creates an instance of the class and runs any methods marked with the attribute [Test()]. The [ExpectedException()] attribute tells NUnit that the given method should throw the given exception. The test code itself uses NUnit’s Assert object to verify that expected properties hold.

Any test fixture that inherits from an abstract base class also “inherits”[2] any test methods. Therefore, AddressTest, a concrete test fixture, inherits the SaveAndFetch(), EditAndFetch(), and Delete() test methods from PersistentObjectTest. Note that a derived class can override these test methods if, for example, its corresponding BLL class does not support deleting:

[Test()]
public override void Delete() {
    Assert.Ignore("This object does not support deleting");
}

Inheritance

Now that we have the basic test suite implemented, say the requirements change and we need to add a class representing a preferred client that receives discounts and special credit. First we will create a PreferredClient class derived from Client:

public class PreferredClient : Client {
    private double _discountRate = 1;
    private decimal _accountCredit = 0.00M;

    //...

    public override void Save() {
        base.Save();
        // call DAL to save this object's fields
    }

    //...

}

Next, we must create a PreferredClientTest test fixture derived from ClientTest. But this causes a problem: ClientTest inherits from PersistentObjectTest<Client>, but we need PreferredClientTest to inherit indirectly from PersistentObjectTest<PreferredClient> so that PersistentObjectTest’s methods use the correct type of object. The solution is to move the generic signature “down the hierarchy” to ClientTest:

/// <summary>
/// Generic tester for classes derived from Client
/// </summary>
public class ClientTest<DerivedClientType> 
: PersistentObjectTest<DerivedClientType> 
    where DerivedClientType : Client, new() {

    public override void FillSample(DerivedClientType sample) {
        base.FillSample(sample);
        sample.FirstName = "FIRST" + DateTime.Now.Ticks.ToString();
        sample.MiddleName = "MIDDLE" + DateTime.Now.Ticks.ToString();
        sample.LastName = "LAST" + DateTime.Now.Ticks.ToString();
        sample.AddressUID = new AddressTest().GetSample
          (SampleSaveStatus.SAVED_SAMPLE).UID;
    }

    public override void AssertIdentical
      (DerivedClientType expected, DerivedClientType actual) {
        base.AssertIdentical(expected, actual);
        Assert.AreEqual(expected.FirstName, actual.FirstName, 
          "FirstName does not match");
        Assert.AreEqual(expected.MiddleName, actual.MiddleName, 
          "MiddleName does not match");
        Assert.AreEqual(expected.LastName, actual.LastName,
          "LastName does not match");
        Assert.AreEqual(expected.AddressUID, actual.AddressUID, 
          "AddressUID does not match");
    }

}

But we need to keep the non-generic tester so Client's tests will still run:

/// <summary>
/// Non-generic tester for base Client type
/// </summary>
[TestFixture()]
public class ClientTest : ClientTest<Client> {
    // add Client-specific tests as needed
}

Finally, we define PreferredClientTest in terms of the generic version of ClientTest:

[TestFixture()]
public class PreferredClientTest : ClientTest<PreferredClient> {

    public override void FillSample(PreferredClient sample) {
        base.FillSample(sample);
        Random r = new Random();
        // some random dollars and cents
        sample.AccountCredit =1 + .25M; 
        sample.DiscountRate = r.NextDouble();
    }

    public override void AssertIdentical
     (PreferredClient expected, PreferredClient actual) {
        base.AssertIdentical(expected, actual);
        Assert.AreEqual(expected.AccountCredit, actual.AccountCredit, 
          "AccountCredit does not match");
        Assert.AreEqual(expected.DiscountRate, actual.DiscountRate, 
          "DiscountRate does not match");
    }
}

Note that the FillSample() and AssertIdentical() methods simply extend their base class counterparts. One can easily see how this type of expansion can continue as the application grows; it is simply a matter of adding a subclass and implementing the appropriate methods.

Drawbacks

Primary Keys

This hypothetical test suite makes one glaring assumption: it assumes that PersistentObject is a valid base class for real-world classes. This assumption becomes most apparent in the Fetch/Fill methods which always take a long as a unique database identifier. Often, a real-world database will not be normalized such that all data has a bigint primary key (if only!). One can get around this problem by expanding the generic signature of PersistentObjectTest and PersistentObject.Fetch() to include the type of the derived class’ unique identifier.

Dummy Data Overload

Because of its dependence on sample generators, the form of test suite creates a large amount of dummy data in the database. This is acceptable since a large part of testing a database-driven application is verifying that data is saved and retrieved correctly. However, it means that the development application must have a dedicated testing database server that is regularly reset to some known state to prevent dummy data from overshadowing valid data. Also, the recursive nature of the sample generators may make it possible to get into a never-ending sample generation cycle that could very quickly bring a database (not to mention the stack frame) to its knees.

Randomness

The implementation I have outlined assumes that random dummy data will often suffice for most tests that use the generated objects. In other words, the consumer of the sample object must ensure that a generated object meets the desired preconditions. Bounds on randomness can often be achieved with parameterized sample generators such as the following:

/// <summary>
/// Return a client with one of the given first names
/// </summary>
/// <param name="firstNames">
/// The list of possible first names
/// </param>
public static Client GetBoundedSample
  (string[] firstNames, SampleSaveStatus saveStatus) {
    Client toReturn = new ClientTest().GetSample(SampleSaveStatus.UNSAVED_SAMPLE);
    Random r = new Random();
    toReturn.FirstName = firstNames[r.Next(0, firstNames.Length)];
    if (saveStatus == SampleSaveStatus.SAVED_SAMPLE) {
        toReturn.Save();
    }
    return toReturn;
}

However, there is no general, easily-implemented way for the sample generators to control randomness or return a bounded exhaustive list of all possible samples. In fact, exhaustive test generation is an ongoing research problem.

Conclusion

The hypothetical test suite architecture that I have outlined is useful for testing tiered, database-driven applications in which reasonable, random sample data is often needed. By using test fixture inheritance and sample generators, it becomes very easy to expand the test suite as the application grows. It also reduces the amount of code needed to test the most important aspect of a database-driven application: that data travels to and from the database correctly. Variations on this testing implementation have performed well for several .NET applications with from several dozen to several thousand classes.

Footnotes

  1. In reality, Save, Fill, and Delete would usually wrap protected overridable methods like DoSave, DoFill, and DoDelete. This would allow the base class to define common pre- and post-database operation steps while leaving the derived class to handle its own data. Also, Delete would usually set an “Ignore” flag rather than completely remove the data from the database. Regardless, we can ignore those complications in this article. Just assume that a derived class would override Save, Fill, and Delete in the obvious manner if the class supports the appropriate database operation.
  2. This is not true inheritance. NUnit uses reflection to find any methods marked with the attribute [Test()] regardless of where the method occurs in the class hierarchy. Also, overriding a test method does not retain the [Test()] attribute.

Files

  • nunittestsuite.zip – The Visual Studio 2005 solution with the sample code. It will compile and run, but the tests will not pass since the DAL is left unspecified.

Notes

  1. Decimal)r.Next(

Software Engineering Course

I am taking CS527, UIUC's graduate-level software engineering course, this semester. It is taught by Ralph Johnson who cowrote the classic Design Patterns: Elements of Reusable Object-Oriented Software.

Ralph Johnson on the cover of Design Patterns

One interesting feature of the course is that we must keep a journal of the presentations we attend and the books we read for the course. Professor Johnson recommends keeping the journal as a weblog. He mentioned eventually setting up an aggregator, but right now he is just keeping a list of the students' weblogs. I will be keeping my journal under this weblog's CS527 category (RSS, Atom). You can probably expect more software architecture-oriented posts in the near future.

Weblogs, a wiki, an excellent reading list, and a well-known expert for a teacher... I expect good things from this class.

Illinermaker

I am now an Illinermaker (or would that be Boillini?). I have completed my move from Purdue to the University of Illinois and have spent the last four days settling in and preparing for the semester.

Me wearing a Purdue jacket over an Illinois shirt

The apartment is furnished, so the move was easier than it would have been had I kept my table, couch, and desk. I brought the table home to go into storage until my family or I have need for it. We did not just get rid of it because, in the Sue's words, "That was your grandmother's first table." The couch went outside to be picked up by a former coworker. I hope he got it; he was in the middle of a family emergency as I was moving out. If not, that couch certainly had a good run. It remained comfortable and clean through one bedroom, three dorm rooms, one apartment, and four moves. Finally, the desk went to Doug who wrestled that 6-foot monster into his new apartment at Purdue. Since I had such a huge desk at the old apartment, I think the universe needed to restore balance by giving me a really really tiny desk at my new apartment. It has just over half the surface area of the old desk. Aside from the small desk, I am very pleased with the new apartment. It is clean, quiet, as close to campus as I can get, and has many of the conveniences and appliances of a larger apartment.

I am going to be a research assistant for Professor Darko Marinov's group this semester. That means I will not have to grade undergraduate work like I would if I was a teaching assistant, but I will have to do a whole lot more programming, reading, and other "research"-ey things. I do not know yet what I will be working on, but it will probably involve software testing and automated program generation. I will write about that and the classes I am taking when I know more.

I am ready and eager to start what my father accurately called, "the next chapter of my life".

Skateboarders

While leaving the apartment to run some errands, I noticed some skateboarders practicing their hobby in a nearby parking lot. I had my camera with me, so I practiced my hobby by taking some pictures:

Police Man Drawing

I just posted a new drawing of a police man:

The original pencil sketch has been on my wall for the last six months, begging to be posted.

I had a terrible time finishing the line art because my drawing tablet was acting up. Periodically the pointer would fly around the screen and lose pressure sensitivity, destroying whatever line I had in progress. I think that my current mouse driver is fighting with the drawing tablet driver for some reason. Regardless, I am pleased with how the drawing turned out.

Update 8/8/06

In another bit of comic strip synchronicity, today's Toothpaste for Dinner cartoon involves a faulty drawing tablet.

Newspapers Can’t Do This

I suppose this was the obvious thing to do with today's Non Sequitur strip.

Today's Non Sequitur strip, animated

Also, in an interesting bit of synchronicity, today's strips for both Calvin and Hobbes and Rose is Rose involve wagons racing down hills. I would not be surprised if Pat Brady, RiR's creator, did that on purpose.

New Location, New Host, New Design, New Backend

This site has lain stagnant for far too long, and I realized it was time for a change. What better time than right before I change schools?

First, some backstory: I graduated from Purdue during my long silence. I now have my bachelor's degrees in computer science and math and will be moving to Champaign Illinois in nine days to begin my graduate career at the University of Illinois.

Because I graduated, my Purdue web account will expire at the end of the summer. I have kept this website for six years; I knew I wanted to keep it going through graduate school and beyond. This meant I needed to find other hosting. I ended up with Dreamhost because I have heard good things about them and they have all the functionality that I wanted for a good price. PHP 5, MySQL, Subversion, Unix shell, email, the works.

At first I was just going to move my old site over to the new hosting, but what fun would that be? One of the reasons that I think the old website stagnated was because I was handcuffed by the backend design. Some of you may remember that the site was split into two sections: the "Main" website with my artistic content and the weblog with my writing. What should I do when I wanted to write about an artistic creation? Also, the weblog picture gallery that I wrote was getting unweildy. My parents gave me a Nikon D70 as a graduation gift, and I wanted to be able to post a large number of pictures easily. The old website backend made it more difficult than it needed to be.

So I decided to rebuild the website. First, the design. I had a terrible time creating an attractive visual layout. I went through four mockups before deciding on the one you see now.

Blue Mockup Jagged Blue Mockup Reeds Mockup Sticks Mockup

I also drastically changed the backend. Instead of using my custom-built weblog system, I decided to go with Wordpress. The application is amazing. I was able to convert the mockup of my design into a custom Theme in just a few hours. Data conversion was even easier: I just wrote a quick SQL query to retrieve posts from my old database and insert them into the Wordpress database. Unfortunately, I was unable to transfer the old comments because I did not want to have to mess with re-linking all of them to the posts' new database IDs. Other than that, I do not think it could have gone more smoothly.

I moved the rest of my non-database website content into the gallery and music sections. I completely rebuilt the gallery to integrate it into a Wordpress template and support more user interaction. Now one can browse images without having to reload the page. It also supports subgalleries which allow me to display images by simply uploading a folder with XML metadata. It is pretty nice.

So here it is: the lastest version of BrettDaniel.com. What do you think? Please let me know if you have any questions or find any problems.