Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
749fd94
Create test_usb_logupload.py
Abhinavpv28 Feb 22, 2026
772bb94
Update test_uploadstblogs_normal_upload.py
Abhinavpv28 Feb 23, 2026
1c2349e
Update test_uploadstblogs_normal_upload.py
Abhinavpv28 Feb 23, 2026
fac910f
L2
Feb 26, 2026
6a43107
Update test_uploadLogsNow.py
Abhinavpv28 Feb 26, 2026
61268e9
Update test_uploadLogsNow.py
Abhinavpv28 Feb 26, 2026
11fff1c
Fix command execution in run_uploadlogsnow function
Abhinavpv28 Feb 26, 2026
f4182d5
Update test_uploadLogsNow.py
Abhinavpv28 Feb 26, 2026
b8fb729
Update test_uploadLogsNow.py
Abhinavpv28 Feb 26, 2026
8278af6
Update test_uploadLogsNow.py
Abhinavpv28 Feb 27, 2026
5818f1e
Update test_uploadLogsNow.py
Abhinavpv28 Feb 27, 2026
d3ad9be
Update test_uploadLogsNow.py
Abhinavpv28 Feb 27, 2026
91a3102
Update cov_build.sh
Abhinavpv28 Feb 27, 2026
7b80aa4
Update L2-tests.yml
Abhinavpv28 Mar 2, 2026
da2e8f1
Update run_uploadstblogs_l2.sh
Abhinavpv28 Mar 2, 2026
2d4e3a6
Apply suggestions from code review
Abhinavpv28 Mar 2, 2026
d4a9002
Update run_uploadstblogs_l2.sh
Abhinavpv28 Mar 2, 2026
ff322fb
L2
Mar 2, 2026
23d0fc9
L2 failure
Mar 2, 2026
a6e03c0
L2
Mar 2, 2026
85df765
Update cov_build.sh
Abhinavpv28 Mar 2, 2026
2c7c5fa
Clone telemetry repo and copy header files
Abhinavpv28 Mar 2, 2026
9e6ac13
Update uploadstblogs_helper.py
Abhinavpv28 Mar 2, 2026
c419adf
Update uploadstblogs_helper.py
Abhinavpv28 Mar 2, 2026
b114d46
Update uploadstblogs_helper.py
Abhinavpv28 Mar 2, 2026
1294aef
Update test_uploadstblogs_upload_strategies.py
Abhinavpv28 Mar 2, 2026
d4fe9fd
Update test_uploadstblogs_upload_strategies.py
Abhinavpv28 Mar 2, 2026
3cbf156
Update uploadstblogs_helper.py
Abhinavpv28 Mar 2, 2026
256f22e
Create usblogupload.feature
Abhinavpv28 Mar 2, 2026
6e4755c
Update test_log_upload_onreboot_false_case.py
Abhinavpv28 Mar 2, 2026
2dbec5f
Update test_log_upload_onreboot_true_case.py
Abhinavpv28 Mar 2, 2026
1b5c64b
Update L2-tests.yml
Abhinavpv28 Mar 2, 2026
0ff7872
Update test_log_upload_onreboot_true_case.py
Abhinavpv28 Mar 2, 2026
2cb8759
Update test_log_upload_onreboot_false_case.py
Abhinavpv28 Mar 2, 2026
3488e8d
Update test_log_upload_onreboot_true_case.py
Abhinavpv28 Mar 2, 2026
4bf28f5
Update test_log_upload_onreboot_false_case.py
Abhinavpv28 Mar 2, 2026
e0c1b64
Update test_log_upload_onreboot_false_case.py
Abhinavpv28 Mar 2, 2026
c6e358b
Update test_uploadstblogs_upload_strategies.py
Abhinavpv28 Mar 2, 2026
cb104a7
Update L2-tests.yml
Abhinavpv28 Mar 3, 2026
bce77d9
Update cov_build.sh
Abhinavpv28 Mar 3, 2026
b9184a8
Update L2-tests.yml
Abhinavpv28 Mar 3, 2026
5712cdb
Update test_uploadstblogs_upload_strategies.py
Abhinavpv28 Mar 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/L2-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ jobs:

- name: Start mock-xconf service
run: |
docker run -d --name mockxconf -p 50050:50050 -p 50051:50051 -p 50052:50052 -v ${{ github.workspace }}:/mnt/L2_CONTAINER_SHARED_VOLUME ghcr.io/rdkcentral/docker-device-mgt-service-test/mockxconf:latest
docker run -d --name mockxconf -p 50050:50050 -p 50051:50051 -p 50052:50052 -e ENABLE_MTLS=true -v ${{ github.workspace }}:/mnt/L2_CONTAINER_SHARED_VOLUME ghcr.io/rdkcentral/docker-device-mgt-service-test/mockxconf:latest

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .astylerc options file referenced in the --options=.astylerc argument does not exist in the repository. This will cause the astyle command to fail with an error like "Cannot find options file: .astylerc", which in turn will fail the entire "Check for formatting errors" step and break the CI pipeline on every PR. The .astylerc file needs to be created in the repository root, or the reference to it should be removed (using astyle with default options or another options file that exists).

