diff --git a/README.md b/README.md index de28a8fc..8dfe6835 100644 --- a/README.md +++ b/README.md @@ -403,6 +403,7 @@ Licensed under the Apache License, Version 2.0 - [rfcapi API Reference](rfcapi/docs/README.md) - [tr181api API Reference](tr181api/docs/README.md) +- [RFC Parameter Runtime Priority](docs/rfc-parameter-runtime-priority.md) - [Build System Instructions](.github/instructions/build-system.instructions.md) - [C Embedded Standards](.github/instructions/c-embedded.instructions.md) - [L2 Test Runner Agent](.github/agents/l2-test-runner.agent.md) diff --git a/docs/rfc-parameter-runtime-priority.md b/docs/rfc-parameter-runtime-priority.md index df914c43..b1301c6f 100644 --- a/docs/rfc-parameter-runtime-priority.md +++ b/docs/rfc-parameter-runtime-priority.md @@ -196,6 +196,106 @@ If duplicate keys exist across files, effective precedence is determined by merg --- +## Path 4: XConf Write Path and Firmware Upgrade Behavior + +This is the path used by `rfcMgr` when it applies a fresh XConf response to the device. +Understanding this path is essential for explaining why parameter values set by XConf +persist across firmware upgrades. + +### How XConf Writes Persist + +When `rfcMgr` successfully downloads an XConf response, it calls +`processXconfResponseConfigDataPart()`, which writes every parameter received from XConf +directly into `/opt/secure/RFC/tr181store.ini`. + +`/opt/secure/` is a **persistent storage partition** that survives firmware upgrades. +A firmware upgrade does not erase this partition. + +This means any value XConf has ever pushed to a device remains at **priority 2** in the +runtime stack until XConf explicitly sends a different value or the parameter is manually +cleared. + +### Firmware Upgrade Sequence + +On every `rfcMgr` startup, `IsNewFirmwareFirstRequest()` compares the firmware string +stored in `/opt/secure/RFC/.version` against the currently running firmware: + +```cpp +// rfc_xconf_handler.cpp +bool RuntimeFeatureControlProcessor::IsNewFirmwareFirstRequest(void) +{ + if ((_last_firmware.empty()) || + (!_firmware_version.empty() && + (_last_firmware.compare(_firmware_version) != 0))) + { + return true; // new firmware detected + } + return false; +} +``` + +When this returns `true`, `clearDB()` is called **before** the XConf response is +processed. `clearDB()` is not a factory reset — it truncates `tr181store.ini` and signals +`tr69hostif` to flush its in-memory state, then immediately re-populates the store from +the fresh XConf response. + +```mermaid +sequenceDiagram + participant rfcMgr + participant VersionFile as /opt/secure/RFC/.version + participant XConf as XConf Server + participant Store as tr181store.ini + + rfcMgr->>VersionFile: Read _last_firmware + rfcMgr->>rfcMgr: IsNewFirmwareFirstRequest() + alt firmware changed + rfcMgr->>XConf: GET featureControl/getSettings + XConf-->>rfcMgr: { param: value, ... } + rfcMgr->>Store: clearDB() — truncate + rfcMgr->>Store: processXconfResponseConfigDataPart() — write XConf values + rfcMgr->>VersionFile: WriteFile(".version", new_fw) + else same firmware + rfcMgr->>XConf: GET featureControl/getSettings + XConf-->>rfcMgr: { param: value, ... } + rfcMgr->>Store: processXconfResponseConfigDataPart() — write XConf values + end +``` + +### Why a Parameter Value Is the Same After Upgrade + +A firmware upgrade does not notify XConf. XConf continues to send the same value for a +parameter until an operator changes the XConf server-side configuration for that +device/account combination. The sequence is: + +1. XConf sends `param=value` → written to `tr181store.ini` +2. Firmware is upgraded +3. `clearDB()` truncates `tr181store.ini` +4. XConf is queried again and **sends the same `param=value`** +5. `param=value` is written back into `tr181store.ini` + +The XML `` element in `data-model.xml` is at priority 5 and is never consulted +because the XConf response at priority 2 fills `tr181store.ini` before any fallback is +needed. + +### Key Files Involved + +| File | Partition | Survives upgrade | Purpose | +|---|---|---|---| +| `/opt/secure/RFC/tr181store.ini` | `/opt/secure/` (persistent) | **Yes** | XConf-applied parameter values | +| `/opt/secure/RFC/.version` | `/opt/secure/` (persistent) | **Yes** | Last firmware that processed XConf | +| `/opt/secure/RFC/bootstrap.ini` | `/opt/secure/` (persistent) | **Yes** | Bootstrap-backed overrides | +| `/tmp/data-model.xml` | tmpfs | No | XML factory defaults — lowest priority | + +### Changing an XConf-Applied Value + +| Method | Command | Effect | +|---|---|---| +| Update XConf rule | Change device/account rule in XConf server | Permanent; takes effect on next `rfcMgr` run | +| Local override | `tr181 -s -v -n string` | Temporary; overwritten on next successful XConf fetch | +| Clear the parameter | `tr181 -c ` | Removes entry from store; XML default takes effect until XConf runs again | + +--- + ## Combined Conceptual Priority If you want one combined conceptual ordering across all runtime sources, the safest summary is: @@ -298,6 +398,64 @@ Why: - `validateAgainstDataModel()` had already captured XML `defaultValue` - the live request path returns the XML default, not `/etc/rfcdefaults` +### Example F: parameter value is the same after firmware upgrade + +Consider: + +```text +Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.TR069support.Enable +``` + +State before upgrade: + +- XConf has a rule pushing `Enable=true` for this device +- `/opt/secure/RFC/tr181store.ini` contains `...TR069support.Enable=true` +- `data-model.xml` has `` + +After upgrading from one firmware version to another: + +Returned value: + +```text +true +``` + +Why: + +- `/opt/secure/` is persistent — `tr181store.ini` survives the upgrade +- `rfcMgr` detects a new firmware via `/opt/secure/RFC/.version`, calls `clearDB()` which + truncates `tr181store.ini`, then immediately queries XConf +- XConf still has the same rule and returns `Enable=true` +- `processXconfResponseConfigDataPart()` writes `true` back into `tr181store.ini` +- `tr181store.ini` is at priority 2; the XML default of `false` at priority 5 is never reached + +**Diagnostic check:** + +```bash +# Confirm XConf set the value +grep -i "TR069support\|Feature Name" /opt/logs/rfcscript.log | tail -20 + +# Confirm clearDB ran on the upgrade +grep -i "Clearing DB\|last_firmware\|different" /opt/logs/rfcscript.log | head -20 + +# Confirm version file was updated +cat /opt/secure/RFC/.version +``` + +Expected log evidence of firmware-change detection and re-apply: + +``` +GetLastProcessedFirmware: [] +Last Image version and current image version \ + are different +[clearDB] Clearing DB +[processXconfResponseConfigDataPart] Feature Name \ + [Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.TR069support.Enable] Value[true] +``` + +To change the value, update the XConf server-side rule — not the firmware. See +[Path 4](#path-4-xconf-write-path-and-firmware-upgrade-behavior) for the options. + --- ## See Also diff --git a/rfcapi/docs/README.md b/rfcapi/docs/README.md index a6bc8206..0bac627e 100644 --- a/rfcapi/docs/README.md +++ b/rfcapi/docs/README.md @@ -2,7 +2,7 @@ ## Overview -`librfcapi` is a C/C++ library that provides the canonical interface for reading and writing RFC (Remote Feature Control) parameters on RDK devices. It resolves parameter values from a layered file store: XConf-applied overrides first, then component defaults. All other RDK components use this library instead of accessing the INI files directly. +`librfcapi` is a C/C++ library that provides the canonical interface for reading and writing RFC (Remote Feature Control) parameters on RDK devices. When the TR-181 data model service (`tr69hostif`) is ready, reads are served from the live data model. Before that service is ready, the library falls back to layered file-backed state: XConf-applied overrides first, bootstrap values where applicable, and component defaults last. All other RDK components use this library instead of accessing the INI files directly. --- @@ -27,20 +27,37 @@ graph TB ### Lookup Priority +For TR181-style RFC keys (`Device.*`), the effective priority is: + +1. Live TR181 data model via `tr69hostif` when `/tmp/.tr69hostif_http_server_ready` exists +2. `/opt/secure/RFC/tr181store.ini` when the host interface is not ready +3. `/opt/secure/RFC/bootstrap.ini` when the host interface is not ready and the key is not present in `tr181store.ini` +4. `/tmp/rfcdefaults.ini` as the final fallback + +For legacy `RFC_xxxx` keys without a dot, the lookup remains file-based and reads `/opt/secure/RFC/rfcVariable.ini` directly. + ```mermaid flowchart LR A[getRFCParameter called] --> B{Key starts with RFC_\nand no dot?} B -->|Yes| C[Read rfcVariable.ini] - B -->|No| D[Read tr181store.ini] - D --> E{Found?} - E -->|Yes| F[Return value] - E -->|No| G[Read rfcdefaults.ini\n merged from /etc/rfcdefaults/] - G --> H{Found?} - H -->|Yes| F - H -->|No| I[Return WDMP_FAILURE] - C --> J{Found?} - J -->|Yes| F - J -->|No| I + B -->|No| D{tr69hostif ready?} + D -->|Yes| E[Read live data model\nvia localhost HTTP] + D -->|No| F[Read tr181store.ini] + F --> G{Found?} + G -->|Yes| H[Return value] + G -->|No| I[Read bootstrap.ini] + I --> J{Found?} + J -->|Yes| H + J -->|No| K[Read rfcdefaults.ini\nmerged from /etc/rfcdefaults/] + K --> L{Found?} + L -->|Yes| H + L -->|No| M[Return WDMP_FAILURE] + C --> N{Found?} + N -->|Yes| H + N -->|No| M + E --> O{Found?} + O -->|Yes| H + O -->|No| M ``` --- @@ -78,7 +95,7 @@ typedef enum { ### `getRFCParameter()` -Reads a single RFC parameter value from the local file store. +Reads a single RFC parameter value from the live TR181 data model when available, otherwise from the local fallback stores. **Signature (non-RDKB):** ```c @@ -230,6 +247,13 @@ bool isFileInDirectory(const char *filename, const char *directory); `getRFCParameter` merges all `.ini` files under `/etc/rfcdefaults/` into `/tmp/rfcdefaults.ini` on first access if the merged file does not exist. Component default files must be named `.ini` and placed in `/etc/rfcdefaults/`. +`/etc/rfcdefaults/*.ini` only affects the result when a requested key was not resolved from a higher-priority source. In practice that means: + +1. If `tr69hostif` is up, the live data model wins and defaults are not consulted. +2. If `tr69hostif` is not up, `tr181store.ini` wins over defaults. +3. `bootstrap.ini` also wins over defaults during the pre-hostif phase. +4. Defaults are used only as the final fallback for missing keys. + ```mermaid graph TD A["/etc/rfcdefaults/\nauth.ini\ntelemetry.ini\nip.ini\n..."] -->|"concat at runtime"| B["/tmp/rfcdefaults.ini"] diff --git a/test/docs/l2-test-coverage-analysis.md b/test/docs/l2-test-coverage-analysis.md index 77ed4272..c5a8dd5b 100644 --- a/test/docs/l2-test-coverage-analysis.md +++ b/test/docs/l2-test-coverage-analysis.md @@ -8,6 +8,74 @@ This document analyzes the L2 (integration/functional) test coverage for the RFC **Test Infrastructure:** Mock XConf HTTPS server (`rfcData.js`), mock parodus binary, Docker container **Execution Scripts:** `run_l2.sh` (main), `run_l2_reboot_trigger.sh` (reboot-specific) +### Test Directory Structure + +``` +test/functional-tests/ +├── features/ # Gherkin feature specifications (19 files) +│ ├── rfc_data.feature +│ ├── rfc_device_offline_status.feature +│ ├── rfc_dynamic_cert_selector.feature +│ ├── rfc_factory_reset.feature +│ ├── rfc_feature_enable.feature +│ ├── rfc_initialization_failure.feature +│ ├── rfc_override_rfc_prop.feature +│ ├── rfc_reboot_required.feature +│ ├── rfc_setget_param.feature +│ ├── rfc_single_instance_run.feature +│ ├── rfc_static_cert_selector.feature +│ ├── rfc_tr181_setget_local_param.feature +│ ├── rfc_trigger_reboot.py # Note: .py extension but Gherkin content +│ ├── rfc_unknown_accountid.feature +│ ├── rfc_valid_accountid.feature +│ ├── rfc_webpa.feature +│ ├── rfc_xconf_communication.feature +│ ├── rfc_xconf_configsetHash_time.feature +│ └── rfc_xconf_request_params.feature +└── tests/ # pytest implementation (20 files) + ├── rfc_test_helper.py # Shared utility functions + ├── test_rfc_device_offline_status.py + ├── test_rfc_dynamic_static_cert_selector.py + ├── test_rfc_factory_reset.py + ├── test_rfc_feature_enable.py + ├── test_rfc_initialization_failure.py + ├── test_rfc_override_rfc_prop.py + ├── test_rfc_setget_param.py + ├── test_rfc_single_instance_run.py + ├── test_rfc_static_cert_selector.py + ├── test_rfc_tr181_setget_local_param.py + ├── test_rfc_trigger_reboot.py + ├── test_rfc_unknown_accountid.py + ├── test_rfc_valid_accountid.py + ├── test_rfc_webpa.py + ├── test_rfc_xconf_communication.py + ├── test_rfc_xconf_configsethash_time.py + ├── test_rfc_xconf_reboot.py + ├── test_rfc_xconf_request_params.py + └── test_rfc_xconf_rfc_data.py +``` + +### Test Execution Model + +All L2 tests operate by: + +1. Setting up preconditions (files, properties, environment) +2. Running the `rfcMgr` binary directly (`/usr/bin/rfcMgr`) +3. Asserting expected log messages in `/opt/logs/rfcscript.log` +4. Optionally verifying file-system side effects or CLI outputs + +The test helper (`rfc_test_helper.py`) provides: + +| Helper | Purpose | +|--------|---------| +| `initial_rfc_setup()` | Creates route file, gateway IP, partner ID, MAC address, XConf URL, secure dir, firmware version file | +| `rfc_run_binary()` | Executes `/usr/bin/rfcMgr` | +| `grep_log_file(file, string)` | Asserts string presence in rfcMgr log output | +| `search_log_file(file, string)` | Returns last matching log line | +| `run_shell_command(cmd)` | Executes shell command and returns stdout | +| `write_on_file(file, content)` | Writes content to file | +| `get_FWversion()` | Reads firmware version from `/version.txt` | + --- ## 1. Current L2 Test Coverage @@ -828,9 +896,161 @@ Feature: RFC Manager Scheduled Reboot --- -## 3. Coverage Matrix +## 3. RFC Manager Component Coverage + +This section maps the `rfcMgr` daemon's core responsibilities to the specific L2 tests that +exercise them. The rfcMgr binary is the primary component under test — every active L2 test +invokes it via the `rfc_run_binary()` helper. + +### 3.1 rfcMgr Lifecycle vs L2 Test Coverage + +```mermaid +sequenceDiagram + participant Test as pytest + participant rfcMgr + participant Lock as /tmp/.rfcServiceLock + participant Net as isDnsResolve() + participant Props as rfc.properties + participant XConf as Mock XConf Server + participant Store as tr181store.ini + + Test->>rfcMgr: rfc_run_binary() + rfcMgr->>Lock: Acquire file lock + Note right of Lock: test_rfc_single_instance_run.py + rfcMgr->>Net: CheckDeviceIsOnline() + Note right of Net: test_rfc_device_offline_status.py + rfcMgr->>Props: GetServURL() + Note right of Props: test_rfc_initialization_failure.py
test_rfc_override_rfc_prop.py + rfcMgr->>XConf: DownloadRuntimeFeatures() + Note right of XConf: test_rfc_xconf_communication.py
test_rfc_feature_enable.py
test_rfc_xconf_request_params.py + XConf-->>rfcMgr: JSON Response + configSetHash header + rfcMgr->>rfcMgr: PreProcessJsonResponse() + Note right of rfcMgr: test_rfc_valid_accountid.py
test_rfc_unknown_accountid.py
test_rfc_trigger_reboot.py + rfcMgr->>Store: processXconfResponseConfigDataPart() + Note right of Store: test_rfc_factory_reset.py
test_rfc_xconf_rfc_data.py
test_rfc_xconf_configsethash_time.py + rfcMgr->>rfcMgr: SendEventToMaintenanceManager() + Note right of rfcMgr: test_rfc_xconf_reboot.py +``` + +### 3.2 rfcMgr Function-to-Test Mapping -### 3.1 Source File Coverage +| rfcMgr Function | Responsibility | L2 Test File(s) | Coverage Status | +|---|---|---|---| +| `main()` → lock file check | Single-instance guard | `test_rfc_single_instance_run.py` | Covered | +| `main()` → `createDirectoryIfNotExists()` | `/opt/secure/RFC` creation | `test_rfc_xconf_rfc_data.py` (implicit) | Partial | +| `RFCManager()` → `InitializeIARM()` | IARM bus setup | None | **Gap** | +| `CheckDeviceIsOnline()` → `isDnsResolve()` | DNS-based online check | `test_rfc_device_offline_status.py` | Covered | +| `CheckDeviceIsOnline()` → `CheckIProuteConnectivity()` | IP route retry | None | **Gap** | +| `RFCManagerProcess()` → `InitializeRuntimeFeatureControlProcessor()` | Props parsing, URL, FW version | `test_rfc_initialization_failure.py` | Covered | +| `GetServURL()` → persistent file override | `/opt/rfc.properties` priority | `test_rfc_override_rfc_prop.py` | Covered | +| `IsNewFirmwareFirstRequest()` | Firmware change detection | None | **Gap** | +| `clearDB()` + `rfcStashStoreParams()` | DB clear on FW change | None | **Gap** | +| `CreateXconfHTTPUrl()` | URL construction with device params | `test_rfc_xconf_request_params.py` | Covered | +| `DownloadRuntimeFeatutres()` | mTLS + CURL download | `test_rfc_xconf_communication.py` | Partial (HTTP only) | +| `ProcessRuntimeFeatureControlReq()` → HTTP 200 | Successful XConf processing | `test_rfc_xconf_communication.py` | Covered | +| `ProcessRuntimeFeatureControlReq()` → HTTP 304 | Not-modified handling | `test_rfc_feature_enable.py` | Covered | +| `ProcessRuntimeFeatureControlReq()` → HTTP 404 | Not-found handling | `test_rfc_xconf_communication.py` | Covered | +| `ProcessRuntimeFeatureControlReq()` → CURL code 6 | DNS resolution failure | `test_rfc_xconf_communication.py` | Covered | +| `ProcessRuntimeFeatureControlReq()` → retry logic | 3 retries with 15s delay | None | **Gap** | +| `PreProcessJsonResponse()` → `GetValidAccountId()` | AccountID validation | `test_rfc_valid_accountid.py` | Covered | +| `PreProcessJsonResponse()` → `GetValidPartnerId()` | PartnerID validation | None | **Gap** | +| `PreProcessJsonResponse()` → `GetXconfSelect()` | Slot selection (prod/ci/automation) | None | **Gap** | +| `processXconfResponseConfigDataPart()` | Config data apply to store | `test_rfc_xconf_rfc_data.py`, `test_rfc_factory_reset.py` | Covered | +| `processXconfResponseConfigDataPart()` → empty value reject | EMPTY value rejection | `test_rfc_factory_reset.py` | Covered | +| `isConfigValueChange()` | Change detection | `test_rfc_trigger_reboot.py` (indirect) | Partial | +| `rfcCheckAccountId()` | Unknown → AuthService replacement | `test_rfc_unknown_accountid.py` | Covered | +| `updateHashAndTimeInDB()` | configSetHash + time persistence | `test_rfc_xconf_configsethash_time.py` | Covered | +| `SendEventToMaintenanceManager()` | Reboot required event | `test_rfc_xconf_reboot.py` | Covered | +| `RFCManagerPostProcess()` | Post-process script exec | None | **Gap** | +| `manageCronJob()` | DCM cron scheduling | None | **Gap** | +| `getMtlscert()` | mTLS certificate selection | `test_rfc_dynamic_static_cert_selector.py` | **Disabled** | +| `NotifyTelemetry2Count()` / `NotifyTelemetry2Value()` | Telemetry markers | None | **Gap** | + +### 3.3 rfcMgr Coverage by Execution Phase + +```mermaid +flowchart LR + subgraph "Phase 1: Startup" + A[Lock file] --> B[Dir creation] + B --> C[IARM init] + end + subgraph "Phase 2: Online Check" + D[DNS resolve] --> E[IP route check] + end + subgraph "Phase 3: XConf Fetch" + F[URL construction] --> G[mTLS cert] + G --> H[CURL download] + H --> I[Retry on failure] + end + subgraph "Phase 4: Response Processing" + J[JSON parse] --> K[Account/Partner validate] + K --> L[Config data apply] + L --> M[Hash/time update] + end + subgraph "Phase 5: Post-Process" + N[Reboot event] --> O[Post-process script] + O --> P[Cron management] + P --> Q[Telemetry report] + end + + style A fill:#4CAF50,color:white + style B fill:#A5D6A7 + style C fill:#F44336,color:white + style D fill:#4CAF50,color:white + style E fill:#F44336,color:white + style F fill:#4CAF50,color:white + style G fill:#FFC107,color:black + style H fill:#4CAF50,color:white + style I fill:#F44336,color:white + style J fill:#F44336,color:white + style K fill:#4CAF50,color:white + style L fill:#4CAF50,color:white + style M fill:#4CAF50,color:white + style N fill:#4CAF50,color:white + style O fill:#F44336,color:white + style P fill:#F44336,color:white + style Q fill:#F44336,color:white +``` + +**Legend:** Green = covered | Yellow = disabled test | Red = gap + +### 3.4 Feature File to Test Script Mapping + +Each `.feature` file in `test/functional-tests/features/` specifies expected behavior in Gherkin +syntax. The corresponding pytest file in `tests/` implements the actual verification. Not all +feature scenarios have 1:1 implementation — some tests verify multiple scenarios in a single +function. + +| Feature File | Test Script | Scenario Count (Feature) | Test Functions (Actual) | +|---|---|---|---| +| `rfc_single_instance_run.feature` | `test_rfc_single_instance_run.py` | 1 | 1 | +| `rfc_device_offline_status.feature` | `test_rfc_device_offline_status.py` | 1 | 1 | +| `rfc_initialization_failure.feature` | `test_rfc_initialization_failure.py` | 2 | 2 | +| `rfc_xconf_communication.feature` | `test_rfc_xconf_communication.py` | 3 | 3 | +| `rfc_feature_enable.feature` | `test_rfc_feature_enable.py` | 3 | 1 (304 only) | +| `rfc_setget_param.feature` | `test_rfc_setget_param.py` | 2 | 2 | +| `rfc_tr181_setget_local_param.feature` | `test_rfc_tr181_setget_local_param.py` | 2 | 2 | +| `rfc_data.feature` | `test_rfc_xconf_rfc_data.py` | 1 | 2 | +| `rfc_xconf_request_params.feature` | `test_rfc_xconf_request_params.py` | 1 | 1 | +| `rfc_valid_accountid.feature` | `test_rfc_valid_accountid.py` | 1 | 2 | +| `rfc_unknown_accountid.feature` | `test_rfc_unknown_accountid.py` | 1 | 1 | +| `rfc_factory_reset.feature` | `test_rfc_factory_reset.py` | 2 | 4 | +| `rfc_trigger_reboot.py` | `test_rfc_trigger_reboot.py` | 1 | 1 | +| `rfc_reboot_required.feature` | `test_rfc_xconf_reboot.py` | 1 | 2 | +| `rfc_xconf_configsetHash_time.feature` | `test_rfc_xconf_configsethash_time.py` | 3 | 3 | +| `rfc_override_rfc_prop.feature` | `test_rfc_override_rfc_prop.py` | 1 | 3 | +| `rfc_dynamic_cert_selector.feature` | `test_rfc_dynamic_static_cert_selector.py` | 1 | 1 (disabled) | +| `rfc_static_cert_selector.feature` | `test_rfc_static_cert_selector.py` | 2 | 1 (disabled) | +| `rfc_webpa.feature` | `test_rfc_webpa.py` | 2 | 2 (disabled) | + +**Total active test functions:** 31 +**Total disabled test functions:** 4 + +--- + +## 4. Coverage Matrix + +### 4.1 Source File Coverage | Source File | Functions | L2 Tested | Coverage | |---|---|---|---| @@ -845,7 +1065,7 @@ Feature: RFC Manager Scheduled Reboot | `jsonhandler.cpp` | 7 | 1 (indirect via data files) | ~14% | | `tr181utils.cpp` | 8 | 2 (CLI get/set used in tests) | ~25% | -### 3.2 Error Path Coverage +### 4.2 Error Path Coverage | Error Category | Total Paths | Covered | Gap | |---|---|---|---| @@ -857,7 +1077,7 @@ Feature: RFC Manager Scheduled Reboot | Type conversion errors | 5 | 0 | 5 paths | | Semaphore failures | 4 | 0 | 4 paths | -### 3.3 Feature Branch Coverage +### 4.3 Feature Branch Coverage | Compile Flag | Active Tests | Gap | |---|---|---| @@ -870,7 +1090,7 @@ Feature: RFC Manager Scheduled Reboot --- -## 4. Recommended Test Implementation Priority +## 5. Recommended Test Implementation Priority ### Phase 1 — Critical (Immediate) @@ -904,7 +1124,7 @@ Feature: RFC Manager Scheduled Reboot --- -## 5. Mock Server Enhancement Requirements +## 6. Mock Server Enhancement Requirements To support the missing test scenarios, the mock XConf server (`rfcData.js`) needs: @@ -936,7 +1156,7 @@ test/test-artifacts/mockxconf/ --- -## 6. Issues Found in Existing Tests +## 7. Issues Found in Existing Tests | Issue | File | Description | |---|---|---| @@ -949,7 +1169,9 @@ test/test-artifacts/mockxconf/ --- -## 7. Test Coverage Summary +## 8. Test Coverage Summary + +### Overall Module ``` Total source functions (approx): ~120 @@ -968,12 +1190,38 @@ Estimated current L2 functional coverage: ~35% Target L2 functional coverage: ~80% ``` +### rfcMgr Component Specific + +``` +rfcMgr source files: 6 (rfc_main, rfc_manager, rfc_common, + rfc_xconf_handler, xconf_handler, mtlsUtils) +rfcMgr total functions: ~75 +rfcMgr functions with L2 coverage: ~22 (including indirect) +rfcMgr functions with NO L2 coverage: ~53 + +rfcMgr execution phases: 5 (Startup, Online Check, XConf Fetch, + Response Processing, Post-Process) +Phases with adequate coverage: 2 (Online Check, Response Processing) +Phases with partial coverage: 2 (Startup, XConf Fetch) +Phases with poor coverage: 1 (Post-Process) + +Key coverage gaps: + - IARM bus initialization/events 0% tested + - mTLS certificate selection 0% active (tests disabled) + - Firmware upgrade flow (clearDB/stash) 0% tested + - Retry logic (3x15s on failure) 0% tested + - JSON parse error handling 0% tested + - RDKB-specific code paths 0% tested + - Post-process script execution 0% tested + - Telemetry notification 0% tested +``` + --- ## See Also -- [RFC Parameter Runtime Priority](rfc-parameter-runtime-priority.md) — Parameter precedence documentation -- [RFC API Reference](../rfcapi/docs/README.md) — RFC API documentation -- [TR181 API Reference](../tr181api/docs/README.md) — TR181 API documentation -- [Test Execution](../run_l2.sh) — L2 test execution script -- [Mock XConf Server](../test/test-artifacts/mockxconf/rfcData.js) — Mock server implementation +- [RFC Parameter Runtime Priority](../../docs/rfc-parameter-runtime-priority.md) — Parameter precedence documentation +- [RFC API Reference](../../rfcapi/docs/README.md) — RFC API documentation +- [TR181 API Reference](../../tr181api/docs/README.md) — TR181 API documentation +- [Test Execution](../../run_l2.sh) — L2 test execution script +- [Mock XConf Server](../test-artifacts/mockxconf/rfcData.js) — Mock server implementation diff --git a/tr181api/docs/README.md b/tr181api/docs/README.md index a87e764b..f42fb4ab 100644 --- a/tr181api/docs/README.md +++ b/tr181api/docs/README.md @@ -33,25 +33,48 @@ graph TB ### Store Hierarchy +For TR181 reads through `getParam()`, the effective priority is: + +1. Live TR181 data model via `getRFCParameter()` when `tr69hostif` is ready +2. `/opt/secure/RFC/tr181store.ini` when `tr69hostif` is not ready +3. `/opt/secure/RFC/bootstrap.ini` when `tr69hostif` is not ready and the key is not in `tr181store.ini` +4. Merged defaults from `/etc/rfcdefaults/*.ini` via `/tmp/rfcdefaults.ini` + +For local reads through `getLocalParam()`, the priority is: + +1. `/opt/secure/RFC/tr181localstore.ini` +2. `/etc/rfcdefaults/.ini` + ```mermaid flowchart LR - A[getParam called] --> B["Read tr181store.ini\n(via getRFCParameter)"] - B --> C{Found?} - C -->|Yes| D[Return value] - C -->|No| E["Read rfcdefaults.ini\n(via getRFCParameter)"] - E --> F{Found?} - F -->|Yes| D - F -->|No| G[Return tr181Failure] - - H[getLocalParam called] --> I["Read tr181localstore.ini\n(direct file read)"] + A[getParam called] --> B{tr69hostif ready?} + B -->|Yes| C["Read live data model\n(via getRFCParameter)"] + B -->|No| D["Read tr181store.ini\n(via getRFCParameter)"] + D --> E{Found?} + E -->|Yes| F[Return value] + E -->|No| G["Read bootstrap.ini\n(via getRFCParameter)"] + G --> H{Found?} + H -->|Yes| F + H -->|No| I["Read rfcdefaults.ini\n(via getRFCParameter)"] I --> J{Found?} - J -->|Yes| D - J -->|No| G - - K[getDefaultValue called] --> L["Read /etc/rfcdefaults/\n.ini"] - L --> M{Found?} - M -->|Yes| D - M -->|No| G + J -->|Yes| F + J -->|No| K[Return tr181Failure] + C --> L{Found?} + L -->|Yes| F + L -->|No| K + + M[getLocalParam called] --> N["Read tr181localstore.ini\n(direct file read)"] + N --> O{Found?} + O -->|Yes| F + O -->|No| P["Read /etc/rfcdefaults/\n.ini"] + P --> Q{Found?} + Q -->|Yes| F + Q -->|No| K + + R[getDefaultValue called] --> S["Read /etc/rfcdefaults/\n.ini"] + S --> T{Found?} + T -->|Yes| F + T -->|No| K ``` --- @@ -214,7 +237,7 @@ tr181ErrorCode_t clearParam(char *pcCallerID, ### `getLocalParam()` -Reads a parameter exclusively from the device-local store (`tr181localstore.ini`). XConf-applied values are **not** consulted. +Reads a parameter from the device-local store (`tr181localstore.ini`) and, if it is absent there, falls back to the caller-specific defaults file in `/etc/rfcdefaults/.ini`. XConf-applied values are **not** consulted. **Signature:** ```c @@ -223,7 +246,7 @@ tr181ErrorCode_t getLocalParam(char *pcCallerID, TR181_ParamData_t *pstParamData); ``` -**Use case:** Components that manage their own persistent state independently of XConf policy. +**Use case:** Components that manage their own persistent state independently of XConf policy, while still allowing a component-owned default to be supplied from `/etc/rfcdefaults/.ini`. ---