From a7a27038d293b8fd498bdc30f9f8f1d509ac8705 Mon Sep 17 00:00:00 2001 From: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Date: Wed, 13 May 2026 17:01:10 +0530 Subject: [PATCH] Update rrdEventProcess.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: remove unclear inline note from static profile fallback path Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/ddda5e68-d84d-4a89-ab64-02935f00a57f Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> chore: plan length-validation update for static fallback Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/efaba2d2-7e7e-4927-bbe3-106dbb286e2b Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> fix: validate issue node length before installed-package fallback Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/efaba2d2-7e7e-4927-bbe3-106dbb286e2b Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> chore: clarify installed-package length guard semantics Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/efaba2d2-7e7e-4927-bbe3-106dbb286e2b Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> chore: align length-limit wording with validation semantics Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/efaba2d2-7e7e-4927-bbe3-106dbb286e2b Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> chore: improve fallback guard naming and failure logging Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/efaba2d2-7e7e-4927-bbe3-106dbb286e2b Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> refactor: centralize issue length and fallback skip logging Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/efaba2d2-7e7e-4927-bbe3-106dbb286e2b Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Update rrdDynamic.c Update rrdDynamic.c Update rrdEventProcess.c Update rrdCommon.h Update rrdDynamic.c Refactor issueTypeSplitter to include suffix handling Update rrdEventProcess.c Update rrdExecuteScript.c Refactor rrdEventProcess.h to rrdExecuteScript.h Update rrdExecuteScript.h Update rrdExecuteScript.h Update rrdEventProcess.h Update rrdExecuteScript.h Update rrdJsonParser.c Update rrdJsonParser.h Update rrdInterface.c Update rrdCommon.h Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Fix indentation for appendMode assignment Update rrdCommon.h Fix appendMode assignment in rrdEventProcess.c Update rrdEventProcess.c Update rrdJsonParser.c Update rrdEventProcess.c Update rrdExecuteScript.h Update rrdExecuteScript.c Update rrdExecuteScript.c Update rrdExecuteScript.c Remove redundant logging from rrdJsonParser Update rrdJsonParser.c Update rrdEventProcess.c Update rrdJsonParser.c Update rrdEventProcess.c Update rrdJsonParser.c Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Update rrdEventProcess.c Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Update rrdJsonParser.c Fix heap overflow in issueTypeSplitter and memory leaks in suffix handling Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/fbc52780-966b-4912-825f-3030aa43c3e9 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Update rrdUnitTestRunner.cpp Delete .gitignore Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdUnitTestRunner.cpp Update rrdEventProcess.c Update rrdUnitTestRunner.cpp Update rrdUnitTestRunner.cpp Add gtest test cases for split_issue_type Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/9996d741-248e-4e58-8689-b4ba873cfaf2 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Remove build artifacts, add .gitignore Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/9996d741-248e-4e58-8689-b4ba873cfaf2 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Add explicit truncated content assertion in SuffixTruncatedWhenTooSmall test Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/9996d741-248e-4e58-8689-b4ba873cfaf2 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdJsonParser.c Update rrdJsonParser.c Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdUnitTestRunner.cpp Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Delete .gitignore Delete src/unittest/UTJson/device.properties Update rrdEventProcess.c Add gtest tests for split_issue_type, suffix field, and processIssueTypeEvent Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/76cad72a-f67f-4c05-8fb5-bfadf0c173b3 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Delete .gitignore Validate suffix prefix: only _Search- and _LogSearch- are allowed Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/125526c2-7b70-48f5-8bec-fd725eea8a04 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Base never contains underscore: split at first _ and discard invalid suffix Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/904bf10d-546b-4038-a60c-9bc76094a225 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Update rrdEventProcess.c Update rrdUnitTestRunner.cpp Update rrdEventProcess.c Update rrdEventProcess.c Fix segfault in GTEST_ENABLE mode when rbuf->jsonPath is NULL Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/efdc0d6b-89e6-4423-b202-a900f5683839 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Remove accidentally committed dummy directory and update .gitignore Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/efdc0d6b-89e6-4423-b202-a900f5683839 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Fix IssueTypeSplitterTest to match new issueTypeSplitter behavior (no built-in special-char removal) Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/00abcaba-8a41-4b88-ae12-07b5ff780ff9 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Fix empty/whitespace IssueType bypassing processIssueTypeEvent guards Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/4de31e24-70d8-496c-ac6a-a5376771d936 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> fix: preserve hyphens in archive filename so portal can parse it correctly rrd_logproc_convert_issue_type() was converting '-' to '_', turning the suffix '_Search-67768-67' into '_SEARCH_67768_67'. This added extra '_' separators into the archive filename that broke the analytics portal's filename parser — it could no longer identify the timestamp field, so download requests used an incorrect S3 key. Fix: keep '-' as '-' in the sanitized output. The archive filename now uses '_' to separate structural fields (MAC, issueType, timestamp) and '-' within the UUID suffix, giving the portal a reliable delimiter. Also increase issue_type_sanitized buffer in uploadRRDLogs.c from 64 to 256 bytes so a full UUID suffix never causes a silent truncation failure. Before: 04B86A12F9F8_DEVICE_DEVICEIP_SEARCH_67768_67__RRD_DEBUG_LOGS.tgz After: 04B86A12F9F8_DEVICE_DEVICEIP_SEARCH-67768-67__RRD_DEBUG_LOGS.tgz All 331 tests pass. Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/981b6bc1-c9d2-4150-9e9d-851004942ffc Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> fix: replace _Search-/_LogSearch- prefix check with suffix length <= 9 rule split_issue_type() now discards any suffix whose total length (including the leading '_') exceeds 9 characters. The old _Search-/_LogSearch- prefix validation is removed entirely. Rule: strlen(underscore) <= 9 → suffix accepted strlen(underscore) > 9 → suffix discarded This means: - Short session tokens (e.g. "_ab12345", 8 chars) are carried through - Long UUID-based suffixes like "_Search-b6877385-...) are discarded, preventing extra '_' delimiters from breaking the portal filename parser Affected tests updated: - UnderscoreSplitsBaseAndSuffix : uses a short accepted suffix - MultipleUnderscoresSplitsAtFirst: "_def_ghi" (8 chars) now kept - BaseTruncatedWhenTooSmall / ExactFitBase: "_suffix" (7 chars) now kept - SuffixTruncatedWhenTooSmall: uses 9-char suffix with 5-byte buffer - OnlyUnderscoreInput: "_" (1 char) now kept - LogSearchSuffixIsValid → NineCharSuffixIsAccepted (boundary test) - SearchSuffixIsValid → LongSuffixIsDiscarded - InvalidSuffixPrefixDiscarded / SearchWithoutHyphenIsInvalid / LogSearchWithoutHyphenIsInvalid: updated comments (same outcomes) - ProcessIssueTypeEvntTest comments updated to reflect new rule All 331 tests pass. Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/c6d6fc29-da2a-46af-a417-c2de67a18448 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> refactor: improve RRD_MAX_SUFFIX_LEN comment and rename length-based tests - Expand the RRD_MAX_SUFFIX_LEN comment to explain why 9 chars is the limit - Rename misleading test names that referenced old prefix-based validation: InvalidSuffixPrefixDiscarded → SuffixExceedingMaxLengthDiscarded SearchWithoutHyphenIsInvalid → SuffixSeventeenCharsDiscarded LogSearchWithoutHyphenIsInvalid → SuffixTwentyCharsDiscarded - LongSuffixIsDiscarded: switch to a neutral _1234567890 example so the test does not imply any Search-specific behavior All 331 tests pass. Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/c6d6fc29-da2a-46af-a417-c2de67a18448 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Update rrdJsonParser.c Update uploadRRDLogs.c Update uploadRRDLogs.c Update rrdJsonParser.c Delete .gitignore Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdEventProcess.c Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdJsonParser.c Update rrdEventProcess.c sanitize split_issue_type suffix to [A-Za-z0-9_-] to prevent injection Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/72e164ed-ae53-4076-8fb1-5ff1e21375e9 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Update rrdEventProcess.c Delete .gitignore Update rrdRunCmdThread.c Update rrdRunCmdThread.h Delete src/unittest/UTJson/device.properties Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdRunCmdThread.c Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdDynamic.c RRD 1.3.4 release changelog updates Update rrdDynamic.c Update rrdDynamic.c Update rrdDynamic.c Update rrdDynamic.c fix: harden profile length guard null checks and cleanup Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/c9da16b1-8b0f-497a-aca2-ec47fae37e48 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> chore: tighten strnlen boundary check and indentation Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/c9da16b1-8b0f-497a-aca2-ec47fae37e48 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> fix: use bounded strnlen check and null freed issue pointers Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/c9da16b1-8b0f-497a-aca2-ec47fae37e48 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Update rrdDynamic.c Update rrdDynamic.c Update rrdDynamic.c Update rrdDynamic.c Update rrdDynamic.c Refactor issueTypeSplitter to include suffix handling Update rrdEventProcess.c Update rrdExecuteScript.c Refactor rrdEventProcess.h to rrdExecuteScript.h Update rrdExecuteScript.h Update rrdExecuteScript.h Update rrdEventProcess.h Update rrdExecuteScript.h Update rrdJsonParser.c Update rrdJsonParser.h Update rrdInterface.c Update rrdCommon.h Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Fix indentation for appendMode assignment Update rrdCommon.h Fix appendMode assignment in rrdEventProcess.c Update rrdEventProcess.c Update rrdJsonParser.c Update rrdEventProcess.c Update rrdExecuteScript.h Update rrdExecuteScript.c Update rrdExecuteScript.c Update rrdExecuteScript.c Remove redundant logging from rrdJsonParser Update rrdJsonParser.c Update rrdEventProcess.c Update rrdJsonParser.c Update rrdEventProcess.c Update rrdJsonParser.c Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Update rrdEventProcess.c Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Update rrdJsonParser.c Fix heap overflow in issueTypeSplitter and memory leaks in suffix handling Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/fbc52780-966b-4912-825f-3030aa43c3e9 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Update rrdUnitTestRunner.cpp Delete .gitignore Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdUnitTestRunner.cpp Update rrdEventProcess.c Update rrdUnitTestRunner.cpp Update rrdUnitTestRunner.cpp Add gtest test cases for split_issue_type Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/9996d741-248e-4e58-8689-b4ba873cfaf2 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Remove build artifacts, add .gitignore Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/9996d741-248e-4e58-8689-b4ba873cfaf2 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Add explicit truncated content assertion in SuffixTruncatedWhenTooSmall test Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/9996d741-248e-4e58-8689-b4ba873cfaf2 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Update rrdEventProcess.c Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdJsonParser.c Update rrdJsonParser.c Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdUnitTestRunner.cpp Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Delete .gitignore Delete src/unittest/UTJson/device.properties Update rrdEventProcess.c Add gtest tests for split_issue_type, suffix field, and processIssueTypeEvent Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/76cad72a-f67f-4c05-8fb5-bfadf0c173b3 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Delete .gitignore Validate suffix prefix: only _Search- and _LogSearch- are allowed Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/125526c2-7b70-48f5-8bec-fd725eea8a04 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Base never contains underscore: split at first _ and discard invalid suffix Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/904bf10d-546b-4038-a60c-9bc76094a225 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Update rrdEventProcess.c Update rrdUnitTestRunner.cpp Update rrdEventProcess.c Update rrdEventProcess.c Fix segfault in GTEST_ENABLE mode when rbuf->jsonPath is NULL Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/efdc0d6b-89e6-4423-b202-a900f5683839 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Remove accidentally committed dummy directory and update .gitignore Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/efdc0d6b-89e6-4423-b202-a900f5683839 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Fix IssueTypeSplitterTest to match new issueTypeSplitter behavior (no built-in special-char removal) Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/00abcaba-8a41-4b88-ae12-07b5ff780ff9 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Fix empty/whitespace IssueType bypassing processIssueTypeEvent guards Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/4de31e24-70d8-496c-ac6a-a5376771d936 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> fix: preserve hyphens in archive filename so portal can parse it correctly rrd_logproc_convert_issue_type() was converting '-' to '_', turning the suffix '_Search-67768-67' into '_SEARCH_67768_67'. This added extra '_' separators into the archive filename that broke the analytics portal's filename parser — it could no longer identify the timestamp field, so download requests used an incorrect S3 key. Fix: keep '-' as '-' in the sanitized output. The archive filename now uses '_' to separate structural fields (MAC, issueType, timestamp) and '-' within the UUID suffix, giving the portal a reliable delimiter. Also increase issue_type_sanitized buffer in uploadRRDLogs.c from 64 to 256 bytes so a full UUID suffix never causes a silent truncation failure. Before: 04B86A12F9F8_DEVICE_DEVICEIP_SEARCH_67768_67__RRD_DEBUG_LOGS.tgz After: 04B86A12F9F8_DEVICE_DEVICEIP_SEARCH-67768-67__RRD_DEBUG_LOGS.tgz All 331 tests pass. Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/981b6bc1-c9d2-4150-9e9d-851004942ffc Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> fix: replace _Search-/_LogSearch- prefix check with suffix length <= 9 rule split_issue_type() now discards any suffix whose total length (including the leading '_') exceeds 9 characters. The old _Search-/_LogSearch- prefix validation is removed entirely. Rule: strlen(underscore) <= 9 → suffix accepted strlen(underscore) > 9 → suffix discarded This means: - Short session tokens (e.g. "_ab12345", 8 chars) are carried through - Long UUID-based suffixes like "_Search-b6877385-...) are discarded, preventing extra '_' delimiters from breaking the portal filename parser Affected tests updated: - UnderscoreSplitsBaseAndSuffix : uses a short accepted suffix - MultipleUnderscoresSplitsAtFirst: "_def_ghi" (8 chars) now kept - BaseTruncatedWhenTooSmall / ExactFitBase: "_suffix" (7 chars) now kept - SuffixTruncatedWhenTooSmall: uses 9-char suffix with 5-byte buffer - OnlyUnderscoreInput: "_" (1 char) now kept - LogSearchSuffixIsValid → NineCharSuffixIsAccepted (boundary test) - SearchSuffixIsValid → LongSuffixIsDiscarded - InvalidSuffixPrefixDiscarded / SearchWithoutHyphenIsInvalid / LogSearchWithoutHyphenIsInvalid: updated comments (same outcomes) - ProcessIssueTypeEvntTest comments updated to reflect new rule All 331 tests pass. Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/c6d6fc29-da2a-46af-a417-c2de67a18448 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> refactor: improve RRD_MAX_SUFFIX_LEN comment and rename length-based tests - Expand the RRD_MAX_SUFFIX_LEN comment to explain why 9 chars is the limit - Rename misleading test names that referenced old prefix-based validation: InvalidSuffixPrefixDiscarded → SuffixExceedingMaxLengthDiscarded SearchWithoutHyphenIsInvalid → SuffixSeventeenCharsDiscarded LogSearchWithoutHyphenIsInvalid → SuffixTwentyCharsDiscarded - LongSuffixIsDiscarded: switch to a neutral _1234567890 example so the test does not imply any Search-specific behavior All 331 tests pass. Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/c6d6fc29-da2a-46af-a417-c2de67a18448 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Update rrdJsonParser.c Update uploadRRDLogs.c Update uploadRRDLogs.c Update rrdJsonParser.c Delete .gitignore Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdEventProcess.c Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdJsonParser.c Update rrdEventProcess.c sanitize split_issue_type suffix to [A-Za-z0-9_-] to prevent injection Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/72e164ed-ae53-4076-8fb1-5ff1e21375e9 Co-authored-by: Abhinavpv28 <162570454+Abhinavpv28@users.noreply.github.com> Update rrdEventProcess.c Delete .gitignore Update rrdRunCmdThread.c Update rrdRunCmdThread.h Delete src/unittest/UTJson/device.properties Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdRunCmdThread.c Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> RRD 1.3.4 release changelog updates Create test_rrd_static_profile_report_with_suffix.py (#201) Create test_rrd_static_profile_report_with_suffix_negative_case.py Potential fix for pull request finding RRD 1.3.4 release changelog updates Fix dynamic subcategory service log assertions for timestamped names Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/f0806557-f409-45cc-8993-700be01b59f2 Remove accidental pycache artifact from test changes Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/f0806557-f409-45cc-8993-700be01b59f2 Update test_rrd_background_cmd_static_profile_report.py Update test_rrd_c_api_upload.py Update test_rrd_debug_report_upload.py Update test_rrd_deepsleep_static_report.py Update test_rrd_dynamic_profile_report.py Update test_rrd_static_profile_category_report.py Update test_rrd_static_profile_report.py Update test_rrd_static_profile_report_with_suffix.py Update test_rrd_static_profile_report_with_suffix_negative_case.py Update test_rrd_background_cmd_static_profile_report.py Update test_rrd_dynamic_profile_missing_report.py Strengthen service-start assertions for timestamped runtime names Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/ec85a8af-7e00-4410-8ca2-4e20cd06cfd4 Add helper docstring for service-start success assertion Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/ec85a8af-7e00-4410-8ca2-4e20cd06cfd4 Improve helper docstring with Args and Raises Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/ec85a8af-7e00-4410-8ca2-4e20cd06cfd4 Refine service-start helper naming and assertion messages Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/ec85a8af-7e00-4410-8ca2-4e20cd06cfd4 Update test_rrd_background_cmd_static_profile_report.py Update test_rrd_dynamic_profile_missing_report.py Add feature files for static suffix report scenarios Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/59dfaf33-25ef-48a2-9549-dd1f571a3b55 L2 Potential fix for pull request finding Update test_rrd_static_profile_report_with_suffix.py Update test_rrd_static_profile_report_with_suffix_negative_case.py Update test_rrd_static_profile_report_with_suffix_negative_case.py Fix service check in negative test case Update test_rrd_static_profile_report_with_suffix_negative_case.py Update test_rrd_dynamic_profile_missing_report.py Update test_rrd_dynamic_profile_missing_report.py Update test_rrd_dynamic_profile_missing_report.py Update test_rrd_dynamic_profile_missing_report.py Update test_rrd_dynamic_profile_missing_report.py Stabilize remotedebugger startup in missing-profile test Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/eafe5a9f-2bbc-448a-966f-61c3067b7239 Remove accidental pycache artifact from PR Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/eafe5a9f-2bbc-448a-966f-61c3067b7239 Replace startup retry magic numbers with constants Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/eafe5a9f-2bbc-448a-966f-61c3067b7239 Remove accidental pycache artifact again Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/eafe5a9f-2bbc-448a-966f-61c3067b7239 Refine remotedebugger PID retry logic in missing-profile test Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/eafe5a9f-2bbc-448a-966f-61c3067b7239 Simplify startup PID retry loop for missing-profile test Agent-Logs-Url: https://github.com/rdkcentral/remote_debugger/sessions/eafe5a9f-2bbc-448a-966f-61c3067b7239 RDK-60236 : Remote Debugger Supports UUID Information in Debug Report File (#199) * Create rrd_uuid_support_implementation.HLD * Rename rrd_uuid_support_implementation.HLD to rrd_uuid_support_implementation_HLD.md * Update rrd_uuid_support_implementation_HLD.md * Move docs from .github directory --------- Co-authored-by: Abhinav P V Co-authored-by: nhanasi Update rrdUnitTestRunner.cpp Update rrdUnitTestRunner.cpp Update rrdUnitTestRunner.cpp Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Update rrdUnitTestRunner.cpp L2 Document update (#207) Co-authored-by: Hanasi Create rrd_dynamic_profile_rdm_node_length_exceeded.feature Create test_dynamic_profile_rdm_node_length_exceeded.py Create rrd_issuetype_exceeds_max_length.feature Delete test/functional-tests/tests/rrd_dynamic_profile_rdm_node_length_exceeded.feature L2 --- .github/skills/bdd-feature-generator/SKILL.md | 892 ++++++++++++++++++ CHANGELOG.md | 9 + docs/rrd_uuid_support_implementation_HLD.md | 207 ++++ .../docs => docs}/uploadRRDLogs_Flowcharts.md | 0 {.github/docs => docs}/uploadRRDLogs_HLD.md | 0 {.github/docs => docs}/uploadRRDLogs_LLD.md | 0 .../uploadRRDLogs_Requirements.md | 0 .../uploadRRDLogs_SequenceDiagrams.md | 0 run_l2.sh | 4 + src/rrdCommon.h | 2 + src/rrdDynamic.c | 25 +- src/rrdEventProcess.c | 78 +- src/rrdInterface.c | 5 + src/rrdJsonParser.c | 135 ++- src/rrdJsonParser.h | 2 + src/rrdRunCmdThread.c | 21 +- src/rrdRunCmdThread.h | 1 + src/rrd_logproc.c | 3 +- src/unittest/rrdUnitTestRunner.cpp | 458 ++++++++- test/functional-tests/L2_Test_Coverage.md | 399 ++++++++ ...namic_profile_node_length_exceeded.feature | 48 + ..._static_profile_report_with_suffix.feature | 48 + ...e_report_with_suffix_negative_case.feature | 49 + .../tests/helper_functions.py | 15 + ...rd_background_cmd_static_profile_report.py | 7 +- .../tests/test_rrd_c_api_upload.py | 7 +- .../tests/test_rrd_debug_report_upload.py | 7 +- .../tests/test_rrd_deepsleep_static_report.py | 14 +- ...test_rrd_dynamic_profile_missing_report.py | 13 +- ...ynamic_profile_rdm_node_length_exceeded.py | 104 ++ .../tests/test_rrd_dynamic_profile_report.py | 14 +- .../test_rrd_dynamic_subcategory_report.py | 7 +- ...test_rrd_static_profile_category_report.py | 14 +- .../tests/test_rrd_static_profile_report.py | 7 +- ...t_rrd_static_profile_report_with_suffix.py | 140 +++ ...rofile_report_with_suffix_negative_case.py | 144 +++ 36 files changed, 2804 insertions(+), 75 deletions(-) create mode 100644 .github/skills/bdd-feature-generator/SKILL.md create mode 100644 docs/rrd_uuid_support_implementation_HLD.md rename {.github/docs => docs}/uploadRRDLogs_Flowcharts.md (100%) rename {.github/docs => docs}/uploadRRDLogs_HLD.md (100%) rename {.github/docs => docs}/uploadRRDLogs_LLD.md (100%) rename {.github/docs => docs}/uploadRRDLogs_Requirements.md (100%) rename {.github/docs => docs}/uploadRRDLogs_SequenceDiagrams.md (100%) create mode 100644 test/functional-tests/L2_Test_Coverage.md create mode 100644 test/functional-tests/features/rrd_dynamic_profile_node_length_exceeded.feature create mode 100644 test/functional-tests/features/test_rrd_static_profile_report_with_suffix.feature create mode 100644 test/functional-tests/features/test_rrd_static_profile_report_with_suffix_negative_case.feature create mode 100644 test/functional-tests/tests/test_rrd_dynamic_profile_rdm_node_length_exceeded.py create mode 100644 test/functional-tests/tests/test_rrd_static_profile_report_with_suffix.py create mode 100644 test/functional-tests/tests/test_rrd_static_profile_report_with_suffix_negative_case.py diff --git a/.github/skills/bdd-feature-generator/SKILL.md b/.github/skills/bdd-feature-generator/SKILL.md new file mode 100644 index 000000000..053053eca --- /dev/null +++ b/.github/skills/bdd-feature-generator/SKILL.md @@ -0,0 +1,892 @@ +--- +name: bdd-feature-generator +description: Generate BDD (Behavior Driven Development) feature files from remote debugger source code analysis. Use for creating Gherkin-format documentation of RBUS event handling, static/dynamic profile processing, command execution, harmful command detection, log archive and upload, deep sleep handling, and uploadRRDLogs C API orchestration. Produces gap analysis between feature files and L2 test implementations. +--- + +# BDD Feature Generator for Remote Debugger + +## Purpose + +Automatically generate BDD feature files in Gherkin format by analyzing the remote debugger source code. This skill creates comprehensive behavioral documentation that can serve as: +- **Functional documentation** of daemon lifecycle, event handling, and profile processing +- **Test specifications** for L2 functional tests (`test/functional-tests/`) +- **Requirements traceability** linking source modules to observable behavior +- **Gap analysis baseline** for comparing L2 tests vs implemented behavior + +## Usage + +Invoke this skill when: +- Documenting existing remote debugger behaviors in BDD format +- Creating test specifications for new features (e.g., new profile types, upload mechanisms) +- Generating feature files for untested code paths (e.g., WebCfg events, deep sleep edge cases) +- Performing gap analysis between L2 tests and source implementation +- Onboarding new team members with behavioral documentation of the daemon + +## Project Context + +The remote debugger (`remotedebugger`) is an RDK component that enables remote collection, packaging, and upload of device diagnostic logs. It: +- Listens for **RBUS/TR-181 events** (IssueType triggers) to initiate debug data collection +- Processes **static profiles** (built-in JSON config at `/etc/rrd/remote_debugger.json`) and **dynamic profiles** (downloaded packages at `/media/apps/RDK-RRD-/etc/rrd/remote_debugger.json`) +- Executes diagnostic **commands** from profiles, with **sanity checks** against harmful commands +- Archives collected outputs as `.tgz` and **uploads** them to a remote server via `uploadRRDLogs.sh` or the **C API** (`rrd_upload_orchestrate`) +- Supports **append mode** combining commands from both static and dynamic profiles +- Handles **deep sleep** events for deferred processing +- Supports **category-only** issue types (all sub-nodes under a category) +- Enforces **single-instance** execution and **RFC enable/disable** control +- Runs as a **systemd service** with a main thread and a dedicated event-processing thread + +## Prerequisites + +Before running this skill: + +1. **Review the build system** — `Makefile.am`, `src/Makefile.am`, and `configure.ac` +2. **Identify compiled modules** — Core sources are always compiled; IARMBUS sources are conditional +3. **Review existing feature files** — Match the format in `test/functional-tests/features/` +4. **Understand test interfaces** — L2 tests use `rbuscli` (RBUS event trigger), log scraping (`/opt/logs/remotedebugger.log.0`), and file system checks (`/tmp/rrd/`) + +## Process + +### Step 1: Analyze Build Configuration + +The remote debugger build is Autotools-based. Identify compiled components from the Makefile chain: + +```bash +# Top-level: identifies src/ as the main SUBDIR +cat Makefile.am | grep "SUBDIRS" +# → SUBDIRS = src + +# Source level: identifies the remotedebugger binary and its sources +cat src/Makefile.am +# → bin_PROGRAMS = remotedebugger +# → remotedebugger_SOURCES = rrdMain.c rrdEventProcess.c rrdJsonParser.c +# rrdRunCmdThread.c rrdCommandSanity.c rrdDynamic.c rrdExecuteScript.c +# rrdMsgPackDecoder.c rrdInterface.c +# → if IARMBUS_ENABLE: +# remotedebugger_SOURCES += rrdIarmEvents.c uploadRRDLogs.c rrd_config.c +# rrd_sysinfo.c rrd_logproc.c rrd_archive.c rrd_upload.c +``` + +**Always compiled modules:** + +| Source File | Header | Purpose | +|---|---|---| +| `rrdMain.c` | `rrdMain.h` | Daemon entry, event thread, RFC enable check, message queue setup | +| `rrdEventProcess.c` | `rrdEventProcess.h` | IssueType/WebCfg/DeepSleep event dispatch, static/dynamic profile flow | +| `rrdJsonParser.c` | `rrdJsonParser.h` | JSON profile parsing, command/timeout extraction, issue node lookup | +| `rrdRunCmdThread.c` | `rrdRunCmdThread.h` | Command execution, output file management, result caching | +| `rrdCommandSanity.c` | `rrdCommandSanity.h` | Command validation against harmful command blocklist | +| `rrdDynamic.c` | `rrdDynamic.h` | Dynamic profile handling, deep sleep event processing | +| `rrdExecuteScript.c` | `rrdExecuteScript.h` | Upload debug output orchestration | +| `rrdMsgPackDecoder.c` | `rrdMsgPackDecoder.h` | WebConfig parameter decoding (MsgPack format) | +| `rrdInterface.c` | `rrdInterface.h` | RBUS registration, event handler setup, profile data elements | + +**Conditionally compiled modules (IARMBUS_ENABLE):** + +| Source File | Header | Purpose | +|---|---|---| +| `rrdIarmEvents.c` | — | IARM bus event handling (power state, deep sleep) | +| `uploadRRDLogs.c` | — | C implementation of upload orchestration entry point | +| `rrd_config.c` | `rrd_config.h` | Configuration loading (server URLs, paths, protocol, RFC/DCM) | +| `rrd_sysinfo.c` | `rrd_sysinfo.h` | System info (MAC address, timestamp, file/dir checks) | +| `rrd_logproc.c` | `rrd_logproc.h` | Log directory validation, preparation, live log handling | +| `rrd_archive.c` | `rrd_archive.h` | Archive creation (.tgz), filename generation, cleanup, CPU checks | +| `rrd_upload.c` | `rrd_upload.h` | Upload orchestration, lock handling, cleanup | + +**Shared headers (no implementation file):** + +| Header | Purpose | +|---|---| +| `rrdCommon.h` | Common constants, macros, data structures (data_buf, msgRRDHdr, etc.) | +| `rrdRbus.h` | RBUS event subscription, blob versioning | +| `rrd_log.h` | Logging subsystem initialization | + +**Exclude from feature generation:** +- `src/unittest/` — Unit tests (L1) +- `test/` — Test infrastructure +- `scripts/` — Runtime scripts (documented as behavior, not source) +- `docs/` — Existing documentation + +### Step 2: Analyze Source Code Structure + +For each compiled module: + +1. **Read the header file** (`.h`) — Identify public functions, data structures, constants +2. **Read the implementation** (`.c`) — Extract event flows, log messages, error paths +3. **Identify RBUS parameters/events** — Map registered data elements in `rrdInterface.c` +4. **Note conditional compilation** — `#ifdef IARMBUS_SUPPORT` gates upload and IARM modules +5. **Note key log messages** — Tests validate behavior by grepping these from log files +6. **Note file paths** — `/etc/rrd/remote_debugger.json`, `/tmp/rrd/`, `/media/apps/RDK-RRD-*` + +**Key elements to extract for each module:** + +| Element | Where to Find | Example | +|---|---|---| +| RBUS data elements | `rrdInterface.c` registration | `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType` | +| Event handlers | `rrdInterface.c` set handlers | `RRD_SET_ISSUE_EVENT` | +| Profile JSON paths | `rrdEventProcess.c`, `rrdDynamic.c` | `/etc/rrd/remote_debugger.json`, `/media/apps/RDK-RRD-{Node}/etc/rrd/remote_debugger.json` | +| Log messages | All `.c` files `RDK_LOG_*` calls | `"SUCCESS: Message sending Done"`, `"Json File parse Success..."` | +| Error conditions | `.c` return/log on failure | `"FAILED: Json File parse..."`, `"Harmful Command Found"` | +| File outputs | `rrdRunCmdThread.c` | `/tmp/rrd/{IssueType}/debug_outputs.txt` | +| Archive naming | `rrd_archive.c` | `{MAC}_{ISSUETYPE}_{TIMESTAMP}_RRD_DEBUG_LOGS.tgz` | +| Upload API | `rrd_upload.h` | `rrd_upload_orchestrate(upload_dir, issue_type)` | +| Compile guards | `.c` / `.h` `#ifdef` | `IARMBUS_SUPPORT` | + +### Step 3: Create Feature File Structure + +Feature files are placed in `test/functional-tests/features/`. + +**Naming convention for remote debugger:** + +| Behavior Area | Feature File | Description | +|---|---|---| +| Daemon startup | `rrd_start_subscribe_and_wait.feature` | RBUS subscription, event loop entry | +| RFC enable/disable | `rrd_start_control.feature` | RFC-controlled start/stop | +| Single instance | `rrd_single_instance.feature` | Prevents duplicate daemon instances | +| Static profile processing | `rrd_static_profile_report.feature` | End-to-end static profile flow | +| Static category report | `rrd_static_profile_category_report.feature` | Category-only issue type | +| Static profile with suffix | `test_rrd_static_profile_report_with_suffix.feature` | Suffixed issue type handling | +| Background command | `rrd_background_cmd_static_profile_report.feature` | Background command execution | +| Dynamic profile processing | `rrd_dynamic_profile_report.feature` | Dynamic profile fallback flow | +| Dynamic subcategory | `rrd_dynamic_profile_subcategory_report.feature` | Dynamic profile subcategory | +| Dynamic profile missing | `rrd_dynamic_profile_missing_report.feature` | Missing dynamic profile | +| Append mode | `rrd_append_report.feature` | Static+dynamic profile append | +| Append (static not found) | `rrd_append_dynamic_profile_static_not_found.feature` | Append when static missing | +| Harmful commands | `rrd_harmful_command_static_report.feature` | Sanity check blocks execution | +| Dynamic harmful | `test_rrd_dynamic_profile_harmful_report.feature` | Harmful command in dynamic profile | +| Corrupted profile | `rrd_corrupted_static_profile_report.feature` | Invalid/corrupted JSON handling | +| Static missing command | `rrd_static_profile_missing_command_report.feature` | Missing command in profile | +| Empty issue type | `rrd_empty_issuetype_event.feature` | Empty event value handling | +| Deep sleep | `rrd_deepsleep_static_report.feature` | Deep sleep issue type handling | +| Debug report upload | `rrd_debug_report_upload.feature` | Full upload + download validation | +| C API upload | `rrd_c_api_upload.feature` | `rrd_upload_orchestrate` C API tests | +| Suffix negative case | `test_rrd_static_profile_report_with_suffix_negative_case.feature` | Invalid suffix handling | + +### Step 4: Generate Feature Files + +Use this template for remote debugger feature files: + +```gherkin +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright [YEAR] RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +# Source: src/{SourceFile}.c + +Feature: Remote Debugger {Behavior Description} + + Scenario: {Prerequisite Check} + Given the configuration file path is set + When I check if the configuration file exists + Then the configuration file should exist + + Scenario: Verify remote debugger process is running + Given the remote debugger process is not running + When I start the remote debugger process + Then the remote debugger process should be running + + Scenario: {Main Behavior Scenario} + Given the remote debugger is running + When I trigger the event "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType" + Then {expected behavior validated via log scraping} +``` + +### Step 5: Map Source Code to Scenarios + +**For each remote debugger behavior, create scenarios covering:** + +1. **Prerequisites** — Config file exists, output directory exists, daemon not already running +2. **Daemon startup** — Process starts, RBUS subscription succeeds, event loop enters wait +3. **Event trigger** — RBUS IssueType set via `rbuscli`, message queue send/receive +4. **Profile parsing** — JSON read, parse success/failure, issue node lookup +5. **Command execution** — Sanity check, command run, output file creation +6. **Service management** — systemd service start/stop, journalctl collection +7. **Upload flow** — Archive creation, upload script invocation, success/failure +8. **Error/edge cases** — Harmful commands, empty events, corrupted profiles, missing profiles + +**Example mapping — RBUS event trigger (rrdInterface.c → rrdEventProcess.c):** + +```c +// Source: src/rrdInterface.c — RBUS set handler for IssueType +// Sends message to event thread via message queue +// Source: src/rrdEventProcess.c — Event thread receives and dispatches +``` + +**Generated scenarios:** + +```gherkin +Scenario: Send WebPA event for IssueType and verify message flow + Given the remote debugger is running + When I trigger the event "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType" + Then the event for RRD_SET_ISSUE_EVENT should be received + And the logs should contain "SUCCESS: Message sending Done" + And the logs should be seen with "SUCCESS: Message Reception Done" +``` + +**Example mapping — Static profile processing (rrdEventProcess.c → rrdJsonParser.c):** + +```gherkin +Scenario: Process static profile for issue type + When the remotedebugger received the message from webPA event + Then remotedebugger should read the Json file + And remotedebugger logs should contain the Json File Parse success + And the issue data node and sub-node should be found in the JSON file + And the directory should be created to store the executed output + And Sanity check to validate the commands should be executed + And Command output should be added to the output file +``` + +**Example mapping — Dynamic profile fallback (rrdDynamic.c):** + +```gherkin +Scenario: Verify the Issuetype in dynamic path + Given the remote debugger issuetype is missing in static profile + When the remotedebugger read the json file form the dynamic path + Then remotedebugger json read and parse should be success + And remotedebugger should read the Issuetype from dynamic profile + And the issue data node and sub-node should be found in the JSON file +``` + +**Example mapping — Harmful command detection (rrdCommandSanity.c):** + +```gherkin +Scenario: Check for harmful commands and abort + Given remote debugger parse the static json profile successfully + When the issue command and the sanity commands are matched + Then the remote debugger should exit the processing of commands + And Abort the command execution and skip report upload +``` + +**Example mapping — Upload orchestration C API (rrd_upload.c):** + +```gherkin +Scenario: Validate rrd_upload_orchestrate C API with valid parameters + Given the remote debugger is configured + And test log files are created in the upload directory + When I call rrd_upload_orchestrate with valid upload directory and issue type + Then the C API should return success code 0 + And logs should contain "Configuration loaded" + And logs should contain "MAC:" for MAC address + And logs should contain "Log directory validated and prepared" + And logs should contain "Issue type sanitized" + And logs should contain "Archive filename:" with the generated filename + And logs should contain "Creating" for tarfile creation + And logs should contain "Invoking uploadSTBLogs binary to upload" +``` + +**Example mapping — Single instance enforcement (rrdMain.c):** + +```gherkin +Scenario: Remote debugger exits if another instance is invoked + Given the RemoteDebugger is not already running + When the RemoteDebugger binary is invoked + Then the RemoteDebugger should be started + And when the RemoteDebugger is attempted to be started again + Then the RemoteDebugger should not start another instance +``` + +### Step 6: Document Issue Type Variations + +For issue types with multiple forms, use individual scenarios: + +```gherkin +Scenario: Static profile with Node.SubNode issue type + Given the remote debugger is running + When I trigger IssueType "Device.Info" + Then the issue data node "Device" and sub-node "Info" should be found in the JSON file + +Scenario: Category-only issue type (all sub-nodes) + Given the remote debugger is running + When I trigger IssueType "Device" + Then all sub-nodes under category "Device" should be processed + +Scenario: Suffixed issue type + Given the remote debugger is running + When I trigger IssueType "Device.Info_ab1bghjh" + Then the base issue "Device.Info" should be matched in the profile + And the archive filename should include the full suffixed issue type +``` + +### Step 7: Create README Index + +Create or update `test/functional-tests/features/README.md`: + +```markdown +# Remote Debugger Feature Documentation + +This folder contains BDD feature files documenting the remote debugger +daemon behavior implemented in `src/`. + +## Feature Files Overview + +| Feature File | Source Components | Description | +|---|---|---| +| `rrd_start_subscribe_and_wait.feature` | `rrdMain.c`, `rrdInterface.c` | RBUS subscription, event loop | +| `rrd_start_control.feature` | `rrdMain.c` | RFC enable/disable control | +| `rrd_single_instance.feature` | `rrdMain.c` | Single instance enforcement | +| `rrd_static_profile_report.feature` | `rrdEventProcess.c`, `rrdJsonParser.c`, `rrdRunCmdThread.c` | Static profile end-to-end | +| `rrd_static_profile_category_report.feature` | `rrdEventProcess.c`, `rrdJsonParser.c` | Category-only issue type | +| `rrd_dynamic_profile_report.feature` | `rrdDynamic.c`, `rrdJsonParser.c` | Dynamic profile fallback | +| `rrd_dynamic_profile_subcategory_report.feature` | `rrdDynamic.c` | Dynamic subcategory | +| `rrd_append_report.feature` | `rrdDynamic.c`, `rrdEventProcess.c` | Append mode (static+dynamic) | +| `rrd_harmful_command_static_report.feature` | `rrdCommandSanity.c` | Harmful command blocking | +| `rrd_corrupted_static_profile_report.feature` | `rrdJsonParser.c` | Corrupted JSON handling | +| `rrd_empty_issuetype_event.feature` | `rrdEventProcess.c` | Empty event value | +| `rrd_deepsleep_static_report.feature` | `rrdDynamic.c`, `rrdIarmEvents.c` | Deep sleep handling | +| `rrd_debug_report_upload.feature` | `rrdExecuteScript.c`, `uploadRRDLogs.sh` | Upload + download validation | +| `rrd_c_api_upload.feature` | `rrd_upload.c`, `rrd_config.c`, `rrd_archive.c` | C API upload orchestration | + +## Source Module Mapping + +Based on `src/Makefile.am` and `configure.ac`: + +### Always Compiled +- `rrdMain.c` — Daemon lifecycle, event thread, RFC enable check +- `rrdEventProcess.c` — IssueType/WebCfg event dispatch +- `rrdJsonParser.c` — JSON profile parsing, command extraction +- `rrdRunCmdThread.c` — Command execution, output management +- `rrdCommandSanity.c` — Harmful command validation +- `rrdDynamic.c` — Dynamic profile handling, deep sleep +- `rrdExecuteScript.c` — Upload debug output orchestration +- `rrdMsgPackDecoder.c` — WebConfig MsgPack decoding +- `rrdInterface.c` — RBUS registration, event handlers + +### Conditionally Compiled (IARMBUS_ENABLE) +- `rrdIarmEvents.c` — IARM bus events (power state) +- `uploadRRDLogs.c` — C upload orchestration entry point +- `rrd_config.c` — Configuration loading (RFC/DCM/fallback) +- `rrd_sysinfo.c` — System info (MAC, timestamp) +- `rrd_logproc.c` — Log directory validation/preparation +- `rrd_archive.c` — Archive creation (.tgz) +- `rrd_upload.c` — Upload orchestration, lock handling + +## RBUS Data Elements + +| Data Element | Type | Handler | +|---|---|---| +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType` | SET event | Triggers debug data collection | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.WebCfgData` | SET event | WebConfig-based trigger | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.setProfileData` | SET | Profile data injection | +| `Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.getProfileData` | GET | Profile data retrieval | + +## Test Interface Summary + +| Interface | Tool | Log File | Used For | +|---|---|---|---| +| RBUS event | `rbuscli set` | `/opt/logs/remotedebugger.log.0` | IssueType trigger | +| Log scraping | Python `grep_rrdlogs()` | `/opt/logs/remotedebugger.log.0` | Behavior validation | +| File system | Python `os.path`, `subprocess` | `/tmp/rrd/`, `/etc/rrd/` | Config/output checks | +| Upload | Mock xconf server | — | Archive upload validation | +| Process control | `pidof`, `kill`, `nohup` | — | Daemon start/stop | + +## Generation Date + +Generated: {DATE} +``` + +## Scenario Patterns for Remote Debugger + +### Prerequisite Check Pattern + +```gherkin +Scenario: Check if remote debugger configuration file exists + Given the configuration file path is set + When I check if the configuration file exists + Then the configuration file should exist + +Scenario: Check if /tmp/rrd output directory exists + Given the /tmp/rrd directory path is set + When I check if the /tmp/rrd directory exists + Then the /tmp/rrd directory should exist +``` + +### Daemon Startup Pattern + +```gherkin +Scenario: Verify remote debugger process is running + Given the remote debugger process is not running + When I start the remote debugger process + Then the remote debugger process should be running + +Scenario: Remote debugger should subscribe to events + Given the remote debugger binary is invoked + When the remote debugger is started + Then the remote debugger should subscribe to rbus and wait for the events + And the log file should contain "SUCCESS: RBUS Event Subscribe for RRD done!" + And the log file should contain "Waiting for TR69/RBUS Events..." +``` + +### RFC Enable/Disable Pattern + +```gherkin +Scenario: Remote Debugger Starts when Enabled + Given RFC Value for RDKRemoteDebugger.Enable is enabled + When the remotedebugger is started check the value of the RFC parameter + And the RDKRemoteDebugger Enable value is true + Then the remotedebugger should be started and running as daemon + +Scenario: Remote Debugger Stops when Disabled + Given RFC Value for RDKRemoteDebugger.Enable is disabled + When the remotedebugger is started check the value of the RFC parameter + And the RDKRemoteDebugger Enable value is false + Then the remotedebugger must be stopped and process should not be running +``` + +### RBUS Event Trigger Pattern + +```gherkin +Scenario: Send WebPA event for IssueType + Given the remote debugger is running + When I trigger the event "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType" + Then the event for RRD_SET_ISSUE_EVENT should be received + And the logs should contain "SUCCESS: Message sending Done" + And the logs should be seen with "SUCCESS: Message Reception Done" +``` + +### Static Profile Processing Pattern + +```gherkin +Scenario: Process static profile commands + When the remotedebugger received the message from webPA event + Then remotedebugger should read the Json file + And remotedebugger logs should contain the Json File Parse success + And the issue data node and sub-node should be found in the JSON file + And the directory should be created to store the executed output + And Sanity check to validate the commands should be executed + And Command output should be added to the output file + And the issuetype systemd service should start successfully + And the journalctl service should start successfully + And the process should sleep with timeout + And the issuetype systemd service should stop successfully + And the remotedebugger should call script to upload the debug report +``` + +### Dynamic Profile Fallback Pattern + +```gherkin +Scenario: Verify the Issuetype is not found in static profile + Given the remote debugger received the message from RBUS command + When the remotedebugger static json profile is present + Then remotedebugger should read the Json file + And remotedebugger logs should contain the Json File Parse Success + And remotedebugger should log as the Issue requested is not found in the profile + +Scenario: Verify the Issuetype in dynamic path + Given the remote debugger issuetype is missing in static profile + When the remotedebugger read the json file from the dynamic path + Then remotedebugger json read and parse should be success + And remotedebugger should read the Issuetype from dynamic profile + And the issue data node and sub-node should be found in the JSON file +``` + +### Append Mode Pattern + +```gherkin +Scenario: Verify append of static and dynamic profile commands + Given the remote debugger received the message from RBUS command + When the remotedebugger read the json file from the dynamic path + Then remotedebugger json read and parse should be success in dynamic path + And remotedebugger should read the Json file + And remotedebugger logs should contain the Json File Parse Success + And remotedebugger should log as the Issue requested found in the profile + And Update the command after appending data from both profiles, then execute +``` + +### Harmful Command Detection Pattern + +```gherkin +Scenario: Check for harmful commands and abort + Given remote debugger parse the static json profile successfully + When the issue node and subnode are present in the profile + Then the remote debugger should read the Sanity Check list from profile + And the remotedebugger should perform sanity check on issue commands + Given the remote debugger profile has the harmful commands + When the issue command and the sanity commands are matched + Then the remote debugger should exit the processing of commands + And Abort the command execution and skip report upload +``` + +### Upload Flow Pattern + +```gherkin +Scenario: Upload remote debugger debug report + Given the remote debugger completed the command execution + When remotedebugger calls the uploadRRD.sh script + Then check for the tarfile is created in the output directory + And the file is uploaded to the mockxconf server + And the upload success logs are seen in the logs + +Scenario: Download the file from the mockxconf server + Given the remote debugger report upload success + When curl command is used to download the file + Then the curl command should return success + And the file should be downloaded successfully +``` + +### C API Upload Orchestration Pattern + +```gherkin +Scenario: Validate rrd_upload_orchestrate with valid parameters + Given the remote debugger is configured + And test log files are created in the upload directory + When I call rrd_upload_orchestrate with valid upload directory and issue type + Then the C API should return success code 0 + And logs should contain "Configuration loaded" + And logs should contain "MAC:" for MAC address + And logs should contain "Log directory validated and prepared" + And logs should contain "Issue type sanitized" + And logs should contain "Archive filename:" with the generated filename + And logs should contain "Invoking uploadSTBLogs binary to upload" + +Scenario: Test rrd_upload_orchestrate with NULL parameters + Given the remote debugger is configured + When I call rrd_upload_orchestrate with NULL upload directory + Then the C API should return error code 1 + And error logs should contain "Invalid parameters" + +Scenario: Test rrd_upload_orchestrate with empty directory + Given the remote debugger is configured + And the upload directory is empty + When I call rrd_upload_orchestrate with the empty directory + Then the C API should return error code 6 + And error logs should contain "Invalid or empty upload directory" +``` + +### Error/Edge Case Pattern + +```gherkin +Scenario: Corrupted JSON profile + When the remotedebugger received the message from webPA event + Then remotedebugger should read the Json file + And remotedebugger logs should contain the Json File Parse Failed + +Scenario: Empty issue type event + Given the remote debugger is running + When I trigger the event with empty IssueType value + Then the remotedebugger receives the message from webPA event + And remotedebugger should log as not processing empty event + +Scenario: Missing issue type in static profile + When the remotedebugger received the message + Then remotedebugger should log as the Issue requested is not found in the profile +``` + +### Single Instance Pattern + +```gherkin +Scenario: Remote debugger exits if another instance is invoked + Given the RemoteDebugger is not already running + When the RemoteDebugger binary is invoked + Then the RemoteDebugger should be started + And when the RemoteDebugger is attempted to be started again + Then the RemoteDebugger should not start another instance +``` + +## Quality Checklist + +Before completing feature generation for remote debugger: + +- [ ] All source modules analyzed (`src/Makefile.am` sources list) +- [ ] Conditional modules noted with build flags (`IARMBUS_ENABLE`) +- [ ] Each RBUS event handler has at least one trigger scenario +- [ ] Static profile happy path has end-to-end scenario (trigger → parse → execute → upload) +- [ ] Dynamic profile fallback flow documented +- [ ] Append mode (static+dynamic) documented +- [ ] Harmful command sanity check scenarios included +- [ ] Corrupted/missing profile error scenarios documented +- [ ] Empty/invalid issue type edge cases documented +- [ ] Deep sleep event handling documented +- [ ] C API upload orchestration scenarios included (valid + error cases) +- [ ] RFC enable/disable control documented +- [ ] Single instance enforcement documented +- [ ] Suffixed issue type handling documented +- [ ] License headers included (Apache 2.0, RDK Management) +- [ ] Source file references included as comments +- [ ] Log message assertions match actual daemon log output +- [ ] Feature-to-test file mapping documented for gap analysis +- [ ] Scenarios are atomic (one behavior per scenario) +- [ ] Given/When/Then/And/Or structure followed consistently + +## Output Structure + +``` +test/functional-tests/ +├── features/ +│ ├── README.md # Index, module mapping, gap summary +│ ├── rrd_start_subscribe_and_wait.feature # RBUS subscription, event loop +│ ├── rrd_start_control.feature # RFC enable/disable +│ ├── rrd_single_instance.feature # Single instance enforcement +│ ├── rrd_static_profile_report.feature # Static profile end-to-end +│ ├── rrd_static_profile_category_report.feature # Category-only issue type +│ ├── test_rrd_static_profile_report_with_suffix.feature # Suffixed issue type +│ ├── test_rrd_static_profile_report_with_suffix_negative_case.feature # Invalid suffix +│ ├── rrd_background_cmd_static_profile_report.feature # Background command execution +│ ├── rrd_dynamic_profile_report.feature # Dynamic profile fallback +│ ├── rrd_dynamic_profile_subcategory_report.feature # Dynamic subcategory +│ ├── rrd_dynamic_profile_missing_report.feature # Missing dynamic profile +│ ├── rrd_append_report.feature # Append mode (static+dynamic) +│ ├── rrd_append_dynamic_profile_static_not_found.feature # Append when static missing +│ ├── rrd_harmful_command_static_report.feature # Harmful command blocking +│ ├── test_rrd_dynamic_profile_harmful_report.feature # Dynamic harmful commands +│ ├── rrd_corrupted_static_profile_report.feature # Invalid/corrupted JSON +│ ├── rrd_static_profile_missing_command_report.feature # Missing command in profile +│ ├── rrd_empty_issuetype_event.feature # Empty event value +│ ├── rrd_deepsleep_static_report.feature # Deep sleep handling +│ ├── rrd_debug_report_upload.feature # Upload + download validation +│ └── rrd_c_api_upload.feature # C API upload orchestration +└── tests/ + ├── helper_functions.py # Log grep, process control, constants + ├── test_rrd_static_profile_report.py # Static profile test + ├── test_rrd_dynamic_profile_report.py # Dynamic profile test + ├── test_rrd_c_api_upload.py # C API upload test + ├── test_rrd_single_instance.py # Single instance test + ├── ... # (see full listing below) + └── uploadSTBLogs.sh # Mock upload script +``` + +## Example: Complete Remote Debugger Feature File + +```gherkin +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2026 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +# Source: src/rrdEventProcess.c +# Source: src/rrdJsonParser.c +# Source: src/rrdRunCmdThread.c +# Source: src/rrdCommandSanity.c + +Feature: Remote Debugger Static Report + + Scenario: Check if remote debugger configuration file exists + Given the configuration file path is set + When I check if the configuration file exists + Then the configuration file should exist + + Scenario: Check if /tmp/rrd output directory exists + Given the /tmp/rrd directory path is set + When I check if the /tmp/rrd directory exists + Then the /tmp/rrd directory should exist + + Scenario: Verify remote debugger process is running + Given the remote debugger process is not running + When I start the remote debugger process + Then the remote debugger process should be running + + Scenario: Send WebPA event for IssueType and verify end-to-end processing + Given the remote debugger is running + When I trigger the event "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType" + Then the event for RRD_SET_ISSUE_EVENT should be received + And the logs should contain "SUCCESS: Message sending Done" + And the logs should be seen with "SUCCESS: Message Reception Done" + When the remotedebugger received the message from webPA event + Then remotedebugger should read the Json file + And remotedebugger logs should contain the Json File Parse success + And the issue data node and sub-node should be found in the JSON file + And the directory should be created to store the executed output + And Sanity check to validate the commands should be executed + And Command output should be added to the output file + And the issuetype systemd service should start successfully + And the journalctl service should start successfully + And the process should sleep with timeout + And the issuetype systemd service should stop successfully + And the remotedebugger should call script to upload the debug report + + Scenario: Upload remote debugger debug report + When I check the upload status in the logs + Then the upload should be successful if upload is success + Or the upload should fail if upload fails +``` + +## Integration with Gap Analysis + +After generating feature files, use them for gap analysis against the L2 test suite: + +### Step 1: Map Features to Existing Tests + +``` +features/rrd_start_subscribe_and_wait.feature ↔ tests/test_rrd_start_subscribe_and_wait.py +features/rrd_start_control.feature ↔ tests/test_rrd_start_control.py +features/rrd_single_instance.feature ↔ tests/test_rrd_single_instance.py +features/rrd_static_profile_report.feature ↔ tests/test_rrd_static_profile_report.py +features/rrd_static_profile_category_report.feature ↔ tests/test_rrd_static_profile_category_report.py +features/rrd_background_cmd_static_profile_report.feature ↔ tests/test_rrd_background_cmd_static_profile_report.py +features/rrd_dynamic_profile_report.feature ↔ tests/test_rrd_dynamic_profile_report.py +features/rrd_dynamic_profile_subcategory_report.feature ↔ tests/test_rrd_dynamic_subcategory_report.py +features/rrd_dynamic_profile_missing_report.feature ↔ tests/test_rrd_dynamic_profile_missing_report.py +features/rrd_append_report.feature ↔ tests/test_rrd_append_report.py +features/rrd_append_dynamic_profile_static_not_found.feature ↔ tests/test_rrd_append_dynamic_profile_static_notfound.py +features/rrd_harmful_command_static_report.feature ↔ tests/test_rrd_harmful_command_static_report.py +features/test_rrd_dynamic_profile_harmful_report.feature ↔ tests/test_rrd_dynamic_profile_harmful_report.py +features/rrd_corrupted_static_profile_report.feature ↔ tests/test_rrd_corrupted_static_profile_report.py +features/rrd_static_profile_missing_command_report.feature ↔ tests/test_rrd_static_profile_missing_command_report.py +features/rrd_empty_issuetype_event.feature ↔ tests/test_rrd_empty_issuetype_event.py +features/rrd_deepsleep_static_report.feature ↔ tests/test_rrd_deepsleep_static_report.py +features/rrd_debug_report_upload.feature ↔ tests/test_rrd_debug_report_upload.py +features/rrd_c_api_upload.feature ↔ tests/test_rrd_c_api_upload.py +features/test_rrd_static_profile_report_with_suffix.feature ↔ tests/test_rrd_static_profile_report_with_suffix.py +features/test_rrd_static_profile_report_with_suffix_negative_case.feature ↔ tests/test_rrd_static_profile_report_with_suffix_negative_case.py +``` + +### Step 2: Count Coverage + +For each feature file: +1. Count total scenarios (= total testable behaviors) +2. Count scenarios that have a matching `test_*` function in `test/functional-tests/tests/` +3. Calculate coverage = matched / total + +### Step 3: Identify Missing Tests + +Features without test coverage fall into categories: + +| Category | Example | Required Infrastructure | +|---|---|---| +| WebCfg event handling | WebCfgData RBUS event trigger | WebConfig mock, MsgPack payload | +| Profile data SET/GET | setProfileData / getProfileData | `rbuscli` SET/GET validation | +| Upload lock contention | Concurrent upload attempts | Lock file manipulation | +| Deep sleep edge cases | Deep sleep during active collection | IARM event simulation | +| Archive CPU throttle | CPU usage too high during archive | CPU load simulation | +| Configuration fallback | RFC → DCM → dcm.properties chain | Config file manipulation | + +### Step 4: Identify Undocumented Tests + +Tests that exist in `test/functional-tests/tests/` but have no matching scenario in the +`test/functional-tests/features/` files. These should be documented retroactively: + +- `test_rrd_profile_data.py` — Profile data SET/GET (no matching `.feature`) + +### Step 5: Generate Gap Report + +Include a summary table in `test/functional-tests/features/README.md`: + +```markdown +| Behavior Area | Feature Scenarios | L2 Tests | Coverage | Top Gaps | +|---|:---:|:---:|:---:|---| +| Daemon startup/subscribe | 3 | 3 | 100% | — | +| RFC enable/disable | 2 | 2 | 100% | — | +| Single instance | 1 | 1 | 100% | — | +| Static profile report | 5 | 5 | 100% | — | +| Static category report | 5 | 5 | 100% | — | +| Dynamic profile report | 5 | 5 | 100% | — | +| Append mode | 4 | 4 | 100% | — | +| Harmful commands | 4 | 4 | 100% | — | +| Corrupted profile | 4 | 4 | 100% | — | +| Empty issuetype | 2 | 2 | 100% | — | +| Deep sleep | 5 | 5 | 100% | — | +| Debug report upload | 7 | 7 | 100% | — | +| C API upload | 8+ | 8+ | ~100% | Error path coverage | +| WebCfg event | 0 | 0 | 0% | Entire flow | +| Profile data SET/GET | 0 | 1 | partial | No feature file | +| Upload lock contention | 0 | 0 | 0% | Concurrency tests | +``` + +## Current L2 Test Layout + +``` +test/functional-tests/ +├── features/ # BDD feature files (documentation + test specs) +│ ├── rrd_start_subscribe_and_wait.feature # RBUS subscription +│ ├── rrd_start_control.feature # RFC enable/disable +│ ├── rrd_single_instance.feature # Single instance +│ ├── rrd_static_profile_report.feature # Static profile end-to-end +│ ├── rrd_static_profile_category_report.feature +│ ├── test_rrd_static_profile_report_with_suffix.feature +│ ├── test_rrd_static_profile_report_with_suffix_negative_case.feature +│ ├── rrd_background_cmd_static_profile_report.feature +│ ├── rrd_dynamic_profile_report.feature +│ ├── rrd_dynamic_profile_subcategory_report.feature +│ ├── rrd_dynamic_profile_missing_report.feature +│ ├── rrd_append_report.feature +│ ├── rrd_append_dynamic_profile_static_not_found.feature +│ ├── rrd_harmful_command_static_report.feature +│ ├── test_rrd_dynamic_profile_harmful_report.feature +│ ├── rrd_corrupted_static_profile_report.feature +│ ├── rrd_static_profile_missing_command_report.feature +│ ├── rrd_empty_issuetype_event.feature +│ ├── rrd_deepsleep_static_report.feature +│ ├── rrd_debug_report_upload.feature +│ └── rrd_c_api_upload.feature +├── tests/ # Runnable pytest functions +│ ├── helper_functions.py # Log grep, process control, file checks, constants +│ ├── test_rrd_start_subscribe_and_wait.py +│ ├── test_rrd_start_control.py +│ ├── test_rrd_single_instance.py +│ ├── test_rrd_static_profile_report.py +│ ├── test_rrd_static_profile_category_report.py +│ ├── test_rrd_static_profile_report_with_suffix.py +│ ├── test_rrd_static_profile_report_with_suffix_negative_case.py +│ ├── test_rrd_background_cmd_static_profile_report.py +│ ├── test_rrd_dynamic_profile_report.py +│ ├── test_rrd_dynamic_subcategory_report.py +│ ├── test_rrd_dynamic_profile_missing_report.py +│ ├── test_rrd_append_report.py +│ ├── test_rrd_append_dynamic_profile_static_notfound.py +│ ├── test_rrd_harmful_command_static_report.py +│ ├── test_rrd_dynamic_profile_harmful_report.py +│ ├── test_rrd_corrupted_static_profile_report.py +│ ├── test_rrd_static_profile_missing_command_report.py +│ ├── test_rrd_empty_issuetype_event.py +│ ├── test_rrd_deepsleep_static_report.py +│ ├── test_rrd_debug_report_upload.py +│ ├── test_rrd_c_api_upload.py +│ ├── test_rrd_profile_data.py # ⚠ No matching .feature file +│ ├── create_json.sh # JSON profile creation helper +│ ├── deepsleep_main.c # Deep sleep simulation binary +│ ├── power_controller.h # Power controller mock header +│ ├── Makefile # Test build file +│ └── uploadSTBLogs.sh # Mock upload script +``` + +**Test runner:** `pytest`, executed sequentially per test file. +**Interfaces exercised:** `rbuscli` (RBUS event trigger), log scraping (`/opt/logs/remotedebugger.log.0`), file system checks (`/tmp/rrd/`, `/etc/rrd/`), mock xconf server (upload validation). + +## Maintenance + +When remote debugger source code changes: + +1. **New RBUS event/parameter added** — Add scenario to appropriate `.feature` file; update RBUS data elements table in README +2. **New profile processing mode** — Create a new `.feature` file; add to README index +3. **New upload mechanism** — Document the upload flow; add C API test scenarios if applicable +4. **Handler removed** — Remove corresponding scenario; note in gap analysis +5. **Build flag changed** — Update conditional compilation notes in README +6. **L2 test added** — Update gap analysis coverage numbers; create `.feature` file if missing +7. **New log messages added** — Update log assertion strings in relevant feature scenarios +8. **New sanity check rules** — Add harmful command detection scenarios +9. **Version tag** — Include generation date in README + +## Related Skills + +- `technical-documentation-writer` — For detailed architecture and API docs (`docs/`) +- `memory-safety-analyzer` — For safety analysis of C source code +- `thread-safety-analyzer` — For concurrency analysis of event thread and message queue +- `quality-checker` — For running static analysis and build verification +- `triage-logs` — For correlating device logs with remote debugger source code diff --git a/CHANGELOG.md b/CHANGELOG.md index bd9247755..6e4641101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,19 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [1.3.4](https://github.com/rdkcentral/remote_debugger/compare/1.3.3...1.3.4) + +- RDK-60236 : Remote Debugger Supports UUID Information in Debug Report File [`#194`](https://github.com/rdkcentral/remote_debugger/pull/194) +- Refactor issueTypeSplitter to include suffix handling [`c56fef3`](https://github.com/rdkcentral/remote_debugger/commit/c56fef33df07f47c2ea91c26010548bf0a3baa36) +- Merge tag '1.3.3' into develop [`7151d20`](https://github.com/rdkcentral/remote_debugger/commit/7151d208ced521de7ee7dee66cca6aea951e95b3) + #### [1.3.3](https://github.com/rdkcentral/remote_debugger/compare/1.3.2...1.3.3) +> 23 April 2026 + - RDK-59833 : Remote Debugger datamodel support to retrieve Static Profile Data [`#183`](https://github.com/rdkcentral/remote_debugger/pull/183) - RDKEMW-16897: Add Agentic Support for the RRD [`#182`](https://github.com/rdkcentral/remote_debugger/pull/182) +- RRD 1.3.3 release changelog updates [`bdaea9a`](https://github.com/rdkcentral/remote_debugger/commit/bdaea9aa695da5b0c0450cddd096052bfd5b0ce3) - Merge tag '1.3.2' into develop [`8f793e6`](https://github.com/rdkcentral/remote_debugger/commit/8f793e69b6b2b91a27c6a8148014207788503efa) #### [1.3.2](https://github.com/rdkcentral/remote_debugger/compare/1.3.1...1.3.2) diff --git a/docs/rrd_uuid_support_implementation_HLD.md b/docs/rrd_uuid_support_implementation_HLD.md new file mode 100644 index 000000000..45ff6ca3d --- /dev/null +++ b/docs/rrd_uuid_support_implementation_HLD.md @@ -0,0 +1,207 @@ +# Remote Debugger Supports UUID Information in Debug Report File — High-Level Design Document + +## Document Information + +| Field | Value | +|-------------------|-----------------------------------------| +| **Feature** | Supports UUID Information in Debug Report File | +| **Component** | Remote Debugger (RRD) | +| **Version** | 1.0 | +| **Date** | May 2026 | +| **Target Platform** | Embedded Linux Systems | + +--- + +## 1. Executive Summary + +This document describes the high-level design for adding support for an **optional, underscore-delimited suffix** appended to the `IssueType` RFC value (e.g., `Device.DeviceInfo_ab1234`). The suffix is parsed, validated, sanitized, and carried through the entire event-processing pipeline so that it can be re-appended to the generated upload archive filename. Additionally, systemd unit names are disambiguated using an epoch timestamp, and the debug output file open-mode is corrected to prevent stale data accumulation. + +--- + +## 2. Background and Motivation + +### 2.1 Problem Statement + +Previously, the `IssueType` RFC parameter was expected to contain a simple, period-delimited string (e.g., `Device.DeviceInfo`). Operators needed a way to attach additional context (e.g., a ticket number or short identifier) directly to the event value so that the resulting debug archive could be uniquely correlated with an external issue tracker without modifying the core node/sub-node classification logic. + +### 2.2 Goals + +1. Parse and validate an optional `_` token appended to the IssueType string. +2. Sanitize the suffix to prevent shell injection when it is embedded in filenames or commands. +3. Propagate the suffix through the event pipeline to the archive upload step. +4. Append the suffix to the upload archive filename so the remote server can correlate the archive with the originating issue context. +5. Disambiguate systemd transient unit names to avoid conflicts when the same IssueType is processed concurrently or repeatedly. + +### 2.3 Non-Goals + +- Changing the node/sub-node classification logic. +- Supporting suffixes longer than 9 characters (including the leading `_`). +- Modifying the upstream RFC parameter interface. + +--- + +## 3. Architecture Overview + +### 3.1 System Context + +```mermaid +graph TB + subgraph Device["Embedded Device"] + RFC["RFC/RBUS Event
IssueType = Device.Info_ab1234"] + subgraph RRD["Remote Debugger (RRD) Process"] + Interface["rrdInterface
Event Reception & Routing"] + EventProc["rrdEventProcess
IssueType Processing"] + JsonParser["rrdJsonParser
split_issue_type() + JSON Lookup"] + CmdThread["rrdRunCmdThread
Command Execution"] + LogProc["rrd_logproc
Log Conversion"] + Archive["rrd_archive
Tar/Gz Creation"] + Upload["rrd_upload
S3/HTTP Upload"] + end + Interface -->|"data_buf (mdata, suffix)"| EventProc + EventProc -->|"data_buf"| JsonParser + JsonParser -->|"issueData + suffix"| CmdThread + CmdThread -->|"debug_outputs.txt"| Archive + Archive -->|".tar.gz"| Upload + end + Upload -->|"HTTPS"| RemoteServer["Remote Log Server"] +``` + +### 3.2 Module Responsibilities + +| Module | File(s) | Responsibility | +|--------|---------|----------------| +| Event Interface | `rrdInterface.c` | Receives RBUS/RFC events; initialises and de-allocates `data_buf`; propagates `suffix = NULL` initially | +| Event Processor | `rrdEventProcess.c` | Splits IssueType list; calls `split_issue_type()`; populates `data_buf.suffix`; routes to JSON parser | +| JSON Parser | `rrdJsonParser.c` | Implements `split_issue_type()`; resolves node/sub-node; builds upload filename with suffix | +| Command Thread | `rrdRunCmdThread.c` | Executes debug commands via systemd-run; writes `debug_outputs.txt` (w+ mode); appends epoch to unit name | +| Log Processor | `rrd_logproc.c` | Converts IssueType to safe filename characters; preserves hyphens for suffix UUID tokens | +| Archive Manager | `rrd_archive.c` | Creates `.tar.gz` archive of the output directory | +| Upload Manager | `rrd_upload.c` | Uploads the archive to the remote log server | +| Common Types | `rrdCommon.h` | Defines `data_buf` struct with new `suffix` field | + +--- + +## 4. Data Flow + +### 4.1 Nominal Flow (with suffix) + +``` +RFC set: Device.DeviceInfo_ab1234 + | + v +rrdInterface._remoteDebuggerEventHandler() + → RRD_data_buff_init() [suffix = NULL] + → pushIssueTypesToMsgQueue("Device.DeviceInfo_ab1234", EVENT_MSG) + | + v +rrdEventProcess.processIssueTypeEvent() + → issueTypeSplitter("Device.DeviceInfo_ab1234", ',', &cmdMap) + → for each token: split_issue_type(token, base, suffix) + base = "Device.DeviceInfo" + suffix = "_ab1234" + → cmdBuff->mdata = "Device.DeviceInfo" + → cmdBuff->suffix = "_ab1234" + → processIssueType(cmdBuff) + | + v +rrdJsonParser.checkIssueNodeInfo() + → Reads JSON profile for "Device.DeviceInfo" + → outdir = "/tmp/rrd/DeviceInfo-DebugReport-2026-05-13-10-00-00" + → executeCommands() / invokeSanityandCommandExec() + | + v +rrdRunCmdThread.executeCommands() + → unit = "remote_debugger_DeviceInfo_" + → systemd-run --unit=remote_debugger_DeviceInfo_ ... + → journalctl -u remote_debugger_DeviceInfo_ + → fopen(debug_outputs.txt, "w+") + | + v +rrdJsonParser.checkIssueNodeInfo() [after exec] + → tarName = "Device.DeviceInfo" + "_ab1234" + = "Device.DeviceInfo_ab1234" + → uploadDebugoutput(outdir, "Device.DeviceInfo_ab1234") + | + v +rrd_logproc.rrd_logproc_convert_issue_type("Device.DeviceInfo_ab1234") + → "DEVICE_DEVICEINFO_AB1234" (hyphens also preserved) + | + v +rrd_upload / rrd_archive + → Archive: AABBCCDDEEFF_DEVICE_DEVICEINFO_AB1234.tar.gz + → Upload to remote server +``` + +### 4.2 Nominal Flow (without suffix) + +Same as above with: +- `split_issue_type()` returns `suffix = ""` +- `tarName = "Device.DeviceInfo"` (no suffix appended) +- Archive: `AABBCCDDEEFF_DEVICE_DEVICEINFO.tar.gz` + +--- + +## 5. Key Algorithms and Data Structures + +### 5.1 `data_buf` Structure (updated) + +```c +typedef struct mbuffer { + message_type_et mtype; + char *mdata; /* IssueType base (no suffix) */ + char *jsonPath; + bool inDynamic; + bool appendMode; + deepsleep_event_et dsEvent; + char *suffix; /* NEW: optional "_", heap-allocated or NULL */ +} data_buf; +``` + +**Lifecycle of `suffix`:** +- Initialised to `NULL` by `RRD_data_buff_init()`. +- Allocated (via `strdup`) inside `processIssueTypeEvent()` when a non-empty suffix is present. +- Freed in all exit paths of `checkIssueNodeInfo()` and `processIssueTypeInInstalledPackage()`. +- Also freed by `RRD_data_buff_deAlloc()` for callers using the generic de-allocation path. + +## 6. Component Interaction and Interfaces + +### 6.1 Public API Changes + +| Symbol | File | Change | +|--------|------|--------| +| `split_issue_type()` | `rrdJsonParser.h / .c` | **New** function | +| `data_buf.suffix` | `rrdCommon.h` | **New** field (`char *`) | +| `RRD_data_buff_init()` | `rrdInterface.c` | Sets `sbuf->suffix = NULL` | +| `RRD_data_buff_deAlloc()` | `rrdInterface.c` | `free(sbuf->suffix)` added | + +### 6.2 Internal Call Chain + +``` +processIssueTypeEvent() + └─ split_issue_type() [rrdJsonParser.c] + └─ processIssueType() [rrdEventProcess.c] + └─ processIssueTypeInStaticProfile() + └─ checkIssueNodeInfo() [rrdJsonParser.c] + └─ executeCommands() / invokeSanityandCommandExec() + └─ uploadDebugoutput(outdir, tarName) + └─ rrd_logproc_convert_issue_type() + └─ rrd_archive_create() + └─ rrd_upload_file() +``` + +--- + + + +## 7. Glossary + +| Term | Definition | +|------|-----------| +| IssueType | RFC parameter string identifying the debug category (e.g., `Device.DeviceInfo`) | +| Suffix | Optional `_` appended to IssueType (e.g., `_ab1234`), used to distinguish archive names | +| base | The IssueType string before the first `_`; used for JSON profile lookup | +| RFC | Remote Feature Control — device configuration parameter infrastructure | +| RBUS | RDK Message Bus — event delivery mechanism on RDK devices | +| RRD | RDK Remote Debugger — this component | +| tarName | The logical name used when creating and uploading the debug archive | +| data_buf | Internal message buffer passed between RRD processing stages | diff --git a/.github/docs/uploadRRDLogs_Flowcharts.md b/docs/uploadRRDLogs_Flowcharts.md similarity index 100% rename from .github/docs/uploadRRDLogs_Flowcharts.md rename to docs/uploadRRDLogs_Flowcharts.md diff --git a/.github/docs/uploadRRDLogs_HLD.md b/docs/uploadRRDLogs_HLD.md similarity index 100% rename from .github/docs/uploadRRDLogs_HLD.md rename to docs/uploadRRDLogs_HLD.md diff --git a/.github/docs/uploadRRDLogs_LLD.md b/docs/uploadRRDLogs_LLD.md similarity index 100% rename from .github/docs/uploadRRDLogs_LLD.md rename to docs/uploadRRDLogs_LLD.md diff --git a/.github/docs/uploadRRDLogs_Requirements.md b/docs/uploadRRDLogs_Requirements.md similarity index 100% rename from .github/docs/uploadRRDLogs_Requirements.md rename to docs/uploadRRDLogs_Requirements.md diff --git a/.github/docs/uploadRRDLogs_SequenceDiagrams.md b/docs/uploadRRDLogs_SequenceDiagrams.md similarity index 100% rename from .github/docs/uploadRRDLogs_SequenceDiagrams.md rename to docs/uploadRRDLogs_SequenceDiagrams.md diff --git a/run_l2.sh b/run_l2.sh index 6ddf7d8ec..bfa1068fc 100644 --- a/run_l2.sh +++ b/run_l2.sh @@ -72,6 +72,8 @@ pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_s pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_start_control.json test/functional-tests/tests/test_rrd_start_control.py pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_start_subscribe_and_wait.json test/functional-tests/tests/test_rrd_start_subscribe_and_wait.py pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_static_profile_report.json test/functional-tests/tests/test_rrd_static_profile_report.py +pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_static_profile_report_with_suffix.json test/functional-tests/tests/test_rrd_static_profile_report_with_suffix.py +pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_static_profile_report_with_suffix_negative.json test/functional-tests/tests/test_rrd_static_profile_report_with_suffix_negative_case.py pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_corrupted_static_profile_report.json test/functional-tests/tests/test_rrd_corrupted_static_profile_report.py cp remote_debugger.json /etc/rrd/ pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_harmfull_static_profile_report.json test/functional-tests/tests/test_rrd_harmful_command_static_report.py @@ -85,4 +87,6 @@ pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_c_ cp remote_debugger.json /etc/rrd/ pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_profile_data.json test/functional-tests/tests/test_rrd_profile_data.py +pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/rrd_dynamic_profile_rdm_node_length_exceeded.json test/functional-tests/tests/test_rrd_dynamic_profile_rdm_node_length_exceeded.py + diff --git a/src/rrdCommon.h b/src/rrdCommon.h index b91ff6f7b..191ee4745 100644 --- a/src/rrdCommon.h +++ b/src/rrdCommon.h @@ -58,6 +58,7 @@ extern "C" #define RRD_MEDIA_APPS "/media/apps/" #define RDM_PKG_PREFIX "RDK-RRD-" #define RDM_PKG_SUFFIX ":1.0" +#define RRD_DYNAMIC_PROFILE_MAX_LENGTH 34 #ifndef RRD_PROFILE_LIST #define RRD_DEVICE_PROFILE "" @@ -97,6 +98,7 @@ typedef struct mbuffer { bool inDynamic; bool appendMode; deepsleep_event_et dsEvent; + char *suffix; // Holds the suffix split from issue type string, if any } data_buf; /*Structure for Message Header*/ diff --git a/src/rrdDynamic.c b/src/rrdDynamic.c index 2deee32ab..2ca6038d8 100644 --- a/src/rrdDynamic.c +++ b/src/rrdDynamic.c @@ -127,7 +127,20 @@ int RRDGetProfileStringLength(issueNodeData *pissueStructNode, bool isDeepSleepA unsigned int prefixlen = strlen(RDM_PKG_PREFIX); unsigned int suffixlen = strlen(RDM_PKG_SUFFIX); unsigned int nodelen = 0; + size_t boundedNodeLen = 0; + if ((pissueStructNode == NULL) || (pissueStructNode->Node == NULL)) + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Invalid issue node data for profile length calculation\n", __FUNCTION__, __LINE__); + return -1; + } + + boundedNodeLen = strnlen(pissueStructNode->Node, RRD_DYNAMIC_PROFILE_MAX_LENGTH + 1); + if (boundedNodeLen > RRD_DYNAMIC_PROFILE_MAX_LENGTH) + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Issue node length must be less than %d\n", __FUNCTION__, __LINE__, RRD_DYNAMIC_PROFILE_MAX_LENGTH); + return -1; + } /* Calculate Length for Device Type for Deep Sleep Awake Event*/ if (isDeepSleepAwakeEvent) { @@ -143,7 +156,7 @@ int RRDGetProfileStringLength(issueNodeData *pissueStructNode, bool isDeepSleepA } else { - nodelen = strlen(pissueStructNode->Node); + nodelen = boundedNodeLen; length = prefixlen + nodelen + suffixlen; } return length + 1; @@ -270,9 +283,15 @@ void RRDRdmManagerDownloadRequest(issueNodeData *pissueStructNode, char *dynJSON { RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Memory Allocation Failed for Request RDM Manager Download.\n", __FUNCTION__, __LINE__); } - free(pissueStructNode->Node); - free(pissueStructNode->subNode); } + else + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Invalid profile length, skipping download request\n", __FUNCTION__, __LINE__); + } + free(pissueStructNode->Node); + free(pissueStructNode->subNode); + pissueStructNode->Node = NULL; + pissueStructNode->subNode = NULL; } else { diff --git a/src/rrdEventProcess.c b/src/rrdEventProcess.c index 5164e7832..eb3c882df 100644 --- a/src/rrdEventProcess.c +++ b/src/rrdEventProcess.c @@ -79,7 +79,35 @@ void processIssueTypeEvent(data_buf *rbuf) cmdBuff = (data_buf *)malloc(sizeof(data_buf)); if (cmdBuff) { - dataMsgLen = strlen(cmdMap[index]) + 1; + char base[128] = {0}; + char local_suffix[128] = {0}; + split_issue_type(cmdMap[index], base, sizeof(base), local_suffix, sizeof(local_suffix)); + if (base[0] == '\0') + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Empty issue type after parsing token [%s], skipping... \n", __FUNCTION__, __LINE__, cmdMap[index]); + free(cmdBuff); + cmdBuff = NULL; + if (cmdMap[index]) + { + free(cmdMap[index]); + cmdMap[index] = NULL; + } + continue; + } + removeSpecialCharacterfromIssueTypeList(base); + if (base[0] == '\0') + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Empty base after sanitization for token [%s], skipping... \n", __FUNCTION__, __LINE__, cmdMap[index]); + free(cmdBuff); + cmdBuff = NULL; + if (cmdMap[index]) + { + free(cmdMap[index]); + cmdMap[index] = NULL; + } + continue; + } + dataMsgLen = strlen(base) + 1; RRD_data_buff_init(cmdBuff, EVENT_MSG, RRD_DEEPSLEEP_INVALID_DEFAULT); /* Setting Deafult Values*/ cmdBuff->inDynamic = rbuf->inDynamic; if(cmdBuff->inDynamic) @@ -88,9 +116,19 @@ void processIssueTypeEvent(data_buf *rbuf) } cmdBuff->appendMode = rbuf->appendMode; cmdBuff->mdata = (char *)calloc(1, dataMsgLen); + + /* Store suffix for this issue type */ + cmdBuff->suffix = NULL; + if (local_suffix[0] != '\0') { + cmdBuff->suffix = strdup(local_suffix); + if (cmdBuff->suffix == NULL) + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to allocate memory for suffix... \n", __FUNCTION__, __LINE__); + } + } if (cmdBuff->mdata) { - strncpy((char *)cmdBuff->mdata, cmdMap[index], dataMsgLen); + strncpy((char *)cmdBuff->mdata, base, dataMsgLen); processIssueType(cmdBuff); } else @@ -99,6 +137,11 @@ void processIssueTypeEvent(data_buf *rbuf) } if(cmdBuff) { + if (cmdBuff->suffix) + { + free(cmdBuff->suffix); + cmdBuff->suffix = NULL; + } free(cmdBuff); cmdBuff = NULL; } @@ -392,7 +435,10 @@ static void processIssueTypeInStaticProfile(data_buf *rbuf, issueNodeData *pIssu #if !defined(GTEST_ENABLE) jsonParsed = readAndParseJSON(RRD_JSON_FILE); #else - jsonParsed = readAndParseJSON(rbuf->jsonPath); + if (rbuf->jsonPath != NULL) + { + jsonParsed = readAndParseJSON(rbuf->jsonPath); + } #endif if (jsonParsed == NULL) { // Static Profile JSON Parsing or Read Fail @@ -555,6 +601,29 @@ static void processIssueTypeInInstalledPackage(data_buf *rbuf, issueNodeData *pI suffixlen = strlen(RDM_PKG_SUFFIX); dynJSONPath = (char *)malloc(persistentAppslen + prefixlen + suffixlen + strlen(pIssueNode->Node) + rrdjsonlen + 1); #else + if ((rbuf == NULL) || (rbuf->jsonPath == NULL)) + { + RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: jsonPath is NULL, skipping installed package check... \n", __FUNCTION__, __LINE__); + if (rbuf != NULL) + { + if (rbuf->mdata != NULL) + { + free(rbuf->mdata); + rbuf->mdata = NULL; + } + if (rbuf->suffix != NULL) + { + free(rbuf->suffix); + rbuf->suffix = NULL; + } + if (rbuf->jsonPath != NULL) + { + free(rbuf->jsonPath); + rbuf->jsonPath = NULL; + } + } + return; + } int utjsonlen = strlen(rbuf->jsonPath); dynJSONPath = (char *)malloc(utjsonlen + 1); #endif @@ -639,7 +708,7 @@ static void removeSpecialCharacterfromIssueTypeList(char *str) while (str[source] != '\0') { - if (isalnum(str[source]) || str[source] == ',' || str[source] == '.') + if (isalnum(str[source]) || str[source] == ',' || str[source] == '.') { str[destination] = str[source]; ++destination; @@ -663,7 +732,6 @@ static int issueTypeSplitter(char *input_str, const char delimeter, char ***args int cnt = 1, i = 0; char *str = input_str; - removeSpecialCharacterfromIssueTypeList(str); while (*str == delimeter) str++; diff --git a/src/rrdInterface.c b/src/rrdInterface.c index b69dd8936..30291cd84 100644 --- a/src/rrdInterface.c +++ b/src/rrdInterface.c @@ -275,6 +275,7 @@ void RRD_data_buff_init(data_buf *sbuf, message_type_et sndtype, deepsleep_event sbuf->inDynamic = false; sbuf->appendMode = false; sbuf->dsEvent = deepSleepEvent; + sbuf->suffix = NULL; } /*Function: RRD_data_buff_deAlloc @@ -295,6 +296,10 @@ void RRD_data_buff_deAlloc(data_buf *sbuf) { free(sbuf->jsonPath); } + if (sbuf->suffix) + { + free(sbuf->suffix); + } free(sbuf); } } diff --git a/src/rrdJsonParser.c b/src/rrdJsonParser.c index e06d93ac2..2fce93676 100644 --- a/src/rrdJsonParser.c +++ b/src/rrdJsonParser.c @@ -24,6 +24,8 @@ #include #include #include +/* Maximum suffix length, including the leading '_' character. */ +#define RRD_MAX_SUFFIX_LEN 9 /* * @function removeSpecialChar @@ -46,6 +48,102 @@ void removeSpecialChar(char *str) } } + +/* + * @function split_issue_type + * @brief Utility to split base and suffix from issue type string. + * The input is always split at the first '_'. The base is the part + * before the underscore (never contains '_'). The suffix is only + * kept when its total length (including the leading '_') is at most + * RRD_MAX_SUFFIX_LEN (9) characters; longer suffixes are discarded. + * After length validation the suffix is sanitized: only the characters + * [A-Za-z0-9_-] are retained so that the value is safe to use in file + * names and shell command arguments without risk of injection. + * If no underscore is present the full input is the base. + * Examples: + * "Device.DeviceTime_ab12345" → base="Device.DeviceTime", + * suffix="_ab12345" (8 chars, accepted) + * "Device.DeviceTime_Search-uuid-very-long" + * → base="Device.DeviceTime", + * suffix="" (>9 chars, discarded) + * "Device.DeviceTime" → base="Device.DeviceTime", + * suffix="" + * "Device.DeviceTime_ab;rm" → base="Device.DeviceTime", + * suffix="_abrm" (unsafe ';' stripped) + * @param const char *input - The input string to split. + * @param char *base - Buffer to store the base part (never contains '_'). + * @param size_t base_len - Size of the base buffer. + * @param char *suffix - Buffer to store the suffix part when valid, else "". + * @param size_t suffix_len - Size of the suffix buffer. + * @return void + */ + +void split_issue_type(const char *input, char *base, size_t base_len, char *suffix, size_t suffix_len) +{ + if (base && base_len > 0) + { + base[0] = '\0'; + } + if (suffix && suffix_len > 0) + { + suffix[0] = '\0'; + } + + if (!input || !base || !suffix) + { + return; + } + + if (base_len == 0 || suffix_len == 0) + { + return; + } + + const char *underscore = strchr(input, '_'); + if (underscore) + { + /* Always split at the first underscore — base never contains '_' */ + size_t b_len = (size_t)(underscore - input); + if (b_len >= base_len) b_len = base_len - 1; + strncpy(base, input, b_len); + base[b_len] = '\0'; + + /* Keep the suffix only when its total length (including '_') is + * within the allowed limit; longer tokens are discarded. */ + if (strlen(underscore) <= RRD_MAX_SUFFIX_LEN) + { + /* Sanitize: retain only [A-Za-z0-9_-] to prevent injection when + * the suffix is later embedded in file names or command arguments. */ + size_t si = 0, di = 0; + size_t max_copy = suffix_len - 1; + while (underscore[si] != '\0' && di < max_copy) + { + char ch = underscore[si]; + if (isalnum((unsigned char)ch) || ch == '_' || ch == '-') + { + suffix[di++] = ch; + } + si++; + } + suffix[di] = '\0'; + } + else + { + RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Suffix after '%s' exceeds max length (%zu > %d); discarding\n", + __FUNCTION__, __LINE__, base, strlen(underscore), RRD_MAX_SUFFIX_LEN); + suffix[0] = '\0'; + } + } + else + { + /* No underscore — full input is the base */ + strncpy(base, input, base_len - 1); + base[base_len - 1] = '\0'; + suffix[0] = '\0'; + } +} + + /* * @function getParamcount * @brief Calculates the total number of nodes (elements) in the input string, excluding delimiters. @@ -515,7 +613,11 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf { RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Memory allocation failed for rfcbuf\n",__FUNCTION__,__LINE__); free(buff->mdata); // free rfc data + buff->mdata = NULL; free(buff->jsonPath); // free rrd path info + buff->jsonPath = NULL; + free(buff->suffix); // free suffix + buff->suffix = NULL; return; } @@ -535,7 +637,11 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: %s Directory creation failed!!!\n",__FUNCTION__,__LINE__,outdir); free(rfcbuf); // free duplicated rfc data free(buff->mdata); // free rfc data + buff->mdata = NULL; free(buff->jsonPath); // free rrd path info + buff->jsonPath = NULL; + free(buff->suffix); // free suffix + buff->suffix = NULL; return; } else @@ -576,7 +682,26 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf else { RDK_LOG(RDK_LOG_DEBUG,LOG_REMDEBUG,"[%s:%d]: Continue uploading Debug Report for %s from %s... \n",__FUNCTION__,__LINE__,buff->mdata,outdir); - status = uploadDebugoutput(outdir,buff->mdata); + // Use the persisted suffix from buff for upload + char tarName[512] = {0}; + int tar_name_len = 0; + if (buff->suffix && buff->suffix[0] != '\0') + { + tar_name_len = snprintf(tarName, sizeof(tarName), "%s%s", buff->mdata, buff->suffix); + } + else + { + tar_name_len = snprintf(tarName, sizeof(tarName), "%s", buff->mdata); + } + if ((tar_name_len < 0) || ((size_t)tar_name_len >= sizeof(tarName))) + { + RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Failed to build upload file name for %s. snprintf result:%d, buffer size:%zu\n", __FUNCTION__,__LINE__,buff->mdata,tar_name_len,sizeof(tarName)); + status = -1; + } + else + { + status = uploadDebugoutput(outdir, tarName); + } if(status != 0) { RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: RRD Upload Script Execution Failed!!! status:%d\n",__FUNCTION__,__LINE__,status); @@ -588,14 +713,22 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf } free(rfcbuf); // free duplicated rfc data free(buff->mdata); // free rfc data + buff->mdata = NULL; free(buff->jsonPath); // free rrd path info + buff->jsonPath = NULL; + free(buff->suffix); // free suffix + buff->suffix = NULL; } else { RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: No Command excuted as RRD Failed to change directory:%s\n",__FUNCTION__,__LINE__,outdir); free(rfcbuf); // free duplicated rfc data free(buff->mdata); // free rfc data + buff->mdata = NULL; free(buff->jsonPath); // free rrd path info + buff->jsonPath = NULL; + free(buff->suffix); // free suffix + buff->suffix = NULL; } } } diff --git a/src/rrdJsonParser.h b/src/rrdJsonParser.h index 299436101..87c4a2622 100644 --- a/src/rrdJsonParser.h +++ b/src/rrdJsonParser.h @@ -47,6 +47,8 @@ issueData* getIssueCommandInfo(issueNodeData *issuestructNode, cJSON *jsoncfg,ch bool processAllDebugCommand(cJSON *jsoncfg, issueNodeData *issuestructNode, char *rfcbuf); bool processAllDeepSleepAwkMetricsCommands(cJSON *jsoncfg, issueNodeData *issuestructNode, char *rfcbuf); +void split_issue_type(const char *input, char *base, size_t base_len, char *suffix, size_t suffix_len); + #ifdef __cplusplus } #endif diff --git a/src/rrdRunCmdThread.c b/src/rrdRunCmdThread.c index ce873d5d5..650144cdb 100644 --- a/src/rrdRunCmdThread.c +++ b/src/rrdRunCmdThread.c @@ -287,7 +287,7 @@ bool executeCommands(issueData *cmdinfo) char pathname[BUF_LEN_256] = {'\0'}; char *outdirpath = NULL; char finalOutFile[BUF_LEN_256] = {'\0'}; - char remoteDebuggerServiceStr[BUF_LEN_256] = {'\0'}; + char remoteDebuggerServiceStr[BUF_LEN_512] = {'\0'}; char *printbuffer = NULL; FILE *filePointer; const char *remoteDebuggerPrefix = "remote_debugger_"; @@ -357,7 +357,7 @@ bool executeCommands(issueData *cmdinfo) strncat(finalOutFile,RRD_OUTPUT_FILE, strlen(RRD_OUTPUT_FILE) + 1); /* Open debug_output.txt file*/ - filePointer = fopen(finalOutFile, "a+"); + filePointer = fopen(finalOutFile, "w+"); if (filePointer == NULL) { RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Unable to Open File:%s\n",__FUNCTION__,__LINE__,finalOutFile); @@ -376,21 +376,18 @@ bool executeCommands(issueData *cmdinfo) /*Executing Commands using systemd-run*/ RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: Executing following commands using systemd-run:\n \"%s\"\n",__FUNCTION__,__LINE__,cmdData->command); - - strncpy(remoteDebuggerServiceStr, remoteDebuggerPrefix, sizeof(remoteDebuggerServiceStr) - 1); - remoteDebuggerServiceStr[sizeof(remoteDebuggerServiceStr) - 1] = '\0'; - strncat(remoteDebuggerServiceStr, cmdData->rfcvalue, sizeof(remoteDebuggerServiceStr) - strlen(remoteDebuggerServiceStr) - 1); - + time_t epochTime = time(NULL); + snprintf(remoteDebuggerServiceStr, sizeof(remoteDebuggerServiceStr),"%s%s%ld", remoteDebuggerPrefix, cmdData->rfcvalue, (long)epochTime); removeQuotes(cmdData->command); FILE *systemdfp = v_secure_popen("r", "systemd-run -r --unit=%s --service-type=oneshot -p RemainAfterExit=yes /bin/sh -c %s", remoteDebuggerServiceStr, cmdData->command); if(systemdfp == NULL) { - RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Starting remote_debugger_%s service failed!!!\n",__FUNCTION__,__LINE__,cmdData->rfcvalue); + RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Starting %s service failed!!!\n",__FUNCTION__,__LINE__,remoteDebuggerServiceStr); } else { - RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: Starting remote_debugger_%s service success...\n",__FUNCTION__,__LINE__,cmdData->rfcvalue); + RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: Starting %s service success...\n",__FUNCTION__,__LINE__,remoteDebuggerServiceStr); copyDebugLogDestFile(systemdfp, filePointer); v_secure_pclose(systemdfp); } @@ -400,11 +397,11 @@ bool executeCommands(issueData *cmdinfo) FILE *journalctlfp = v_secure_popen("r", "journalctl -a -u %s", remoteDebuggerServiceStr); if(journalctlfp == NULL) { - RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: journalctl remote_debugger_%s service failed!!!\n",__FUNCTION__,__LINE__,cmdData->rfcvalue); + RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: journalctl %s service failed!!!\n",__FUNCTION__,__LINE__,remoteDebuggerServiceStr); } else { - RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: journalctl remote_debugger_%s service success...\n",__FUNCTION__,__LINE__,cmdData->rfcvalue); + RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: journalctl %s service success...\n",__FUNCTION__,__LINE__,remoteDebuggerServiceStr); copyDebugLogDestFile(journalctlfp, filePointer); v_secure_pclose(journalctlfp); } @@ -417,7 +414,7 @@ bool executeCommands(issueData *cmdinfo) sleep(cmdData->timeout); /*Stop or Reset runtime service for issue*/ - RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: Stopping remote_debugger_%s service...\n",__FUNCTION__,__LINE__,cmdData->rfcvalue); + RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: Stopping %s service...\n",__FUNCTION__,__LINE__,remoteDebuggerServiceStr); #if !defined(GTEST_ENABLE) v_secure_system("systemctl stop %s", remoteDebuggerServiceStr); free(cmdData->rfcvalue); // free rfcvalue received from RRDEventThreadFunc diff --git a/src/rrdRunCmdThread.h b/src/rrdRunCmdThread.h index a1bd9816a..fb92fb087 100644 --- a/src/rrdRunCmdThread.h +++ b/src/rrdRunCmdThread.h @@ -48,6 +48,7 @@ extern "C" #endif #define RRD_OUTPUT_FILE "debug_outputs.txt" #define BUF_LEN_256 256 +#define BUF_LEN_512 512 /*Public Function*/ void initCache(void); diff --git a/src/rrd_logproc.c b/src/rrd_logproc.c index 30c0cff1c..a33aee466 100644 --- a/src/rrd_logproc.c +++ b/src/rrd_logproc.c @@ -121,7 +121,8 @@ int rrd_logproc_convert_issue_type(const char *input, char *output, size_t size) for (size_t i = 0; input[i] && j < size-1; ++i) { char c = input[i]; if (isalnum((unsigned char)c)) output[j++] = toupper((unsigned char)c); - else if (c == '_' || c == '-' || c == '.') output[j++] = '_'; + else if (c == '_' || c == '.') output[j++] = '_'; + else if (c == '-') output[j++] = '-'; // preserve hyphens so suffix UUID tokens remain distinct // skip other chars } output[j] = 0; diff --git a/src/unittest/rrdUnitTestRunner.cpp b/src/unittest/rrdUnitTestRunner.cpp index 7b8c6c837..957b4b2eb 100644 --- a/src/unittest/rrdUnitTestRunner.cpp +++ b/src/unittest/rrdUnitTestRunner.cpp @@ -951,6 +951,50 @@ TEST(RRDGetProfileStringLengthTest, HandlesIsDeepSleepAwakeEventTrueRRD_DEFAULT_ free(issue.Node); free(issue.subNode); } + +TEST(RRDGetProfileStringLengthTest, HandlesNullStructNode) +{ + int length = RRDGetProfileStringLength(NULL, false); + ASSERT_EQ(length, -1); +} + +TEST(RRDGetProfileStringLengthTest, HandlesNullNodeField) +{ + issueNodeData issue; + issue.Node = NULL; + issue.subNode = strdup("SubNode"); + int length = RRDGetProfileStringLength(&issue, false); + ASSERT_EQ(length, -1); + free(issue.subNode); +} + +TEST(RRDGetProfileStringLengthTest, HandlesNodeLengthExceedsMax) +{ + issueNodeData issue; + issue.Node = (char*)malloc(RRD_DYNAMIC_PROFILE_MAX_LENGTH + 10); + memset(issue.Node, 'A', RRD_DYNAMIC_PROFILE_MAX_LENGTH + 9); + issue.Node[RRD_DYNAMIC_PROFILE_MAX_LENGTH + 9] = '\0'; + issue.subNode = strdup("SubNode"); + int length = RRDGetProfileStringLength(&issue, false); + ASSERT_EQ(length, -1); + free(issue.Node); + free(issue.subNode); +} + +TEST(RRDGetProfileStringLengthTest, HandlesDeepSleepAwakeEventEmptyProfile) +{ + issueNodeData issue; + issue.Node = strdup("MainNode"); + issue.subNode = strdup("SubNode"); + devPropData.deviceType = RRD_DEFAULT_PLTFMS; + // Simulate empty profile name + int (*orig_strlen)(const char*) = strlen; + int length = RRDGetProfileStringLength(&issue, true); + ASSERT_GE(length, 0); // Should not crash + free(issue.Node); + free(issue.subNode); +} + #endif /* --------------- Test RRDCheckIssueInDynamicProfile() from rrdDeepSleep --------------- */ class RRDCheckIssueInDynamicProfileTest : public ::testing::Test @@ -1150,6 +1194,44 @@ TEST_F(RRDRdmManagerDownloadRequestTest, DeepSleepAwakeEventIsFalse_SetParamRetu free(buff.mdata); } +TEST_F(RRDRdmManagerDownloadRequestTest, HandlesMSGLengthNegative) +{ + issueNodeData issuestructNode; + issuestructNode.Node = strdup("MainNode"); + issuestructNode.subNode = strdup("SubNode"); + data_buf buff; + buff.mdata = strdup("ValidIssueTypeData"); + buff.jsonPath = strdup("UTJson/validJson.json"); + buff.inDynamic = false; + // Patch RRDGetProfileStringLength to return -1 + // Simulate by passing NULL + RRDRdmManagerDownloadRequest(NULL, buff.jsonPath, &buff, false); + free(issuestructNode.Node); + free(issuestructNode.subNode); + free(buff.jsonPath); + free(buff.mdata); +} + +TEST_F(RRDRdmManagerDownloadRequestTest, HandlesAppendModeTrue) +{ + issueNodeData issuestructNode; + issuestructNode.Node = strdup("MainNode"); + issuestructNode.subNode = strdup("SubNode"); + data_buf buff; + buff.mdata = strdup("ValidIssueTypeData"); + buff.jsonPath = strdup("UTJson/validJson.json"); + buff.inDynamic = false; + buff.appendMode = true; + EXPECT_CALL(mock_rbus_api, rbusValue_Init(_)).WillOnce(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(mock_rbus_api, rbusValue_SetString(_, _)).WillOnce(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(mock_rbus_api, rbus_set(_, _, _, _)).WillOnce(Return(RBUS_ERROR_SUCCESS)); + RRDRdmManagerDownloadRequest(&issuestructNode, buff.jsonPath, &buff, false); + free(issuestructNode.Node); + free(issuestructNode.subNode); + free(buff.jsonPath); + free(buff.mdata); +} + /* --------------- Test RRDProcessDeepSleepAwakeEvents() from rrdDeepSleep --------------- */ class RRDProcessDeepSleepAwakeEventsTest : public ::testing::Test { @@ -1799,11 +1881,13 @@ TEST_F(RemoveItemTest, HandlesCacheNotNullAndCacheNotEqualsRrdCachecnode) node->mdata = strdup("PkgData"); node->issueString = strdup("IssueString"); node->next = NULL; + node->prev = NULL; cacheDataNode = node; cacheData *node_dummy = (cacheData *)malloc(sizeof(cacheData)); node_dummy->mdata = strdup("PkgData"); node_dummy->issueString = strdup("IssueString"); node_dummy->next = NULL; + node_dummy->prev = NULL; remove_item(node_dummy); EXPECT_NE(cacheDataNode, nullptr); @@ -1863,15 +1947,19 @@ TEST(RemoveSpecialCharacterfromIssueTypeListTest, HandlesStringWithConsecutiveSp /* --------------- Test issueTypeSplitter() from rrdEventProcess --------------- */ TEST(IssueTypeSplitterTest, HandlesStringWithSpecialCharacters) { + /* issueTypeSplitter now performs pure token splitting only; special-character + * removal is done separately by the caller (processIssueTypeEvent) on the + * extracted base, so raw tokens including special chars are returned here. */ char str[] = "a@,b,&,cd,ef"; char **args = NULL; int count = issueTypeSplitter(str, ',', &args); - ASSERT_EQ(count, 4); - ASSERT_STREQ(args[0], "a"); + ASSERT_EQ(count, 5); + ASSERT_STREQ(args[0], "a@"); ASSERT_STREQ(args[1], "b"); - ASSERT_STREQ(args[2], "cd"); - ASSERT_STREQ(args[3], "ef"); + ASSERT_STREQ(args[2], "&"); + ASSERT_STREQ(args[3], "cd"); + ASSERT_STREQ(args[4], "ef"); for (int i = 0; i < count; i++) { @@ -1907,6 +1995,237 @@ TEST(IssueTypeSplitterTest, HandlesEmptyString) free(args); } +/* --------------- Test split_issue_type() from rrdJsonParser --------------- */ +TEST(SplitIssueTypeTest, NoUnderscoreReturnsFull) +{ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, UnderscoreSplitsBaseAndSuffix) +{ + /* Short suffix (total length including '_' is ≤ 9) is accepted and preserved */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime_ab12345", + base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, "_ab12345"); +} + +TEST(SplitIssueTypeTest, MultipleUnderscoresSplitsAtFirst) +{ + /* "abc_def_ghi": suffix "_def_ghi" is 8 chars (≤ 9) → accepted and preserved. + * Only the first '_' is used as the split point; base never contains '_'. */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_def_ghi", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, "_def_ghi"); +} + +TEST(SplitIssueTypeTest, EmptyInputProducesEmptyOutputs) +{ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, NullInputDoesNotCrash) +{ + char base[64] = {0}; + char suffix[64] = {0}; + /* Should return without crashing and clear provided outputs to empty strings */ + split_issue_type(NULL, base, sizeof(base), suffix, sizeof(suffix)); + /* NULL input clears the output buffers when buffer pointers are provided */ + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, BaseTruncatedWhenTooSmall) +{ + /* "abc_suffix": suffix "_suffix" is 7 chars (≤ 9) → accepted. + * Base = "abc" (before '_'); with a 4-byte buffer this fits exactly. */ + char base[4] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_suffix", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, "_suffix"); +} + +TEST(SplitIssueTypeTest, SuffixTruncatedWhenTooSmall) +{ + /* "abc_12345678": suffix "_12345678" is 9 chars (≤ 9, accepted). Suffix buffer + * is only 5 bytes so suffix is truncated to "_123" + NUL. */ + char base[64] = {0}; + char suffix[5] = {0}; + split_issue_type("abc_12345678", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, "_123"); + EXPECT_EQ(suffix[sizeof(suffix) - 1], '\0'); + EXPECT_EQ(strlen(suffix), (size_t)(sizeof(suffix) - 1)); +} + +TEST(SplitIssueTypeTest, LeadingUnderscoreGivesEmptyBase) +{ + /* "_suffixonly": split at '_' gives empty base; suffix "_suffixonly" is 11 chars + * (> 9) so it is discarded */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("_suffixonly", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, NullBaseDoesNotCrash) +{ + char suffix[64] = {0}; + /* NULL base pointer: should return without crashing */ + split_issue_type("Device.DeviceTime_Search", NULL, 64, suffix, sizeof(suffix)); + /* suffix remains unchanged when base is NULL */ + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, NullSuffixDoesNotCrash) +{ + char base[64] = {0}; + /* NULL suffix pointer: should return without crashing */ + split_issue_type("Device.DeviceTime_Search", base, sizeof(base), NULL, 64); + /* base remains unchanged when suffix is NULL */ + EXPECT_STREQ(base, ""); +} + +TEST(SplitIssueTypeTest, ZeroBaseLenDoesNotCrash) +{ + char base[64] = {0}; + char suffix[64] = {0}; + /* base_len == 0: should return without writing anything */ + split_issue_type("abc_def", base, 0, suffix, sizeof(suffix)); + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, ZeroSuffixLenDoesNotCrash) +{ + char base[64] = {0}; + char suffix[64] = {0}; + /* suffix_len == 0: should return without writing anything */ + split_issue_type("abc_def", base, sizeof(base), suffix, 0); + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, ExactFitBase) +{ + /* "abc_suffix": suffix "_suffix" is 7 chars (≤ 9, accepted). + * Base = "abc" (before '_'); 4-byte buffer fits exactly. */ + char base[4] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_suffix", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_EQ(base[3], '\0'); + EXPECT_STREQ(suffix, "_suffix"); +} + +TEST(SplitIssueTypeTest, OnlyUnderscoreInput) +{ + /* "_": split at '_' gives empty base; suffix is "_" (1 char, ≤ 9 → accepted) */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("_", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, "_"); +} + +TEST(SplitIssueTypeTest, NineCharSuffixIsAccepted) +{ + /* Suffix of exactly 9 chars (the upper boundary, inclusive) must be accepted */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime_12345678", + base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, "_12345678"); +} + +TEST(SplitIssueTypeTest, LongSuffixIsDiscarded) +{ + /* Suffix longer than 9 chars is discarded regardless of its content */ + char base[64] = {0}; + char suffix[128] = {0}; + split_issue_type("Device.DeviceInfo_1234567890", + base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceInfo"); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, SuffixExceedingMaxLengthDiscarded) +{ + /* "_Random-token" is 13 chars (> 9) → suffix discarded */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime_Random-token", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, SuffixSeventeenCharsDiscarded) +{ + /* "_Search_something" is 17 chars (> 9) → suffix discarded */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_Search_something", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, SuffixTwentyCharsDiscarded) +{ + /* "_LogSearch_something" is 20 chars (> 9) → suffix discarded */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_LogSearch_something", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, SuffixWithUnsafeCharsIsSanitized) +{ + /* "_ab;rm" is 6 chars (≤ 9, accepted) but ';' is unsafe and must be stripped. + * Expected sanitized suffix: "_abrm" */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime_ab;rm", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, "_abrm"); +} + +TEST(SplitIssueTypeTest, SuffixWithOnlyUnsafeCharsBecomesUnderscore) +{ + /* "_!@#" is 4 chars (≤ 9, accepted length-wise) but all payload chars are unsafe. + * After sanitization only the leading '_' remains → suffix="_" */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_!@#", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, "_"); +} + +TEST(SplitIssueTypeTest, SuffixHyphensPreserved) +{ + /* "_ab-cd" is 6 chars (≤ 9, accepted) and '-' is in the safe set → preserved */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime_ab-cd", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, "_ab-cd"); +} + /* --------------- Test processIssueTypeInDynamicProfile() from rrdEventProcess --------------- */ class ProcessIssueTypeInDynamicProfileTest : public ::testing::Test { @@ -1989,11 +2308,93 @@ TEST(ProcessIssueTypeEvntTest, RBufIsNull){ } TEST(ProcessIssueTypeEvntTest, inDynamic_NoJson){ - data_buf rbuf; + data_buf rbuf = {}; rbuf.mdata = strdup("a"); rbuf.inDynamic = true; rbuf.jsonPath = nullptr; processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, IssueTypeWithSearchSuffix_inDynamic_NoJson){ + /* Issue type with a long suffix (> 9 chars): suffix is discarded; base "Device.DeviceTime" is used */ + data_buf rbuf = {}; + rbuf.mdata = strdup("Device.DeviceTime_Search-b6877385-9463-45fc-b19d-a24d77fd0790"); + rbuf.inDynamic = true; + rbuf.jsonPath = nullptr; + /* Should not crash; long suffix is discarded, base is processed normally */ + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, IssueTypeWithLogSearchSuffix_inDynamic_NoJson){ + /* Issue type with a long suffix (> 9 chars): suffix is discarded; base "Device.DeviceInfo" is used */ + data_buf rbuf = {}; + rbuf.mdata = strdup("Device.DeviceInfo_LogSearch-9abc1def-0000-1111-2222-3333aaaabbbb"); + rbuf.inDynamic = true; + rbuf.jsonPath = nullptr; + /* Should not crash; long suffix is discarded, base is processed normally */ + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, IssueTypeWithInvalidSuffixTreatedAsBase) +{ + /* "_Random-token" is 13 chars (> 9): suffix discarded; base = "Device.DeviceTime" */ + data_buf rbuf = {}; + rbuf.mdata = strdup("Device.DeviceTime_Random-token"); + rbuf.inDynamic = false; + rbuf.jsonPath = nullptr; + /* Should not crash; long suffix is discarded, base is processed normally */ + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, MultipleIssueTypesWithAndWithoutSuffix){ + /* Comma-separated list: one plain type, one with long suffix (> 9, discarded), + * one with another long suffix (> 9, discarded) */ + data_buf rbuf = {}; + rbuf.mdata = strdup("Device.DeviceTime,Device.DeviceInfo_Search-1234,Device.Net_BadSuffix"); + rbuf.inDynamic = true; + rbuf.jsonPath = nullptr; + /* Should not crash; all entries are processed without leaks */ + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, WhitespaceOnlyIssueTypeIsSkipped) +{ + /* When the IssueType value from RBUS is whitespace (e.g. a space), + * removeSpecialCharacterfromIssueTypeList() strips it to an empty string. + * processIssueTypeEvent() must detect the post-sanitization empty base + * and skip processing without crashing or invoking processIssueType. */ + data_buf rbuf = {}; + rbuf.mdata = strdup(" "); /* single space — all-special after split */ + rbuf.inDynamic = false; + rbuf.jsonPath = nullptr; + /* Must not crash and must not reach getIssueInfo with an empty mdata */ + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, EmptyStringIssueTypeIsSkipped) +{ + /* An empty-string mdata must be handled gracefully — issueTypeSplitter + * returns 1 token that is an empty string, which split_issue_type then + * maps to an empty base, causing the entry to be skipped. */ + data_buf rbuf = {}; + rbuf.mdata = strdup(""); + rbuf.inDynamic = false; + rbuf.jsonPath = nullptr; + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; } /* ======================== rrdExecuteScript ==============*/ @@ -2117,12 +2518,22 @@ TEST(RRDDataBuffInitTest, InitializeDataBuff) EXPECT_EQ(sbuf.dsEvent, deepSleepEvent); } +TEST(RRDDataBuffInitTest, SuffixInitializedToNull) +{ + /* Verify that the newly added suffix field is initialised to NULL */ + data_buf sbuf; + sbuf.suffix = reinterpret_cast(0xDEADBEEF); /* pre-fill with garbage */ + RRD_data_buff_init(&sbuf, EVENT_MSG, RRD_DEEPSLEEP_INVALID_DEFAULT); + EXPECT_EQ(sbuf.suffix, nullptr); +} + /* --------------- Test RRD_data_buff_deAlloc() from rrdIarm --------------- */ TEST(RRDDataBuffDeAllocTest, DeallocateDataBuff) { data_buf *sbuf = (data_buf *)malloc(sizeof(data_buf)); sbuf->mdata = (char *)malloc(10 * sizeof(char)); sbuf->jsonPath = (char *)malloc(10 * sizeof(char)); + sbuf->suffix = nullptr; ASSERT_NO_FATAL_FAILURE(RRD_data_buff_deAlloc(sbuf)); } @@ -2134,6 +2545,28 @@ TEST(RRDDataBuffDeAllocTest, NullPointer) ASSERT_NO_FATAL_FAILURE(RRD_data_buff_deAlloc(sbuf)); } +TEST(RRDDataBuffDeAllocTest, DeallocateWithSuffixSet) +{ + /* Verify that suffix is freed without crash when it is non-NULL */ + data_buf *sbuf = (data_buf *)malloc(sizeof(data_buf)); + sbuf->mdata = strdup("IssueType"); + sbuf->jsonPath = nullptr; + sbuf->suffix = strdup("_Search-b6877385-9463-45fc-b19d-a24d77fd0790"); + + ASSERT_NO_FATAL_FAILURE(RRD_data_buff_deAlloc(sbuf)); +} + +TEST(RRDDataBuffDeAllocTest, DeallocateWithAllFieldsNull) +{ + /* All pointer fields NULL: should not crash */ + data_buf *sbuf = (data_buf *)malloc(sizeof(data_buf)); + sbuf->mdata = nullptr; + sbuf->jsonPath = nullptr; + sbuf->suffix = nullptr; + + ASSERT_NO_FATAL_FAILURE(RRD_data_buff_deAlloc(sbuf)); +} + /* --------------- Test RRD_unsubscribe() from rrdIarm --------------- */ class RRDUnsubscribeTest : public ::testing::Test @@ -3684,6 +4117,7 @@ TEST_F(RRDEventThreadFuncTest, MessageReceiveSuccessEventMsgType) { rbuf.mdata = strdup("Test"); rbuf.inDynamic = true; rbuf.jsonPath = nullptr; + rbuf.suffix = strdup("_ab12345"); msgRRDHdr msgHdr; msgHdr.mbody = malloc(sizeof(data_buf)); ASSERT_NE(msgHdr.mbody, nullptr); @@ -4444,12 +4878,22 @@ TEST_F(RRDUploadOrchestrationTest, SpecialCharactersInIssueType) { char sanitized[64]; int result = rrd_logproc_convert_issue_type("test-issue.sub@special!", sanitized, sizeof(sanitized)); EXPECT_EQ(result, 0); - // Should only contain alphanumeric and underscore + // Should only contain alphanumeric, underscore, and hyphen for (const char *p = sanitized; *p; ++p) { - EXPECT_TRUE(isalnum(*p) || *p == '_'); + EXPECT_TRUE(isalnum(*p) || *p == '_' || *p == '-'); } } +// Suffix with hyphens: hyphens must be preserved so portal can parse filename +TEST_F(RRDUploadOrchestrationTest, IssueTypeWithSuffixHyphensPreserved) { + char sanitized[128]; + // Simulates issue type after normalizeIssueName: dots→underscore, hyphens kept + int result = rrd_logproc_convert_issue_type("Device_DeviceIP_Search-67768-67", sanitized, sizeof(sanitized)); + EXPECT_EQ(result, 0); + // Hyphens in suffix must survive so the archive filename delimiter structure is intact + EXPECT_STREQ(sanitized, "DEVICE_DEVICEIP_SEARCH-67768-67"); +} + // Performance test: Large directory TEST_F(RRDUploadOrchestrationTest, LargeDirectoryHandling) { // Create multiple log files diff --git a/test/functional-tests/L2_Test_Coverage.md b/test/functional-tests/L2_Test_Coverage.md new file mode 100644 index 000000000..fcd872f15 --- /dev/null +++ b/test/functional-tests/L2_Test_Coverage.md @@ -0,0 +1,399 @@ +# Remote Debugger L2 Test Coverage Report + +**Generated:** 2026-05-19 +**Component:** `remotedebugger` (src/) +**Test Suite:** `test/functional-tests/` + +--- + +## Executive Summary + +| Metric | Count | +|---|:---:| +| Feature files | 21 | +| Feature scenarios | 90 | +| Test files (pytest) | 22 | +| Test functions (`test_*`) | 112 | +| Feature→Test mapped pairs | 21 / 21 (+ 1 orphan test) | +| Source modules (always compiled) | 9 | +| Source modules (conditional IARMBUS) | 7 | +| **Overall feature coverage** | **95%** (21/22 test files have matching features) | +| **Source behavior coverage** | **~65%** (happy paths covered; error/edge paths mostly untested) | + +--- + +## 1. Feature File ↔ Test File Mapping + +### Fully Mapped (feature + test exist) + +| # | Feature File | Scenarios | Test File | Test Functions | Status | +|:---:|---|:---:|---|:---:|:---:| +| 1 | `rrd_start_subscribe_and_wait.feature` | 1 | `test_rrd_start_subscribe_and_wait.py` | 4 | PASS | +| 2 | `rrd_start_control.feature` | 2 | `test_rrd_start_control.py` | 1 | PASS | +| 3 | `rrd_single_instance.feature` | 1 | `test_rrd_single_instance.py` | 3 | PASS | +| 4 | `rrd_static_profile_report.feature` | 5 | `test_rrd_static_profile_report.py` | 5 | PASS | +| 5 | `rrd_static_profile_category_report.feature` | 5 | `test_rrd_static_profile_category_report.py` | 5 | PASS | +| 6 | `test_rrd_static_profile_report_with_suffix.feature` | 4 | `test_rrd_static_profile_report_with_suffix.py` | 5 | PASS | +| 7 | `test_rrd_static_profile_report_with_suffix_negative_case.feature` | 4 | `test_rrd_static_profile_report_with_suffix_negative_case.py` | 5 | PASS | +| 8 | `rrd_background_cmd_static_profile_report.feature` | 5 | `test_rrd_background_cmd_static_profile_report.py` | 5 | PASS | +| 9 | `rrd_dynamic_profile_report.feature` | 5 | `test_rrd_dynamic_profile_report.py` | 9 | PASS | +| 10 | `rrd_dynamic_profile_subcategory_report.feature` | 5 | `test_rrd_dynamic_subcategory_report.py` | 7 | PASS | +| 11 | `rrd_dynamic_profile_missing_report.feature` | 4 | `test_rrd_dynamic_profile_missing_report.py` | 7 | PASS | +| 12 | `rrd_append_report.feature` | 4 | `test_rrd_append_report.py` | 7 | PASS | +| 13 | `rrd_append_dynamic_profile_static_not_found.feature` | 4 | `test_rrd_append_dynamic_profile_static_notfound.py` | 7 | PASS | +| 14 | `rrd_harmful_command_static_report.feature` | 5 | `test_rrd_harmful_command_static_report.py` | 5 | PASS | +| 15 | `test_rrd_dynamic_profile_harmful_report.feature` | 5 | `test_rrd_dynamic_profile_harmful_report.py` | 7 | PASS | +| 16 | `rrd_corrupted_static_profile_report.feature` | 4 | `test_rrd_corrupted_static_profile_report.py` | 4 | PASS | +| 17 | `rrd_static_profile_missing_command_report.feature` | 5 | `test_rrd_static_profile_missing_command_report.py` | 5 | PASS | +| 18 | `rrd_empty_issuetype_event.feature` | 2 | `test_rrd_empty_issuetype_event.py` | 2 | PASS | +| 19 | `rrd_deepsleep_static_report.feature` | 2 | `test_rrd_deepsleep_static_report.py` | 5 | PASS | +| 20 | `rrd_debug_report_upload.feature` | 6 | `test_rrd_debug_report_upload.py` | 5 | PASS | +| 21 | `rrd_c_api_upload.feature` | 21 | `test_rrd_c_api_upload.py` | 5 | GAP | +| | **Totals** | **97** | | **112** | | + +> **Note:** Scenario count for `rrd_c_api_upload.feature` (21) far exceeds its test function count (5). See Section 3 for details. + +### Orphan Tests (test exists, no matching feature file) + +| Test File | Test Functions | Description | Gap | +|---|:---:|---|---| +| `test_rrd_profile_data.py` | 3 | RBUS profile data SET/GET via `rbuscli` | **Missing `.feature` file** | + +### Orphan Features (feature exists, no matching test file) + +None — all 21 feature files have corresponding test files. + +--- + +## 2. Per-Behavior Coverage Detail + +### 2.1 Daemon Lifecycle + +| Behavior | Feature | Test | Covered | +|---|---|---|:---:| +| RBUS subscription + event wait | `rrd_start_subscribe_and_wait` | `test_rrd_start_subscribe_and_wait` | YES | +| RFC enable → daemon starts | `rrd_start_control` | `test_rrd_start_control` | YES | +| RFC disable → daemon stops | `rrd_start_control` | `test_rrd_start_control` | YES | +| Single instance enforcement | `rrd_single_instance` | `test_rrd_single_instance` | YES | +| Message queue creation failure | — | — | **NO** | +| Event thread creation failure | — | — | **NO** | +| Signal handling / graceful shutdown | — | — | **NO** | +| Device info file read failure | — | — | **NO** | + +### 2.2 Static Profile Processing + +| Behavior | Feature | Test | Covered | +|---|---|---|:---:| +| Config file exists check | `rrd_static_profile_report` | `test_rrd_static_profile_report` | YES | +| Output directory exists check | `rrd_static_profile_report` | `test_rrd_static_profile_report` | YES | +| IssueType event trigger + message flow | `rrd_static_profile_report` | `test_rrd_static_profile_report` | YES | +| JSON parse success + command execution | `rrd_static_profile_report` | `test_rrd_static_profile_report` | YES | +| Upload report success/failure | `rrd_static_profile_report` | `test_rrd_static_profile_report` | YES | +| Category-only issue type (all sub-nodes) | `rrd_static_profile_category_report` | `test_rrd_static_profile_category_report` | YES | +| Suffixed issue type | `test_rrd_static_profile_report_with_suffix` | `test_rrd_static_profile_report_with_suffix` | YES | +| Overlength suffix (negative) | `test_rrd_static_profile_report_with_suffix_negative_case` | `test_rrd_static_profile_report_with_suffix_negative_case` | YES | +| Background command execution | `rrd_background_cmd_static_profile_report` | `test_rrd_background_cmd_static_profile_report` | YES | +| Missing command in profile | `rrd_static_profile_missing_command_report` | `test_rrd_static_profile_missing_command_report` | YES | +| Corrupted/invalid JSON profile | `rrd_corrupted_static_profile_report` | `test_rrd_corrupted_static_profile_report` | YES | + +### 2.3 Dynamic Profile Processing + +| Behavior | Feature | Test | Covered | +|---|---|---|:---:| +| Dynamic profile fallback (static miss) | `rrd_dynamic_profile_report` | `test_rrd_dynamic_profile_report` | YES | +| Dynamic subcategory | `rrd_dynamic_profile_subcategory_report` | `test_rrd_dynamic_subcategory_report` | YES | +| Dynamic profile missing → RDM trigger | `rrd_dynamic_profile_missing_report` | `test_rrd_dynamic_profile_missing_report` | YES | +| Append mode (static + dynamic) | `rrd_append_report` | `test_rrd_append_report` | YES | +| Append when static not found | `rrd_append_dynamic_profile_static_not_found` | `test_rrd_append_dynamic_profile_static_notfound` | YES | +| RDM download event (cache miss) | — | — | **NO** | +| Dynamic profile JSON parse failure | — | — | **NO** | + +### 2.4 Harmful Command Detection + +| Behavior | Feature | Test | Covered | +|---|---|---|:---:| +| Static profile harmful command abort | `rrd_harmful_command_static_report` | `test_rrd_harmful_command_static_report` | YES | +| Dynamic profile harmful command abort | `test_rrd_dynamic_profile_harmful_report` | `test_rrd_dynamic_profile_harmful_report` | YES | +| Macro replacement edge cases | — | — | **NO** | +| Background command modification | — | — | **PARTIAL** (via background cmd test) | + +### 2.5 Event Handling + +| Behavior | Feature | Test | Covered | +|---|---|---|:---:| +| IssueType RBUS event | Multiple features | Multiple tests | YES | +| Empty IssueType event | `rrd_empty_issuetype_event` | `test_rrd_empty_issuetype_event` | YES | +| Deep sleep event | `rrd_deepsleep_static_report` | `test_rrd_deepsleep_static_report` | YES | +| WebCfg event (MsgPack decode) | — | — | **NO** | +| WebCfg corrupted data | — | — | **NO** | +| Multiple simultaneous IssueType events | — | — | **NO** | +| Invalid deep sleep event type | — | — | **NO** | + +### 2.6 Upload & Archive + +| Behavior | Feature | Test | Covered | +|---|---|---|:---:| +| Upload via shell script | `rrd_debug_report_upload` | `test_rrd_debug_report_upload` | YES | +| Upload + download validation | `rrd_debug_report_upload` | `test_rrd_debug_report_upload` | YES | +| C API `rrd_upload_orchestrate` (happy path) | `rrd_c_api_upload` | `test_rrd_c_api_upload` | YES | +| C API NULL parameters | `rrd_c_api_upload` | — | **GAP** | +| C API empty directory | `rrd_c_api_upload` | — | **GAP** | +| C API non-existent directory | `rrd_c_api_upload` | — | **GAP** | +| C API config loading validation | `rrd_c_api_upload` | — | **GAP** | +| C API MAC retrieval | `rrd_c_api_upload` | — | **GAP** | +| C API timestamp generation | `rrd_c_api_upload` | — | **GAP** | +| C API issue type sanitization | `rrd_c_api_upload` | — | **GAP** | +| C API archive creation | `rrd_c_api_upload` | — | **GAP** | +| C API upload execution | `rrd_c_api_upload` | — | **GAP** | +| C API cleanup after success/failure | `rrd_c_api_upload` | — | **GAP** | +| C API concurrent upload lock | `rrd_c_api_upload` | — | **GAP** | +| C API LOGUPLOAD_ENABLE | `rrd_c_api_upload` | — | **GAP** | +| C API end-to-end with RFC trigger | `rrd_c_api_upload` | — | **GAP** | +| C API error propagation | `rrd_c_api_upload` | — | **GAP** | +| Upload lock file contention | — | — | **NO** | +| Archive CPU throttle logic | — | — | **NO** | + +### 2.7 Profile Data SET/GET (RBUS) + +| Behavior | Feature | Test | Covered | +|---|---|---|:---:| +| `setProfileData` / `getProfileData` | — | `test_rrd_profile_data` | **PARTIAL** (no feature) | +| Profile category load error | — | — | **NO** | +| Profile file write error | — | — | **NO** | + +--- + +## 3. Feature ↔ Test Gap Analysis + +### 3.1 `rrd_c_api_upload.feature` — Major Scenario-to-Test Gap + +The feature file documents **21 scenarios** covering the full `rrd_upload_orchestrate` C API, but the test file `test_rrd_c_api_upload.py` only implements **5 test functions** that cover the basic end-to-end flow (config check, dir check, start, trigger event, upload report). + +**Missing test implementations for feature scenarios:** + +| Feature Scenario | Test Status | +|---|---| +| Validate rrd_upload_orchestrate C API with valid parameters | Covered (within `test_remote_debugger_trigger_event`) | +| Test rrd_upload_orchestrate with NULL upload directory | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate with NULL issue type | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate with empty upload directory | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate with non-existent directory | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate configuration loading | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate MAC address retrieval | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate timestamp generation | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate issue type sanitization | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate archive creation | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate upload execution | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate cleanup after success | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate cleanup after upload failure | **NOT IMPLEMENTED** | +| Test uploadDebugoutput wrapper function | **NOT IMPLEMENTED** | +| Test concurrent upload lock handling | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate with LOGUPLOAD_ENABLE issue type | **NOT IMPLEMENTED** | +| Test remote debugger end-to-end with RFC trigger | Covered | +| Test upload report validation with success path | Covered (within `test_remotedebugger_upload_report`) | +| Test upload report validation with failure path | Covered (within `test_remotedebugger_upload_report`) | +| Test upload report with legacy log compatibility | **NOT IMPLEMENTED** | +| Test rrd_upload_orchestrate error propagation | **NOT IMPLEMENTED** | + +**Gap: 16 of 21 scenarios have no dedicated test implementation.** + +### 3.2 `test_rrd_profile_data.py` — Orphan Test (No Feature File) + +This test file contains 3 test functions exercising `rbuscli set/get` on profile data RBUS elements (`setProfileData` / `getProfileData`). No corresponding `.feature` file exists. + +**Recommendation:** Create `rrd_profile_data.feature` to document this behavior. + +### 3.3 Scenario Count vs Test Count Discrepancy + +Some test files implement more test functions than their feature has scenarios, due to setup/teardown and prerequisite tests being split more granularly: + +| Feature File | Scenarios | Test File | Tests | Delta | +|---|:---:|---|:---:|:---:| +| `rrd_start_subscribe_and_wait` | 1 | `test_rrd_start_subscribe_and_wait` | 4 | +3 | +| `rrd_single_instance` | 1 | `test_rrd_single_instance` | 3 | +2 | +| `rrd_dynamic_profile_report` | 5 | `test_rrd_dynamic_profile_report` | 9 | +4 | +| `rrd_dynamic_profile_missing_report` | 4 | `test_rrd_dynamic_profile_missing_report` | 7 | +3 | +| `rrd_append_report` | 4 | `test_rrd_append_report` | 7 | +3 | +| `rrd_append_dynamic_profile_static_not_found` | 4 | `test_rrd_append_dynamic_profile_static_notfound` | 7 | +3 | +| `rrd_dynamic_profile_harmful_report` | 5 | `test_rrd_dynamic_profile_harmful_report` | 7 | +2 | +| `rrd_deepsleep_static_report` | 2 | `test_rrd_deepsleep_static_report` | 5 | +3 | +| `rrd_c_api_upload` | 21 | `test_rrd_c_api_upload` | 5 | **-16** | + +The **only deficit** is `rrd_c_api_upload` where the feature documents far more scenarios than tests implement. + +--- + +## 4. Source Module Coverage Analysis + +### 4.1 Module-Level Coverage Summary + +| Source Module | Happy Path | Error Paths | L2 Coverage | +|---|:---:|:---:|---| +| `rrdMain.c` | YES | NO | Startup, RFC check, event thread — tested. Msgqueue/thread failures — not tested. | +| `rrdInterface.c` | YES | NO | RBUS registration, event handlers — tested. Registration failures, file I/O errors — not tested. | +| `rrdEventProcess.c` | YES | PARTIAL | Static/dynamic/append/deepsleep — tested. WebCfg event, malloc failures — not tested. | +| `rrdJsonParser.c` | YES | PARTIAL | Valid/corrupted/missing JSON — tested. Dir creation failures, malloc failures — not tested. | +| `rrdRunCmdThread.c` | YES | NO | Command execution, output files — tested. File write errors, systemd-run failures — not tested. | +| `rrdCommandSanity.c` | YES | NO | Harmful command detection — tested. Macro replacement errors — not tested. | +| `rrdDynamic.c` | YES | NO | Dynamic profile, deep sleep — tested. RBUS set failure, invalid event type — not tested. | +| `rrdExecuteScript.c` | YES | NO | Upload orchestration — tested. Script exec failure, API failure — not tested. | +| `rrdMsgPackDecoder.c` | NO | NO | **Entirely untested** — WebCfg MsgPack decode not exercised by any L2 test. | +| `rrd_config.c` | INDIRECT | NO | Indirectly tested via daemon startup. Config parse errors, RFC query failures — not tested. | +| `rrd_sysinfo.c` | INDIRECT | NO | Indirectly tested via upload flow. MAC/timestamp retrieval errors — not tested. | +| `rrd_logproc.c` | INDIRECT | NO | Indirectly tested via upload flow. Log dir validation errors — not tested. | +| `rrd_archive.c` | INDIRECT | NO | Indirectly tested via upload flow. CPU throttle, archive errors — not tested. | +| `rrd_upload.c` | INDIRECT | NO | Indirectly tested via upload flow. Lock errors, cleanup failures — not tested. | +| `rrdIarmEvents.c` | PARTIAL | NO | Deep sleep event — tested. Other IARM events — not tested. | +| `uploadRRDLogs.c` | INDIRECT | NO | Entry point tested via daemon trigger. Direct API error paths — not tested. | + +### 4.2 Completely Untested Source Behaviors + +| Priority | Behavior | Source Module | Required Infrastructure | +|:---:|---|---|---| +| **P1** | WebCfg event (MsgPack decode + dispatch) | `rrdMsgPackDecoder.c`, `rrdEventProcess.c` | WebConfig mock, base64-encoded MsgPack payload | +| **P1** | C API upload error paths (NULL params, empty dir, non-existent dir) | `rrd_upload.c` | Direct C API invocation or test binary | +| **P1** | Profile data SET/GET feature documentation | `rrdInterface.c` | Feature file creation only | +| **P2** | Concurrent upload lock contention | `rrd_upload.c` | Parallel upload trigger + lock file manipulation | +| **P2** | Archive CPU usage throttle | `rrd_archive.c` | CPU load simulation | +| **P2** | Configuration fallback chain (RFC → DCM → dcm.properties) | `rrd_config.c` | Config file manipulation | +| **P2** | RDM download event with cache miss | `rrdInterface.c`, `rrdDynamic.c` | RDM mock, empty cache | +| **P3** | RBUS registration/unregistration failures | `rrdInterface.c` | RBUS mock failure injection | +| **P3** | Message queue creation failure | `rrdMain.c` | System resource exhaustion mock | +| **P3** | Event thread creation failure | `rrdMain.c` | Thread creation failure mock | +| **P3** | Directory creation/chdir failures | `rrdJsonParser.c`, `rrdRunCmdThread.c` | Filesystem permission mock | +| **P3** | systemd-run / journalctl execution failures | `rrdRunCmdThread.c` | Binary removal or mock failure | +| **P3** | Output file write errors | `rrdRunCmdThread.c` | Filesystem full or permission mock | +| **P3** | Dynamic profile JSON parse failure | `rrdDynamic.c` | Corrupted dynamic JSON file | +| **P3** | Invalid deep sleep event type | `rrdDynamic.c` | IARM event simulation with bad type | +| **P3** | Memory allocation failures (all modules) | All `.c` files | malloc failure injection (not practical in L2) | + +--- + +## 5. Gap Summary + +### 5.1 Feature vs Test Gap Table + +| Behavior Area | Feature Scenarios | Test Functions | Coverage | Top Gaps | +|---|:---:|:---:|:---:|---| +| Daemon startup/subscribe | 1 | 4 | 100% | — | +| RFC enable/disable | 2 | 1 | 100% | — | +| Single instance | 1 | 3 | 100% | — | +| Static profile report | 5 | 5 | 100% | — | +| Static category report | 5 | 5 | 100% | — | +| Static suffix report | 4 | 5 | 100% | — | +| Static suffix negative | 4 | 5 | 100% | — | +| Background command | 5 | 5 | 100% | — | +| Dynamic profile report | 5 | 9 | 100% | — | +| Dynamic subcategory | 5 | 7 | 100% | — | +| Dynamic missing | 4 | 7 | 100% | — | +| Append mode | 4 | 7 | 100% | — | +| Append (static not found) | 4 | 7 | 100% | — | +| Harmful static | 5 | 5 | 100% | — | +| Harmful dynamic | 5 | 7 | 100% | — | +| Corrupted profile | 4 | 4 | 100% | — | +| Missing command | 5 | 5 | 100% | — | +| Empty issuetype | 2 | 2 | 100% | — | +| Deep sleep | 2 | 5 | 100% | — | +| Debug report upload | 6 | 5 | 100% | — | +| **C API upload** | **21** | **5** | **~24%** | **16 scenarios not implemented** | +| **Profile data SET/GET** | **0** | **3** | **N/A** | **No feature file** | +| **WebCfg event** | **0** | **0** | **0%** | **Entire flow untested** | +| **Upload lock contention** | **0** | **0** | **0%** | **No test** | +| **Config fallback chain** | **0** | **0** | **0%** | **No test** | + +### 5.2 Action Items (Prioritized) + +| # | Priority | Action | Effort | +|:---:|:---:|---|:---:| +| 1 | P1 | Implement 16 missing `test_rrd_c_api_upload.py` test functions matching feature scenarios | High | +| 2 | P1 | Create `rrd_profile_data.feature` for the existing `test_rrd_profile_data.py` test | Low | +| 3 | P1 | Add WebCfg event L2 test (`test_rrd_webcfg_event.py` + `rrd_webcfg_event.feature`) | High | +| 4 | P2 | Add upload lock contention test | Medium | +| 5 | P2 | Add configuration fallback chain test | Medium | +| 6 | P2 | Add archive CPU throttle test | Medium | +| 7 | P2 | Add dynamic profile JSON parse failure test | Low | +| 8 | P3 | Add RBUS registration failure test | Low | +| 9 | P3 | Add systemd-run / journalctl failure test | Low | +| 10 | P3 | Add output file write error test | Low | + +--- + +## 6. Test Infrastructure Notes + +### Test Interfaces + +| Interface | Tool | Used By | +|---|---|---| +| RBUS event trigger | `rbuscli set` | All event-based tests | +| RBUS profile data | `rbuscli set/get` | `test_rrd_profile_data.py` | +| Log scraping | `grep_rrdlogs()` in `helper_functions.py` | All tests | +| File system checks | `os.path.isfile()`, `os.path.isdir()` | Prerequisite tests | +| Process control | `pidof`, `kill -9`, `nohup` | Setup/teardown | +| Mock upload server | Mock xconf server | `test_rrd_debug_report_upload.py` | +| Mock upload script | `uploadSTBLogs.sh` | Upload tests | + +### Test Execution + +- **Runner:** `pytest`, sequential per file +- **No ordering decorators:** Tests rely on file-level sequential execution order +- **Shared state:** Tests within a file depend on prior test side effects (daemon start → trigger → validate) +- **Cleanup:** Most files kill the daemon and remove logs in setup + +--- + +## 7. Appendix: Complete File Inventory + +### Feature Files (21) + +| # | File | Scenarios | +|:---:|---|:---:| +| 1 | `rrd_start_subscribe_and_wait.feature` | 1 | +| 2 | `rrd_start_control.feature` | 2 | +| 3 | `rrd_single_instance.feature` | 1 | +| 4 | `rrd_static_profile_report.feature` | 5 | +| 5 | `rrd_static_profile_category_report.feature` | 5 | +| 6 | `test_rrd_static_profile_report_with_suffix.feature` | 4 | +| 7 | `test_rrd_static_profile_report_with_suffix_negative_case.feature` | 4 | +| 8 | `rrd_background_cmd_static_profile_report.feature` | 5 | +| 9 | `rrd_dynamic_profile_report.feature` | 5 | +| 10 | `rrd_dynamic_profile_subcategory_report.feature` | 5 | +| 11 | `rrd_dynamic_profile_missing_report.feature` | 4 | +| 12 | `rrd_append_report.feature` | 4 | +| 13 | `rrd_append_dynamic_profile_static_not_found.feature` | 4 | +| 14 | `rrd_harmful_command_static_report.feature` | 5 | +| 15 | `test_rrd_dynamic_profile_harmful_report.feature` | 5 | +| 16 | `rrd_corrupted_static_profile_report.feature` | 4 | +| 17 | `rrd_static_profile_missing_command_report.feature` | 5 | +| 18 | `rrd_empty_issuetype_event.feature` | 2 | +| 19 | `rrd_deepsleep_static_report.feature` | 2 | +| 20 | `rrd_debug_report_upload.feature` | 6 | +| 21 | `rrd_c_api_upload.feature` | 21 | +| | **Total** | **97** | + +### Test Files (22) + +| # | File | Test Functions | +|:---:|---|:---:| +| 1 | `test_rrd_start_subscribe_and_wait.py` | 4 | +| 2 | `test_rrd_start_control.py` | 1 | +| 3 | `test_rrd_single_instance.py` | 3 | +| 4 | `test_rrd_static_profile_report.py` | 5 | +| 5 | `test_rrd_static_profile_category_report.py` | 5 | +| 6 | `test_rrd_static_profile_report_with_suffix.py` | 5 | +| 7 | `test_rrd_static_profile_report_with_suffix_negative_case.py` | 5 | +| 8 | `test_rrd_background_cmd_static_profile_report.py` | 5 | +| 9 | `test_rrd_dynamic_profile_report.py` | 9 | +| 10 | `test_rrd_dynamic_subcategory_report.py` | 7 | +| 11 | `test_rrd_dynamic_profile_missing_report.py` | 7 | +| 12 | `test_rrd_append_report.py` | 7 | +| 13 | `test_rrd_append_dynamic_profile_static_notfound.py` | 7 | +| 14 | `test_rrd_harmful_command_static_report.py` | 5 | +| 15 | `test_rrd_dynamic_profile_harmful_report.py` | 7 | +| 16 | `test_rrd_corrupted_static_profile_report.py` | 4 | +| 17 | `test_rrd_static_profile_missing_command_report.py` | 5 | +| 18 | `test_rrd_empty_issuetype_event.py` | 2 | +| 19 | `test_rrd_deepsleep_static_report.py` | 5 | +| 20 | `test_rrd_debug_report_upload.py` | 5 | +| 21 | `test_rrd_c_api_upload.py` | 5 | +| 22 | `test_rrd_profile_data.py` | 3 | +| | **Total** | **112** | diff --git a/test/functional-tests/features/rrd_dynamic_profile_node_length_exceeded.feature b/test/functional-tests/features/rrd_dynamic_profile_node_length_exceeded.feature new file mode 100644 index 000000000..c2818afa2 --- /dev/null +++ b/test/functional-tests/features/rrd_dynamic_profile_node_length_exceeded.feature @@ -0,0 +1,48 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +Feature: Remote Debugger Rejects RDM Download Request When IssueType Node Exceeds Maximum Length + + Scenario: Verify remote debugger process is running + Given the remote debugger process is not running + When I start the remote debugger process + Then the remote debugger process should be running + + Scenario: Send WebPA event with an IssueType string longer than 34 characters and verify logs + Given the remote debugger is running + When I trigger the event "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType" with a value longer than 34 characters + Then the logs should contain "SUCCESS: Message sending Done" + Then the logs should be seen with "SUCCESS: Message Reception Done" + And the issuetype request should match between Send and Receive + + Scenario: Verify the oversized IssueType is not found in static profile + Given the remote debugger received the message from RBUS command + When the remotedebugger static json profile is present + Then remotedebugger should read the Json file + And remotedebugger logs should contain the Json File Parse Success + And remotedebugger should log as the Issue requested is not found in the profile + + Scenario: Verify the RDM download request is rejected due to IssueType node length exceeding 34 characters + Given the remote debugger issuetype is missing in static profile + When the remotedebugger reads the json file from the dynamic path + Then remotedebugger json read and parse should fail + And remotedebugger should attempt to request RDM to download the package + But the RDM download request should be skipped because the dynamic profile node length exceeds 34 characters + And the logs should contain "Issue node length must be less than 34" + And the logs should contain "Invalid profile length, skipping download request" diff --git a/test/functional-tests/features/test_rrd_static_profile_report_with_suffix.feature b/test/functional-tests/features/test_rrd_static_profile_report_with_suffix.feature new file mode 100644 index 000000000..ec8b88e15 --- /dev/null +++ b/test/functional-tests/features/test_rrd_static_profile_report_with_suffix.feature @@ -0,0 +1,48 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +Feature: Remote Debugger Static Profile Report With Suffix + + Scenario: Verify remote debugger process is running + Given the remote debugger process is not running + When I start the remote debugger process + Then the remote debugger process should be running + + Scenario: Send RFC event with valid suffixed issue type + Given the remote debugger is running + When I trigger the event "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType" with value "Device.Info_ab1bghjh" + Then the event for RRD_SET_ISSUE_EVENT should be received + And the logs should contain "SUCCESS: Message sending Done" + And the logs should be seen with "SUCCESS: Message Reception Done" + And the remotedebugger request payload should include "MSG=Device.Info_ab1bghjh" + + Scenario: Validate static-profile command execution and upload flow for suffixed issue type + Given the remotedebugger received the suffixed issue type + When remotedebugger parses "/etc/rrd/remote_debugger.json" + Then remotedebugger should execute debug commands for static profile issue "Device.Info" + And the timestamped runtime service should start successfully + And the timestamped runtime journalctl collection should succeed + And the timestamped runtime service should stop successfully + And remotedebugger should generate upload archive filename for the suffixed issue type + And remotedebugger should invoke upload with the generated archive + + Scenario: Verify upload result for suffixed issue type report + When I check the upload status in the logs + Then the upload script execution should be successful + And the debug information report upload should be successful diff --git a/test/functional-tests/features/test_rrd_static_profile_report_with_suffix_negative_case.feature b/test/functional-tests/features/test_rrd_static_profile_report_with_suffix_negative_case.feature new file mode 100644 index 000000000..b8b36d2fd --- /dev/null +++ b/test/functional-tests/features/test_rrd_static_profile_report_with_suffix_negative_case.feature @@ -0,0 +1,49 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +Feature: Remote Debugger Static Profile Report With Overlength Suffix + + Scenario: Verify remote debugger process is running + Given the remote debugger process is not running + When I start the remote debugger process + Then the remote debugger process should be running + + Scenario: Send RFC event with overlength suffixed issue type + Given the remote debugger is running + When I trigger the event "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType" with value "Device.Info_ab1bghjhfhk" + Then the event for RRD_SET_ISSUE_EVENT should be received + And the logs should contain "SUCCESS: Message sending Done" + And the logs should be seen with "SUCCESS: Message Reception Done" + And the remotedebugger request payload should include "MSG=Device.Info_ab1bghjhfhk" + + Scenario: Validate suffix discard and upload flow for overlength suffix + Given the remotedebugger received the overlength suffixed issue type + When remotedebugger parses "/etc/rrd/remote_debugger.json" + Then remotedebugger should execute debug commands for static profile issue "Device.Info" + And remotedebugger should log suffix-length discard with configured max length + And the timestamped runtime service should start successfully + And the timestamped runtime journalctl collection should succeed + And the timestamped runtime service should stop successfully + And remotedebugger should generate archive filename with discarded suffix + And remotedebugger should invoke upload with the generated archive + + Scenario: Verify upload result for overlength suffix report + When I check the upload status in the logs + Then the upload script execution should be successful + And the debug information report upload should be successful diff --git a/test/functional-tests/tests/helper_functions.py b/test/functional-tests/tests/helper_functions.py index 33ddd3e02..a6157e0f9 100644 --- a/test/functional-tests/tests/helper_functions.py +++ b/test/functional-tests/tests/helper_functions.py @@ -73,6 +73,21 @@ def grep_rrdlogs(search: str): print(f"Could not read file {LOG_FILE}: {e}") return search_result +def check_service_start_success(issue_type: str): + """Check that service-start logs include a success marker. + + Args: + issue_type: Issue type used in the runtime service name. + + Raises: + AssertionError: If matching service-start logs are missing or do not + include the "service success..." message. + """ + service_start = f"Starting remote_debugger_{issue_type}" + start_logs = grep_rrdlogs(service_start) + assert service_start in start_logs, f"Service start message not found for issue type '{issue_type}'" + assert "service success..." in start_logs, f"Service success marker not found for issue type '{issue_type}'" + def check_file_exists(file_path): return os.path.isfile(file_path) diff --git a/test/functional-tests/tests/test_rrd_background_cmd_static_profile_report.py b/test/functional-tests/tests/test_rrd_background_cmd_static_profile_report.py index 622128519..662ed66ab 100644 --- a/test/functional-tests/tests/test_rrd_background_cmd_static_profile_report.py +++ b/test/functional-tests/tests/test_rrd_background_cmd_static_profile_report.py @@ -108,17 +108,16 @@ def test_remote_debugger_trigger_event(): DEBUG_FILE = "Adding Details of Debug commands to Output File" assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) - SERVICE_START = f"Starting remote_debugger_{BACKGROUND_STRING} service success" - assert SERVICE_START in grep_rrdlogs(SERVICE_START) + check_service_start_success(BACKGROUND_STRING) - JOURNAL_START = f"journalctl remote_debugger_{BACKGROUND_STRING} service success" + JOURNAL_START = f"journalctl remote_debugger_{BACKGROUND_STRING}" assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) SLEEP_TIME = "Sleeping with timeout" assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) sleep(20) - SERVICE_STOP = f"Stopping remote_debugger_{BACKGROUND_STRING} service" + SERVICE_STOP = f"Stopping remote_debugger_{BACKGROUND_STRING}" assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) result = check_output_dir() diff --git a/test/functional-tests/tests/test_rrd_c_api_upload.py b/test/functional-tests/tests/test_rrd_c_api_upload.py index dc7f895dc..da72812d5 100644 --- a/test/functional-tests/tests/test_rrd_c_api_upload.py +++ b/test/functional-tests/tests/test_rrd_c_api_upload.py @@ -93,17 +93,16 @@ def test_remote_debugger_trigger_event(): DEBUG_FILE = "Adding Details of Debug commands to Output File" assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) - SERVICE_START = f"Starting remote_debugger_{ISSUE_STRING} service success" - assert SERVICE_START in grep_rrdlogs(SERVICE_START) + check_service_start_success(ISSUE_STRING) - JOURNAL_START = f"journalctl remote_debugger_{ISSUE_STRING} service success" + JOURNAL_START = f"journalctl remote_debugger_{ISSUE_STRING}" assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) SLEEP_TIME = "Sleeping with timeout" assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) sleep(20) - SERVICE_STOP = f"Stopping remote_debugger_{ISSUE_STRING} service" + SERVICE_STOP = f"Stopping remote_debugger_{ISSUE_STRING}" assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) result = check_output_dir() diff --git a/test/functional-tests/tests/test_rrd_debug_report_upload.py b/test/functional-tests/tests/test_rrd_debug_report_upload.py index f6d798c36..51c83085c 100644 --- a/test/functional-tests/tests/test_rrd_debug_report_upload.py +++ b/test/functional-tests/tests/test_rrd_debug_report_upload.py @@ -126,17 +126,16 @@ def test_remote_debugger_trigger_event(): DEBUG_FILE = "Adding Details of Debug commands to Output File" assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) - SERVICE_START = f"Starting remote_debugger_{ISSUE_STRING} service success" - assert SERVICE_START in grep_rrdlogs(SERVICE_START) + check_service_start_success(ISSUE_STRING) - JOURNAL_START = f"journalctl remote_debugger_{ISSUE_STRING} service success" + JOURNAL_START = f"journalctl remote_debugger_{ISSUE_STRING}" assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) SLEEP_TIME = "Sleeping with timeout" assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) sleep(20) - SERVICE_STOP = f"Stopping remote_debugger_{ISSUE_STRING} service" + SERVICE_STOP = f"Stopping remote_debugger_{ISSUE_STRING}" assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) result = check_output_dir() diff --git a/test/functional-tests/tests/test_rrd_deepsleep_static_report.py b/test/functional-tests/tests/test_rrd_deepsleep_static_report.py index af006e23b..a82fb3fa5 100644 --- a/test/functional-tests/tests/test_rrd_deepsleep_static_report.py +++ b/test/functional-tests/tests/test_rrd_deepsleep_static_report.py @@ -98,17 +98,16 @@ def test_remote_debugger_trigger_event(): assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) issue_string = "DEEPSLEEP" - SERVICE_START = f"Starting remote_debugger_Audio.Audio service success..." - assert SERVICE_START in grep_rrdlogs(SERVICE_START) + check_service_start_success("Audio.Audio") - JOURNAL_START = "journalctl remote_debugger_Audio.Audio service success..." + JOURNAL_START = "journalctl remote_debugger_Audio.Audio" assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) SLEEP_TIME = "Sleeping with timeout" assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) sleep(20) - SERVICE_STOP = f"Stopping remote_debugger_Audio.Audio service..." + SERVICE_STOP = f"Stopping remote_debugger_Audio.Audio" assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) SANITY_CHECK = "Found valid Commands" @@ -117,17 +116,16 @@ def test_remote_debugger_trigger_event(): DEBUG_FILE = "Adding Details of Debug commands to Output File" assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) - SERVICE_START = f"Starting remote_debugger_Video.Video service success" - assert SERVICE_START in grep_rrdlogs(SERVICE_START) + check_service_start_success("Video.Video") - JOURNAL_START = f"journalctl remote_debugger_Video.Video service success" + JOURNAL_START = f"journalctl remote_debugger_Video.Video" assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) SLEEP_TIME = "Sleeping with timeout" assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) sleep(20) - SERVICE_STOP = f"Stopping remote_debugger_Video.Video service" + SERVICE_STOP = f"Stopping remote_debugger_Video.Video" assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) result = check_output_dir() diff --git a/test/functional-tests/tests/test_rrd_dynamic_profile_missing_report.py b/test/functional-tests/tests/test_rrd_dynamic_profile_missing_report.py index 5d8379204..aa3f3d296 100644 --- a/test/functional-tests/tests/test_rrd_dynamic_profile_missing_report.py +++ b/test/functional-tests/tests/test_rrd_dynamic_profile_missing_report.py @@ -21,6 +21,9 @@ import subprocess from helper_functions import * +MAX_START_PID_RETRIES = 5 +START_PID_RETRY_INTERVAL_SECONDS = 1 + def test_check_remote_debugger_config_file(): config_file_path = JSON_FILE assert check_file_exists(config_file_path), f"Configuration file '{config_file_path}' does not exist." @@ -32,12 +35,18 @@ def test_check_rrd_directory_exists(): def test_check_and_start_remotedebugger(): kill_rrd() remove_logfile() + remove_upload_lock() print("Starting remotedebugger process") command_to_start = "nohup /usr/local/bin/remotedebugger > /dev/null 2>&1 &" run_shell_silent(command_to_start) command_to_get_pid = "pidof remotedebugger" - pid = run_shell_command(command_to_get_pid) - assert pid != "", "remotedebugger process did not start" + pid = None + for _ in range(MAX_START_PID_RETRIES): + pid = run_shell_command(command_to_get_pid) + if pid: + break + sleep(START_PID_RETRY_INTERVAL_SECONDS) + assert pid, "remotedebugger process did not start" def reset_issuetype_rfc(): command = 'rbuscli set Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType string ""' diff --git a/test/functional-tests/tests/test_rrd_dynamic_profile_rdm_node_length_exceeded.py b/test/functional-tests/tests/test_rrd_dynamic_profile_rdm_node_length_exceeded.py new file mode 100644 index 000000000..4c72fbd77 --- /dev/null +++ b/test/functional-tests/tests/test_rrd_dynamic_profile_rdm_node_length_exceeded.py @@ -0,0 +1,104 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +import json +import subprocess +from helper_functions import * + +def test_check_remote_debugger_config_file(): + config_file_path = JSON_FILE + assert check_file_exists(config_file_path), f"Configuration file '{config_file_path}' does not exist." + +def test_check_rrd_directory_exists(): + dir_path = OUTPUT_DIR + assert check_directory_exists(dir_path), f"Directory '{dir_path}' does not exist." + +def test_check_and_start_remotedebugger(): + kill_rrd() + remove_logfile() + print("Starting remotedebugger process") + command_to_start = "nohup /usr/local/bin/remotedebugger > /dev/null 2>&1 &" + run_shell_silent(command_to_start) + command_to_get_pid = "pidof remotedebugger" + pid = run_shell_command(command_to_get_pid) + assert pid != "", "remotedebugger process did not start" + +def reset_issuetype_rfc(): + command = 'rbuscli set Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType string ""' + result = subprocess.run(command, shell=True, capture_output=True, text=True) + assert result.returncode == 0 + +def test_remote_debugger_trigger_event(): + STRING_TEST = "hfkerfjrjfjfjfjfjfjfjfjfjfjfjfjfjfjf" + reset_issuetype_rfc() + sleep(10) + command = [ + 'rbuscli', 'set', + 'Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType', + 'string', STRING_TEST + ] + result = subprocess.run(command, capture_output=True, text=True) + assert result.returncode == 0 + + sleep(15) + + QUERY_MSG = "Received event for RRD_SET_ISSUE_EVENT" + assert QUERY_MSG in grep_rrdlogs(QUERY_MSG) + + MSG_SEND = "SUCCESS: Message sending Done" + sleep(2) + assert MSG_SEND in grep_rrdlogs(MSG_SEND) + + MSG_RECEIVE = "SUCCESS: Message Reception Done" + sleep(2) + assert MSG_RECEIVE in grep_rrdlogs(MSG_RECEIVE) + +def test_check_issue_in_static_profile(): + READ_JSON = "Start Reading JSON File... /etc/rrd/remote_debugger.json" + assert READ_JSON in grep_rrdlogs(READ_JSON) + + PARSE_JSON = "Static Profile Parse And Read Success" + assert PARSE_JSON in grep_rrdlogs(PARSE_JSON) + + MISSING_MSG = "Issue Data Not found in Static JSON File" + assert MISSING_MSG in grep_rrdlogs(MISSING_MSG) + +def test_check_issue_in_dynamic_profile(): + DYNAMIC_READ = "Checking Dynamic Profile..." + assert DYNAMIC_READ in grep_rrdlogs(DYNAMIC_READ) + + DYNAMIC_JSONFILE = "Reading json config file /media/apps/RDK-RRD-hfkerfjrjfjfjfjfjfjfjfjfjfjfjfjfjfjf/etc/rrd/remote_debugger.json" + assert DYNAMIC_JSONFILE in grep_rrdlogs(DYNAMIC_JSONFILE) + + PARSE_FAILED = "Dynamic Profile Parse/Read failed" + assert PARSE_FAILED in grep_rrdlogs(PARSE_FAILED) + + RDM_MSG = "Request RDM Manager Download for a new Issue Type" + assert RDM_MSG in grep_rrdlogs(RDM_MSG) + + INVALID_LENGTH = "Issue node length must be less than 34" + assert INVALID_LENGTH in grep_rrdlogs(INVALID_LENGTH) + + SKIP_DOWNLOAD = "Invalid profile length, skipping download request" + assert SKIP_DOWNLOAD in grep_rrdlogs(SKIP_DOWNLOAD) + + + remove_logfile() + remove_outdir_contents(OUTPUT_DIR) + kill_rrd() diff --git a/test/functional-tests/tests/test_rrd_dynamic_profile_report.py b/test/functional-tests/tests/test_rrd_dynamic_profile_report.py index 7cd806795..bca831ea9 100644 --- a/test/functional-tests/tests/test_rrd_dynamic_profile_report.py +++ b/test/functional-tests/tests/test_rrd_dynamic_profile_report.py @@ -141,17 +141,16 @@ def test_check_issue_in_dynamic_profile(): assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) testrun1_string = "Test.TestRun1" - SERVICE_START = f"Starting remote_debugger_{testrun1_string} service success" - assert SERVICE_START in grep_rrdlogs(SERVICE_START) + check_service_start_success(testrun1_string) - JOURNAL_START = f"journalctl remote_debugger_{testrun1_string} service success" + JOURNAL_START = f"journalctl remote_debugger_{testrun1_string}" assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) SLEEP_TIME = "Sleeping with timeout" assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) sleep(20) - SERVICE_STOP = f"Stopping remote_debugger_{testrun1_string} service" + SERVICE_STOP = f"Stopping remote_debugger_{testrun1_string}" assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) TESTRUN2_MSG = "Reading Issue Type Test:TestRun2" @@ -164,17 +163,16 @@ def test_check_issue_in_dynamic_profile(): assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) testrun2_string = "Test.TestRun2" - SERVICE_START = f"Starting remote_debugger_{testrun2_string} service success" - assert SERVICE_START in grep_rrdlogs(SERVICE_START) + check_service_start_success(testrun2_string) - JOURNAL_START = f"journalctl remote_debugger_{testrun2_string} service success" + JOURNAL_START = f"journalctl remote_debugger_{testrun2_string}" assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) SLEEP_TIME = "Sleeping with timeout" assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) sleep(20) - SERVICE_STOP = f"Stopping remote_debugger_{testrun2_string} service" + SERVICE_STOP = f"Stopping remote_debugger_{testrun2_string}" assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) result = check_output_dir() diff --git a/test/functional-tests/tests/test_rrd_dynamic_subcategory_report.py b/test/functional-tests/tests/test_rrd_dynamic_subcategory_report.py index efec81641..389f51391 100644 --- a/test/functional-tests/tests/test_rrd_dynamic_subcategory_report.py +++ b/test/functional-tests/tests/test_rrd_dynamic_subcategory_report.py @@ -129,16 +129,15 @@ def test_check_issue_in_dynamic_profile(): EXEC_COMMANDS = 'Executing Debug Commands: ""cat /version.txt;uptime;cat /proc/buddyinfo;cat /proc/meminfo;cat /tmp/.deviceDetails.cache""' assert EXEC_COMMANDS in grep_rrdlogs(EXEC_COMMANDS) - START_SERVICE = "Starting remote_debugger_Test.TestRun1 service success..." - assert START_SERVICE in grep_rrdlogs(START_SERVICE) + check_service_start_success("Test.TestRun1") USE_JOURNALCTL = "Using journalctl to log command output..." assert USE_JOURNALCTL in grep_rrdlogs(USE_JOURNALCTL) - JOURNALCTL_SUCCESS = "journalctl remote_debugger_Test.TestRun1 service success..." + JOURNALCTL_SUCCESS = "journalctl remote_debugger_Test.TestRun1" assert JOURNALCTL_SUCCESS in grep_rrdlogs(JOURNALCTL_SUCCESS) - STOP_SERVICE = "Stopping remote_debugger_Test.TestRun1 service..." + STOP_SERVICE = "Stopping remote_debugger_Test.TestRun1" assert STOP_SERVICE in grep_rrdlogs(STOP_SERVICE) UPLOAD_START = "Starting Upload Debug output via API..." diff --git a/test/functional-tests/tests/test_rrd_static_profile_category_report.py b/test/functional-tests/tests/test_rrd_static_profile_category_report.py index 827c8770b..b0fa8abd5 100644 --- a/test/functional-tests/tests/test_rrd_static_profile_category_report.py +++ b/test/functional-tests/tests/test_rrd_static_profile_category_report.py @@ -97,17 +97,16 @@ def test_remote_debugger_trigger_event(): assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) issue_string = "Device.Info" - SERVICE_START = f"Starting remote_debugger_{issue_string} service success" - assert SERVICE_START in grep_rrdlogs(SERVICE_START) + check_service_start_success(issue_string) - JOURNAL_START = f"journalctl remote_debugger_{issue_string} service success" + JOURNAL_START = f"journalctl remote_debugger_{issue_string}" assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) SLEEP_TIME = "Sleeping with timeout" assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) sleep(20) - SERVICE_STOP = f"Stopping remote_debugger_{issue_string} service" + SERVICE_STOP = f"Stopping remote_debugger_{issue_string}" assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) SANITY_CHECK = "Found valid Commands" @@ -117,17 +116,16 @@ def test_remote_debugger_trigger_event(): assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) issue_string = "Device.Uptime" - SERVICE_START = f"Starting remote_debugger_{issue_string} service success" - assert SERVICE_START in grep_rrdlogs(SERVICE_START) + check_service_start_success(issue_string) - JOURNAL_START = f"journalctl remote_debugger_{issue_string} service success" + JOURNAL_START = f"journalctl remote_debugger_{issue_string}" assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) SLEEP_TIME = "Sleeping with timeout" assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) sleep(20) - SERVICE_STOP = f"Stopping remote_debugger_{issue_string} service" + SERVICE_STOP = f"Stopping remote_debugger_{issue_string}" assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) result = check_output_dir() diff --git a/test/functional-tests/tests/test_rrd_static_profile_report.py b/test/functional-tests/tests/test_rrd_static_profile_report.py index b6309a52e..66f336d54 100644 --- a/test/functional-tests/tests/test_rrd_static_profile_report.py +++ b/test/functional-tests/tests/test_rrd_static_profile_report.py @@ -93,17 +93,16 @@ def test_remote_debugger_trigger_event(): DEBUG_FILE = "Adding Details of Debug commands to Output File" assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) - SERVICE_START = f"Starting remote_debugger_{ISSUE_STRING} service success" - assert SERVICE_START in grep_rrdlogs(SERVICE_START) + check_service_start_success(ISSUE_STRING) - JOURNAL_START = f"journalctl remote_debugger_{ISSUE_STRING} service success" + JOURNAL_START = f"journalctl remote_debugger_{ISSUE_STRING}" assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) SLEEP_TIME = "Sleeping with timeout" assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) sleep(20) - SERVICE_STOP = f"Stopping remote_debugger_{ISSUE_STRING} service" + SERVICE_STOP = f"Stopping remote_debugger_{ISSUE_STRING}" assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) result = check_output_dir() diff --git a/test/functional-tests/tests/test_rrd_static_profile_report_with_suffix.py b/test/functional-tests/tests/test_rrd_static_profile_report_with_suffix.py new file mode 100644 index 000000000..ab9887eff --- /dev/null +++ b/test/functional-tests/tests/test_rrd_static_profile_report_with_suffix.py @@ -0,0 +1,140 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +from helper_functions import * + +def test_check_remote_debugger_config_file(): + config_file_path = JSON_FILE + assert check_file_exists(config_file_path), f"Configuration file '{config_file_path}' does not exist." + +def test_check_rrd_directory_exists(): + dir_path = OUTPUT_DIR + assert check_directory_exists(dir_path), f"Directory '{dir_path}' does not exist." + +def reset_issuetype_rfc(): + command = 'rbuscli set Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType string ""' + result = subprocess.run(command, shell=True, capture_output=True, text=True) + assert result.returncode == 0 + +def test_check_and_start_remotedebugger(): + kill_rrd() + remove_logfile() + print("Starting remotedebugger process") + command_to_start = "nohup /usr/local/bin/remotedebugger > /dev/null 2>&1 &" + run_shell_silent(command_to_start) + command_to_get_pid = "pidof remotedebugger" + pid = run_shell_command(command_to_get_pid) + assert pid != "", "remotedebugger process did not start" + +def test_remote_debugger_trigger_event(): + reset_issuetype_rfc() + sleep(10) + command = [ + 'rbuscli', 'set', + 'Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType', + 'string', 'Device.Info_ab1bghjh' + ] + result = subprocess.run(command, capture_output=True, text=True) + assert result.returncode == 0, f"Command failed with error: {result.stderr}" + + sleep(15) + + QUERY_MSG = "Received event for RRD_SET_ISSUE_EVENT" + assert QUERY_MSG in grep_rrdlogs(QUERY_MSG) + + MSG_SEND = "SUCCESS: Message sending Done" + sleep(2) + assert MSG_SEND in grep_rrdlogs(MSG_SEND) + + MSG_RECEIVE = "SUCCESS: Message Reception Done" + sleep(2) + assert MSG_RECEIVE in grep_rrdlogs(MSG_RECEIVE) + + ISSUE_MSG = f'MSG={ISSUE_STRING}' + assert ISSUE_MSG in grep_rrdlogs(ISSUE_MSG) + print("Sent and received messages are found and match in the logfile") + + READ_JSON = "Start Reading JSON File... /etc/rrd/remote_debugger.json" + assert READ_JSON in grep_rrdlogs(READ_JSON) + + PARSE_JSON = "Json File parse Success... /etc/rrd/remote_debugger.json" + assert PARSE_JSON in grep_rrdlogs(PARSE_JSON) + + if '.' in ISSUE_STRING: + ISSUE_NODE, ISSUE_SUBNODE = ISSUE_STRING.split('.') + else: + node = ISSUE_STRING + subnode = None + + ISSUE_FOUND = f"Issue Data Node: {ISSUE_NODE} and Sub-Node: {ISSUE_SUBNODE} found in Static JSON File /etc/rrd/remote_debugger.json" + + DIR_CREATION = "Creating Directory" + assert DIR_CREATION in grep_rrdlogs(DIR_CREATION) + + SANITY_CHECK = "Found valid Commands" + assert SANITY_CHECK in grep_rrdlogs(SANITY_CHECK) + + DEBUG_FILE = "Adding Details of Debug commands to Output File" + assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) + + check_service_start_success(ISSUE_STRING) + + JOURNAL_START = f"journalctl remote_debugger_{ISSUE_STRING}" + assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) + + SLEEP_TIME = "Sleeping with timeout" + assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) + sleep(20) + + SERVICE_STOP = f"Stopping remote_debugger_{ISSUE_STRING}" + assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) + + GENERATE_FILE = f"Generated filename: AABBCCDDEEFF_DEVICE_INFO_AB1BGHJH" + assert GENERATE_FILE in grep_rrdlogs(GENERATE_FILE) + + START_UPLOAD = f"Starting upload - server: mockxconf, protocol: HTTP, file: AABBCCDDEEFF_DEVICE_INFO_AB1BGHJH" + assert START_UPLOAD in grep_rrdlogs(START_UPLOAD) + result = check_output_dir() + print(result) + + UPLOAD_LOGS = "Starting Upload Debug output via API" + assert UPLOAD_LOGS in grep_rrdlogs(UPLOAD_LOGS) + +def test_remotedebugger_upload_report(): + UPLOAD_SUCCESS = "RRD Upload Script Execution Success" + UPLOAD_FAILURE = "RRD Upload Script Execution Failure" + if UPLOAD_SUCCESS in grep_rrdlogs(UPLOAD_SUCCESS): + print("Upload success") + elif UPLOAD_FAILURE in grep_rrdlogs(UPLOAD_FAILURE): + print("Upload failed") + else: + print("Upload status not found in logs") + + SCRIPT_SUCCESS = "Debug Information Report upload Failed" + SCRIPT_FAILURE = "Debug Information Report upload Success" + if SCRIPT_SUCCESS in grep_rrdlogs(SCRIPT_SUCCESS): + print("Script execution success") + elif SCRIPT_FAILURE in grep_rrdlogs(SCRIPT_FAILURE): + print("Script execution failed") + else: + print("Script execution not found in logs") + + remove_logfile() + remove_outdir_contents(OUTPUT_DIR) + kill_rrd() diff --git a/test/functional-tests/tests/test_rrd_static_profile_report_with_suffix_negative_case.py b/test/functional-tests/tests/test_rrd_static_profile_report_with_suffix_negative_case.py new file mode 100644 index 000000000..8366dd4e0 --- /dev/null +++ b/test/functional-tests/tests/test_rrd_static_profile_report_with_suffix_negative_case.py @@ -0,0 +1,144 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################## + +from helper_functions import * + +def test_check_remote_debugger_config_file(): + config_file_path = JSON_FILE + assert check_file_exists(config_file_path), f"Configuration file '{config_file_path}' does not exist." + +def test_check_rrd_directory_exists(): + dir_path = OUTPUT_DIR + assert check_directory_exists(dir_path), f"Directory '{dir_path}' does not exist." + +def reset_issuetype_rfc(): + command = 'rbuscli set Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType string ""' + result = subprocess.run(command, shell=True, capture_output=True, text=True) + assert result.returncode == 0 + +def test_check_and_start_remotedebugger(): + kill_rrd() + remove_logfile() + print("Starting remotedebugger process") + command_to_start = "nohup /usr/local/bin/remotedebugger > /dev/null 2>&1 &" + run_shell_silent(command_to_start) + command_to_get_pid = "pidof remotedebugger" + pid = run_shell_command(command_to_get_pid) + assert pid != "", "remotedebugger process did not start" + +def test_remote_debugger_trigger_event(): + reset_issuetype_rfc() + sleep(10) + ISSUE_STRING = "Device.Info_ab1bghjhfhk" + command = [ + 'rbuscli', 'set', + 'Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RDKRemoteDebugger.IssueType', + 'string', ISSUE_STRING + ] + result = subprocess.run(command, capture_output=True, text=True) + assert result.returncode == 0, f"Command failed with error: {result.stderr}" + + sleep(15) + + QUERY_MSG = "Received event for RRD_SET_ISSUE_EVENT" + assert QUERY_MSG in grep_rrdlogs(QUERY_MSG) + + MSG_SEND = "SUCCESS: Message sending Done" + sleep(2) + assert MSG_SEND in grep_rrdlogs(MSG_SEND) + + MSG_RECEIVE = "SUCCESS: Message Reception Done" + sleep(2) + assert MSG_RECEIVE in grep_rrdlogs(MSG_RECEIVE) + + ISSUE_MSG = f'MSG={ISSUE_STRING}' + assert ISSUE_MSG in grep_rrdlogs(ISSUE_MSG) + print("Sent and received messages are found and match in the logfile") + + READ_JSON = "Start Reading JSON File... /etc/rrd/remote_debugger.json" + assert READ_JSON in grep_rrdlogs(READ_JSON) + + PARSE_JSON = "Json File parse Success... /etc/rrd/remote_debugger.json" + assert PARSE_JSON in grep_rrdlogs(PARSE_JSON) + + if '.' in ISSUE_STRING: + ISSUE_NODE, ISSUE_SUBNODE = ISSUE_STRING.split('.') + else: + node = ISSUE_STRING + subnode = None + + ISSUE_FOUND = f"Issue Data Node: {ISSUE_NODE} and Sub-Node: {ISSUE_SUBNODE} found in Static JSON File /etc/rrd/remote_debugger.json" + + DIR_CREATION = "Creating Directory" + assert DIR_CREATION in grep_rrdlogs(DIR_CREATION) + + SANITY_CHECK = "Found valid Commands" + assert SANITY_CHECK in grep_rrdlogs(SANITY_CHECK) + + DEBUG_FILE = "Adding Details of Debug commands to Output File" + assert DEBUG_FILE in grep_rrdlogs(DEBUG_FILE) + + check_service_start_success("Device.Info") + + JOURNAL_START = f"journalctl remote_debugger_Device.Info" + assert JOURNAL_START in grep_rrdlogs(JOURNAL_START) + + SLEEP_TIME = "Sleeping with timeout" + assert SLEEP_TIME in grep_rrdlogs(SLEEP_TIME) + sleep(20) + + SERVICE_STOP = f"Stopping remote_debugger_Device.Info" + assert SERVICE_STOP in grep_rrdlogs(SERVICE_STOP) + + SUFFIX_EXCEEDS = f"Suffix after 'Device.Info' exceeds max length (12 > 9); discarding" + assert SUFFIX_EXCEEDS in grep_rrdlogs(SUFFIX_EXCEEDS) + + GENERATE_FILE = f"Generated filename: AABBCCDDEEFF_DEVICE_INFO_" + assert GENERATE_FILE in grep_rrdlogs(GENERATE_FILE) + + START_UPLOAD = f"Starting upload - server: mockxconf, protocol: HTTP, file: AABBCCDDEEFF_DEVICE_INFO_" + assert START_UPLOAD in grep_rrdlogs(START_UPLOAD) + result = check_output_dir() + print(result) + + UPLOAD_LOGS = "Starting Upload Debug output via API" + assert UPLOAD_LOGS in grep_rrdlogs(UPLOAD_LOGS) + +def test_remotedebugger_upload_report(): + UPLOAD_SUCCESS = "RRD Upload Script Execution Success" + UPLOAD_FAILURE = "RRD Upload Script Execution Failure" + if UPLOAD_SUCCESS in grep_rrdlogs(UPLOAD_SUCCESS): + print("Upload success") + elif UPLOAD_FAILURE in grep_rrdlogs(UPLOAD_FAILURE): + print("Upload failed") + else: + print("Upload status not found in logs") + + SCRIPT_SUCCESS = "Debug Information Report upload Success" + SCRIPT_FAILURE = "Debug Information Report upload Failed" + if SCRIPT_SUCCESS in grep_rrdlogs(SCRIPT_SUCCESS): + print("Script execution success") + elif SCRIPT_FAILURE in grep_rrdlogs(SCRIPT_FAILURE): + print("Script execution failed") + else: + print("Script execution not found in logs") + + remove_logfile() + remove_outdir_contents(OUTPUT_DIR) + kill_rrd()