Copilot uses AI. Check for mistakes.

- name: Start l2-container service
run: |
docker run -d --name native-platform --link mockxconf -v ${{ github.workspace }}:/mnt/L2_CONTAINER_SHARED_VOLUME ghcr.io/rdkcentral/docker-device-mgt-service-test/native-platform:latest
docker run -d --name native-platform --link mockxconf -e ENABLE_MTLS=true -v ${{ github.workspace }}:/mnt/L2_CONTAINER_SHARED_VOLUME ghcr.io/rdkcentral/docker-device-mgt-service-test/native-platform:latest

- name: Enter Inside Platform native container and run L2 Test
run: |
Expand Down
44 changes: 44 additions & 0 deletions test/functional-tests/features/usblogupload.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Feature: USB Log Upload
This feature covers the USB log upload functionality, including error handling, archive creation, MAC address logging, temp directory cleanup, and success/failure scenarios.
Comment on lines +1 to +2

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new usblogupload.feature file is missing the standard Apache 2.0 copyright header that is present in all other feature files in this directory (e.g., dcm-agent_bootup_sequence.feature, uploadstblogs_normal_upload.feature). The header should be added for consistency with the codebase conventions.

Copilot uses AI. Check for mistakes.

Scenario: USB not mounted or missing log path
Given the USB log upload binary is available
When I run usblogupload with a non-existent mount point
Then the process should fail with code 2 or 3
And a failure message should be logged

Scenario: Archive creation on valid mount
Given a valid USB mount point
When I run usblogupload
Then the process should exit with code 0 or 3
And an archive creation log may appear

Scenario: MAC address and file log
Given a valid USB mount point
When I run usblogupload
Then the process should exit with code 0 or 3
And a log line with MAC address and file name may appear

Scenario: Temp directory cleanup
Given a valid USB mount point
When I run usblogupload
Then the process should exit with code 0 or 3
And a cleanup log may appear

Scenario: Successful USB log upload
Given the USB log upload binary is available
When I run usblogupload with a valid mount point
Then the process should exit with code 0
And a completion message should be logged

Scenario: Invalid usage
Given the USB log upload binary is available
When I run usblogupload with no arguments
Then the process should exit with code 4
And a log about failed logging system initialization may appear

Scenario: USB not mounted
Given the USB log upload binary is available
When I run usblogupload with an unmounted path
Then the process should exit with code 2
And a log about failed USB mount point validation may appear
Comment on lines +4 to +44

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In usblogupload.feature, the scenario "USB not mounted or missing log path" (line 4-8) and "USB not mounted" (lines 40-44) cover similar scenarios – both test with non-existent paths. Their corresponding Python tests test_usblogupload_missing_log_path and test_usblogupload_usb_not_mounted assert different exit codes: codes 2 or 3 vs. strictly code 2. If these scenarios are truly distinct (missing log path vs. USB mount point validation failure), the feature file should clearly differentiate the inputs and expected behavior to avoid ambiguity.

Copilot uses AI. Check for mistakes.
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,9 @@ def test_upload_cron_present():
sleep(20)
assert "urn:settings:LogUploadSettings:UploadSchedule:cron" in grep_dcmdlogs("is present setting cron jobs")

@pytest.mark.run(order=2)
def test_upload_script_started_onboot_false():
assert "UploadOnReboot=0" in grep_dcmdlogs("Triggered uploadSTBLogs.sh with arguments")
assert "Called uploadLogOnReboot with false" in grep_dcmdlogs("Called uploadLogOnReboot with false")
sleep(420)

@pytest.mark.run(order=3)
def test_fw_cron_scheduled():
sleep(420)
Comment on lines 37 to +39

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deletion of test_upload_script_started_onboot_false (order=2) leaves a gap in the ordering sequence (1, 3, 4, 5, 6). Similarly to the true-case test, the remaining tests should be renumbered for consistency. More importantly, the sleep(420) from the deleted test_upload_script_started_onboot_false was moved into test_fw_cron_scheduled (order=3). This changes the overall test timing: previously the 420-second sleep was in an independent test step (order=2) before order=3. Now it is at the beginning of order=3. This effectively preserves the timing but it is worth ensuring the intent was to continue waiting 420 seconds before checking the FW cron schedule.

Suggested change
@pytest.mark.run(order=3)
def test_fw_cron_scheduled():
sleep(420)
@pytest.mark.run(order=2)
def test_wait_before_fw_cron_check():
# Wait for FW cron scheduling window before validation
sleep(420)
@pytest.mark.run(order=3)
def test_fw_cron_scheduled():

Copilot uses AI. Check for mistakes.
assert "Scheduling DCM_FW_UPDATE Job handle" in grep_dcmdlogs("Scheduling DCM_FW_UPDATE Job handle")
Comment on lines 37 to 40

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the removal of the order=2 test in this file, there is now a gap from order=1 directly to order=3, skipping order=2. While pytest-ordering handles gaps gracefully, it is good practice to renumber the remaining tests sequentially to keep the intent clear and avoid confusion for future maintainers.

