Unit Testing PDF Page Size in ColdFusion

I ran into a difficult Unit testing problem the other day. I had trouble determining the correct test to write, and finally did so. I thought that perhaps someone else might benefit from an exploration of testing strategy.

As you might know, we create pdf’s using ColdFusion for ExportReports.com. One of the new features we are adding is the ability to set the page size (legal, letter, A4… etc.) As page size is a feature, I need to test it. So I need to create a pdf file then determine what its page size is. At first I explored <cfpdf> and its ability to get at PDF metadata. No joy – it turned out that page size isn’t one of the things returned in the metadata. Then I looked at <cfpdf>’s ability to get thumbnails of the pages in the pdf. I could use <cfimage> to get the information about height and width of the image files, put that was in pixels and hard to translate to inches. (I tried assuming 300dpi, it did not yield good results.) Then it dawned on me that ratio of height to width could yield helpful answers. I found the dimensions in millimeters for the various page types, created ratios from them, and compared them to the ratio of the dimensions of the thumbnails. Then I had to correct a bit for precision, and it yielded me a solution. Here’s what it looked like in code:

<!— Grab a processable view of the pdf —>
<cfpdf
action=“thumbnail”
       source=“#pdfQuery.directory#/#pdfQuery.name#”
      
destination=“#dirreports#tn”
      
scale=“100”
overwrite=“TRUE”/>

<cfdirectory
action=“list”
             name=“thumbnailQuery”
            
directory=“#dirreports#tn”
            
filter=“*.jpg”
/>

<cfimage
action=“info”
         source=“#thumbnailQuery.directory#/#thumbnailQuery.name#”
        
structName=“pInfo”>

<cfset assertTRUE(validatePaperSize(width=pInfo.width, height=pInfo.height, pageType=‘legal’))/>

<cffunction
name=“validatePaperSize” access=“private”
output=“false” returntype=“boolean”
>
     <cfargument

name=“width”
type=“string”
required=“yes”
/>
     <cfargument

name=“height”
type=“string”
required=“yes”
/>
     <cfargument

name=“pageType”
type=“string”
required=“yes”
/>

    
<cfset
var ratios = structNew() />
     <cfset

var ratioToTest = NumberFormat(height/width, “_.___”) />

<!— These ratios were derived by using sizes in millimeters
listed here: http://en.wikipedia.org/wiki/Paper_size —>

     <cfset ratios[‘letter’] = NumberFormat(279/216, “_.___”) />
     <cfset ratios[‘legal’] = NumberFormat(356/216, “_.___”) />
     <cfset ratios[‘A4’] = NumberFormat(297/210, “_.___”) />
     <cfset ratios[‘A5’] = NumberFormat(210/148, “_.___”) />
     <cfset ratios[‘B4’] = NumberFormat(353/250, “_.___”) />
     <cfset ratios[‘B5’] = NumberFormat(250/176, “_.___”) />

     <!— Fudge precision —>
    
<cfif
Abs(ratioToTest – ratios[arguments.pageType]) lt .005
>
    
     <cfreturn
true
/>
    
<cfelse>
    
     <cfreturn
false
/>
    
</cfif>    

</cffunction>

So it might not be perfect, but it works for me. Of course, I could just file a feature request with Adobe to add page size to PDF metadata.

Speaking at Max 2008

I found out last week, that I was accepted to speak at Max 2008 North America. I’ll be talking about using ColdFusion to act as middleware for enterprise applications. That may not be clear from the title of my session: ColdFusion as Enterprise Middleware.

I’m pretty excited about the whole thing, and very grateful to the crew at Adobe, especially Adam Lehman, for giving me a shot to speak.

If anyone has any suggestions, or content they would like to hear about, feel free to drop me a line. I don’t guarantee I’ll do what you say, but I will definitely will listen.

Use Selenium? Use XPath Checker.

Just a quick note if you are using Selenium to do your tests, and writing your own, you’ve probably had to write some XPath expressions. I had to write a particularly hard XPath string and got tired of trying to test my XPath expression in Selenium over and over again.

I did some quick Google searching and discovered a Firefox plug-in named XPath Checker. It works two ways:

  • It allows you to write an XPath expression and see what it returns.
  • It allows you to click on piece of a page and see an XPath expression that would find it

It rocks.

Using Selenium to Test ColdFusion Richtext

In the course of trying to get ready to release the code for Yet Another ColdFusion CMS, or Yacc, I was trying to test working with the CMS. In order to do that, I needed to get Selenium to work with it. It worked fine until I tried to edit content. I’m using cftextarea richtext=true to provide CMS editing capabilities. However, when I went to record the test in Selenium, it couldn’t handle working with FCKeditor which provides the richtext capabilities for ColdFusion. After a few hours of trial and error, I was able to test them.

