<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 <title>Jason Foreman</title>
 <link rel="self" href="http://threeve.org/atom.xml" />
 <link rel="alternate" href="http://threeve.org/" />
 <id>http://threeve.org/</id>
 <updated>2015-09-29T20:16:50+00:00</updated>
 <author>
   <name>Jason Foreman</name>
   <email>jason@threeve.org</email>
 </author>
 
 <entry>
   <title>OCUnit XML File Output</title>
   <link rel="alternate" href="http://threeve.org/2009/10/24/OCUnit-Output-XML" />
   <published>2009-10-24T00:00:00+00:00</published>
   <updated>2009-10-24T00:00:00+00:00</updated>
   <id>http://threeve.org/2009/10/24/OCUnit-Output-XML</id>
   <summary>In the world of Mac OS X programming, OCUnit reigns supreme as the unit testing library of choice.  Apple has put a great deal of work into ensuring that OCUnit is tightly integrated into the standard development workflow with Xcode.  Unfortunately many other tools--such as continuous integration servers or test metric tools--expect test output in an XML file, typically in the format of the jUnit output from the Java Ant build tool.  OCunit does not provide xml file output by default, but adding support for it is quite easy.
</summary>
   <content type="html" xml:lang="en" xml:base="http://threeve.org/">
       &lt;p&gt;In the world of Mac OS X programming, OCUnit reigns supreme as the unit
testing library of choice.  Apple has put a great deal of work into
ensuring that OCUnit is tightly integrated into the standard development
workflow with Xcode.  Unfortunately many other tools–such as continuous
integration servers or test metric tools–expect test output in an XML
file, typically in the format of the jUnit output from the Java Ant build
tool.  OCunit does not provide xml file output by default, but adding
support for it is quite easy.&lt;/p&gt;

&lt;h2 id=&quot;the-goal&quot;&gt;The Goal&lt;/h2&gt;

&lt;p&gt;Build servers such as &lt;a href=&quot;http://hudson-ci.org/&quot;&gt;Hudson&lt;/a&gt; have the ability to process test
output and generate trend graphs and notifications.  Most such tools rely
on having the output in a particular format, often the jUnit format.  OCUnit
by default only prints its output to the console. &lt;/p&gt;

&lt;p&gt;The jUnit XML format produced by Ant is horribly undocumented.  It does
seem to be a rather simple format though, which basically looks like
this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;testsuites&amp;gt;
    &amp;lt;testsuite name=&quot;MyTestSuite&quot;&amp;gt;
        &amp;lt;testcase name=&quot;MyTestCase.testFooMethod&quot;&amp;gt;&amp;lt;/testcase&amp;gt;
        &amp;lt;testcase name=&quot;MyTestCase.testBarMethod&quot;&amp;gt;
            &amp;lt;failure&amp;gt;Exception, blah blah blah&amp;lt;/failure&amp;gt;
        &amp;lt;/testcase&amp;gt;
    &amp;lt;/testsuite&amp;gt;
&amp;lt;/testsuites&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;the-means&quot;&gt;The Means&lt;/h2&gt;

&lt;p&gt;OCUnit does offer a few hooks which can be used to follow test
execution.  A quick peek around the SenTestingKit framework
headers–found in
&lt;code&gt;/Developer/Library/Frameworks/SenTestingKit.framework/Headers&lt;/code&gt;– shows
a couple of obvious ways to watch test execution.  The first is the
&lt;code&gt;SenTestObserver&lt;/code&gt; class, and the second is a collection of 
&lt;code&gt;NSNotifications&lt;/code&gt; that &lt;code&gt;SenTestObserver&lt;/code&gt; is presumably based upon.&lt;/p&gt;

&lt;h4 id=&quot;sentestobserver&quot;&gt;SenTestObserver&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;SenTestObserver&lt;/code&gt; class defines a set of methods that will be
called at the beginning and end of both test suites and test methods.
This looks like a perfect solution, but it is completely non-obvious how
to actually use a subclass of &lt;code&gt;SenTestObserver&lt;/code&gt;.  Also, there are at
least some reports that &lt;a href=&quot;http://stackoverflow.com/questions/247607/how-do-i-trap-ocunit-test-pass-failure-messages-events/329674#329674&quot;&gt;&lt;code&gt;SenTestObserver&lt;/code&gt; is broken&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;notifications&quot;&gt;Notifications&lt;/h4&gt;