Copilot uses AI. Check for mistakes.


Comment on lines +39 to 42

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In test_log_upload_onreboot_false_case.py, the test test_upload_script_started_onboot_false (order=2) was removed, but the sleep(420) that was inside it has been moved into test_fw_cron_scheduled (order=3). This means the sleep now runs unconditionally for that test case instead of only in the reboot-false path. The sleep was previously in the order=2 function to wait before checking for the false-onboot case. The intent and correctness of this logic change should be verified — if the sleep is truly needed here and in the right test is fine, but the duplication of logic (the moved sleep) could indicate an oversight.

Suggested change
sleep(420)
assert "Scheduling DCM_FW_UPDATE Job handle" in grep_dcmdlogs("Scheduling DCM_FW_UPDATE Job handle")
max_wait = 420
interval = 10
for _ in range(int(max_wait / interval)):
if "Scheduling DCM_FW_UPDATE Job handle" in grep_dcmdlogs("Scheduling DCM_FW_UPDATE Job handle"):
return
sleep(interval)
# Final check after waiting up to max_wait seconds
assert "Scheduling DCM_FW_UPDATE Job handle" in grep_dcmdlogs("Scheduling DCM_FW_UPDATE Job handle")

Copilot uses AI. Check for mistakes.
Expand All @@ -52,8 +47,7 @@ def test_upload_cron_scheduled():

@pytest.mark.run(order=5)
def test_upload_script_started():
assert "Start log upload Script" in grep_dcmdlogs("Start log upload Script")
assert "Called uploadDCMLogs" in grep_dcmdlogs("Called uploadDCMLogs")
assert "Start log upload via library API" in grep_dcmdlogs("Start log upload via library API")

@pytest.mark.run(order=6)
def test_fwupdate_script_started():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@
def test_upload_cron_present():
assert "urn:settings:LogUploadSettings:UploadSchedule:cron" in grep_dcmdlogs("is present setting cron jobs")

@pytest.mark.run(order=2)
def test_upload_script_started():
assert "UploadOnReboot=1" in grep_dcmdlogs("Triggered uploadSTBLogs.sh with arguments")
assert "Called uploadLogOnReboot with true" in grep_dcmdlogs("Called uploadLogOnReboot with true")

@pytest.mark.run(order=3)
def test_fw_cron_scheduled():
sleep(540)
Expand All @@ -50,7 +45,5 @@ def test_upload_cron_scheduled():

@pytest.mark.run(order=6)
def test_upload_started():
assert "Start log upload Script" in grep_dcmdlogs("Start log upload Script")
assert "FLAG=0" in grep_dcmdlogs("Triggered uploadSTBLogs.sh with arguments")
assert "Called uploadDCMLogs" in grep_dcmdlogs("Called uploadDCMLogs")
assert "Start log upload via library API" in grep_dcmdlogs("Start log upload via library API")
Comment on lines 47 to +48

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deletion of test_upload_script_started (order=2) in test_log_upload_onreboot_true_case.py leaves a gap in the test ordering: the sequence goes from order=1 to order=3, skipping order=2. While pytest-ordering handles this gracefully (tests will still run), the ordering numbers are inconsistent and may cause confusion when adding new tests in the future. The remaining tests should be renumbered to maintain a consistent sequence (1, 2, 3, 4, 5, 6) rather than (1, 3, 4, 4, 6).

Copilot uses AI. Check for mistakes.

Comment on lines 45 to 49

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the removal of the order=2 test, the ordering in test_log_upload_onreboot_true_case.py now has a gap (1, then 3) and also has two tests both marked with @pytest.mark.run(order=4): test_fwupdate_script_started and test_upload_cron_scheduled. Having duplicate order values means the execution order between these two tests is non-deterministic when using pytest-ordering. If either test produces state that the other relies on, this could cause intermittent failures. The order numbers should be updated to be unique and sequential.

Copilot uses AI. Check for mistakes.
4 changes: 1 addition & 3 deletions test/functional-tests/tests/test_uploadLogsNow.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,9 @@

def run_uploadlogsnow():
"""Execute uploadlogsnow using the specific binary command"""
cmd = "/usr/local/bin/logupload uploadlogsnow"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=300)
result = subprocess.run("/usr/local/bin/logupload uploadlogsnow >> /opt/logs/logupload.log.0",shell=True)

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The run_uploadlogsnow() function was changed to use subprocess.run without capture_output=True, text=True, or timeout. This means:

  1. The process's stdout and stderr are not captured and go directly to the terminal, instead of being redirected to the log file via the >> shell redirect.
  2. The timeout=300 guard was removed, so if the binary hangs, the test will block indefinitely.
  3. The return value's stdout and stderr attributes will not be available (they will be None), which can cause AttributeError if any callers try to access them.

The original approach of using capture_output=True, text=True, timeout=300 was correct and should be restored. If log file redirection is also needed, it can be achieved separately.

