Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy

BoudinFatal's Gamercard

Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

April 2013 - Posts

Testing Orchard drivers

If you’ve ever tried to test Orchard part drivers, you may have been blocked by the fact that the methods on drivers are protected. That, fortunately, doesn’t mean they are untestable. Those methods are still accessible through explicit interface implementations. In particular, drivers implement IContentPartDriver, which is defined as follows.

public interface IContentPartDriver : IDependency {
    DriverResult BuildDisplay(BuildDisplayContext context);
    DriverResult BuildEditor(BuildEditorContext context);
    DriverResult UpdateEditor(UpdateEditorContext context);
    void Importing(ImportContentContext context);
    void Imported(ImportContentContext context);
    void Exporting(ExportContentContext context);
    void Exported(ExportContentContext context);
    IEnumerable<ContentPartInfo> GetPartInfo();
    void GetContentItemMetadata(GetContentItemMetadataContext context);
}

By casting your driver to this interface, you get public access to these methods.

For example, here is some code I wrote recently to test the import and export methods of a driver:

[Test]
public void ImportGetAllDefinedProperties() {
    var doc = XElement.Parse(@"
<data>
<UspsShippingMethodPart
    Name=""Foo""
    Size=""L""
    WidthInInches=""10""
    LengthInInches=""11""
    HeightInInches=""12""
    MaximumWeightInOunces=""1.3""
    Priority=""14""
    International=""true""
    RegisteredMail=""true""
    Insurance=""false""
    ReturnReceipt=""true""
    CertificateOfMailing=""true""
    ElectronicConfirmation=""true""/>
</data>
");
    var driver = new UspsShippingMethodPartDriver(null)
        as IContentPartDriver;
    var part = new UspsShippingMethodPart();
    Helpers.PreparePart<UspsShippingMethodPart, UspsShippingMethodPartRecord>(
        part, "UspsShippingMethod");
    var context = new ImportContentContext(
        part.ContentItem, doc, new ImportContentSession(null));
    driver.Importing(context);

    Assert.That(part.Name, Is.EqualTo("Foo"));
    Assert.That(part.Size, Is.EqualTo("L"));
    Assert.That(part.WidthInInches, Is.EqualTo(10));
    Assert.That(part.LengthInInches, Is.EqualTo(11));
    Assert.That(part.HeightInInches, Is.EqualTo(12));
    Assert.That(part.MaximumWeightInOunces, Is.EqualTo(1.3));
    Assert.That(part.Priority, Is.EqualTo(14));
    Assert.That(part.International, Is.True);
    Assert.That(part.RegisteredMail, Is.True);
    Assert.That(part.Insurance, Is.False);
    Assert.That(part.ReturnReceipt, Is.True);
    Assert.That(part.CertificateOfMailing, Is.True);
    Assert.That(part.ElectronicConfirmation, Is.True);
}

[Test]
public void ExportSetsAllAttributes() {
    var driver = new UspsShippingMethodPartDriver(null)
        as IContentPartDriver;
    var part = new UspsShippingMethodPart();
    Helpers.PreparePart<UspsShippingMethodPart, UspsShippingMethodPartRecord>(
        part, "UspsShippingMethod");
    part.Name = "Foo";
    part.Size = "L";
    part.WidthInInches = 10;
    part.LengthInInches = 11;
    part.HeightInInches = 12;
    part.MaximumWeightInOunces = 1.3;
    part.Priority = 14;
    part.International = true;
    part.RegisteredMail = true;
    part.Insurance = false;
    part.ReturnReceipt = true;
    part.CertificateOfMailing = true;
    part.ElectronicConfirmation = true;

    var doc = new XElement("data");
    var context = new ExportContentContext(part.ContentItem, doc);
    driver.Exporting(context);
    var el = doc.Element("UspsShippingMethodPart");

    Assert.That(el, Is.Not.Null);
    Assert.That(el.Attr("Name"), Is.EqualTo("Foo"));
    Assert.That(el.Attr("Size"), Is.EqualTo("L"));
    Assert.That(el.Attr("WidthInInches"), Is.EqualTo("10"));
    Assert.That(el.Attr("LengthInInches"), Is.EqualTo("11"));
    Assert.That(el.Attr("HeightInInches"), Is.EqualTo("12"));
    Assert.That(el.Attr("MaximumWeightInOunces"), Is.EqualTo("1.3"));
    Assert.That(el.Attr("Priority"), Is.EqualTo("14"));
    Assert.That(el.Attr("International"), Is.EqualTo("true"));
    Assert.That(el.Attr("RegisteredMail"), Is.EqualTo("true"));
    Assert.That(el.Attr("Insurance"), Is.EqualTo("false"));
    Assert.That(el.Attr("ReturnReceipt"), Is.EqualTo("true"));
    Assert.That(el.Attr("CertificateOfMailing"), Is.EqualTo("true"));
    Assert.That(el.Attr("ElectronicConfirmation"), Is.EqualTo("true"));
}

The Attr method, in case you're wondering, is an extension method I blogged about yesterday.

The Helper class that I’m using here massages a fake part to behave like a real part. It gives the part a fake record, and adds a fake content item around it. It might not be enough in all situations, but it does make the fake convincing enough in this case.

public static ContentItem PreparePart<TPart, TRecord>(
    TPart part, string contentType, int id = -1)
    where TPart: ContentPart<TRecord>
    where TRecord: ContentPartRecord, new() {

    part.Record = new TRecord();
    var contentItem = part.ContentItem = new ContentItem
    {
        VersionRecord = new ContentItemVersionRecord
        {
            ContentItemRecord = new ContentItemRecord()
        },
        ContentType = contentType
    };
    contentItem.Record.Id = id;
    contentItem.Weld(part);
    return contentItem;
}
A C# helper to read and write XML from and to objects

I really like jQuery’s pattern of attribute getters and setters. They are fluent and work really well with HTML and XML DOMs. If you specify a value in addition to the name, it’s setting, otherwise it’s getting. In C#, we have an OK API for XML, XElement, but it’s not as easy to use as jQuery’s attr methods. It is also missing the flexibility of Javascript with regards to parameter types. To recreate the simplicity of attr in C#, I built a set of extension methods for the most common simple types:

var el = new XElement("node");
el.Attr("foo", "bar")
  .Attr("baz", 42)
  .Attr("really", true);
var answer = el.Attr("baz");

The element built by this code looks like this:

<node foo="bar" baz="42" really="true"/>

And the answer variable will contain “42”.

Even with this API, there is still a fair amount of repetition in code that reads and writes XML from and to objects. You could rely on serialization in those cases, of course, but when you need a little more control, and the types are not necessarily serializable, or if you just want to do it manually, you need something more. This is why I also built ToAttr and FromAttr. Both are extension methods that take an object and an expression for what property of the object to get or set from XML attributes. The methods will infer the type and name from the property.

This is especially useful when writing the import and export methods in an Orchard part driver:

protected override void Importing(
UspsShippingMethodPart part,
ImportContentContext context) {
var el = context.Data.Element(typeof(UspsShippingMethodPart).Name); if (el == null) return; el.FromAttr(part, p => p.Name) .FromAttr(part, p => p.Size) .FromAttr(part, p => p.WidthInInches) .FromAttr(part, p => p.LengthInInches) .FromAttr(part, p => p.HeightInInches) .FromAttr(part, p => p.MaximumWeightInOunces) .FromAttr(part, p => p.Priority) .FromAttr(part, p => p.International) .FromAttr(part, p => p.RegisteredMail) .FromAttr(part, p => p.Insurance) .FromAttr(part, p => p.ReturnReceipt) .FromAttr(part, p => p.CertificateOfMailing) .FromAttr(part, p => p.ElectronicConfirmation); } protected override void Exporting(
UspsShippingMethodPart part, ExportContentContext context) {
context.Element(typeof (UspsShippingMethodPart).Name) .ToAttr(part, p => p.Name) .ToAttr(part, p => p.Size) .ToAttr(part, p => p.WidthInInches) .ToAttr(part, p => p.LengthInInches) .ToAttr(part, p => p.HeightInInches) .ToAttr(part, p => p.MaximumWeightInOunces) .ToAttr(part, p => p.Priority) .ToAttr(part, p => p.International) .ToAttr(part, p => p.RegisteredMail) .ToAttr(part, p => p.Insurance) .ToAttr(part, p => p.ReturnReceipt) .ToAttr(part, p => p.CertificateOfMailing) .ToAttr(part, p => p.ElectronicConfirmation); }

There is no need to specify attribute names or types here, everything is inferred from the expression. Both methods manipulate XML looking like this:

<UspsShippingMethodPart
        Name="Foo"
        Size="L"
        WidthInInches="10"
        LengthInInches="11"
        HeightInInches="12"
        MaximumWeightInOunces="1.3"
        Priority="14"
        International="true"
        RegisteredMail="true"
        Insurance="false"
        ReturnReceipt="true"
        CertificateOfMailing="true"
        ElectronicConfirmation="true"/>

UPDATE: You may notice that there is still quite some repetition of the part parameter in the import/export code above. In order to remove this repetition, I’ve added a small class that aggregates the XML element with a context and has simpler ToAttr and From Attr methods. With this new helper class, we can rewrite the driver’s import/export code to be even more concise:

protected override void Importing(
UspsShippingMethodPart part, ImportContentContext context) {
var el = context.Data.Element(typeof (UspsShippingMethodPart).Name); if (el == null) return; el.With(part) .FromAttr(p => p.Name) .FromAttr(p => p.Size) .FromAttr(p => p.WidthInInches) .FromAttr(p => p.LengthInInches) .FromAttr(p => p.HeightInInches) .FromAttr(p => p.MaximumWeightInOunces) .FromAttr(p => p.Priority) .FromAttr(p => p.International) .FromAttr(p => p.RegisteredMail) .FromAttr(p => p.Insurance) .FromAttr(p => p.ReturnReceipt) .FromAttr(p => p.CertificateOfMailing) .FromAttr(p => p.ElectronicConfirmation); } protected override void Exporting(
UspsShippingMethodPart part, ExportContentContext context) {
context.Element(typeof (UspsShippingMethodPart).Name) .With(part) .ToAttr(p => p.Name) .ToAttr(p => p.Size) .ToAttr(p => p.WidthInInches) .ToAttr(p => p.LengthInInches) .ToAttr(p => p.HeightInInches) .ToAttr(p => p.MaximumWeightInOunces) .ToAttr(p => p.Priority) .ToAttr(p => p.International) .ToAttr(p => p.RegisteredMail) .ToAttr(p => p.Insurance) .ToAttr(p => p.ReturnReceipt) .ToAttr(p => p.CertificateOfMailing) .ToAttr(p => p.ElectronicConfirmation); }

You can find the code for this helper class here:
https://gist.github.com/bleroy/5384405

And I have a small test suite for the whole thing here:
https://gist.github.com/bleroy/5385284

Getting your Raspberry Pi to output the right resolution

I was setting up a new Raspberry Pi under Raspbian on a Samsung monitor the other day. If you don’t do anything, Raspbian and the Pi will attempt to detect the modes supported by your monitor and will make a choice of what seems best to it. And sometimes it gets that very wrong. In those cases, you’ll need to find what the right mode is and to set it up.

It took me quite a few attempts before I succeeded, mostly misled by misinformed forum and blog posts. The right post, the one that has all the correct info does exist however: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=26&t=5851. Let me distil this to a short set of instructions, in case you don’t want to dive in and assimilate all that information. Here is what worked for me…

From the command-line, logged-in as root…

1. Get the list of what’s supported by your monitor:

tvservice -d edid
edidparser edid

2. Find the mode that you want in the resulting list (for me it was “HDMI:EDID DMT mode (82) 1920x1080p @ 60 Hz with pixel clock 148 MHz” with a score of 124,416, which wasn’t the highest, explaining why I had a lower resolution by default). The mode number is the one between parentheses: 82.

3. Edit the config file:

nano /boot/config.txt

Find the section about HDMI, uncomment it and set the right group and mode from step 2. If your mode description contains “DMT”, the group should be 2, and if it contains “CEA”, it should be 1, so for me that was:

hdmi_group=2
hdmi_mode=82

Exit the editor with CTRL+X, followed by Y.

4. Reboot:

shutdown -r now

Logging SQL queries in Orchard

It is often useful to see what database queries were made during a specific request in Orchard. There are quite a few ways to do this (you can trace right from SQL Server, or you can use Mini-Profiler for instance), but this morning Sébastien showed me a really easy one that I thought I’d share.

Find the log4net.config file in /src/Orchard.Web/Config and edit it to add the following tag:

<logger name="NHibernate.SQL">
  <priority value="DEBUG" />
</logger>

Restart the application, then hit the URL you want to test, and look at your logs in App_data/logs. You should see new entries looking like this:

2013-04-03 18:57:30,367 [17] NHibernate.SQL -
  SELECT warmupsett0_.Id as Id575_0_,
         warmupsett0_.Urls as Urls575_0_,
         warmupsett0_.Scheduled as Scheduled575_0_,
         warmupsett0_.Delay as Delay575_0_,
         warmupsett0_.OnPublish as OnPublish575_0_
  FROM VuLu_Orchard_Warmup_WarmupSettingsPartRecord warmupsett0_
  WHERE warmupsett0_.Id=@p0;

  @p0 = 1 [Type: Int32 (0)]

(only not as nicely formatted)

To disable the SQL trace, just edit log4net.config and set the value to ERROR instead of DEBUG.

More Posts