&lt;p&gt;Since there appear to be some issues with using &lt;code&gt;SenTestObserver&lt;/code&gt;
directly, the next step is to directly observe the set of notifications
upon which it is based.  The following notifications are defined in the
SenTestingKit headers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;SenTestSuiteDidStartNotification&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;SenTestSuiteDidStopNotification&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;SenTestCaseDidStartNotification&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;SenTestCaseDidStopNotification&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;SenTestCaseDidFailNotification&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using these notifications it is possible to create a class that can
write to an XML file as test execution progresses.  The only interesting
task remaining is to load the class before test execution starts, which
is easily accomplished using a &lt;a href=&quot;http://developer.apple.com/mac/library/documentation/DeveloperTools/gcc-4.0.1/gcc/Function-Attributes.html&quot;&gt;gcc constructor attribute&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-result&quot;&gt;The Result&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;BPOCUnitXMLReporter&lt;/code&gt; is a small bit of code that records OCUnit test
execution and writes it to an XML file.  The code, along with a small
sample project, is &lt;a href=&quot;http://github.com/threeve/BPOCUnitXMLReporter&quot;&gt;available on github&lt;/a&gt; and can 
be easily added to any OCUnit test bundle.  Simply add the
&lt;code&gt;BPOCUnitXMLReporter.m&lt;/code&gt; file to the test bundle target, and every time
the tests run a file &lt;code&gt;ocunit.xml&lt;/code&gt; will be written with the test results.
Contributions are certainly welcome, so fork away!&lt;/p&gt;


   </content>
   <author>
     <name>Jason Foreman</name>
     <uri>http://threeve.org/</uri>
   </author>
 </entry>
 
 <entry>
   <title>Run Ruby Script Automator Action</title>
   <link rel="alternate" href="http://threeve.org/blog/2008/01/run-ruby-script-automator-action.html" />
   <published>2008-01-12T00:00:00+00:00</published>
   <updated>2008-01-12T00:00:00+00:00</updated>
   <id>http://threeve.org/blog/2008/01/Run-Ruby-Script-Automator-Action</id>
   <summary>In a recent blog post, Jonathan Wight unveiled a custom action for Automator which can run Python code.  His code uses PyObjC to allow the action to take advantage of all the Cocoa goodness that comes with it.  While I am doing more Python work these days, my heart still belongs to Ruby, so I was compelled to respond with a Run Ruby Script action of my own.
</summary>
   <content type="html" xml:lang="en" xml:base="http://threeve.org/">
       &lt;p&gt;In a &lt;a href=&quot;http://toxicsoftware.com/run-python-script/&quot;&gt;recent blog post&lt;/a&gt;, &lt;a href=&quot;http://toxicsoftware.com/&quot;&gt;Jonathan Wight&lt;/a&gt; unveiled a custom action for &lt;a href=&quot;http://www.apple.com/macosx/features/300.html#automator&quot;&gt;Automator&lt;/a&gt; which can run Python code.  His code uses &lt;a href=&quot;http://pyobjc.sourceforge.net/&quot;&gt;PyObjC&lt;/a&gt; to allow the action to take advantage of all the Cocoa goodness that comes with it.  While I am doing more Python work these days, my heart still belongs to &lt;a href=&quot;http://www.ruby-lang.org/&quot;&gt;Ruby&lt;/a&gt;, so I was compelled to respond with a &lt;a href=&quot;http://cloud.github.com/downloads/threeve/RunRubyScriptAction/Run_Ruby_Script-1.0.zip&quot;&gt;“Run Ruby Script”&lt;/a&gt; action of my own.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/RunRubyScript.png&quot; alt=&quot;RunRubyScript.png&quot; border=&quot;0&quot; width=&quot;300px&quot; align=&quot;left&quot; style=&quot;margin-right: 8px&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://github.com/threeve/RunRubyScriptAction&quot;&gt;source code&lt;/a&gt; for this project is available under the &lt;a href=&quot;http://www.opensource.org/licenses/mit-license.php&quot;&gt;MIT license&lt;/a&gt;.  Like Jonathan’s Python original, this action includes a (rather naïve) syntax highlighting NSTextView.  More on that perhaps in a future post.  But the real star of this action is the integration with Leopard’s &lt;a href=&quot;http://developer.apple.com/documentation/Cocoa/Conceptual/ScriptingBridgeConcepts/Introduction/chapter_1_section_1.html&quot;&gt;Scripting Bridge&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When Automator passes objects in to a custom Cocoa-based action, it converts them into one of a handful of possible Cocoa types.  Types such as strings (com.apple.cocoa.string), paths (com.apple.cocoa.path), and URLs (com.apple.cocoa.url) are happily handled for you.  Everything else can be sent as an Apple Event descriptor (com.apple.applescript.object).  That’s all well and good, but I haven’t the foggiest what to do when my script receives an NSAppleEventDescriptor.  What we really want is an object with methods and properties that can be used to access the underlying object directly.  With the help of Scripting Bridge, this is no problem at all.&lt;/p&gt;