Suggested change
result = subprocess.run("/usr/local/bin/logupload uploadlogsnow >> /opt/logs/logupload.log.0",shell=True)
# Run the command with captured output and a timeout to avoid hanging tests
result = sp.run(
["/usr/local/bin/logupload", "uploadlogsnow"],
capture_output=True,
text=True,
timeout=300,
)
# Append stdout and stderr to the log file to mimic the original shell redirection
try:
with open("/opt/logs/logupload.log.0", "a", encoding="utf-8") as log_file:
if result.stdout:
log_file.write(result.stdout)
if result.stderr:
log_file.write(result.stderr)
except Exception as exc:
# In test code, print the error but do not fail the test solely due to logging issues
print(f"Failed to write uploadlogsnow output to log file: {exc}")

Copilot uses AI. Check for mistakes.

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subprocess module is imported as sp (alias), but sp is never used anywhere in this file. All calls use the bare name subprocess which is implicitly available through the from uploadstblogs_helper import * wildcard import. The unused import subprocess as sp on line 27 is dead code and should be removed to avoid confusion.

Copilot uses AI. Check for mistakes.
return result
Comment on lines +37 to 38

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactored call removes capture_output=True, text=True, and timeout=300. Without capture_output=True, result.stdout and result.stderr will be None, which will cause AttributeError in any test that accesses those attributes. The missing timeout also means the test can hang indefinitely. These arguments should be restored.

Copilot uses AI. Check for mistakes.


class TestUploadLogsNow:
"""Test suite for uploadLogsNow immediate upload functionality"""

Expand Down
29 changes: 3 additions & 26 deletions test/functional-tests/tests/test_uploadstblogs_normal_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,15 @@ def test_normal_upload_initialization(self):

# Run uploadSTBLogs
#result = run_uploadstblogs()

result = subprocess.run([
"/usr/local/bin/logupload",
"",
"1",
"1",
"true",
"HTTP",
"https://mockxconf:50058/"
])


result = subprocess.run("/usr/local/bin/logupload '' 1 1 true HTTP https://mockxconf:50058/ >> /opt/logs/logupload.log.0",shell=True)

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using shell=True with output redirected to a file (>>) means result.stdout and result.stderr will be empty, yet result.returncode is asserted on line 60. More importantly, the log-grep assertions on lines 63 and 66 depend on the log file containing the output, but previously capture_output=True was used so those logs may not have been written to the file either. Also, passing an empty string '' as a shell argument may not be interpreted correctly as an empty argument by all shells.

Copilot uses AI. Check for mistakes.

# Verify initialization
assert result.returncode == 0 or result.returncode == 1, "Upload process should complete"

# Check initialization logs
init_logs = grep_uploadstb_logs("Context initialization successful")
assert len(init_logs) > 0, "Context should be initialized successfully"

# Verify device properties loaded
logs = grep_uploadstb_logs("DEVICE_TYPE")
assert len(logs) > 0, "Device type should be loaded from properties"

collection_logs = grep_uploadstb_logs_regex(r"collect|archive|gather")
assert len(collection_logs) > 0, "Log collection should be attempted"
Comment on lines 63 to 67

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion in the removed assertion block checks for DEVICE_TYPE log lines using grep_uploadstb_logs("DEVICE_TYPE"). These assertions have been removed in this PR, but the initialization test still asserts on grep_uploadstb_logs("Context initialization successful") and collect|archive|gather. The removal of the DEVICE_TYPE assertion reduces test coverage for verifying that device properties are loaded correctly, which is an important initialization step.

Copilot uses AI. Check for mistakes.

Expand Down Expand Up @@ -109,16 +95,7 @@ def test_large_file_collection(self):
"""Test: Service collects large log files within limits"""
# Create large test files (10MB each)
large_files = create_large_test_log_files(count=3, size_mb=10)

result = subprocess.run([
"/usr/local/bin/logupload",
"",
"1",
"1",
"true",
"HTTP",
"https://mockxconf:50058/"
])
result = subprocess.run("/usr/local/bin/logupload '' 1 1 true HTTP https://mockxconf:50058/ >> /opt/logs/logupload.log.0",shell=True)
Comment on lines 57 to +98

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both subprocess.run calls were changed to use shell=True with no capture_output=True or text=True. The >> shell redirect in the command string will redirect the process's output to the log file, but the returned result object will have stdout=None and stderr=None. This means any test logic that relies on inspecting result.stdout or result.stderr will silently get None, potentially leading to misleading test results or AttributeError. The helper function run_uploadstblogs() in uploadstblogs_helper.py already provides the proper invocation pattern (capture_output=True, text=True, timeout=300) and could be reused here instead.

Copilot uses AI. Check for mistakes.

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as line 57: subprocess.run uses shell=True with a shell redirect but no capture_output or timeout. The binary could hang indefinitely.

Copilot uses AI. Check for mistakes.

# Verify files were processed

Expand Down
122 changes: 122 additions & 0 deletions test/functional-tests/tests/test_usb_logupload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import subprocess
import os
import re
import pytest

