{"id":1251,"date":"2014-01-20T13:00:19","date_gmt":"2014-01-20T13:00:19","guid":{"rendered":"http:\/\/writeasync.net\/?p=1251"},"modified":"2014-01-15T08:34:04","modified_gmt":"2014-01-15T08:34:04","slug":"async-process-functionality-take-2","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=1251","title":{"rendered":"Async Process functionality &#8211; take 2"},"content":{"rendered":"<p>In my <a href=\"http:\/\/writeasync.net\/?p=1141\" title=\"Providing async functionality for System.Diagnostics.Process\">previous post<\/a>, I showed a simple way to add a few Task-based async methods for dealing with <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.diagnostics.process(v=vs.110).aspx\"><code>System.Diagnostics.Process<\/code><\/a>. The code I showed, while simple, has some flaws:<\/p>\n<ol>\n<li><strong>It has low cohesion.<\/strong> In violation of the <a href=\"http:\/\/www.objectmentor.com\/resources\/articles\/srp.pdf\">single responsibility principle<\/a>, the <code>ProcessEx<\/code> class is doing at least three things: acting as a factory for starting processes, watching for process exit events, and providing access to a more full-featured underlying <code>Process<\/code> object. (The relatively weak &#8220;<code>Ex<\/code>&#8221; name is a good clue that this class doesn&#8217;t have a clear purpose.)<\/li>\n<li><strong>The wait operation cannot be canceled.<\/strong> Perhaps a somewhat minor point, but it is a good idea for any &#8220;<code>Wait<em>Xxx<\/em>Async<\/code>&#8221; API to support <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd997396(v=vs.110).aspx\">cancellation<\/a>.\n<li><strong>It is not testable.<\/strong> I can&#8217;t write <a href=\"http:\/\/www.artima.com\/weblogs\/viewpost.jsp?thread=126923\">&#8220;pure&#8221; unit tests<\/a> against a class that requires me to spawn a real, live external process and waste time waiting for a real, live exit condition.<\/li>\n<\/ol>\n<p>The last point (testability) was what initially bothered me. I was able to convince myself of the correctness of the code via integration testing, but since <a href=\"http:\/\/bradwilson.typepad.com\/blog\/2009\/04\/its-not-tdd-its-design-by-example.html\">TDD is primarily about design<\/a>, I couldn&#8217;t vouch for the quality of the implementation. And when I looked more closely, some of problems in the first point (cohesion) jumped out at me.<\/p>\n<p>So&#8230; I dutifully tried again! You can see the evolution of what came to be known as <code>ProcessExitWatcher<\/code> in the <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commits\/master\/projects\/ProcessSample\">ProcessSample project on GitHub<\/a>.<\/p>\n<p>I&#8217;ll highlight some interesting checkpoints of this effort.<\/p>\n<p>In the beginning, I started with the same weak name (<code>ProcessEx<\/code>) and a single <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/0fbf583250ba0df2f457ee0e97c08749d6dd9b1d\">unit test to demonstrate the constructor behavior<\/a> (subscribing to an <code>Exited<\/code> event). At that point, I actually needed something to provide this event &#8212; hence my <a href=\"http:\/\/xunitpatterns.com\/Test%20Stub.html\">test stub<\/a> <code>ProcessStub<\/code> implementing <code>IProcess<\/code>.<\/p>\n<p>A few steps later, I began to implement the <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/535e3d05d6e695cc175bc269d2840d6aca58e18d\"><code>WaitForExitAsync<\/code> functionality<\/a>. Eventually I got to the point where I knew there was a race condition which could possibly complete the exited task twice. However, instead of trying something crazy like resorting to a nondeterministic, multithreaded integration test, I wrote a <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/82f3621656e1153150123799a8f63b4c97b92f2c\">unit test to expose the race<\/a> and solved it. In this case, I was able to extend my test stub and take advantage of code that I could inject via an <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/cc713648.aspx\">event <code>add<\/code> accessor<\/a> to sneakily raise the <code>Exited<\/code> event.<\/p>\n<p>Shortly after I <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/3b72ffaf195d7efa51363f44eb65952d2829df05\">added an <code>Inner<\/code> property<\/a> I started to have some misgivings. Why should this class need to provide outside access to the full process object? After thinking on it a bit, I recognized that choosing the more specific class name <code>ProcessExitWatcher<\/code> and making appropriate <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/84585e0e460305b4b9e5d2c61d8ce500926176d6\">cascading naming changes<\/a> would provide better clarity.<\/p>\n<p>Eventually I had to write the <em>real<\/em> code that wired up my interface with a <code>System.Diagnostics.Process<\/code>. This ended up being as mundane as you could imagine, a <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/a1fcbd0ae3500081cb62e9dd9b97315fcd54ede9\">pass-through implementation<\/a>. That&#8217;s a good thing as it means no unit tests need to be written.<\/p>\n<p>I wrote a few more unit tests but still sensed a <a href=\"http:\/\/martinfowler.com\/bliki\/CodeSmell.html\">code smell<\/a> in returning an <code>IProcessExit<\/code> instance. The most obvious problem was the naming; a property called <code>Inner<\/code> has encapsulation-breaking connotations. But the practical issue was that a caller would now have access to surface area that should not be needed or used (e.g. one could now set <code>EnableRaisingEvents<\/code> to <code>false<\/code> and mess things up). It was now apparent that the interface needed to be broken up (<a href=\"http:\/\/www.objectmentor.com\/resources\/articles\/isp.pdf\">interface segregation<\/a>). Thus, I created <a href=\"https:\/\/github.com\/brian-dot-net\/writeasync\/commit\/d7b9ba94f77c086c0e27af036d447db24ab2c9a5\"><code>IProcessExitEvents<\/code> and <code>IProcessExitStatus<\/code><\/a> to separate concerns between the watcher and its clients. After all, the (conveniently read-only) status of the process is the only thing the caller should care about.<\/p>\n<p>By the end of this, I had a lean and mean <code>ProcessExitWatcher<\/code> and a clean, testable design I felt good about. Here it is in action:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nint exitCode;\r\nDateTime exitTime;\r\n\r\nusing (Process process = await Task.Factory.StartNew(() =&gt; Process.Start(&quot;notepad.exe&quot;)))\r\nusing (ProcessExitWatcher watcher = new ProcessExitWatcher(new ProcessExit(process)))\r\n{\r\n    Console.WriteLine(&quot;Waiting for Notepad to exit...&quot;);\r\n    await watcher.WaitForExitAsync(token);\r\n    exitCode = watcher.Status.ExitCode;\r\n    exitTime = watcher.Status.ExitTime;\r\n}\r\n\r\nConsole.WriteLine(&quot;Done, exited with code {0} at {1}.&quot;, exitCode, exitTime);\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>In my previous post, I showed a simple way to add a few Task-based async methods for dealing with System.Diagnostics.Process. The code I showed, while simple, has some flaws: It has low cohesion. In violation of the single responsibility principle,&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21,91,41],"tags":[],"class_list":["post-1251","post","type-post","status-publish","format-standard","hentry","category-async","category-design","category-tdd"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/1251","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1251"}],"version-history":[{"count":0,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/1251\/revisions"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1251"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1251"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1251"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}