Diferență între revizuiri ale paginii „SDPT Lab 4”
(Pagină nouă: = Lab 4: Software Verification - TDD, Mocking, and Code Coverage = == Introduction == In traditional embedded systems, testing involves compiling code, flashing it to a board, and...) |
|||
| Linia 10: | Linia 10: | ||
* Simulate hardware interactions using Google Mock (GMock). | * Simulate hardware interactions using Google Mock (GMock). | ||
* Generate interactive HTML Code Coverage reports using <code>gcovr</code>. | * Generate interactive HTML Code Coverage reports using <code>gcovr</code>. | ||
| − | |||
== Part 1: Theoretical Reference == | == Part 1: Theoretical Reference == | ||
| Linia 54: | Linia 53: | ||
</pre> | </pre> | ||
| + | === 1.4 Official Documentation & Tutorials === | ||
| + | When you get stuck on syntax or advanced features during the lab, refer to these official resources: | ||
| + | * '''Google Test:''' [https://google.github.io/googletest/primer.html Googletest Primer (Official Tutorial)] | ||
| + | * '''Google Mock:''' [https://google.github.io/googletest/gmock_for_dummies.html gMock for Dummies] and the [https://google.github.io/googletest/reference/mocking.html Mocking Reference] | ||
| + | * '''CMake FetchContent:''' [https://cmake.org/cmake/help/latest/module/FetchContent.html Official CMake FetchContent Documentation] | ||
| + | * '''Code Coverage (gcovr):''' [https://gcovr.com/en/stable/guide.html gcovr User Guide] | ||
== Part 2: The Challenges == | == Part 2: The Challenges == | ||
| Linia 124: | Linia 129: | ||
} | } | ||
</pre> | </pre> | ||
| − | |||
=== Challenge 1: The Verification Build System (Estimated: 20 mins) === | === Challenge 1: The Verification Build System (Estimated: 20 mins) === | ||
| Linia 137: | Linia 141: | ||
'''Verification:''' Run <code>cmake -DCMAKE_BUILD_TYPE=Debug ..</code> inside your build folder. It must configure without errors and download GTest. | '''Verification:''' Run <code>cmake -DCMAKE_BUILD_TYPE=Debug ..</code> inside your build folder. It must configure without errors and download GTest. | ||
| − | |||
=== Challenge 2: Pure Math TDD (Estimated: 25 mins) === | === Challenge 2: Pure Math TDD (Estimated: 25 mins) === | ||
| Linia 148: | Linia 151: | ||
# Compile and run <code>./unit_tests</code>. (Watch it fail! This is the RED phase). | # Compile and run <code>./unit_tests</code>. (Watch it fail! This is the RED phase). | ||
# Refactor <code>OvenController.cpp</code> to fix the integer division bug. Recompile. (Watch it pass! This is the GREEN phase). | # Refactor <code>OvenController.cpp</code> to fix the integer division bug. Recompile. (Watch it pass! This is the GREEN phase). | ||
| − | |||
=== Challenge 3: Hardware Mocking (Estimated: 40 mins) === | === Challenge 3: Hardware Mocking (Estimated: 40 mins) === | ||
| Linia 163: | Linia 165: | ||
#* Program the mock to return exactly <code>250.0f</code>. | #* Program the mock to return exactly <code>250.0f</code>. | ||
#* Run the test. It will FAIL due to Bug 1. Fix the production code so it triggers on `>=`. | #* Run the test. It will FAIL due to Bug 1. Fix the production code so it triggers on `>=`. | ||
| − | |||
=== Challenge 4: The Coverage Report (Estimated: 15 mins) === | === Challenge 4: The Coverage Report (Estimated: 15 mins) === | ||
| Linia 170: | Linia 171: | ||
# Ensure you have run <code>./unit_tests</code> at least once so the <code>.gcda</code> files are generated. | # Ensure you have run <code>./unit_tests</code> at least once so the <code>.gcda</code> files are generated. | ||
# Run the <code>gcovr</code> command provided in the Theoretical Reference section. | # Run the <code>gcovr</code> command provided in the Theoretical Reference section. | ||
| − | # Open the resulting <code>coverage.html</code> file in your web browser. | + | # Open the resulting <code>coverage.html</code> file in your web browser. Check that you have achieved 100% branch coverage. |
| − | + | == Submission and Evaluation (Moodle VPL) == | |
| + | For this lab, your work will be automatically graded by the Moodle Virtual Programming Lab (VPL) engine. | ||
| + | '''How to submit:''' | ||
| + | # Ensure your code compiles locally and all tests pass (Green). | ||
| + | # Do '''NOT''' upload your <code>build/</code> directory or any generated HTML files. | ||
| + | # Select the following files from your workspace: | ||
| + | #* <code>CMakeLists.txt</code> | ||
| + | #* <code>include/ISensor.h</code> | ||
| + | #* <code>include/OvenController.h</code> | ||
| + | #* <code>src/OvenController.cpp</code> | ||
| + | #* <code>tests/test_oven.cpp</code> | ||
| + | # Package these files into a single <code>.zip</code> archive (maintaining the folder structure). | ||
| + | # Go to the Lab 4 assignment on Moodle and upload your <code>.zip</code> file into the VPL submission portal. | ||
| + | # Click the '''Evaluate''' button. | ||
| − | + | The VPL server will automatically compile your CMake project, execute your Google Tests, and run <code>gcovr</code>. Your grade will be calculated based on the number of passing tests and the percentage of branch coverage achieved. | |
| − | |||
| − | |||
| − | |||
| − | |||
Versiunea curentă din 23 martie 2026 13:32
Lab 4: Software Verification - TDD, Mocking, and Code Coverage
Introduction
In traditional embedded systems, testing involves compiling code, flashing it to a board, and physically observing the hardware. This is slow, expensive, and scales poorly. Today, we implement "Shift-Left Testing." We will verify our C++ logic on our Host PC using automated frameworks before it ever touches a microcontroller.
Goals
- Integrate Google Test and Google Mock via CMake.
- Write Unit Tests using the AAA (Arrange, Act, Assert) pattern.
- Use Dependency Injection to decouple business logic from hardware registers.
- Simulate hardware interactions using Google Mock (GMock).
- Generate interactive HTML Code Coverage reports using
gcovr.
Part 1: Theoretical Reference
Use this section as a reference manual to complete the challenges in Part 2.
1.1 Google Test & Mock Cheat Sheet
Basic Assertions:
EXPECT_EQ(actual, expected);(Non-fatal equality check)EXPECT_FLOAT_EQ(actual, expected);(Safe float comparison)EXPECT_TRUE(condition);
Mocking an Interface (GMock):
class MockSensor : public ISensor {
public:
// MOCK_METHOD(ReturnType, MethodName, (Arguments), (override));
MOCK_METHOD(float, read_temperature, (), (override));
};
Setting Mock Expectations:
MockSensor fake_sensor;
// Program the mock to return 45.0 exactly twice.
EXPECT_CALL(fake_sensor, read_temperature())
.Times(2)
.WillRepeatedly(testing::Return(45.0f));
1.2 CMake Debug & Coverage Setup
Coverage mathematically requires a completely unoptimized binary mapping exactly to your source code lines.
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(my_target PRIVATE -O0 -g --coverage)
target_link_options(my_target PRIVATE --coverage)
endif()
1.3 Running gcovr
Execute this from inside your build/ directory after running the tests:
gcovr -r ../ . --html --html-details -o coverage.html -e ".*_deps.*"
1.4 Official Documentation & Tutorials
When you get stuck on syntax or advanced features during the lab, refer to these official resources:
- Google Test: Googletest Primer (Official Tutorial)
- Google Mock: gMock for Dummies and the Mocking Reference
- CMake FetchContent: Official CMake FetchContent Documentation
- Code Coverage (gcovr): gcovr User Guide
Part 2: The Challenges
Setup: The Raw Materials
You are building the safety controller for an industrial oven. It must read temperatures from an I2C sensor and trigger a physical alarm if it overheats.
Create a new workspace (sdpt-lab4). Create include/ and src/ directories. Populate them with these three files:
include/ISensor.h (The Hardware Interface)
#pragma once
// Pure virtual interface. We DO NOT implement hardware logic here.
class ISensor {
public:
virtual float read_temperature() = 0;
virtual ~ISensor() = default;
};
include/OvenController.h (The Logic Module)
#pragma once
#include "ISensor.h"
class OvenController {
private:
ISensor* temp_sensor;
bool alarm_active;
float max_safe_temp;
public:
// Constructor uses Dependency Injection!
OvenController(ISensor* sensor, float safe_limit = 250.0f);
void check_safety();
bool is_alarm_active() const;
// Pure math function
static float raw_adc_to_celsius(int adc_value);
};
src/OvenController.cpp (The Buggy Implementation)
#include "../include/OvenController.h"
OvenController::OvenController(ISensor* sensor, float safe_limit)
: temp_sensor(sensor), alarm_active(false), max_safe_temp(safe_limit) {}
void OvenController::check_safety() {
float current_temp = temp_sensor->read_temperature();
// BUG 1: It only triggers if STRICTLY greater. What if it equals the limit?
if (current_temp > max_safe_temp) {
alarm_active = true;
} else {
alarm_active = false;
}
}
bool OvenController::is_alarm_active() const {
return alarm_active;
}
float OvenController::raw_adc_to_celsius(int adc_value) {
// BUG 2: Integer division truncates the decimal! (e.g., 5/2 = 2, not 2.5)
return (adc_value / 4095) * 330.0f;
}
Challenge 1: The Verification Build System (Estimated: 20 mins)
We need a build system that fetches Google Test and compiles our code with coverage instrumentation. Your Task:
- Create a
CMakeLists.txtin the root directory. - Build a static library named
oven_libcontainingsrc/OvenController.cpp. - Use
FetchContentto pull Google Test (https://github.com/google/googletest.git, tagv1.13.0). - Create an executable named
unit_tests. (You will createtests/test_oven.cppin the next step). - Link
oven_lib,gtest_main, andgmockto your test executable. - Add the CMake logic to inject
-O0 -g --coverageONLY if the build type is "Debug".
Verification: Run cmake -DCMAKE_BUILD_TYPE=Debug .. inside your build folder. It must configure without errors and download GTest.
Challenge 2: Pure Math TDD (Estimated: 25 mins)
Let's catch the integer division bug using the Red-Green-Refactor cycle. Your Task:
- Create a
tests/directory and atests/test_oven.cppfile. - Include
<gtest/gtest.h>and yourOvenController.h. - Write a
TEST(MathTest, ConvertsADCToCelsius). - Use
EXPECT_FLOAT_EQto assert that an ADC value of2047(roughly half of 4095) returns approximately164.957f. - Compile and run
./unit_tests. (Watch it fail! This is the RED phase). - Refactor
OvenController.cppto fix the integer division bug. Recompile. (Watch it pass! This is the GREEN phase).
Challenge 3: Hardware Mocking (Estimated: 40 mins)
We need to test check_safety(), but we are on a PC. We have no I2C hardware. We must mock the ISensor.
Your Task:
- In your test file, include
<gmock/gmock.h>. - Create a
MockSensorclass that inherits fromISensor. UseMOCK_METHODto mockread_temperature. - Write a
TEST(SafetyTest, TriggersAlarmOnOverheat).- Arrange: Instantiate the MockSensor. Instantiate the OvenController, injecting the mock sensor into it with a safe limit of 250.0f.
- Expectation: Program the mock to return
260.0fwhen called. - Act: Call
check_safety(). - Assert: Expect that
is_alarm_active()is true.
- Write a second test:
TEST(SafetyTest, TriggersAlarmExactlyAtLimit).- Program the mock to return exactly
250.0f. - Run the test. It will FAIL due to Bug 1. Fix the production code so it triggers on `>=`.
- Program the mock to return exactly
Challenge 4: The Coverage Report (Estimated: 15 mins)
Did we test every branch of our code? Let's prove it mathematically. Your Task:
- Ensure you have run
./unit_testsat least once so the.gcdafiles are generated. - Run the
gcovrcommand provided in the Theoretical Reference section. - Open the resulting
coverage.htmlfile in your web browser. Check that you have achieved 100% branch coverage.
Submission and Evaluation (Moodle VPL)
For this lab, your work will be automatically graded by the Moodle Virtual Programming Lab (VPL) engine.
How to submit:
- Ensure your code compiles locally and all tests pass (Green).
- Do NOT upload your
build/directory or any generated HTML files. - Select the following files from your workspace:
CMakeLists.txtinclude/ISensor.hinclude/OvenController.hsrc/OvenController.cpptests/test_oven.cpp
- Package these files into a single
.ziparchive (maintaining the folder structure). - Go to the Lab 4 assignment on Moodle and upload your
.zipfile into the VPL submission portal. - Click the Evaluate button.
The VPL server will automatically compile your CMake project, execute your Google Tests, and run gcovr. Your grade will be calculated based on the number of passing tests and the percentage of branch coverage achieved.