Comment on lines +1 to +5

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new test_usb_logupload.py file is missing the standard Apache 2.0 copyright header that is present in all other test files in this directory (e.g., test_uploadLogsNow.py, test_uploadstblogs_normal_upload.py). The header should be added for consistency with the codebase conventions.

Copilot uses AI. Check for mistakes.
USBLOGUPLOAD_BIN = "/usr/local/bin/usblogupload"
LOG_FILE = "/opt/logs/logupload.log" # Adjust if needed

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test_usb_logupload.py file has a development-time placeholder comment # Adjust if needed next to the LOG_FILE constant. This suggests the constant value may not be finalized and should be confirmed and the comment removed before merging.

Suggested change
LOG_FILE = "/opt/logs/logupload.log" # Adjust if needed
LOG_FILE = "/opt/logs/logupload.log"

Copilot uses AI. Check for mistakes.

Copilot AI Mar 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LOG_FILE constant is set to /opt/logs/logupload.log (without the .0 suffix), while the rest of the test suite consistently uses /opt/logs/logupload.log.0 (the UPLOADSTB_LOG constant in uploadstblogs_helper.py). The binary output is redirected to logupload.log.0 in other test files (lines 57 and 98 of test_uploadstblogs_normal_upload.py). Reading from and writing to a different log file may cause tests in this file to never find the expected log entries. The comment # Adjust if needed confirms this uncertainty. This should be resolved to use the correct log file path.

Suggested change
LOG_FILE = "/opt/logs/logupload.log" # Adjust if needed
LOG_FILE = "/opt/logs/logupload.log.0"

Copilot uses AI. Check for mistakes.

# Helper to grep logs

def grep_usblogupload_logs(search: str):
search_result = []
search_pattern = re.compile(re.escape(search), re.IGNORECASE)
try:
with open(LOG_FILE, 'r', encoding='utf-8', errors='ignore') as file:
for line in file:
if search_pattern.search(line):
search_result.append(line)
except Exception as e:
print(f"Could not read file {LOG_FILE}: {e}")
return search_result


@pytest.fixture(autouse=True)
def setup_device_properties(tmp_path):

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setup_device_properties fixture is a module-level fixture (not inside the TestUSBLogUpload class), which means it applies to all tests in the module including both class-based and standalone tests. However, the tmp_path parameter is declared but never actually used by the fixture body — the backup/restore logic only uses the device_properties_path derived from os.path.dirname(__file__). The unused tmp_path parameter is misleading and should be removed.

Suggested change
def setup_device_properties(tmp_path):
def setup_device_properties():

Copilot uses AI. Check for mistakes.
# Path to device.properties for test
device_properties_path = os.path.join(os.path.dirname(__file__), "device.properties")
backup_path = device_properties_path + ".bak"
# Backup original if exists
if os.path.exists(device_properties_path):
os.rename(device_properties_path, backup_path)
# Ensure RDK_PROFILE=TV is present
with open(device_properties_path, "w", encoding="utf-8") as f:
f.write("RDK_PROFILE=TV\n")
yield
# Restore original after test
if os.path.exists(backup_path):
os.remove(device_properties_path)
os.rename(backup_path, device_properties_path)
Comment on lines +25 to +39

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setup_device_properties autouse fixture writes RDK_PROFILE=TV to a device.properties file relative to the test directory (os.path.dirname(__file__)/device.properties). However, the usblogupload binary most likely reads device properties from /etc/device.properties (as established by the rest of the test suite and the setup script). This fixture will therefore not have the intended effect of configuring RDK_PROFILE=TV for the binary under test. The tmp_path parameter is also declared in the fixture signature but never used.

Copilot uses AI. Check for mistakes.

Comment on lines +26 to +40

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fixture writes device.properties relative to the test file's directory (__file__), not under the tmp_path provided by the fixture. This means tests modify a file in the source tree rather than an isolated temporary location, which can cause side-effects between test runs. The tmp_path parameter is accepted but not used for this file. The device_properties_path should be constructed under tmp_path instead, and the binary should be pointed to that location via an environment variable or argument.

Suggested change
# Path to device.properties for test
device_properties_path = os.path.join(os.path.dirname(__file__), "device.properties")
backup_path = device_properties_path + ".bak"
# Backup original if exists
if os.path.exists(device_properties_path):
os.rename(device_properties_path, backup_path)
# Ensure RDK_PROFILE=TV is present
with open(device_properties_path, "w", encoding="utf-8") as f:
f.write("RDK_PROFILE=TV\n")
yield
# Restore original after test
if os.path.exists(backup_path):
os.remove(device_properties_path)
os.rename(backup_path, device_properties_path)
# Path to device.properties for test, created in a per-test temporary directory
device_properties_path = tmp_path / "device.properties"
# Ensure RDK_PROFILE=TV is present
with open(device_properties_path, "w", encoding="utf-8") as f:
f.write("RDK_PROFILE=TV\n")
# Point the usblogupload binary to this device.properties via environment variable
env_var_name = "DEVICE_PROPERTIES_PATH"
old_env_value = os.environ.get(env_var_name)
os.environ[env_var_name] = str(device_properties_path)
try:
yield
finally:
# Restore previous environment and clean up temporary file
if old_env_value is not None:
os.environ[env_var_name] = old_env_value
else:
os.environ.pop(env_var_name, None)
if device_properties_path.exists():
device_properties_path.unlink()