Here’s how you do it:

First you have to do it via Selenium-RC, the Selenium Firefox IDE doesn’t seem to be able to do it. You can launch pretty easily via ANT:

<!–
Prerequsites
for
selenium–>    

<taskdef
resource=“selenium-ant.properties”>

    <classpath>

        <pathelement
location=“${selenium.jar}”/>

    </classpath>

</taskdef>

<target
name=“template.test.selenium”>

    <echo
message=“Running Selenium Tests.”
/>

            

    <selenese

        suite=“${template.test.selenium.suite}”

        browser=“*pifirefox”

        results=“${log.dir}templateuitestresults.html”

        haltOnFailure=“true”

        timeoutInSeconds=“240”

        startURL=“${host.url}”

     />

                

</target>

Where:

${selenium.jar}

The path of the selenium-server.jar file

${template.test.selenium.suite}

The path to the selenium test suite.

${log.dir}

The path to a space where you store logs

${host.url}

The base url of the testing webserver

pifirefox

This is the proxy Injection version of Firefox launcher

 

Now you’re launching your Selenium tests automatically in ANT, but you have to write your tests to actually interact with the FCKEditor. By default Selenium runs its tests as soon as the page loads. But in order to test the FCKEditor, you have to wait for it to exist. Here’s the Selenese to do that:

<tr>

    <td>waitForCondition</td>

    <td>typeof(window.ColdFusion) != ‘undefined’</td>

    <td>5000</td>

</tr>

<tr>

    <td>waitForCondition</td>

    <td>typeof(window.ColdFusion.RichText) != ‘undefined’</td>

    <td>5000</td>

</tr>

<tr>

    <td>waitForCondition</td>

    <td>typeof(window.FCKeditorAPI) != ‘undefined’</td>

    <td>5000</td>

</tr>        

<tr>

    <td>waitForCondition</td>

    <td>typeof(window.ColdFusion.RichText.getEditorObject(‘content’))
!= ‘undefined’
</td>

    <td>5000</td>

</tr>            

<tr>

    <td>storeEval</td>

    <td> selenium.browserbot.getCurrentWindow().ColdFusion.RichText.
getEditorObject(‘content’).SetHTML(‘
&lt;div
id=”testSection”
&gt;
&lt;h2&gt;Welcome&lt;/h2&gt;
&lt;p&gt;Test page added.&lt;/p&gt; &lt;/div&gt;‘) </td>

    <td></td>

</tr>

<tr>

    <td>waitForElementPresent</td>

    <td>id=testSection</td>

    <td></td>

</tr>

 

Assuming that “content” is the name of your cftextarea, this is how you do it. You test for the “Coldfusion” object exists, then the “Coldfusion.RichText”, then the “FCKeditorAPI” and finally the actual richtext area. Once everything exists, you have to set the HTML of the FCKeditor. There is no input element to type into, so you have to use the FCKEditor setHTML method. To do that you have to use a storeEval command, and then you wait for the typed element you added to appear.

Hopefully this saves a few hours of work for someone else at some point.

Knowledge@Wharton Interviews Kevin Lynch

Knowledge@Wharton scored an interview with Adobe CTO Kevin Lynch. I think there’s a lot of great content in the article. The article is mostly concerned with Air, but the part I liked best is where the interviewer asks the question I’ve been wondering about for awhile chiefly:

Knowledge@Wharton: The AIR run time is available as a free download. The AIR Software Development Kit is available for free. How does Adobe make any money from this technology?

Kevin goes on to answer, read the whole article for his exact answer, but basically it comes down to the fact that Adobe uses it’s free products as a loss leader to encourage purchases of their other technologies including tooling and servers. He specifically includes ColdFusion in that.

I think this is another pointer to the fact that Adobe probably won’t be open sourcing or making free ColdFusion anytime in the near future. That might change if OpenBD and Railo gain any sort of traction. But I’m betting against them, as I have said before.

I want my cfvideo

Inspired as I am by Rob Brooks-Bilson’s posts, I’m putting this out there:

I want a cfvideo tag for Centaur that is akin to the cfimage tag in terms of usage and abilities. I want to be able to crop, cut, manipulate, and codec video. I want to be able to easily make Flash Video content from our content.

I posit that video isn’t what’s next for web developers, it is what is.

I wanted to get that in before I hear what anything about the next version of ColdFusion.

cf.Objective 2008 – Postscript

