{"id":1083,"date":"2017-10-16T17:43:35","date_gmt":"2017-10-16T21:43:35","guid":{"rendered":"https:\/\/blogs.mathworks.com\/developer\/?p=1083"},"modified":"2017-10-17T10:18:57","modified_gmt":"2017-10-17T14:18:57","slug":"cobertura-code-coverage","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/developer\/2017\/10\/16\/cobertura-code-coverage\/","title":{"rendered":"We&#8217;ve Got You Covered"},"content":{"rendered":"<div class=\"content\"><p>Coverage. It's all good. Health care coverage is good. Cell phone coverage is good. Even cloud coverage can provide a nice break from the intensity of the sun's rays sometimes. It should come then as no surprise that good code coverage also gives us that nice warm fuzzy feeling, like a nice warm drink on a cold day.<\/p><p>...and often for good reason. Code coverage can help us see areas of our code that are not exercised in some form or another, and can be very helpful, in a <i>good-friend-that-can-be-brutally-honest-with-you<\/i> sort of way. It can point out where we really need to add more testing. Once we react to our good friend's advice and add better testing, we are all the better.<\/p><p>A quick aside to add some caution is in order. We (all) should remember that code coverage does not as much show what code <i>is<\/i> covered as which code <i>is not<\/i> covered. This is because seeing that a piece of code is covered says nothing about how it was covered, whether it was covered by a test specifically targeting that code or some incidental coverage, and there is no information as to whether the coverage verified the correct result in any way. Takeaway, use coverage to see areas of the code are <b><i>not<\/i><\/b> tested. Don't fall into the trap and draw conclusions that covered code is bug free or is even remotely tested sufficiently.<\/p><p>That said, it is a great tool if you have the right mindset about it, and when combined with testing and continuous integration it can help us ensure our testing is up to snuff.<\/p><p>Good news! This just got a lot easier. We have had the <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/matlab.unittest.plugins.codecoverageplugin-class.html\">CodeCoveragePlugin<\/a> for the unit test framework for some time, but it has produced a report that was more designed to be used in interactive MATLAB environments rather than CI system workflows. Now we have the option to produce our coverage into the <a href=\"http:\/\/cobertura.github.io\/cobertura\/\">Cobertura<\/a> format and benefit from things like the <a href=\"https:\/\/wiki.jenkins.io\/display\/JENKINS\/Cobertura+Plugin\">Cobertura plugin for Jenkins<\/a>.<\/p><p>Let's continue with our (matrix'd) build from <a href=\"https:\/\/blogs.mathworks.com\/developer\/2017\/09\/26\/the-build-matrix-laboratory\/\">last time<\/a>, but add some coverage.<\/p><pre class=\"language-matlab\">\r\nimport(<span class=\"string\">'matlab.unittest.plugins.CodeCoveragePlugin'<\/span>);\r\nimport(<span class=\"string\">'matlab.unittest.plugins.codecoverage.CoberturaFormat'<\/span>);\r\n\r\ncoverageFile = fullfile(resultsDir, <span class=\"string\">'coverage.xml'<\/span>);\r\nrunner.addPlugin(CodeCoveragePlugin.forFolder(src,<span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'Producing'<\/span>, CoberturaFormat(coverageFile)));\r\n\r\n<\/pre><p>Easy as pie. Now let's see what it looks like in our CI build:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017InitialFailure.png\" alt=\"\"> <\/p><p>Oops! I forgot about the fact that other releases we are building against don't have the plugin:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017OldReleaseErrorMessage.png\" alt=\"\"> <\/p><p>No biggie, we'll just need to wrap the installation of our plugin into the TestRunner with a version check. Let's put it into a function:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> addCoberturaCoverageIfPossible(runner, src, coverageFile)\r\n\r\n<span class=\"keyword\">if<\/span> ~verLessThan(<span class=\"string\">'matlab'<\/span>,<span class=\"string\">'9.3'<\/span>)\r\n    import(<span class=\"string\">'matlab.unittest.plugins.CodeCoveragePlugin'<\/span>);\r\n    import(<span class=\"string\">'matlab.unittest.plugins.codecoverage.CoberturaFormat'<\/span>);\r\n    \r\n    runner.addPlugin(CodeCoveragePlugin.forFolder(src,<span class=\"keyword\">...<\/span>\r\n        <span class=\"string\">'Producing'<\/span>, CoberturaFormat(coverageFile)));\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>and we can just call that function from our test running script:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">try<\/span>\r\n    import(<span class=\"string\">'matlab.unittest.TestRunner'<\/span>);\r\n    import(<span class=\"string\">'matlab.unittest.plugins.XMLPlugin'<\/span>);\r\n    import(<span class=\"string\">'matlab.unittest.plugins.ToFile'<\/span>);\r\n\r\n    \r\n    ws = getenv(<span class=\"string\">'WORKSPACE'<\/span>);\r\n    \r\n    src = fullfile(ws, <span class=\"string\">'source'<\/span>);\r\n    addpath(src);\r\n    \r\n    tests = fullfile(ws, <span class=\"string\">'tests'<\/span>);\r\n    suite = testsuite(tests);\r\n\r\n    <span class=\"comment\">% Create and configure the runner<\/span>\r\n    runner = TestRunner.withTextOutput(<span class=\"string\">'Verbosity'<\/span>,3);\r\n\r\n    <span class=\"comment\">% Add the TAP plugin<\/span>\r\n    resultsDir = fullfile(ws, <span class=\"string\">'testresults'<\/span>);\r\n    mkdir(resultsDir);\r\n    \r\n    resultsFile = fullfile(resultsDir, <span class=\"string\">'testResults.xml'<\/span>);\r\n    runner.addPlugin(XMLPlugin.producingJUnitFormat(resultsFile));\r\n   \r\n    coverageFile = fullfile(resultsDir, <span class=\"string\">'coverage.xml'<\/span>);\r\n    addCoberturaCoverageIfPossible(runner, src, coverageFile);\r\n    \r\n    results = runner.run(suite) \r\n<span class=\"keyword\">catch<\/span> e\r\n    disp(getReport(e,<span class=\"string\">'extended'<\/span>));\r\n    exit(1);\r\n<span class=\"keyword\">end<\/span>\r\nquit(<span class=\"string\">'force'<\/span>);\r\n\r\n<\/pre><p>Also, the MATLAB code is just one half of the equation. We also need to ensure the Jenkins Cobertura plugin is installed and that the build is configured to track coverage. Here we simply add it as a post-build action and we configure it to look in our results folder, looking for files named <tt>coverage.xml<\/tt>.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017CoverageSettings.png\" alt=\"\"> <\/p><p>Note that since we are running testing across multiple releases, and the earlier releases don't have the plugin, we also need to tell the plugin not to fail the build if there are no coverage files present. If we don't then the builds for all the earlier releases will fail because no Cobertura xml file will be found.<\/p><p>OK, now we are in business. The build now passes across all releases:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017AllPassing.png\" alt=\"\"> <\/p><p>However, when we navigate to the R2017b build, we can see the coverage report:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017CoverageFrontPage.png\" alt=\"\"> <\/p><p>Digging in deeper we can then see where we need to add a test:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017CoverageDetail.png\" alt=\"\"> <\/p><p>The build now shows very clearly that we forgot to add a negative test. Let's remedy that! Here is the test I want to write:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> testInvalidInput(testCase)\r\n<span class=\"comment\">% Test to ensure we fail gracefully with bogus input<\/span>\r\n\r\ntestCase.verifyError(@() simulateSystem(<span class=\"string\">'bunk'<\/span>), <span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'simulateSystem:InvalidDesign:ShouldBeStruct'<\/span>);\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>However, looking again at our source, I notice something horrible, we are not testable! As a reminder, here is the code for the simulator:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> [x, t] = simulateSystem\r\n\r\nspringMassDamperDesign; <span class=\"comment\">% Create design variable.<\/span>\r\n\r\n<span class=\"keyword\">if<\/span> ~isstruct(design) || ~all(isfield(design,{<span class=\"string\">'c'<\/span>,<span class=\"string\">'k'<\/span>}))\r\n    error(<span class=\"string\">'simulateSystem:InvalidDesign:ShouldBeStruct'<\/span>, <span class=\"keyword\">...<\/span>\r\n        <span class=\"string\">'The design should be a structure with fields \"c\" and \"k\"'<\/span>);\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"comment\">% Design variables<\/span>\r\nc = design.c;\r\nk = design.k;\r\n\r\n<span class=\"comment\">% Constant variables<\/span>\r\nz0 = [-0.1; 0];  <span class=\"comment\">% Initial Position and Velocity<\/span>\r\nm = 1500;        <span class=\"comment\">% Mass<\/span>\r\n\r\nodefun = @(t,z) [0 1; -k\/m -c\/m]*z;\r\n[t, z] = ode45(odefun, [0, 3], z0);\r\n\r\n<span class=\"comment\">% The first column is the position (displacement from equilibrium)<\/span>\r\nx = z(:, 1);\r\n\r\n<\/pre><p>...and for the design:<\/p><pre class=\"language-matlab\">\r\nm = 1500; <span class=\"comment\">% Need to know the mass to determine critical damping<\/span>\r\n\r\ndesign.k = 5e6;                  <span class=\"comment\">% Spring Constant<\/span>\r\ndesign.c = 2*m*sqrt(design.k\/m); <span class=\"comment\">% Damping Coefficient to be critically damped<\/span>\r\n\r\nclear <span class=\"string\">m<\/span>;\r\n\r\n<\/pre><p>Yikes! There is no way to get a bad value into the code in order to test it. As such currently the code is dead. If we want we could just remove the error condition because we know that the script produces the right format of the design. However, that would miss the point. The whole reason why we separated out the design from the simulation script is so that we could tweak the design and explore. We really want the <tt>simulateSystem<\/tt> function to be reusable across many different designs, so we should parameterize it proper. Then the software is much more testable, which is synonymous with flexible. To do this all we need to do is make the design script a function and accept it as an input to the simulation function:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> [x, t] = simulateSystem(design)\r\n\r\n<span class=\"keyword\">if<\/span> ~isstruct(design) || ~all(isfield(design,{<span class=\"string\">'c'<\/span>,<span class=\"string\">'k'<\/span>}))\r\n    error(<span class=\"string\">'simulateSystem:InvalidDesign:ShouldBeStruct'<\/span>, <span class=\"keyword\">...<\/span>\r\n        <span class=\"string\">'The design should be a structure with fields \"c\" and \"k\"'<\/span>);\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"comment\">% Design variables<\/span>\r\nc = design.c;\r\nk = design.k;\r\n\r\n<span class=\"comment\">% Constant variables<\/span>\r\nz0 = [-0.1; 0];  <span class=\"comment\">% Initial Position and Velocity<\/span>\r\nm = 1500;        <span class=\"comment\">% Mass<\/span>\r\n\r\nodefun = @(t,z) [0 1; -k\/m -c\/m]*z;\r\n[t, z] = ode45(odefun, [0, 3], z0);\r\n\r\n<span class=\"comment\">% The first column is the position (displacement from equilibrium)<\/span>\r\nx = z(:, 1);\r\n\r\n\r\n\r\n<span class=\"keyword\">function<\/span> design = springMassDamperDesign\r\nm = 1500; <span class=\"comment\">% Need to know the mass to determine critical damping<\/span>\r\n\r\ndesign.k = 5e6;                  <span class=\"comment\">% Spring Constant<\/span>\r\ndesign.c = 2*m*sqrt(design.k\/m); <span class=\"comment\">% Damping Coefficient to be critically damped<\/span>\r\n\r\n<\/pre><p>Make a few test updates:<\/p><pre class=\"language-matlab\">\r\n<span class=\"keyword\">function<\/span> tests = designTest\r\ntests = functiontests(localfunctions); \r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">function<\/span> testSettlingTime(testCase) \r\n<span class=\"comment\">%%Test that the system settles to within 0.001 of zero under 2 seconds.<\/span>\r\n\r\n[position, time] = simulateSystem(springMassDamperDesign); \r\n\r\npositionAfterSettling = position(time &gt; .002);\r\n\r\n<span class=\"comment\">%For this example, verify the first value after the settling time.<\/span>\r\nverifyLessThan(testCase, abs(positionAfterSettling), 2);\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">function<\/span> testOvershoot(testCase)\r\n <span class=\"comment\">%Test to ensure that overshoot is less than 0.01<\/span>\r\n\r\n[position, ~] = simulateSystem(springMassDamperDesign);\r\novershoot = max(position);\r\n\r\nverifyLessThan(testCase, overshoot, 0.01);\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<span class=\"keyword\">function<\/span> testInvalidInput(testCase)\r\n<span class=\"comment\">% Test to ensure we fail gracefully with bogus input<\/span>\r\n\r\ntestCase.verifyError(@() simulateSystem(<span class=\"string\">'bunk'<\/span>), <span class=\"keyword\">...<\/span>\r\n    <span class=\"string\">'simulateSystem:InvalidDesign:ShouldBeStruct'<\/span>);\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>...and now we should be in business. Let's check the build and the resulting coverage:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017FullCoverage.png\" alt=\"\"> <\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017FullCoverageCode.png\" alt=\"\"> <\/p><p>Ah, that's nice. Let's call it a day.<\/p><p>...but not before you check out <a href=\"https:\/\/blogs.mathworks.com\/deep-learning\/\">Steve's new blog on deep learning<\/a>! The man is amazing. He's been a world class blogger on image processing for years, and now he's adding deep learning into his repertoire. He has even promised that he will keep the image processing blog going. What a guy! I'm looking forward to it, be sure to catch up he already has a couple posts <a href=\"https:\/\/blogs.mathworks.com\/deep-learning\/2017\/09\/21\/jumping-into-the-deep-end\/\">here<\/a> and <a href=\"https:\/\/blogs.mathworks.com\/deep-learning\/2017\/10\/06\/deep-learning-with-matlab-r2017b\/\">here<\/a>.<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_7995001be08a4be2aa8272c7f97774c0() {\r\n        \/\/ Remember the title so we can use it in the new page\r\n        title = document.title;\r\n\r\n        \/\/ Break up these strings so that their presence\r\n        \/\/ in the Javascript doesn't mess up the search for\r\n        \/\/ the MATLAB code.\r\n        t1='7995001be08a4be2aa8272c7f97774c0 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 7995001be08a4be2aa8272c7f97774c0';\r\n    \r\n        b=document.getElementsByTagName('body')[0];\r\n        i1=b.innerHTML.indexOf(t1)+t1.length;\r\n        i2=b.innerHTML.indexOf(t2);\r\n \r\n        code_string = b.innerHTML.substring(i1, i2);\r\n        code_string = code_string.replace(\/REPLACE_WITH_DASH_DASH\/g,'--');\r\n\r\n        \/\/ Use \/x3C\/g instead of the less-than character to avoid errors \r\n        \/\/ in the XML parser.\r\n        \/\/ Use '\\x26#60;' instead of '<' so that the XML parser\r\n        \/\/ doesn't go ahead and substitute the less-than character. \r\n        code_string = code_string.replace(\/\\x3C\/g, '\\x26#60;');\r\n\r\n        copyright = 'Copyright 2017 The MathWorks, Inc.';\r\n\r\n        w = window.open();\r\n        d = w.document;\r\n        d.write('<pre>\\n');\r\n        d.write(code_string);\r\n\r\n        \/\/ Add copyright line at the bottom if specified.\r\n        if (copyright.length > 0) {\r\n            d.writeln('');\r\n            d.writeln('%%');\r\n            if (copyright.length > 0) {\r\n                d.writeln('% _' + copyright + '_');\r\n            }\r\n        }\r\n\r\n        d.write('<\/pre>\\n');\r\n\r\n        d.title = title + ' (MATLAB code)';\r\n        d.close();\r\n    }   \r\n     --> <\/script><p style=\"text-align: right; font-size: xx-small; font-weight:lighter;   font-style: italic; color: gray\"><br><a href=\"javascript:grabCode_7995001be08a4be2aa8272c7f97774c0()\"><span style=\"font-size: x-small;        font-style: italic;\">Get \r\n      the MATLAB code <noscript>(requires JavaScript)<\/noscript><\/span><\/a><br><br>\r\n      Published with MATLAB&reg; R2017b<br><\/p><\/div><!--\r\n7995001be08a4be2aa8272c7f97774c0 ##### SOURCE BEGIN #####\r\n%%\r\n% Coverage. It's all good. Health care coverage is good. Cell phone\r\n% coverage is good. Even cloud coverage can provide a nice break from the\r\n% intensity of the sun's rays sometimes. It should come then as no surprise\r\n% that good code coverage also gives us that nice warm fuzzy feeling, like\r\n% a nice warm drink on a cold day.\r\n%\r\n% ...and often for good reason. Code coverage can help us see areas of our\r\n% code that are not exercised in some form or another, and can be very\r\n% helpful, in a _good-friend-that-can-be-brutally-honest-with-you_ sort of\r\n% way. It can point out where we really need to add more testing. Once we\r\n% react to our good friend's advice and add better testing, we are all the\r\n% better.\r\n%\r\n% A quick aside to add some caution is in order. We (all) should remember\r\n% that code coverage does not as much show what code _is_ covered as which\r\n% code _is not_ covered. This is because seeing that a piece of code is\r\n% covered says nothing about how it was covered, whether it was covered by\r\n% a test specifically targeting that code or some incidental coverage, and\r\n% there is no information as to whether the coverage verified the correct\r\n% result in any way. Takeaway, use coverage to see areas of the code are\r\n% *_not_* tested. Don't fall into the trap and draw conclusions that\r\n% covered code is bug free or is even remotely tested sufficiently.\r\n%\r\n% That said, it is a great tool if you have the right mindset about it, and\r\n% when combined with testing and continuous integration it can help us\r\n% ensure our testing is up to snuff.\r\n%\r\n% Good news! This just got a lot easier. We have had the\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/matlab.unittest.plugins.codecoverageplugin-class.html\r\n% CodeCoveragePlugin> for the unit test framework for some time, but it has\r\n% produced a report that was more designed to be used in interactive MATLAB\r\n% environments rather than CI system workflows. Now we have the option to\r\n% produce our coverage into the <http:\/\/cobertura.github.io\/cobertura\/\r\n% Cobertura> format and benefit from things like the\r\n% <https:\/\/wiki.jenkins.io\/display\/JENKINS\/Cobertura+Plugin Cobertura\r\n% plugin for Jenkins>.\r\n%\r\n% Let's continue with our (matrix'd) build from\r\n% <https:\/\/blogs.mathworks.com\/developer\/2017\/09\/26\/the-build-matrix-laboratory\/\r\n% last time>, but add some coverage.\r\n%\r\n% <include>coverage_snippet.m<\/include>\r\n%\r\n% Easy as pie. Now let's see what it looks like in our CI build:\r\n%\r\n% <<y2017InitialFailure.png>>\r\n%\r\n% Oops! I forgot about the fact that other releases we are building against\r\n% don't have the plugin:\r\n%\r\n% <<y2017OldReleaseErrorMessage.png>>\r\n%\r\n% No biggie, we'll just need to wrap the installation of our plugin into\r\n% the TestRunner with a version check. Let's put it into a function:\r\n%\r\n% <include>addCoberturaCoverageIfPossible.m<\/include>\r\n%\r\n% and we can just call that function from our test running script:\r\n%\r\n% <include>runALL_THE_TESTS.m<\/include>\r\n%\r\n% Also, the MATLAB code is just one half of the equation. We also need to\r\n% ensure the Jenkins Cobertura plugin is installed and that the build is\r\n% configured to track coverage. Here we simply add it as a post-build\r\n% action and we configure it to look in our results folder, looking for\r\n% files named |coverage.xml|.\r\n%\r\n% <<y2017CoverageSettings.png>>\r\n%\r\n% Note that since we are running testing across multiple releases, and the\r\n% earlier releases don't have the plugin, we also need to tell the plugin\r\n% not to fail the build if there are no coverage files present. If we don't\r\n% then the builds for all the earlier releases will fail because no\r\n% Cobertura xml file will be found.\r\n%\r\n% OK, now we are in business. The build now passes across all releases:\r\n%\r\n% <<y2017AllPassing.png>>\r\n%\r\n% However, when we navigate to the R2017b build, we can see the coverage\r\n% report:\r\n%\r\n% <<y2017CoverageFrontPage.png>>\r\n%\r\n% Digging in deeper we can then see where we need to add a test:\r\n%\r\n% <<y2017CoverageDetail.png>>\r\n%\r\n% The build now shows very clearly that we forgot to add a negative test.\r\n% Let's remedy that! Here is the test I want to write:\r\n%\r\n% <include>testInvalidInput.m<\/include>\r\n%\r\n% However, looking again at our source, I notice something horrible, we are\r\n% not testable! As a reminder, here is the code for the simulator:\r\n%\r\n% <include>simulateSystemOrig.m<\/include>\r\n%\r\n% ...and for the design:\r\n%\r\n% <include>springMassDamperDesignOrig.m<\/include>\r\n%\r\n% Yikes! There is no way to get a bad value into the code in order to test\r\n% it. As such currently the code is dead. If we want we could just remove\r\n% the error condition because we know that the script produces the right\r\n% format of the design. However, that would miss the point. The whole\r\n% reason why we separated out the design from the simulation script is so\r\n% that we could tweak the design and explore. We really want the\r\n% |simulateSystem| function to be reusable across many different designs,\r\n% so we should parameterize it proper. Then the software is much more\r\n% testable, which is synonymous with flexible. To do this all we need to do\r\n% is make the design script a function and accept it as an input to the\r\n% simulation function:\r\n%\r\n% <include>source\/simulateSystem.m<\/include>\r\n%\r\n% <include>source\/springMassDamperDesign.m<\/include>\r\n%\r\n% Make a few test updates:\r\n% \r\n% <include>tests\/designTest.m<\/include>\r\n%\r\n% ...and now we should be in business. Let's check the build and the\r\n% resulting coverage:\r\n%\r\n% <<y2017FullCoverage.png>>\r\n%\r\n% <<y2017FullCoverageCode.png>>\r\n%\r\n% Ah, that's nice. Let's call it a day.\r\n%\r\n% ...but not before you check out\r\n% <https:\/\/blogs.mathworks.com\/deep-learning\/ Steve's new blog on deep\r\n% learning>! The man is amazing. He's been a world class blogger on image\r\n% processing for years, and now he's adding deep learning into his\r\n% repertoire. He has even promised that he will keep the image processing\r\n% blog going. What a guy! I'm looking forward to it, be sure to catch up he\r\n% already has a couple posts\r\n% <https:\/\/blogs.mathworks.com\/deep-learning\/2017\/09\/21\/jumping-into-the-deep-end\/\r\n% here> and\r\n% <https:\/\/blogs.mathworks.com\/deep-learning\/2017\/10\/06\/deep-learning-with-matlab-r2017b\/\r\n% here>.\r\n\r\n\r\n\r\n##### SOURCE END ##### 7995001be08a4be2aa8272c7f97774c0\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017CoverageDetail.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><p>Coverage. It's all good. Health care coverage is good. Cell phone coverage is good. Even cloud coverage can provide a nice break from the intensity of the sun's rays sometimes. It should come then as... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/developer\/2017\/10\/16\/cobertura-code-coverage\/\">read more >><\/a><\/p>","protected":false},"author":90,"featured_media":1086,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[4,7],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/1083"}],"collection":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/users\/90"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/comments?post=1083"}],"version-history":[{"count":4,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/1083\/revisions"}],"predecessor-version":[{"id":1104,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/1083\/revisions\/1104"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media\/1086"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media?parent=1083"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/categories?post=1083"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/tags?post=1083"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}