{"id":2006,"date":"2013-11-01T09:18:18","date_gmt":"2013-10-31T21:18:18","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2006"},"modified":"2013-11-01T09:18:18","modified_gmt":"2013-10-31T21:18:18","slug":"blowing-smoke","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2006\/","title":{"rendered":"Blowing Smoke&#8230;"},"content":{"rendered":"<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\"> 7<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>To alleviate the grind of polishing and sanitising my code (and, let&#8217;s be honest, just plain &#8216;fixing&#8217; it in some cases) ready for release, I have re-kindled my participation on Stack Overflow.  In a happy confluence yesterday <a href=\"http:\/\/stackoverflow.com\/questions\/19686191\/string-encoding-error-from-delphi-7-to-xe\">a question came up<\/a> which allowed me to exercise one of the libraries that I&#8217;m preparing to release: <strong>Smoketest<\/strong>.<\/p>\n<p><!--more--><\/p>\n<p>The question wasn&#8217;t a complicated one.  It was an obvious case of using a pre-Unicode library with Unicode versions of Delphi and the problems that arise.<\/p>\n<p>The excuse to use my <strong>Smoketest<\/strong> library came when the OP complained that my suggested solution had not worked.  I admit, I had not tested my suggestion at the time that I made it.  The approach suggested was simple enough and should have worked.  But I decided to dog-food the suggestion by downloading the library in question and putting together a <strong>Smoketest<\/strong> for it.<\/p>\n<p>The approach was simply this:<\/p>\n<ul>\n<li>Build a test using the original library.<\/li>\n<li>Run the test in Delphi 2006 and 2010<sup>*1<\/sup><\/li>\n<li>Apply my suggested changes to the AES library<\/li>\n<li>Re-run the tests<\/li>\n<\/ul>\n<p><sup>*1<\/sup> &#8211; <em> The OP was using Delphi 7 and XE but the principle was the same: different, pre-Unicode and Unicode versions of Delphi.<\/em><\/p>\n<p>So, to begin at the beginning&#8230;<\/p>\n<h2>Making the Case for Testing<\/h2>\n<p>Writing test cases with <strong>Smoketest<\/strong> is very simple.<\/p>\n<p>I use the <strong>Deltics.Smoketest<\/strong> unit, derive at least one <strong>Test Case<\/strong> class with <strong>one or more test methods<\/strong>, add that test case to the <strong>Smoketest<\/strong> and then declare my test <strong>ready<\/strong> to run.<\/p>\n<p>In this case since it is a very simple test scenario everything can go in the <strong>DPR<\/strong> so I also use the two units I am actively testing and implement three separate methods, one for each type of string I am testing:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nprogram TestAES;\r\n\r\nuses\r\n  ElAES,\r\n  AES,\r\n  Deltics.Smoketest;\r\n\r\ntype\r\n  TTest = class(TTestCase)\r\n    procedure WithANSIString;\r\n    procedure WithString;\r\n    procedure WithWideString;\r\n  end;\r\n\r\n  procedure TTest.WithANSIString;\r\n  begin\r\n  end;\r\n\r\n  procedure TTest.WithString;\r\n  begin\r\n  end;\r\n\r\n  procedure TTest.WithWideString;\r\n  begin\r\n  end;\r\n\r\n\r\nbegin\r\n  Smoketest.Add(&#x5B;TTest]);\r\n  Smoketest.Ready;\r\nend.\r\n<\/pre>\n<p>Running this I get a ready to run test case:<\/p>\n<figure id=\"attachment_2007\" aria-describedby=\"caption-attachment-2007\" style=\"width: 421px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.33.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.33.png?resize=421%2C417&#038;ssl=1\" alt=\"Ready to Run\" width=\"421\" height=\"417\" class=\"size-full wp-image-2007\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.33.png?w=421&amp;ssl=1 421w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.33.png?resize=150%2C150&amp;ssl=1 150w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.33.png?resize=300%2C297&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.33.png?resize=302%2C300&amp;ssl=1 302w\" sizes=\"auto, (max-width: 421px) 100vw, 421px\" \/><\/a><figcaption id=\"caption-attachment-2007\" class=\"wp-caption-text\">Ready to Run<\/figcaption><\/figure>\n<p>As you can see, <strong>Smoketest<\/strong> tries to prettify names in the suite for human consumption (there are hooks provided to enable the test author to provide specific names for certain things if desired).<\/p>\n<p>We can also see that tests have not been run at this point, although if I specify a command-line start parameter (<strong>-r<\/strong>) I can have all or just some of my tests run automatically at startup.  I&#8217;ll add this to save some time when I re-run these tests and at this point running the tests yields the following:<\/p>\n<figure id=\"attachment_2008\" aria-describedby=\"caption-attachment-2008\" style=\"width: 314px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.53.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.53.png?resize=314%2C176&#038;ssl=1\" alt=\"Work in Progress\" width=\"314\" height=\"176\" class=\"size-full wp-image-2008\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.53.png?w=314&amp;ssl=1 314w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.53.png?resize=300%2C168&amp;ssl=1 300w\" sizes=\"auto, (max-width: 314px) 100vw, 314px\" \/><\/a><figcaption id=\"caption-attachment-2008\" class=\"wp-caption-text\">Work in Progress<\/figcaption><\/figure>\n<p>The little &#8220;men at work&#8221; icons indicate test methods that produce no output and have no result.  In other words, tests still to be written.  But we knew that.  \ud83d\ude42<\/p>\n<p>So now I write those tests.  First, the <strong>WithANSIString<\/strong> test.<\/p>\n<h2>Vectors and Inspections<\/h2>\n<p>Bear in mind that I am not endeavouring to create a comprehensive test suite for the <em>entire<\/em> AES library, only to exercise enough of it to validate that a suggested &#8220;<em>ANSIfication<\/em>&#8221; approach is valid.  One simple function from the library should suffice: <strong>EncryptString()<\/strong><\/p>\n<p>At this point I don&#8217;t have a test vector for this function.  That is, I don&#8217;t know what the expected result is.<\/p>\n<p>Ordinarily in such cases I would track down some suitable vectors to incorporate in my test, but on this occasion I will assume that when working with ANSI Strings the library provides correct results so initially I will make my <strong>WithANSIString<\/strong> test a simple inspection test so that I can see what the result is.<\/p>\n<p>I will use constants for the <strong>KEY<\/strong> and the <strong>VALUE<\/strong> provided to the EncryptString() function that I will use for this test, entirely arbitrary values.<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  const\r\n    KEY    = 'jalphi';\r\n    VALUE  = 'password';\r\n    RESULT = 'TBA'; \r\n\r\n  procedure TTest.WithANSIString;\r\n  var\r\n    s: ANSIString;\r\n  begin\r\n    s := VALUE;\r\n    Inspect('sample').Value(EncryptString(s, KEY));\r\n  end;\r\n<\/pre>\n<p>A technique I have used throughout <strong>Smoketest<\/strong> is to chain methods that return interfaces to allow tests to be written using near natural language.  It&#8217;s similar to so-called &#8220;fluent&#8221; API&#8217;s, but on this occasion intended to approximate a &#8220;domain specific language&#8221;, guiding the correct construction of tests rather than simply offering convenience.<\/p>\n<p>In the case of an inspection, we start with an <strong>Inspect<\/strong> method which (optionally) takes a parameter providing a descriptive label for the value being inspected.  The <strong>Inspect<\/strong> method yields an <strong>IInspector<\/strong> interface which provides a host of overloaded methods for capturing a value to be inspected.<\/p>\n<p>If you wish, you can create your own custom Inspector interfaces (and implementations) and once registered with <strong>Smoketest<\/strong> you can obtain them using the &#8220;as&#8221; operator on the built-in inspector:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n(Inspect('exotic value') as IMyExoticInspector).Value(MyExotica);\r\n<\/pre>\n<p>But this is advanced <strong>Smoketest<\/strong> voodoo and not necessary for this simple scenario.  \ud83d\ude42<\/p>\n<p>Running the test case in this state now provides a useful result in the output of the first test.  The remaining two tests are still &#8220;under construction&#8221;.<\/p>\n<figure id=\"attachment_2009\" aria-describedby=\"caption-attachment-2009\" style=\"width: 626px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.12.12.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.12.12.png?resize=626%2C97&#038;ssl=1\" alt=\"What&#039;s the Vector, Victor ?\" width=\"626\" height=\"97\" class=\"size-full wp-image-2009\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.12.12.png?w=626&amp;ssl=1 626w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.12.12.png?resize=300%2C46&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.12.12.png?resize=500%2C77&amp;ssl=1 500w\" sizes=\"auto, (max-width: 626px) 100vw, 626px\" \/><\/a><figcaption id=\"caption-attachment-2009\" class=\"wp-caption-text\">What&#8217;s the Vector, Victor ?<\/figcaption><\/figure>\n<p>We now have a test result to work with.<\/p>\n<h2>Great Expectations<\/h2>\n<p>I can now revise my <strong>WithANSIString<\/strong> test so that it is an actual test.  The further two test methods are almost exact duplicates of this test.  Only the declared type of our <strong>s<\/strong> string  changes in each case, the input to the <strong>EncryptString()<\/strong> function:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  const\r\n    KEY     = 'jalphi';\r\n    VALUE   = 'password';\r\n    RESULT  = '0800000000000000DEC4FFE94BD6F4396BCAD729D0C905A5';\r\n\r\n  procedure TTest.WithANSIString;\r\n  var\r\n    s: ANSIString;\r\n  begin\r\n    s := VALUE;\r\n    Test('result').Expect(EncryptString(s, KEY)).Equals(RESULT);\r\n  end;\r\n\r\n  procedure TTest.WithNativeString;\r\n  var\r\n    s: String;\r\n  begin\r\n    s := VALUE;\r\n    Test('result').Expect(EncryptString(s, KEY)).Equals(RESULT);\r\n  end;\r\n\r\n  procedure TTest.WithWideString;\r\n  var\r\n    s: WideString;\r\n  begin\r\n    s := VALUE;\r\n    Test('result').Expect(EncryptString(s, KEY)).Equals(RESULT);\r\n  end;\r\n<\/pre>\n<p>Writing tests is similar to writing inspections.  Instead of an <strong>Inspect<\/strong> method, for a test we start with the <strong>Test<\/strong> method.  Rather than an <strong>IInspector<\/strong>, this yields an <strong>ITest<\/strong> and instead of simply identifying a <strong>Value()<\/strong> method we now begin our test by identifying something about which we have an expectation that is to be met, using the <strong>Expect()<\/strong> method.<\/p>\n<p>The <strong>Expect()<\/strong> method is overloaded similar to the <strong>Value()<\/strong> method on an Inspector, but in this case there is more that has to be identified.  Each overloaded version of <strong>Expect()<\/strong> returns an <strong>expectation interface<\/strong> specific to the type of value involved, providing the expectations that might apply to a value of that type.<\/p>\n<p>So for a string value we have the following expectations available to us:<\/p>\n<figure id=\"attachment_2010\" aria-describedby=\"caption-attachment-2010\" style=\"width: 378px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.30.55.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.30.55.png?resize=378%2C195&#038;ssl=1\" alt=\"Great Expectations\" width=\"378\" height=\"195\" class=\"size-full wp-image-2010\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.30.55.png?w=378&amp;ssl=1 378w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.30.55.png?resize=300%2C154&amp;ssl=1 300w\" sizes=\"auto, (max-width: 378px) 100vw, 378px\" \/><\/a><figcaption id=\"caption-attachment-2010\" class=\"wp-caption-text\">Great Expectations<\/figcaption><\/figure>\n<p>The expectations on an integer value would be different.  If a value has qualities that might be better tested with some other expectation these expectations can be exposed as part of the expectation interface.<\/p>\n<p>In the case of strings for example, if we were concerned with the specific length of a string we can write:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  Test('result length').Expect(s).Length.NotLessThan(100);\r\n<\/pre>\n<p>We started with <strong>IStringExpectations<\/strong>, but the <strong>Length<\/strong> of the string is exposed in those expectations itself as a set of <strong>IIntegerExpectations<\/strong>.  We could of course have started with an integer expection more directly by using the Length() function on the string to start with:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  \/\/ These tests are functionally identical:\r\n\r\n  Test('result length').Expect(Length(s)).NotLessThan(100);\r\n  Test('result length').Expect(s).Length.NotLessThan(100);\r\n<\/pre>\n<p>But you at least have the choice. \ud83d\ude42<\/p>\n<p>Similarly, but perhaps more usefully, the default behaviour of string expectations is to act case sensitively.  If we have expectations which are not dependent upon case then we can obtain <strong>CaseInsensitive<\/strong> expectations.<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  Test('result length').Expect(s).CaseInsensitive.Equals(RESULT)\r\n<\/pre>\n<p>You may also have noticed that expectations in turn yield an <strong>IEvaluation<\/strong> interface.  This is used in the self-tests of <strong>Smoketest<\/strong> itself to identify tests that are expected to fail (i.e. where expectations not bein met are actually the hoped for result!) but can also be used to abort a test case.  So for example you can append &#8220;<strong>IsRequired<\/strong>&#8221; to an expectation and if the test fails then the test method will halt at that point.  If you append &#8220;<strong>IsCritical<\/strong>&#8221; then the <em>entire test case<\/em> of which the method is a part will halt.<\/p>\n<p>You would use this when testing that you have successfully obtained an object reference that is subsequently used in a test:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  widget := WidgetManager.LoadWidget;\r\n  Test('Got a widget').Expect(widget).IsAssigned.IsRequired;\r\n\r\n  widget.FlipTheDoodah;  \/\/ &lt;&lt; It's OK, we won't reach this point if we didn't get a widget\r\n<\/pre>\n<p>Again, all of this is far more advanced stuff than we need in this simple case.  \ud83d\ude42<\/p>\n<p>Back to our simple test and all three test methods have passed (YAY!) and we can get a summary of the test outcomes for each test.  In this case there is just one outcome in each test, as expected:<\/p>\n<figure id=\"attachment_2011\" aria-describedby=\"caption-attachment-2011\" style=\"width: 695px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.25.58.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.25.58.png?resize=640%2C114&#038;ssl=1\" alt=\"Sweet Smell of Success\" width=\"640\" height=\"114\" class=\"size-full wp-image-2011\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.25.58.png?w=695&amp;ssl=1 695w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.25.58.png?resize=300%2C53&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.25.58.png?resize=500%2C89&amp;ssl=1 500w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><figcaption id=\"caption-attachment-2011\" class=\"wp-caption-text\">Sweet Smell of Success<\/figcaption><\/figure>\n<h2>Expected Failure<\/h2>\n<p>Opening the test project in Delphi 2010, rebuilding and running we also get the expected result, but in this case that is failure.  Complete and abject.<\/p>\n<figure id=\"attachment_2015\" aria-describedby=\"caption-attachment-2015\" style=\"width: 703px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-10.02.50.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-10.02.50.png?resize=640%2C100&#038;ssl=1\" alt=\"Complete and Utter Failure\" width=\"640\" height=\"100\" class=\"size-full wp-image-2015\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-10.02.50.png?w=703&amp;ssl=1 703w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-10.02.50.png?resize=300%2C46&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-10.02.50.png?resize=500%2C78&amp;ssl=1 500w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><figcaption id=\"caption-attachment-2015\" class=\"wp-caption-text\">Complete and Utter Failure<\/figcaption><\/figure>\n<p>But this is good.  This confirms what the OP on StackOverflow originally reported &#8211; that the AES library was giving different, incorrect results when compiled on Unicode versions of Delphi.<\/p>\n<p>So I can now apply the change I suggested to the AES library and see if the OP is also right in saying that this didn&#8217;t work.<\/p>\n<figure id=\"attachment_2011\" aria-describedby=\"caption-attachment-2011\" style=\"width: 695px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.25.58.png?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.25.58.png?resize=640%2C114&#038;ssl=1\" alt=\"Sweet Smell of Success\" width=\"640\" height=\"114\" class=\"size-full wp-image-2011\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.25.58.png?w=695&amp;ssl=1 695w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.25.58.png?resize=300%2C53&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.25.58.png?resize=500%2C89&amp;ssl=1 500w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><figcaption id=\"caption-attachment-2011\" class=\"wp-caption-text\">Sweet Smell of Success<\/figcaption><\/figure>\n<p>After doing that, rebuilding in Delphi 2010 and re-running the tests I see straight away that if the OP is still seeing problems then there is something else involved in his case since the ANSIfication of that library results in consistent correct results not only in Delphi 2010 also still in Delphi 2006.<\/p>\n<p>Huzzah!<\/p>\n<p>I can only hope that the StackOverflow questioner eventually reaches a similarly happy conclusion.  \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\"> 7<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span>To alleviate the grind of polishing and sanitising my code (and, let&#8217;s be honest, just plain &#8216;fixing&#8217; it in some cases) ready for release, I have re-kindled my participation on Stack Overflow. In a happy confluence yesterday a question came up which allowed me to exercise one of the libraries that I&#8217;m preparing to release: [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[4,48],"tags":[255,292,51,22],"class_list":["post-2006","post","type-post","status-publish","format-standard","hentry","category-delphi","category-testing","tag-ansification","tag-delphi","tag-smoketest","tag-unicode"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-wm","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2006","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=2006"}],"version-history":[{"count":5,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2006\/revisions"}],"predecessor-version":[{"id":2017,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2006\/revisions\/2017"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2006"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2006"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2006"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}