&lt;p&gt;The Scripting Bridge essentially wraps the scriptable interface of an application up into a nice Objective-C object.  With the help of &lt;a href=&quot;http://rubycocoa.sourceforge.net/&quot;&gt;RubyCocoa&lt;/a&gt;, we can take advantage of those nicely wrapped interfaces in the “Run Ruby Script” action.  Since incoming data is passed in the form of an NSAppleEventDescriptor, the action performs some conversion in order to get a Scripting Bridge object for use by the script.  By recursively traversing the descriptor, we can glean the source application and attempt to create an SBApplication.  If that goes well, we work our way back up through the event descriptor, finding items in collections on our way back up.  This is best illustrated by an example:&lt;/p&gt;

&lt;p&gt;Imagine that we ask iTunes to find all songs whose name equals “&lt;a href=&quot;http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewAlbum?playlistId=257638042&amp;amp;s=143441&amp;amp;i=257638202&quot;&gt;The Salmon Dance&lt;/a&gt;” (a &lt;a href=&quot;http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewAlbum?playlistId=257638042&amp;amp;s=143441&amp;amp;i=257638202&quot;&gt;great song&lt;/a&gt; recently recommended by &lt;a href=&quot;http://cabel.name&quot;&gt;Cabel&lt;/a&gt;).  We might normally get an NSAppleEventDescriptor such as&lt;/p&gt;

&lt;pre&gt;
&amp;lt;NSAppleEventDescriptor: &#39;obj &#39;{ 
    &#39;form&#39;:&#39;ID  &#39;, &#39;want&#39;:&#39;cFlT&#39;, &#39;seld&#39;:3592, &#39;from&#39;:
        &#39;obj &#39;{ &#39;form&#39;:&#39;ID  &#39;, &#39;want&#39;:&#39;cLiP&#39;, &#39;seld&#39;:3322, &#39;from&#39;:
            &#39;obj &#39;{ &#39;form&#39;:&#39;ID  &#39;, &#39;want&#39;:&#39;cSrc&#39;, &#39;seld&#39;:42, &#39;from&#39;:
                &#39;psn &#39;(&quot;iTunes&quot;) } } }
&amp;gt;
&lt;/pre&gt;

&lt;p&gt;I don’t know about you, but my brain lost interest at &lt;code&gt;&#39;want&#39;:&#39;cFlT&#39;&lt;/code&gt;.  But let’s not give up yet.  After invoking the powers of Scripting Bridge, we get&lt;/p&gt;

&lt;pre&gt;
&amp;lt;ITunesFileTrack @0x12a5c40: 
    ITunesFileTrack id 3592 
    of ITunesLibraryPlaylist id 3322 
    of ITunesSource id 42 
    of application &quot;iTunes&quot; (98938)&amp;gt;
&lt;/pre&gt;

&lt;p&gt;That gives me a warm fuzzy.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://cloud.github.com/downloads/threeve/RunRubyScriptAction/Run_Ruby_Script-1.0.zip&quot;&gt;Download&lt;/a&gt; the action now!  If you find it useful or come up with an interesting use for it in a workflow, drop me a line or leave a comment here.  I’ll be updating the repository periodically with improvements and examples.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Words of warning&lt;/strong&gt;: There may be some issues if you have both the Ruby and Python script actions installed.  Specifically, Automator &lt;em&gt;may&lt;/em&gt; crash if you try to add both actions into the same workflow.  I described the problem in brief in my article about &lt;a href=&quot;http://threeve.org/blog/2007/12/loadable-bundles-using-rubycocoa.html&quot;&gt;RubyCocoa bundles&lt;/a&gt;, and Bill Bumgarner described it in a bit more detail &lt;a href=&quot;http://www.friday.com/bbum/2007/11/25/can-ruby-python-an-objective-c-co-exist-in-a-single-application/&quot;&gt;on his blog&lt;/a&gt;.  The bug which causes this will hopefully be fixed in 10.5.2, but I may release a custom built version of RubyCocoa in the meantime if there is enough demand for it.&lt;/p&gt;


   </content>
   <author>
     <name>Jason Foreman</name>
     <uri>http://threeve.org/</uri>
   </author>
 </entry>
 
 <entry>
   <title>Loadable bundles using RubyCocoa</title>
   <link rel="alternate" href="http://threeve.org/blog/2007/12/loadable-bundles-using-rubycocoa.html" />
   <published>2007-12-29T00:00:00+00:00</published>
   <updated>2007-12-29T00:00:00+00:00</updated>
   <id>http://threeve.org/blog/2007/12/Loadable-bundles-using-RubyCocoa</id>
   <summary>With the arrival of Leopard, Apple seems to be making a push to support Cocoa and Mac OS X development in languages other than Objective-C. They ship both the PyObjC and RubyCocoa frameworks, and include several Xcode project templates for creating applications built upon these frameworks.  But what they forgot are the templates for building bundles that use these frameworks.
</summary>
   <content type="html" xml:lang="en" xml:base="http://threeve.org/">
       &lt;p&gt;With the arrival of Leopard, Apple seems to be making a push to support Cocoa and Mac OS X development in languages other than Objective-C.  They ship both the PyObjC and RubyCocoa frameworks, and include several Xcode project templates for creating applications built upon these frameworks.  But what they forgot are the templates for building bundles that use these frameworks.&lt;/p&gt;

&lt;h1 id=&quot;initializing-rubycocoa&quot;&gt;Initializing RubyCocoa&lt;/h1&gt;

&lt;p&gt;If we take a look at the boilerplate we get from creating a new application using the “Cocoa-Ruby Application” template, we see the following in &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;return RBApplicationMain(&quot;rb_main.rb&quot;, argc, argv);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This call performs any necessary initialization of the RubyCocoa bridge and then loads the specified Ruby script (rb_main.rb).  The Ruby script then sets up a few load paths and eventually ends with a call to &lt;code&gt;NSApplicationMain&lt;/code&gt;.  This works quite well for an application, but we can’t get away with this in a loadable bundle.  Thankfully the RubyCocoa authors are ahead of us, and have provided the &lt;code&gt;RBBundleInit&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RBBundleInit&lt;/code&gt; performs a similar function to &lt;code&gt;RBApplicationMain&lt;/code&gt; in that it performs any necessary RubyCocoa initialization and then loads a specified Ruby script.  The parameters for this function are slightly different, so let’s examine a call and break it down:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;RBBundleInit(&quot;ruby_bundle.rb&quot;, [MyRubyBundle class], nil);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first parameter is obviously the Ruby script to execute.  The second parameter is a class, which RubyCocoa uses to find the bundle containing the specified Ruby script.  This parameter may be &lt;code&gt;nil&lt;/code&gt;, in which case the first parameter would need to be an absolute path.  If the second parameter is not &lt;code&gt;nil&lt;/code&gt;, then RubyCocoa searches for the bundle containing the class, and appends the bundle’s resources path to the Ruby load path.  The third parameter is simply passed as an argument into the loaded script.&lt;/p&gt;

&lt;h1 id=&quot;caveats&quot;&gt;Caveats&lt;/h1&gt;

&lt;p&gt;There are currently (as of 10.5.1) a couple of issues that unfortunately might discourage anyone from actually shipping a bundle written with RubyCocoa, depending on what application might be loading the bundle.&lt;/p&gt;

&lt;p&gt;In the currently shipping versions of PyObjC and RubyCocoa, classes are created in the Objective-C runtime when either framework loads a bridge support file which contains inline functions.  Because of this, only one framework can safely be loaded into an application at any time.  &lt;a href=&quot;http://www.friday.com/bbum&quot;&gt;Bill Bumgarner&lt;/a&gt; has written a great &lt;a href=&quot;http://www.friday.com/bbum/2007/11/25/can-ruby-python-an-objective-c-co-exist-in-a-single-application/&quot;&gt;example application&lt;/a&gt; which combines both PyObjC and RubyCocoa in a single application, and describes this problem in greater length.  &lt;/p&gt;

&lt;p&gt;This means that writing bundles which are loaded into applications for which you as the developer do not have complete control is unpractical.  This includes System PreferencePanes, Automator Actions, Address Book plugins, and more.&lt;/p&gt;

&lt;p&gt;The good news is that this has already been addressed in the trunk of the PyObjC Subversion repository.  And I hope that it will also be addressed in RubyCocoa the same way.  With any luck, these fixes will be included in the 10.5.2 update (anyone with the seeded version who can verify?).&lt;/p&gt;

&lt;h1 id=&quot;rubycocoa-templates&quot;&gt;RubyCocoa templates&lt;/h1&gt;

&lt;p&gt;In anticipation of the aforementioned bug being fixed, I’ve started preparing some Xcode project templates for loadable bundle projects built with RubyCocoa.  At the time of this writing, templates exist for both a generic loadable RubyCocoa bundle and a RubyCocoa PreferencePane.  Eventually I may add a couple other templates such as Frameworks or Address Book plugins.&lt;/p&gt;

&lt;p&gt;The templates can be found in my &lt;a href=&quot;http://threeve.googlecode.com/svn/trunk/XcodeProjectTemplates/&quot;&gt;Google Code repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install these templates, simply copy them to &lt;code&gt;~/Library/Application Support/Developer/3.0/Xcode&lt;/code&gt;.&lt;/p&gt;


   </content>
   <author>
     <name>Jason Foreman</name>
     <uri>http://threeve.org/</uri>
   </author>
 </entry>
 
</feed>