Copilot uses AI. Check for mistakes.
class TestUSBLogUpload:
def test_usblogupload_missing_log_path(self, tmp_path):
# Simulate missing log path by passing a non-existent mount point
usb_mount = str(tmp_path / "not_a_mount")
result = subprocess.run([USBLOGUPLOAD_BIN, usb_mount], capture_output=True, text=True)
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(result.stdout)
f.write(result.stderr)
assert result.returncode == 2 or result.returncode == 3, "Should fail with USB not mounted or write error"
logs = grep_usblogupload_logs("Failed")
# Accept log file or process output containing 'fail', 'error', or 'not mounted'
output = (result.stdout + result.stderr).lower()
assert (
logs or
"fail" in output or
"error" in output or
"not mounted" in output
), (
f"Should log a failure message. Got stdout: {result.stdout}, stderr: {result.stderr}"
)

def test_usblogupload_archive_creation(self, tmp_path):
# Simulate a valid mount and check for archive creation log
usb_mount = tmp_path / "usb"
usb_mount.mkdir()
result = subprocess.run([USBLOGUPLOAD_BIN, str(usb_mount)], capture_output=True, text=True)
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(result.stdout)
f.write(result.stderr)
# Look for archive or compression log
logs = grep_usblogupload_logs("archive")
assert result.returncode in (0, 3), "Should exit with success or write error code"
# Archive log may or may not appear depending on implementation

def test_usblogupload_mac_address_log(self, tmp_path):
# Simulate a valid mount and check for MAC address log
usb_mount = tmp_path / "usb"
usb_mount.mkdir()
result = subprocess.run([USBLOGUPLOAD_BIN, str(usb_mount)], capture_output=True, text=True)
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(result.stdout)
f.write(result.stderr)
logs = grep_usblogupload_logs(":.*File:")

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grep_usblogupload_logs helper uses re.escape(search) to compile the search string as a literal pattern. However, in test_usblogupload_mac_address_log (line 73), the argument ":.*File:" is intended as a regular expression pattern (using .* as a wildcard), but re.escape will escape the dot and asterisk, causing the function to search for the exact literal string ":.*File:" instead of matching log lines with MAC address and file name. The function signature or usage needs to be consistent: either remove re.escape from the helper to allow regex patterns, or change the test to pass a literal string instead of a regex pattern.

Copilot uses AI. Check for mistakes.

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The grep_usblogupload_logs function applies re.escape() to the search string before compiling it as a regex pattern (line 13). This means that when grep_usblogupload_logs(":.*File:") is called, the metacharacters .* are escaped and treated as literal characters — the function will search for the literal string :.*File: rather than matching MAC address patterns. The search will likely never return any results. Either re.escape() should be removed to allow regex patterns, or the caller should pass a plain string without metacharacters.

Copilot uses AI. Check for mistakes.
# This checks for the log line with MAC address and file name
# (Regex match, may need adjustment based on actual log format)
assert result.returncode in (0, 3), "Should exit with success or write error code"

def test_usblogupload_temp_dir_cleanup(self, tmp_path):
# Simulate a valid mount and check for temp dir cleanup log
usb_mount = tmp_path / "usb"
usb_mount.mkdir()
result = subprocess.run([USBLOGUPLOAD_BIN, str(usb_mount)], capture_output=True, text=True)
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(result.stdout)
f.write(result.stderr)
logs = grep_usblogupload_logs("cleanup")
# This checks for cleanup log line (if implemented)
assert result.returncode in (0, 3), "Should exit with success or write error code"

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a missing blank line between test_usblogupload_temp_dir_cleanup (ending at line 98) and test_usblogupload_success (starting at line 99). All other test methods in this file have a blank line separator. This is a minor style inconsistency, but it reduces readability.

Suggested change
assert result.returncode in (0, 3), "Should exit with success or write error code"
assert result.returncode in (0, 3), "Should exit with success or write error code"

Copilot uses AI. Check for mistakes.
def test_usblogupload_success(self, tmp_path):
usb_mount = "/tmp"
# Run the binary and capture output
result = subprocess.run([USBLOGUPLOAD_BIN, usb_mount], capture_output=True, text=True)
Comment on lines +100 to +102

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test hardcodes /tmp as the USB mount point. Using /tmp in a test environment is fragile and can interact with other tests or system state. The tmp_path fixture is already available and provides an isolated temporary directory — usb_mount = str(tmp_path / "usb") (with mkdir()) should be used instead, consistent with the other tests in the class.

Suggested change
usb_mount = "/tmp"
# Run the binary and capture output
result = subprocess.run([USBLOGUPLOAD_BIN, usb_mount], capture_output=True, text=True)
usb_mount = tmp_path / "usb"
usb_mount.mkdir()
# Run the binary and capture output
result = subprocess.run([USBLOGUPLOAD_BIN, str(usb_mount)], capture_output=True, text=True)

