{"id":1062,"date":"2017-09-26T10:52:37","date_gmt":"2017-09-26T14:52:37","guid":{"rendered":"https:\/\/blogs.mathworks.com\/developer\/?p=1062"},"modified":"2017-09-29T00:05:23","modified_gmt":"2017-09-29T04:05:23","slug":"the-build-matrix-laboratory","status":"publish","type":"post","link":"https:\/\/blogs.mathworks.com\/developer\/2017\/09\/26\/the-build-matrix-laboratory\/","title":{"rendered":"The (Build) Matrix Laboratory"},"content":{"rendered":"<div class=\"content\"><p>Ah the joys of a new release! <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/release-notes.html\">R2017b<\/a> is now out and packed with some great new features. So here I go, heading out to an excitement fueled upgrade-mania when lo and behold, my glee is rudely interrupted when I see my code is broken in the new release.<\/p><p>What a drain on an otherwise pleasant experience! It turns out that the error is due to some of my more \"creative\" and \"clever\" coding practices that have helped me get my job done in the past, and when I dig in I see that I've been warned about this change coming for years. The problem is I never seem to have the time to comb the release notes as much as I'd like to, and even if I did realizing that my code I wrote 5 years ago has some of the problems mentioned is a tall order.<\/p><p>To compound that, even though I'd like to upgrade, when I make the needed adjustments to do so I worry about breaking the users of my code who haven't upgraded yet.<\/p><p>What in the world to do?<\/p><p>Let's use some continuous integration! Last I checked we were still in charge of the machines so let's put them to good use. It is actually quite easy to put this into practice using Jenkins. Jenkins <a href=\"https:\/\/wiki.jenkins.io\/display\/JENKINS\/Matrix+Project+Plugin\">supports<\/a> what is known as matrix builds so that we can ensure our code works across many different MATLAB releases. To set this up, simply create your project as a Multi-Configuration Project in Jenkins.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017MultiConfigProject.png\" alt=\"\"> <\/p><p>Once this project is created we get the opportunity to define our own configuration matrix. This means that we can add an axis of configuration where we can define a particular system property (i.e. environment variable) as well as the different values we'd like to have for each of these values. What I want to do is add the MATLAB version and point it to several releases I'd like to test against, like so:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017ConfigMatrixAxis.png\" alt=\"\"> <\/p><p>Once this is done, Jenkins will create multiple configurations for our build and it will be parameterized across this value. So, as we set up the build step (which we would set up to run our tests against thecode changes) this value can be leveraged in the build script to ensure this happens on all the releases. For example, our build script can point to the different installation locations of the different MATLAB releases I want to test against:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017MultiConfigBuildScript.png\" alt=\"\"> <\/p><p>Simple as that! What does this give us? It gives us a new build configuration (which really feels like a new build) for every value of <tt>MATLAB_VERSION<\/tt>. To see how this can help, let's look at a quick example. Here we have a bit of source code simulating a mass spring damper...<\/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>...as well as a corresponding script which defines the design of the system in terms of the spring stiffness and damping:<\/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>Along with this source we also have a file that defines a couple tests for this design to ensure it achieves the right characteristics in terms of acceptable overshoot and settling time of a step response:<\/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; \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, time] = simulateSystem;\r\novershoot = max(position);\r\n\r\nverifyLessThan(testCase, overshoot, 0.01);\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>Finally, here is our test running script that will kick off the testing:<\/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 plugin for test results<\/span>\r\n    resultsDir = fullfile(ws, <span class=\"string\">'testresults'<\/span>);\r\n    mkdir(resultsDir);\r\n    resultsFile = fullfile(resultsDir, <span class=\"string\">'testResults.xml'<\/span>);\r\n    runner.addPlugin(XMLPlugin.producingJUnitFormat(resultsFile));\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>With this set up you can see that each build actually gives us four different configurations so we test across all releases.<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017Configs.png\" alt=\"\"> <\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017ConfigTestRollup.png\" alt=\"\"> <\/p><p>Here we see we are just fine to upgrade to R2017b. How's that for some peace of mind? Trusting your tests gives you the confidence needed to upgrade without worry and\/or to be notified quickly when adjustments need to be made.<\/p><p>Now let's evolve the code. I am not the biggest fan of the design script because it offends my sensibilities to have the <tt>clear m<\/tt> call in there. I'd like to change this implementation:<\/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>...to this:<\/p><pre class=\"language-matlab\">\r\ndesign.k = 5e6;                   <span class=\"comment\">% Spring Constant<\/span>\r\ndesign.c = 2*getMass*sqrt(design.k\/getMass); <span class=\"comment\">% Damping Coefficient to be critically damped<\/span>\r\n\r\n<span class=\"keyword\">function<\/span> m = getMass\r\nm = 1500; <span class=\"comment\">% Need to know the mass to determine critical damping<\/span>\r\n<span class=\"keyword\">end<\/span>\r\n\r\n<\/pre><p>This seems cleaner to me. I'd rather leverage a local function in the script so that the variable we'd like to clear simply never is created in the workspace.<\/p><p>However, checking this change in produces the following:<\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017FailureMatrix.png\" alt=\"\"> <\/p><p><img decoding=\"async\" vspace=\"5\" hspace=\"5\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017FailureDiagMatrix.png\" alt=\"\"> <\/p><p>It looks like local functions in scripts weren't around in R2016a, so I'll have to wait on that change until I no longer need to support my code in that release. Thanks, build matrix!<\/p><script language=\"JavaScript\"> <!-- \r\n    function grabCode_8730103977ea49fcaacc3ebec0eb21f6() {\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='8730103977ea49fcaacc3ebec0eb21f6 ' + '##### ' + 'SOURCE BEGIN' + ' #####';\r\n        t2='##### ' + 'SOURCE END' + ' #####' + ' 8730103977ea49fcaacc3ebec0eb21f6';\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_8730103977ea49fcaacc3ebec0eb21f6()\"><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\n8730103977ea49fcaacc3ebec0eb21f6 ##### SOURCE BEGIN #####\r\n%%\r\n%\r\n% Ah the joys of a new release!\r\n% <https:\/\/www.mathworks.com\/help\/matlab\/release-notes.html R2017b> is now\r\n% out and packed with some great new features. So here I go, heading out to\r\n% an excitement fueled upgrade-mania when lo and behold, my glee is rudely\r\n% interrupted when I see my code is broken in the new release.\r\n%\r\n% What a drain on an otherwise pleasant experience! It turns out that the\r\n% error is due to some of my more \"creative\" and \"clever\" coding practices\r\n% that have helped me get my job done in the past, and when I dig in I see\r\n% that I've been warned about this change coming for years. The problem is\r\n% I never seem to have the time to comb the release notes as much as I'd\r\n% like to, and even if I did realizing that my code I wrote 5 years ago has\r\n% some of the problems mentioned is a tall order.\r\n%\r\n% To compound that, even though I'd like to upgrade, when I make the needed\r\n% adjustments to do so I worry about breaking the users of my code who\r\n% haven't upgraded yet.\r\n%\r\n% What in the world to do?\r\n%\r\n% Let's use some continuous integration! Last I checked we were still in\r\n% charge of the machines so let's put them to good use. It is actually\r\n% quite easy to put this into practice using Jenkins. Jenkins\r\n% <https:\/\/wiki.jenkins.io\/display\/JENKINS\/Matrix+Project+Plugin supports>\r\n% what is known as matrix builds so that we can ensure our code works\r\n% across many different MATLAB releases. To set this up, simply create your\r\n% project as a Multi-Configuration Project in Jenkins.\r\n%\r\n% \r\n% <<y2017MultiConfigProject.png>>\r\n% \r\n% Once this project is created we get the opportunity to define our own\r\n% configuration matrix. This means that we can add an axis of configuration\r\n% where we can define a particular system property (i.e. environment\r\n% variable) as well as the different values we'd like to have for each of\r\n% these values. What I want to do is add the MATLAB version and point it to\r\n% several releases I'd like to test against, like so:\r\n%\r\n% <<y2017ConfigMatrixAxis.png>>\r\n%\r\n% Once this is done, Jenkins will create multiple configurations for our\r\n% build and it will be parameterized across this value. So, as we set up\r\n% the build step (which we would set up to run our tests against thecode\r\n% changes) this value can be leveraged in the build script to ensure this\r\n% happens on all the releases. For example, our build script can point to\r\n% the different installation locations of the different MATLAB releases I\r\n% want to test against:\r\n%\r\n% <<y2017MultiConfigBuildScript.png>>\r\n%\r\n% Simple as that! What does this give us? It gives us a new build\r\n% configuration (which really feels like a new build) for every value of\r\n% |MATLAB_VERSION|. To see how this can help, let's look at a quick\r\n% example. Here we have a bit of source code simulating a mass spring\r\n% damper...\r\n%\r\n% <include>source\/simulateSystem.m<\/include>\r\n%\r\n% ...as well as a corresponding script which defines the design of the\r\n% system in terms of the spring stiffness and damping:\r\n%\r\n% <include>source\/springMassDamperDesign.m<\/include>\r\n%\r\n% Along with this source we also have a file that defines a couple tests\r\n% for this design to ensure it achieves the right characteristics in terms\r\n% of acceptable overshoot and settling time of a step response:\r\n%\r\n% <include>tests\/designTest.m<\/include>\r\n%\r\n% Finally, here is our test running script that will kick off the testing:\r\n%\r\n% <include>runALL_THE_TESTS.m<\/include>\r\n%\r\n% With this set up you can see that each build actually gives us four\r\n% different configurations so we test across all releases.\r\n%\r\n% <<y2017Configs.png>>\r\n%\r\n% <<y2017ConfigTestRollup.png>>\r\n%\r\n% Here we see we are just fine to upgrade to R2017b. How's that for some\r\n% peace of mind? Trusting your tests gives you the confidence needed to\r\n% upgrade without worry and\/or to be notified quickly when adjustments need\r\n% to be made.\r\n%\r\n% Now let's evolve the code. I am not the biggest fan of the design script\r\n% because it offends my sensibilities to have the |clear m| call in there.\r\n% I'd like to change this implementation:\r\n%\r\n% <include>source\/springMassDamperDesign.m<\/include>\r\n%\r\n% ...to this:\r\n%\r\n% <include>source\/springMassDamperImprovedDesign.m<\/include>\r\n%\r\n% This seems cleaner to me. I'd rather leverage a local function in the\r\n% script so that the variable we'd like to clear simply never is created in\r\n% the workspace.\r\n%\r\n% However, checking this change in produces the following:\r\n%\r\n% <<y2017FailureMatrix.png>>\r\n%\r\n% <<y2017FailureDiagMatrix.png>>\r\n%\r\n% It looks like local functions in scripts weren't around in R2016a, so\r\n% I'll have to wait on that change until I no longer need to support my\r\n% code in that release. Thanks, build matrix! \r\n\r\n\r\n\r\n##### SOURCE END ##### 8730103977ea49fcaacc3ebec0eb21f6\r\n-->","protected":false},"excerpt":{"rendered":"<div class=\"overview-image\"><img decoding=\"async\"  class=\"img-responsive\" src=\"https:\/\/blogs.mathworks.com\/developer\/files\/y2017FailureMatrix.png\" onError=\"this.style.display ='none';\" \/><\/div><p>Ah the joys of a new release! R2017b is now out and packed with some great new features. So here I go, heading out to an excitement fueled upgrade-mania when lo and behold, my glee is rudely... <a class=\"read-more\" href=\"https:\/\/blogs.mathworks.com\/developer\/2017\/09\/26\/the-build-matrix-laboratory\/\">read more >><\/a><\/p>","protected":false},"author":90,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[4,19],"tags":[],"_links":{"self":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/1062"}],"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=1062"}],"version-history":[{"count":4,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/1062\/revisions"}],"predecessor-version":[{"id":1074,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/posts\/1062\/revisions\/1074"}],"wp:attachment":[{"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/media?parent=1062"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/categories?post=1062"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.mathworks.com\/developer\/wp-json\/wp\/v2\/tags?post=1062"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}