It’s over and few things need stating:

  • cf.Objective repeated its performance as the awesomest conference ever
  • The entire steering committee out did themselves in terms of content and speakers.
  • Once again, the cf.Objective not only delivers great lectures from the experts, but the ability to actually interact with them.

Almost everything this year was better:

  • Room scheduling – I saw no ad hoc room changes due to audience size.
  • Power distribution – there were many outlets available in every room
  • Venue – I thought the rooms and facilities were just as good as last year, but the staff was much more helpful.

On the con side:

  • Wireless was terrible.
  • The food was hit or miss.

Neither of those two facts was, in any way, a real problem for me. Put another way, if I traveled back in time to tell me that the wireless services and food would suck at cf.Objective 2009, I would still go. Then I would be very mad that I wasted time travel on that.

As with last year, I find myself building a list of things that I will challenge myself to adopt over the next year, after absorbing all of the geek radiation at cf.O. Last year’s list was about starting things: starting local development; starting to use ColdSpring. I think this is more about refining things I have used before, and getting the most out of them.

My cf.Objective 2009 ToDo list:

  • Perfect my build process
    • Move all of my projects over to “perfect one step builds”
    • Utilize Subversion correctly with tags and branches
  • Adopt an MVC framework
    • I’m leaning towards Model-Glue 3
  • Start using AOP in ColdSpring
  • Come up with a topic to speak again
    • I like talking about soft skills within technical areas, to that end I have a few ideas
      • Formal Code Reviews
      • Influence Techniques for Geeks
      • Hiring Effective Developers
      • Selling Professional Development to the Resistant Shop 2: Rise of the Uniformed
    • I might need to come up with some technical presentations,
      • Writing Boilerplate Code
      • Writing Code for Team Reuse
  • Gain traction on the rumor that Mark Mandel has a marsupial pouch
  • Collaborate more with people in the community
    • Contribute to someone else’s open source project
    • Work with someone just for fun
    • Team up with somebody for a side project

So that’s my list. I challenge everyone to coming up with their own, and posting it on their respective blogs.

cf.Objective 2008 Day 3

It was with great sadness and dehydration with which I faced the final day at cf.Objective 2008.

Due to my state of “sadness” I didn’t really start taking in new information until Joe Rinehart’s talk on Model-Glue 3: Back to its Roots. Model-Glue 2 broke me of my resistance to frameworks, it made me willing to use them. Model-Glue 3 just makes me want to use Model-Glue 3. I really like seeing things being brought in from the RoR writing process. (Ask for something, a shell gets created, then modify it.) I can’t wait until it’s more documented and ready to go, as I can’t consider myself an expert Model-Glue user.

Then I wandered into Mike Brunt’s session on Clustering and Distributing ColdFusion Applications. I really wanted to hear Mike talk, because he is one of the few public voices on server administration in the community. I admit, I think my brain is full. But I can’t really think it, because my brain is full. I did like the tip to use the default install of ColdFusion in multiserver as an admin node.

Despite that, I went to Jason Delmore’s Building Hybrid Applications with ColdFusion and Java. I got a lot out of this session when he gave it a few years ago at MAX. It’s definitely geeky stuff for people who want to know how things are running under the covers. But this type of knowledge is really great to have when you are trying to figure out what’s going on when your application misbehaves. The part about String buffers was worth the entire session. If you deal with large string operations, take a longer look at this if the presentation comes out. I need to implement this stuff immediately.

Finally the closing section was a nice ending beat to the conference. It impresses me just how much passion the people in the room have to this niche of the professional world.

Always Accept Compliments

This is one of those things that’s probably a pet peeve of mine because I use to do it myself, but I figured I would share what I was told during my performance days.

I’ve seen this phenomenon a bunch, a performer or presenter gets done, an audience member comes up and says something along the lines of “Great job,” the complimented responds with something like:

  • “Oh I totally screwed up”
  • “No, I didn’t really do anything”
  • “No, I thought it went awfully”

Invariably there are two things that drive this:

1. The presenter/performer is so caught up in their own self examination, that they are being hyper critical and sharing it with the complementer.

2. The presenter/performer is concerned about the appearance of humility.

Both ignore a greater truth in the interaction: Someone has said something nice to you, and you are immediately telling them they are wrong! Even if they don’t directly perceive this, it can leave them with a bad taste in their mouth.

So what do you do? Say “Thank you,” that’s it. Leave the self examination stuff where it belongs, in your head. If you are concerned with your ego, accept and expand the compliment: “Thank you; I have to say the audience was really great, you guys asked really great questions.”

It’s a silly little thing, but it can have a big impact on how you are perceived.