{"id":51,"date":"2015-01-29T17:00:44","date_gmt":"2015-01-29T17:00:44","guid":{"rendered":"https:\/\/blogs.mathworks.com\/developer\/?p=51"},"modified":"2016-07-13T21:01:55","modified_gmt":"2016-07-13T21:01:55","slug":"tap-plugin","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/developer\/2015\/01\/29\/tap-plugin\/","title":{"rendered":"Just TAP it in&#8230;give it a little TAP-py TAP TAP TAP-aroo"},"content":{"rendered":"<div class=\"content\"><!--introduction--><p><a href=\"https:\/\/blogs.mathworks.com\/developer\/2015\/01\/20\/the-other-kind-of-continuous-integration\/\">Last post<\/a> we explored connecting MATLAB to Jenkins, running tests, and ensuring the build fails upon any test failure. We leveraged the TestResult array returned from the test run in order to exit MATLAB with a non-zero exit status code to communicate to Jenkins when needed that the build should fail.<\/p><p>This will work but there is a better way if you have R2014a or later. To truly integrate with Jenkins, MATLAB and Jenkins need to be communicating more fully with each other. A great way to do that is to leverage the standard, language independent <a href=\"http:\/\/testanything.org\/\">Test Anything Protocol (TAP)<\/a>. Using the <a href=\"https:\/\/wiki.jenkins-ci.org\/display\/JENKINS\/TAP+Plugin\">TAP Plugin for Jenkins<\/a> and the <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/matlab.unittest.plugins.tapplugin-class.html\">TAPPlugin<\/a> for the MATLAB TestRunner, MATLAB can give more detailed information to Jenkins. This information includes which tests were run, which failed, and which were filtered. Furthermore, other Jenkins features, such as history tracking, are enabled.<\/p><p>Enough already! How is this done?<\/p><!--\/introduction--><p><b>Set up the TAP Plugin in Jenkins<\/b><\/p><p>Firstly, Jenkins needs the TAP Plugin in order to interpret TAP streams. This is done from the Jenkins plugin manager and can be found easily by searching for \"TAP\":<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015InstallTAPPlugin.png\" alt=\"\"> <\/p><p>Once installed a new \"Publish TAP Results\" option is available as a post-build action.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015PublishTAPResults.png\" alt=\"\"> <\/p><p>After pointing Jenkins to the location where the tap streams will be generated, we can further configure the plugin behavior as we see fit. For example, we can fail the build if no tests are run or if the number of tests differs from the TAP Plan. Importantly, we need to check the option to fail the build if there are any test failures. Here we are picking up any files with a \".tap\" extension, and we are failing the build if there are any test failures, if no tests are run, or if the TAP Plan is not complete.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015TAPOptions.png\" alt=\"\"> <\/p><p><b>Set up the TAPPlugin in MATLAB<\/b><\/p><p>From the last post we are already connected and running the tests, now we just need that particular test run to output a TAP stream that will be picked up and analyzed by Jenkins. To do this we can create our own TestRunner and add the TAP plugin to it. One consideration, however, is that we don't want the test output produced in MATLAB to get in the way of the TAP stream and invalidate it so we need to ensure the tap stream is directed somewhere other than the Command Window. We also need the TAP stream to output to a file that the Jenkins plugin can find for analysis. Both of these issues are conveniently solved by sending the TAPPlugin output to a <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/ref\/matlab.unittest.plugins.tofile-class.html\">ToFile<\/a> stream. The test running script becomes:<\/p><pre class=\"language-matlab\">import <span class=\"string\">matlab.unittest.TestSuite<\/span>;\r\nimport <span class=\"string\">matlab.unittest.TestRunner<\/span>;\r\nimport <span class=\"string\">matlab.unittest.plugins.TAPPlugin<\/span>;\r\nimport <span class=\"string\">matlab.unittest.plugins.ToFile<\/span>;\r\n\r\n<span class=\"keyword\">try<\/span>\r\n    suite = TestSuite.fromPackage(<span class=\"string\">'testcases'<\/span>,<span class=\"string\">'IncludingSubpackages'<\/span>,true);\r\n    <span class=\"comment\">% Create a typical runner with text output<\/span>\r\n    runner = TestRunner.withTextOutput();\r\n    <span class=\"comment\">% Add the TAP plugin and direct its output to a file<\/span>\r\n    tapFile = fullfile(getenv(<span class=\"string\">'WORKSPACE'<\/span>), <span class=\"string\">'testResults.tap'<\/span>);\r\n    runner.addPlugin(TAPPlugin.producingOriginalFormat(ToFile(tapFile)));\r\n    <span class=\"comment\">% Run the tests<\/span>\r\n    results = runner.run(suite);\r\n    display(results);\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\nexit;\r\n<\/pre><p>Running the Jenkins build now fails the build in the presence of these test failures:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015FinalTestOutput.png\" alt=\"\"> <\/p><p><b>Enjoy the fruit of your labor<\/b><\/p><p>Jenkins will now also give you in depth insight into which tests passed, failed, or were filtered to you can quickly navigate to the failure syndrome and begin investigating.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015TAPTestResults.png\" alt=\"\"> <\/p><p>In addition, many Jenkins\/TAP-Plugin features add improved visibility into the health of your software over time, such as a history of the passing tests, failed tests, and filtered tests over time listed on the project page:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015FailureHistory.png\" alt=\"\"> <\/p><p>Here you can see a history of the test suite growth and health as it pertains to failures and filtered test content. For example, if we fix the failures in this test suite and run a new job we see the red disappear.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015FixedFailed.png\" alt=\"\"> <\/p><p>Likewise if we then do the needed work to unfilter the test content not yet running...&iexcl;Hasta la vista al amarillo!<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015FixedFiltered.png\" alt=\"\"> <\/p><p>Note that there are many different ways to setup your CI system and this is just one quick example. That said, if you use this approach you will want to also make sure you clean up your workspace between builds to remove these TAP files produced so that each build starts with fresh results, otherwise the TAP files from previous jobs will wreak havoc with those from the current job.<\/p><p>What do you think? Have you used the language independent TAP format for test results before? How else do you fit MATLAB tests with the rest of your ecosystem? Let us know in the comments!<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_649c612ab40145eb9eeba97c14b55f1d() {\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='649c612ab40145eb9eeba97c14b55f1d ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 649c612ab40145eb9eeba97c14b55f1d';\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 2015 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_649c612ab40145eb9eeba97c14b55f1d()\"><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; R2014b<br><\/p><\/div><!--\r\n649c612ab40145eb9eeba97c14b55f1d ##### SOURCE BEGIN #####\r\n%% Just TAP it in. Give it a little TAP-py TAP TAP TAP-aroo.\r\n%    \r\n% <https:\/\/blogs.mathworks.com\/developer\/2015\/01\/20\/the-other-kind-of-continuous-integration\/\r\n% Last post> we explored connecting MATLAB to Jenkins, running tests, and\r\n% ensuring the build fails upon any test failure. We leveraged the\r\n% TestResult array returned from the test run in order to exit MATLAB\r\n% with a non-zero exit status code to communicate to Jenkins when needed\r\n% that the build should fail.\r\n%\r\n% This will work but there is a better way if you have R2014a or later. To\r\n% truly integrate with Jenkins, MATLAB and Jenkins need to be communicating\r\n% more fully with each other. A great way to do that is to leverage the\r\n% standard, language independent <http:\/\/testanything.org\/ Test Anything\r\n% Protocol (TAP)>. Using the\r\n% <https:\/\/wiki.jenkins-ci.org\/display\/JENKINS\/TAP+Plugin TAP Plugin for\r\n% Jenkins> and the\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/matlab.unittest.plugins.tapplugin-class.html\r\n% TAPPlugin> for the MATLAB TestRunner, MATLAB can give more detailed\r\n% information to Jenkins. This information includes which tests were run,\r\n% which failed, and which were filtered. Furthermore, other Jenkins\r\n% features, such as history tracking, are enabled.\r\n%\r\n% Enough already! How is this done?\r\n%\r\n%%\r\n% *Set up the TAP Plugin in Jenkins*\r\n%\r\n% Firstly, Jenkins needs the TAP Plugin in order to interpret TAP streams.\r\n% This is done from the Jenkins plugin manager and can be found easily by\r\n% searching for \"TAP\":\r\n%\r\n% <<2015InstallTAPPlugin.png>>\r\n%\r\n% Once installed a new \"Publish TAP Results\" option is available as a\r\n% post-build action.\r\n%\r\n% <<2015PublishTAPResults.png>>\r\n% \r\n% After pointing Jenkins to the location where the tap streams will be\r\n% generated, we can further configure the plugin behavior as we see fit.\r\n% For example, we can fail the build if no tests are run or if the\r\n% number of tests differs from the TAP Plan. Importantly, we need to check\r\n% the option to fail the build if there are any test failures. Here we are\r\n% picking up any files with a \".tap\" extension, and we are failing the\r\n% build if there are any test failures, if no tests are run, or if the TAP\r\n% Plan is not complete.\r\n%\r\n% <<2015TAPOptions.png>>\r\n%\r\n%% \r\n% *Set up the TAPPlugin in MATLAB*\r\n%\r\n% From the last post we are already connected and running the tests, now we\r\n% just need that particular test run to output a TAP stream that will be\r\n% picked up and analyzed by Jenkins. To do this we can create our own\r\n% TestRunner and add the TAP plugin to it. One consideration, however, is\r\n% that we don't want the test output produced in MATLAB to get in the way\r\n% of the TAP stream and invalidate it so we need to ensure the tap stream\r\n% is directed somewhere other than the Command Window. We also need the TAP\r\n% stream to output to a file that the Jenkins plugin can find for analysis.\r\n% Both of these issues are conveniently solved by sending the TAPPlugin\r\n% output to a\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/ref\/matlab.unittest.plugins.tofile-class.html\r\n% ToFile> stream. The test running script becomes:\r\n%\r\n%   import matlab.unittest.TestSuite;\r\n%   import matlab.unittest.TestRunner;\r\n%   import matlab.unittest.plugins.TAPPlugin;\r\n%   import matlab.unittest.plugins.ToFile;\r\n%\r\n%   try\r\n%       suite = TestSuite.fromPackage('testcases','IncludingSubpackages',true);\r\n%       % Create a typical runner with text output\r\n%       runner = TestRunner.withTextOutput();\r\n%       % Add the TAP plugin and direct its output to a file\r\n%       tapFile = fullfile(getenv('WORKSPACE'), 'testResults.tap');\r\n%       runner.addPlugin(TAPPlugin.producingOriginalFormat(ToFile(tapFile)));\r\n%       % Run the tests\r\n%       results = runner.run(suite);\r\n%       display(results);\r\n%   catch e\r\n%       disp(getReport(e,'extended'));\r\n%       exit(1);\r\n%   end\r\n%   exit;\r\n%\r\n% Running the Jenkins build now fails the build in the presence of these test\r\n% failures:\r\n%\r\n% <<2015FinalTestOutput.png>>\r\n%\r\n%%\r\n% *Enjoy the fruit of your labor*\r\n%\r\n% Jenkins will now also give you in depth insight into which tests passed,\r\n% failed, or were filtered to you can quickly navigate to the failure\r\n% syndrome and begin investigating.\r\n%\r\n% <<2015TAPTestResults.png>>\r\n% \r\n% In addition, many Jenkins\/TAP-Plugin features add improved visibility\r\n% into the health of your software over time, such as a history of the\r\n% passing tests, failed tests, and filtered tests over time listed on the\r\n% project page:\r\n%\r\n% <<2015FailureHistory.png>>\r\n%\r\n% Here you can see a history of the test suite growth and health as it\r\n% pertains to failures and filtered test content. For example, if we fix\r\n% the failures in this test suite and run a new job we see the red\r\n% disappear.\r\n%\r\n% <<2015FixedFailed.png>>\r\n%\r\n% Likewise if we then do the needed work to unfilter the test content not\r\n% yet running...\u00a1Hasta la vista al amarillo!\r\n%\r\n% <<2015FixedFiltered.png>>\r\n%\r\n% Note that there are many different ways to setup your CI system and this\r\n% is just one quick example. That said, if you use this approach you will\r\n% want to also make sure you clean up your workspace between builds to\r\n% remove these TAP files produced so that each build starts with fresh\r\n% results, otherwise the TAP files from previous jobs will wreak havoc with\r\n% those from the current job.\r\n%\r\n% What do you think? Have you used the language independent TAP format for\r\n% test results before? How else do you fit MATLAB tests with the rest of\r\n% your ecosystem? Let us know in the comments!\r\n\r\n##### SOURCE END ##### 649c612ab40145eb9eeba97c14b55f1d\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img src=\"https:\/\/blogs.mathworks.com\/developer\/files\/2015FixedFiltered-624x363.png\" class=\"img-responsive attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"\" decoding=\"async\" loading=\"lazy\" \/><\/div><!--introduction--><p><a href=\"https:\/\/blogs.mathworks.com\/developer\/2015\/01\/20\/the-other-kind-of-continuous-integration\/\">Last post<\/a> we explored connecting MATLAB to Jenkins, running tests, and ensuring the build fails upon any test failure. We leveraged the TestResult array returned from the test run in order to exit MATLAB with a non-zero exit status code to communicate to Jenkins when needed that the build should fail.... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/developer\/2015\/01\/29\/tap-plugin\/\">read more >><\/a><\/p>","protected":false},"author":90,"featured_media":35,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[4],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/51"}],"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=51"}],"version-history":[{"count":8,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/51\/revisions"}],"predecessor-version":[{"id":59,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/51\/revisions\/59"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media\/35"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media?parent=51"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/categories?post=51"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/tags?post=51"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}