Copilot uses AI. Check for mistakes.
# Write output to log file
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(result.stdout)
f.write(result.stderr)
assert result.returncode == 0, "Should exit with success code 0"
Comment on lines +99 to +107

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test_usblogupload_success test hardcodes /tmp as the USB mount point instead of using tmp_path like all the other tests in the same class. Using /tmp as a real mount path is semantically incorrect (it is not a USB mount), and it may interact with actual system state across test runs. The parameter tmp_path is already declared in the function signature but is never used — the test should use tmp_path consistently with the rest of the test suite.

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +107

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test_usblogupload_success method declares tmp_path as a parameter (which causes pytest to inject a per-test temporary directory) but then ignores it and hardcodes usb_mount = "/tmp". This is inconsistent: either use tmp_path for the USB mount (as done in test_usblogupload_archive_creation, test_usblogupload_mac_address_log, and test_usblogupload_temp_dir_cleanup), or remove the unused tmp_path parameter.

Copilot uses AI. Check for mistakes.
# Check for expected log
logs = grep_usblogupload_logs("COMPLETED USB LOG UPLOAD")
assert logs, "Should log completion message"
Comment on lines +99 to +110

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test_usblogupload_success test uses the hardcoded path /tmp as the USB mount point. While /tmp always exists on the test system, it is not a real USB mount point and likely does not have the structure expected by the binary (e.g., the MAC address file, available disk space, etc.). This means the test exercises a best-case path that doesn't reflect real USB mount behavior. Additionally, there's a missing blank line before test_usblogupload_success (PEP 8 requires two blank lines between method definitions in a class).

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +110

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In test_usblogupload_success, the tmp_path fixture parameter is declared but unused. Instead, the hardcoded /tmp path is used as the USB mount point. This is inconsistent with other test methods that use tmp_path to create isolated mount directories. Using /tmp directly can cause test pollution or conflicts between test runs.

Copilot uses AI. Check for mistakes.

def test_usblogupload_invalid_usage(self):
result = subprocess.run([USBLOGUPLOAD_BIN], capture_output=True)
assert result.returncode == 4, "Should exit with invalid usage code 4"
logs = grep_usblogupload_logs("Failed to initialize logging system")
# This log may or may not appear depending on implementation
Comment on lines +115 to +116

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logs variable on line 115 is assigned but never asserted or used. The comment on line 116 acknowledges this, but it leaves dead code that contributes nothing to the test. Either assert on logs or remove lines 115-116 entirely.

Suggested change
logs = grep_usblogupload_logs("Failed to initialize logging system")
# This log may or may not appear depending on implementation

Copilot uses AI. Check for mistakes.

def test_usblogupload_usb_not_mounted(self):
result = subprocess.run([USBLOGUPLOAD_BIN, "/tmp/notmounted"], capture_output=True)
assert result.returncode == 2, "Should exit with USB not mounted code 2"
logs = grep_usblogupload_logs("Failed to validate USB mount point")
Comment on lines +112 to +121

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, test_usblogupload_invalid_usage and test_usblogupload_usb_not_mounted each assign a logs variable using grep_usblogupload_logs() to check for specific log output ("Failed to initialize logging system" and "Failed to validate USB mount point" respectively), but then never assert on those variables. The test descriptions imply that these log messages are important for verifying correct behavior, but without an assertion they are not actually checked.

Copilot generated this review using guidance from repository custom instructions.
# This log may or may not appear depending on implementation
Comment on lines +71 to +122

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several tests in test_usb_logupload.py query the log using grep_usblogupload_logs (lines 71, 83, 96, 115, 121) but never assert on the returned logs value. The logs variable is assigned but the test does not check whether it is non-empty or contains expected content. This means the test does not actually validate the expected log output, making it a silent no-op for those checks. Each of these lines should either be removed (if the check is genuinely not needed) or followed by an assert statement.

Copilot uses AI. Check for mistakes.
Comment on lines +115 to +122

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as test_usblogupload_invalid_usage: logs is assigned on line 121 but never used or asserted. Either add an assertion or remove the unused lines.

Suggested change
logs = grep_usblogupload_logs("Failed to initialize logging system")
# This log may or may not appear depending on implementation
def test_usblogupload_usb_not_mounted(self):
result = subprocess.run([USBLOGUPLOAD_BIN, "/tmp/notmounted"], capture_output=True)
assert result.returncode == 2, "Should exit with USB not mounted code 2"
logs = grep_usblogupload_logs("Failed to validate USB mount point")
# This log may or may not appear depending on implementation
def test_usblogupload_usb_not_mounted(self):
result = subprocess.run([USBLOGUPLOAD_BIN, "/tmp/notmounted"], capture_output=True)
assert result.returncode == 2, "Should exit with USB not mounted code 2"

Copilot uses AI. Check for mistakes.
31 changes: 22 additions & 9 deletions test/run_uploadstblogs_l2.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ mkdir -p "$RESULT_DIR"
echo "LOG.RDK.DEFAULT" >> /etc/debug.ini

