M07-L01 — Test policy independently of hardware
Execute the same normal/fault policy source with controlled input. CP07 includes basic native assertions; it does not include thresholds, hysteresis, or the command parser. Read its README and stage requirements before using the final solution as a reference.
The host compiler builds the shared policy and output source with the test harness. The firmware cross-compiler builds those shared files for the documented target. Shared source prevents a test-only reimplementation from drifting away from the firmware, but host execution still does not simulate electronics or MCU timing.
Derive expectations from the contract. Startup is FAULT/missing. One fresh valid sample remains FAULT/recovering; a second recovers to NORMAL. Missing, nonfinite, and stale input fault. Age exactly 3000 milliseconds remains current; age 3001 becomes stale. An invalid sample or stale gap interrupts recovery.
Inspect tests/test_monitor.cpp and identify one assertion tied to each condition. The injected clock avoids waiting in real time. It is a test input, not a measured scheduling result. Ordered calls must respect the documented unsigned-timestamp assumptions.
Run from the project root:
.\scripts\test.cmd
.\scripts\replay.cmd fixtures/stale-recovery.csv
Inspect the actual process result and relevant assertion/output. A passing suite supports behavior for its cases and source. A valid replay can intentionally enter FAULT and still exit zero. Do not label that expected fault as a malformed fixture or a broken test runner.
Write a short requirement-to-check map with input, starting condition, expected outcome, actual result, and evidence pointer. Keep expected output separate from your run. The test's value lies in its ability to distinguish a meaningful error; the next lesson will deliberately challenge the stale boundary.
Resources and completion
Read Chapter 15 and the matching Sensor Monitor checkpoint README, requirements, and evidence notes. Project paths are relative to its root. Figures: SS15-01, SS15-02, D12. Attempt the exercise, preserve actual results, then use the separate instructor answer key.
Source resources: Chapter 15; checkpoint and recovery map. Use the README and requirements inside your named checkpoint; the repository's final CP09 files include features absent from earlier stages. View D12 with its accessible description.
Recording transcript
Testing the policy on your computer gives you a controlled way to ask whether the software follows its requirements. In this lesson, we will inspect and run the basic normal and fault tests. Start with checkpoint seven. This stage has native assertions and synthetic replay, but it does not yet contain the capstone threshold, hysteresis, or command parser. The checkpoint matters when interpreting the output.
Open the README and stage requirements before the implementation. The monitor starts in fault with missing input. One fresh valid sample remains in fault with a recovering error. A second consecutive fresh valid sample recovers to normal. Missing, nonfinite, or stale input enters fault and resets the recovery count. These are the behaviors we will ask the tests to establish for controlled cases.
The host harness compiles the actual shared policy and output source. The firmware build compiles those shared files for the selected embedded target. We are avoiding a separate test-only implementation that could behave differently from the firmware. Shared source is valuable, but it does not make the host an electronic simulator or reproduce the microcontroller's execution timing.
Open the test file and identify the first monitor instance. The assertions inspect startup state, error, and whether a current temperature is available. These expectations come from the requirements. They should not be chosen simply because the present implementation returns those values. A test that copies the implementation's assumption can repeat its error instead of exposing it.
Follow the first two samples. The first is valid and fresh, yet the state remains fault while recovery is incomplete. The second fresh valid sample permits normal operation. Notice the difference between a current numeric temperature and the overall policy state. Recovering can have a fresh value while remaining fault. That distinction will matter again when reading the JSON output.
Now inspect the freshness boundary. The last sample is at timestamp one thousand. At timestamp four thousand, its age is exactly three thousand milliseconds and it remains current. At four thousand and one, the age exceeds the limit and the reading becomes stale. This is a precise equality requirement, not a general instruction to expire old data eventually.
The clock values are injected test inputs. We do not have to wait three real seconds to check the behavior, and the timestamps do not measure physical scheduling. The arithmetic assumes ordered calls within the documented interval. The rollover case exercises the same rule across the unsigned counter boundary. It does not establish behavior for arbitrarily reordered or widely separated timestamps.
Find the missing and nonfinite cases. Missing input means the adapter explicitly failed to supply a value. NaN and infinity are invalid numeric inputs. They must enter fault and prevent the stale or invalid value from being described as current. The recovery sequence then checks that an invalid sample between valid samples restarts the count instead of allowing premature recovery.
Run the maintained test wrapper from the Sensor Monitor root. The exact PowerShell command is in the written lesson and ends in scripts slash test dot cmd. Inspect the compiler stage, process result, and reported assertions. If compilation fails because a tool is missing, record that setup problem. It is different from a compiled program reporting a failed assertion.
A passing test run supports the cases it actually executed against that source. Record the checkpoint, command, compiler information available in the setup record, and actual result. Do not treat an assertion count as a complete quality score. The important question is whether the cases distinguish the requirements that matter. We will deliberately challenge one of those cases in the next lesson.
Now run the stale-recovery fixture with the replay wrapper. This path exercises input interpretation and output as well as the shared policy. Read the expected sequence for checkpoint seven, not the later alert-capable solution. The fixture's valid events intentionally include stale and missing conditions. A fault state can be the correct result of a successful replay.
Distinguish three outcomes as you inspect the terminal. A valid fixture may produce fault while the replay process exits successfully. A malformed fixture can be rejected with a line-number diagnostic. A test executable can fail because an assertion disagrees with the requirement. Calling all three situations a failure without qualification would hide the next useful action.
Your exercise is to make a small requirement-to-check map. Include startup recovery, stale equality, and nonfinite input. For each, write the starting condition, controlled input, expected outcome, actual result, and evidence pointer. Use the requirement to justify the expectation. A copied output line is useful only when its source and procedure are identifiable.
Inspect the timestamped-sample case too. It distinguishes the time when a value was acquired from the time it is processed. A reading supplied now can already be stale if its acquisition timestamp is old. This protects against treating delayed input as a fresh measurement simply because the adapter delivered it during the current call.
Pause here to run the available checks and complete the map. If a dependency prevents execution, finish the static inspection and label the command result open. Do not fill the result column with a supplied reference pass. The reference helps you understand what should happen, but your own execution record must say what actually happened in your environment.
Before finishing, explain the shared-source diagram. Host tests and firmware compilation use the same policy implementation through different build paths. Controlled synthetic input establishes software behavior for selected cases. Physical acquisition, electrical faults, sensor accuracy, USB behavior, and device timing remain outside that result. The limitation is part of the explanation, not a reason to discard the useful software evidence.
Keep the preserved good checkpoint and your receipts. In the next lesson, we will change one comparison in a disposable copy and ask whether the tests reject the wrong boundary. That demonstration gives the passing run more meaning: it shows a specific defect the suite can detect while keeping the original good source available for recovery and independent comparison.
Download transcript