{"id":1973,"date":"2020-08-29T16:55:04","date_gmt":"2020-08-29T23:55:04","guid":{"rendered":"http:\/\/visualgdb.com\/w\/?p=1973"},"modified":"2020-08-30T15:01:17","modified_gmt":"2020-08-30T22:01:17","slug":"improving-your-embedded-firmware-quality-with-unit-tests","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/tests\/arm\/","title":{"rendered":"Improving your Embedded Firmware Quality with Unit Tests"},"content":{"rendered":"<p>This tutorial shows how to use VisualGDB to create unit tests for embedded projects that will run directly on the embedded device. Unit tests are supported on the ARM Cortex devices using Segger J-Link or OpenOCD and can be supported on other devices using VisualGDB extension SDK. Before you begin, install <a href=\"http:\/\/visualgdb.com\/download\/\">VisualGDB 5.2<\/a> or later.<\/p>\n<p>In this tutorial we will create a simple test project for the STM32F4Discovery board that will verify that the execution time of the <strong>sinf()<\/strong> function matches a certain constraint. We will show how switching the FPU mode from hardware to software would instantly cause the test to fail. If your program contains performance-critical code, you can use this approach to separately test the performance of each critical part and catch slowdowns caused by recent changes before they start causing trouble.<\/p>\n<ol>\n<li>Begin with starting Visual Studio and selecting <strong>File-&gt;New Project-&gt;VisualGDB-&gt;Embedded Project Wizard<\/strong>:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/01-newprj.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6630\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/01-newprj.png\" alt=\"\" width=\"1024\" height=\"680\" \/><\/a><\/li>\n<li>Enter the name and location for your project:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/02-name.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6631\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/02-name.png\" alt=\"\" width=\"1024\" height=\"680\" \/><\/a><\/li>\n<li>On the first page select <strong>Create a new project -&gt; Unit test project -&gt; MSBuild -&gt; TinyEmbeddedTest<\/strong>:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/03-test.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6632\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/03-test.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a>VisualGDB also supports <strong>CppUTest<\/strong> and <strong>GoogleTest<\/strong> frameworks out-of-the-box, so you can use one of those if your device has enough FLASH memory. The <strong>TinyEmbeddedTest<\/strong> framework provided by Sysprogs focuses on using as little FLASH space as possible and providing basic CppUTest-compatible API:<\/li>\n<li>On the next page select the ARM toolchain and choose your device from the device list:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/04-device.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6633\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/04-device.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a>Make sure the hardware floating point mode is selected.<\/li>\n<li>Proceed with the default sample for the test project:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/05-sample.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6634\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/05-sample.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>Finally select your debug method. Unit tests work with OpenOCD and Segger J-Link, so we select OpenOCD here:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/06-debug.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6635\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/06-debug.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>Press &#8220;Finish&#8221; to create your project. VisualGDB will create a basic project with 3 tests. Check the <strong>SuccessfulTest1<\/strong> test for a quick overview of various checks supported by the test framework:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/07-discover.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6636\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/07-discover.png\" alt=\"\" width=\"1261\" height=\"847\" \/><\/a><\/li>\n<li>Build your project and select <strong>Test-&gt;Run-&gt;All Tests<\/strong> to verify that the tests can be started properly. Alternatively you can select the tests you want to run in the Test Explorer, right-click there and select &#8220;Run&#8221;: <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/08-fail.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6637\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/08-fail.png\" alt=\"\" width=\"1261\" height=\"847\" \/><\/a><\/li>\n<li>Now we will measure the time taken by the <strong>sinf()<\/strong> function in soft-FP and hard-FP modes. On many ARM Cortex devices (including most STM32F4 devices) this can be done by enabling the debug cycle counter, setting the CYCCNT register to 0 before running a function and then reading the CYCCNT register immediately after. The following function demonstrates this:\n<pre class=\"\">void MeasureSinTime()\r\n{\r\n\u00a0\u00a0\u00a0 volatile double arg = 0.1;\r\n\u00a0\u00a0\u00a0 CoreDebug-&gt;DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;\r\n\u00a0\u00a0\u00a0 DWT-&gt;CTRL |= DWT_CTRL_CYCCNTENA_Msk;\r\n\u00a0\u00a0\u00a0 DWT-&gt;CYCCNT = 0;\r\n\u00a0\u00a0\u00a0 volatile float result = sinf(arg);\r\n\u00a0\u00a0\u00a0 volatile unsigned cycles = DWT-&gt;CYCCNT;\r\n\u00a0\u00a0\u00a0 asm(\"nop\");\r\n}<\/pre>\n<\/li>\n<li>VisualGDB test projects are normal executable projects with a main() function. Hence you can quickly prototype things by calling functions like this one directly from main() after the HAL_Init() call. Then set a breakpoint at the &#8220;nop&#8221; line and start debugging. Once the breakpoint is hit, note down the value of the &#8220;cycles&#8221; variable:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/09-cycles.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6638\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/09-cycles.png\" alt=\"\" width=\"1261\" height=\"847\" \/><\/a>Note how the <strong>IsRunningUnitTests()<\/strong> check in <strong>main() <\/strong>is used to distinguish regular debug sessions from unit test runs. It will only return true when running or debugging unit tests via the VisualGDB interfaces.<\/li>\n<li>Now we will switch the project to the software floating point mode:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/11-sw.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6639\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/11-sw.png\" alt=\"\" width=\"868\" height=\"725\" \/><\/a><\/li>\n<li>Build it and check the new time:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/slow.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6640\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/slow.png\" alt=\"\" width=\"1261\" height=\"847\" \/><\/a>Using the hardware FP mode makes more than 7.5x difference! So if a toolchain update or an accidental change in settings breaks this setting, the performance impact could be critical.<\/li>\n<li>Now let&#8217;s make a simple test that will automatically verify that the<strong> sinf()<\/strong> function completes in time. Declaring tests using TinyEmbeddedTest and CppUTest consists of 2 steps:\n<ul>\n<li>Defining a test group that will share common initialization and cleanup steps<\/li>\n<li>Defining the actual tests<\/li>\n<\/ul>\n<p>The test group can be easily defined using the TEST_GROUP() macro. We will put the code enabling the debug cycle counter there so that we can run multiple timing-related tests without enabling it each time:<\/p>\n<pre class=\"\">TEST_GROUP(TimingTests)\r\n{\r\n\u00a0\u00a0\u00a0 void setup()\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 CoreDebug-&gt;DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 DWT-&gt;CTRL |= DWT_CTRL_CYCCNTENA_Msk;\r\n\u00a0\u00a0\u00a0 }\r\n};<\/pre>\n<p>The actual test is very straight-forward as well:<\/p>\n<pre class=\"\">TEST(TimingTests, sinf)\r\n{\r\n\u00a0\u00a0\u00a0 volatile double arg = 0.1;\r\n\u00a0\u00a0\u00a0 DWT-&gt;CYCCNT = 0;\r\n\u00a0\u00a0\u00a0 volatile float result = sinf(arg);\r\n\u00a0\u00a0\u00a0 CHECK(DWT-&gt;CYCCNT &lt; 100);\r\n}<\/pre>\n<p>Also don&#8217;t forget to remove the <strong>MeasureSinTime()<\/strong> function as we don&#8217;t need it anymore.<\/li>\n<li>If you build your project now and run the newly added <strong>TimingTests::sinf()<\/strong> test, it will pass as long as you use the <strong>hardware FP<\/strong> mode:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/10-passed.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6641\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/10-passed.png\" alt=\"\" width=\"1261\" height=\"847\" \/><\/a><\/li>\n<li>Using software FP mode, will immediately trigger a test failure:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/12-fail.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6642\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/12-fail.png\" alt=\"\" width=\"1261\" height=\"847\" \/><\/a><\/li>\n<li>You can debug failed tests by right-clicking at them and selecting &#8220;Debug Selected Tests&#8221;. Once a test failure is reported, VisualGDB will treat it as an exception and stop:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/13-stack.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6643\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/13-stack.png\" alt=\"\" width=\"1261\" height=\"847\" \/><\/a><\/li>\n<li>Now we will add 2 small improvements to our test. First of all, when a failure is detected, we can output something more meaningful than &#8220;unexpected boolean value&#8221;. Second of all, if the timing is OK, we can still report it to keep a track of it in the test logs. Both tasks can be accomplished with a simple helper class:\n<pre class=\"\">class TimingValidator\r\n{\r\nprivate:\r\n\u00a0\u00a0\u00a0 unsigned m_Timeout;\r\n\u00a0\u00a0 \u00a0\r\npublic:\r\n\u00a0\u00a0\u00a0 TimingValidator(unsigned timeout)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : m_Timeout(timeout)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 DWT-&gt;CYCCNT = 0;\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0\u00a0 ~TimingValidator()\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 unsigned cycles = DWT-&gt;CYCCNT;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (cycles &lt; m_Timeout)\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 printf(\"Timing OK: %d &lt; %d\\n\", cycles, m_Timeout);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 else\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 char sz[64];\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 sprintf(sz, \"Timing violation: %d &gt; %d\", cycles, m_Timeout);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 FAIL(sz);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 }\r\n};<\/pre>\n<p>It will automatically reset the cycle counter when created and will automatically check the timing when leaving the scope. This will reduce the actual test method to this:<\/p>\n<pre class=\"\">TEST(TimingTests, sinf)\r\n{\r\n\u00a0\u00a0\u00a0 volatile double arg = 0.1;\r\n\u00a0\u00a0\u00a0 TimingValidator validator(100);\r\n\u00a0\u00a0\u00a0 volatile float result = sinf(arg);\r\n}<\/pre>\n<p>Note that the &#8216;volatile&#8217; statements are needed to prevent the optimizer from throwing away the call to &#8216;sinf()&#8217; as we are calling it with a constant input and not doing anything with the value.<\/li>\n<li>If you run the test in software FP mode now, you will see a much more meaningful message:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/14-timing.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6644\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/14-timing.png\" alt=\"\" width=\"1261\" height=\"847\" \/><\/a><\/li>\n<li>Similarly a successful test will now output the actual timing into the Output-&gt;Tests window and in the per-test output view:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/15-ok.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6645\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/15-ok.png\" alt=\"\" width=\"1261\" height=\"847\" \/><\/a>Note that if you using the unoptimized debug build, you may need to increase the cycle threshold to account for running the constructor\/destructor. In optimized release builds they will be normally inlined.<\/li>\n<li>You can easily use VisualGDB tests with a continuous integration system. Simply configure it to run the following command line:\n<pre class=\"\">&lt;VisualGDB.exe&gt; \/runtests &lt;test container file&gt; \/output&lt;output file&gt;<\/pre>\n<p><a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/16-cmdline.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-1989\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/16-cmdline.png\" alt=\"16-cmdline\" width=\"962\" height=\"321\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/16-cmdline.png 962w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/16-cmdline-300x100.png 300w\" sizes=\"(max-width: 962px) 100vw, 962px\" \/><\/a><\/li>\n<li>For non-MSBuild projects, the test container file is the .vgdbsettings file corresponding to the debug or release configuration. For MSBuild projects where the binary name is computed in Visual Studio on-the-fly, a test container is a simple text file containing the .vgdbsettings file path on the first line and the full binary path on the next one:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/17-testcontainer.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-1990\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/17-testcontainer.png\" alt=\"17-testcontainer\" width=\"782\" height=\"204\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/17-testcontainer.png 782w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/17-testcontainer-300x78.png 300w\" sizes=\"(max-width: 782px) 100vw, 782px\" \/><\/a>VisualGDB automatically generates those files when you start your tests from Visual Studio.<\/li>\n<li>A test run started from command line will generate an XML file with the details on the test outcome, output produced by it and any encountered errors:\n<pre class=\"\">&lt;?xml version=\"1.0\"?&gt;\r\n&lt;TestRunReport xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\"&gt;\r\n\u00a0 &lt;TestRuns&gt;\r\n\u00a0\u00a0\u00a0 &lt;TestRun&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;TestID&gt;TimingTests::sinf&lt;\/TestID&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;Output \/&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;TestLocation&gt;c:\\projects\\EmbeddedTestDemo\\EmbeddedTestDemoTests.cpp:40&lt;\/TestLocation&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;TestName&gt;TimingTests::sinf&lt;\/TestName&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;Outcome&gt;Failed&lt;\/Outcome&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;Duration&gt;4203&lt;\/Duration&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;Details&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;TestObject xsi:type=\"CallStack\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;Frames&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;CodeLocation&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;File&gt;c:\\projects\\EmbeddedTestDemo\\EmbeddedTestDemoTests.cpp&lt;\/File&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;Function&gt;TimingValidator::~TimingValidator&lt;\/Function&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;Line&gt;35&lt;\/Line&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/CodeLocation&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;CodeLocation&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;File&gt;c:\\projects\\EmbeddedTestDemo\\EmbeddedTestDemoTests.cpp&lt;\/File&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;Function&gt;TestInstance_TimingTests_sinf::run&lt;\/Function&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;Line&gt;43&lt;\/Line&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/CodeLocation&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/Frames&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/TestObject&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;TestObject xsi:type=\"ErrorSummary\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;Summary&gt;Timing violation: 712 &amp;gt; 100&lt;\/Summary&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/TestObject&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 &lt;\/Details&gt;\r\n\u00a0\u00a0\u00a0 &lt;\/TestRun&gt;\r\n\u00a0 &lt;\/TestRuns&gt;\r\n&lt;\/TestRunReport&gt;<\/pre>\n<\/li>\n<li>You can use the <strong>Test Framework<\/strong> page of <strong>VisualGDB Project Properties<\/strong> to customize various properties of the test framework:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/16-props.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-6646\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2016\/09\/16-props.png\" alt=\"\" width=\"868\" height=\"745\" \/><\/a><\/li>\n<\/ol>\n<p>Now that you got the first test project running, try the following tutorials to get the best out of your test setup:<\/p>\n<ul>\n<li><a href=\"https:\/\/visualgdb.com\/tutorials\/arm\/tests\/resources\/\">Feeding data into unit tests with Test Resources<\/a><\/li>\n<li><a href=\"https:\/\/visualgdb.com\/tutorials\/profiler\/embedded\/coverage\/\">Analyzing code coverage for embedded projects<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to use VisualGDB to create unit tests for embedded projects that will run directly on the<\/p>\n","protected":false},"author":1,"featured_media":1993,"comment_status":"closed","ping_status":"closed","sticky":true,"template":"","format":"standard","meta":{"footnotes":""},"categories":[128],"tags":[53,61,129],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/1973"}],"collection":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/comments?post=1973"}],"version-history":[{"count":4,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/1973\/revisions"}],"predecessor-version":[{"id":1995,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/1973\/revisions\/1995"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media\/1993"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=1973"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=1973"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=1973"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}