# Ensure properties files exist

if grep -q '^RDK_PROFILE=' /etc/device.properties; then
sed -i 's/^RDK_PROFILE=.*/RDK_PROFILE=TV/' /etc/device.properties
else
echo 'RDK_PROFILE=TV' >> /etc/device.properties
fi
Comment on lines +35 to +39

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new RDK_PROFILE block (lines 35–39) runs grep and sed/echo against /etc/device.properties before the file existence check at lines 50–52. If /etc/device.properties does not exist when the script is run, the grep -q on line 35 will fail (exit code 2) and the echo >> /etc/device.properties on line 38 will attempt to append to a non-existent file, resulting in an error and leaving the file not created. The fix is to move the RDK_PROFILE block to after the file existence check (after line 52).

Copilot uses AI. Check for mistakes.

if ! grep -q "LOG_PATH=/opt/logs/" /etc/include.properties; then
echo "LOG_PATH=/opt/logs/" >> /etc/include.properties
fi
Expand All @@ -52,6 +59,8 @@ if ! grep -q "BUILD_TYPE=" /etc/device.properties; then
echo "BUILD_TYPE=dev" >> /etc/device.properties
fi

echo "AA:BB:CC:dd:EE:FF" >> /tmp/.estb_mac

Copilot AI Mar 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The echo "AA:BB:CC:dd:EE:FF" >> /tmp/.estb_mac line was moved from after the "Error Handling Tests" block to near the top of the script (before any tests run). This is actually the correct placement since the MAC address is needed by all tests, including the newly added USB log upload tests that now run first. However, the >> (append) operator means that every execution of the script will keep adding more MAC address entries to /tmp/.estb_mac. If the file already exists from a previous run, it will contain duplicate entries. This should use > (overwrite) instead of >> (append) to ensure a clean, single-entry file each time.

Suggested change
echo "AA:BB:CC:dd:EE:FF" >> /tmp/.estb_mac
echo "AA:BB:CC:dd:EE:FF" > /tmp/.estb_mac

Copilot uses AI. Check for mistakes.

cd /usr/common_utilities
sed -i '/file_upload\.sslverify/s/= 1;/= 0;/' uploadutils/mtls_upload.c
sed -i 's/\(ret_code = setCommonCurlOpt(curl, s3url, NULL, \)true\()\)/\1false\2/g' uploadutils/uploadUtil.c
Expand All @@ -72,44 +81,48 @@ echo "====================================="
# Run test suites

echo ""
echo "1. Running UploadLogsNow Tests..."
echo "1. Running usbLogupload Tests..."
pytest -v --json-report --json-report-summary \
--json-report-file $RESULT_DIR/uploadLogsNow.json test/functional-tests/tests/test4.py
--json-report-file $RESULT_DIR/usb_logupload.json test/functional-tests/tests/test_usb_logupload.py

echo ""
echo "2. Running Error Handling Tests..."
echo "2. Running UploadLogsNow Tests..."
pytest -v --json-report --json-report-summary \
--json-report-file $RESULT_DIR/uploadLogsNow.json test/functional-tests/tests/test_uploadLogsNow.py

echo ""
echo "3. Running Error Handling Tests..."
pytest -v --json-report --json-report-summary \
--json-report-file $RESULT_DIR/error_handling.json test/functional-tests/tests/test_uploadstblogs_error_handling.py

echo "AA:BB:CC:dd:EE:FF" >> /tmp/.estb_mac

mkdir -p /opt/logs
mkdir -p /opt/logs/PreviousLogs

echo ""
echo "3. Running Normal Upload Tests..."
echo "4. Running Normal Upload Tests..."
mkdir -p /opt/logs/PreviousLogs
pytest -v --json-report --json-report-summary \
--json-report-file $RESULT_DIR/upload_normal.json test/functional-tests/tests/test_uploadstblogs_normal_upload.py


echo ""
echo "4. Running Retry Logic Tests..."
echo "5. Running Retry Logic Tests..."
pytest -v --json-report --json-report-summary \
--json-report-file $RESULT_DIR/retry_logic.json test/functional-tests/tests/test_uploadstblogs_retry_logic.py

echo ""
echo "5. Running Security Tests..."
echo "6. Running Security Tests..."
pytest -v --json-report --json-report-summary \
--json-report-file $RESULT_DIR/security.json test/functional-tests/tests/test_uploadstblogs_security.py

echo ""
echo "6. Running Resource Management Tests..."
echo "7. Running Resource Management Tests..."
pytest -v --json-report --json-report-summary \
--json-report-file $RESULT_DIR/resource_management.json test/functional-tests/tests/test_uploadstblogs_resource_management.py

echo ""
echo "7. Running Upload Strategy Tests..."
echo "8. Running Upload Strategy Tests..."
pytest -v --json-report --json-report-summary \
--json-report-file $RESULT_DIR/upload_strategies.json test/functional-tests/tests/test_uploadstblogs_upload_strategies.py

Expand Down