blob: 86a5c8d1858acdfb32506301d6ced13d37fc89e0 [file] [log] [blame]
#
# Copyright (c) 2024 Project CHIP Authors
# All rights reserved.
#
# 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.
#
# === BEGIN CI TEST ARGUMENTS ===
# test-runner-runs:
# run1:
# app: ${ALL_CLUSTERS_APP}
# app-args: >
# --discriminator 1234
# --KVS kvs1
# --trace-to json:${TRACE_APP}.json
# --end_user_support_log /tmp/eusl.txt
# --crash_log /tmp/cl.txt
# --network_diagnostics_log /tmp/ndl.txt
# script-args: >
# --storage-path admin_storage.json
# --commissioning-method on-network
# --discriminator 1234
# --passcode 20202021
# --trace-to json:${TRACE_TEST_JSON}.json
# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto
# factory-reset: true
# quiet: true
# === END CI TEST ARGUMENTS ===
import asyncio
import os
import random
from mobly import asserts
import matter.clusters as Clusters
from matter.bdx import BdxProtocol, BdxTransfer
from matter.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
class TestBdxTransfer(MatterBaseTest):
_intents = [
(Clusters.DiagnosticLogs.Enums.IntentEnum.kEndUserSupport, "/tmp/eusl.txt", 9240), # BDX
(Clusters.DiagnosticLogs.Enums.IntentEnum.kCrashLogs, "/tmp/cl.txt", 123), # Inline
(Clusters.DiagnosticLogs.Enums.IntentEnum.kNetworkDiag, "/tmp/ndl.txt", 8765), # BDX
]
@staticmethod
def _expect_bdx(filesize: int) -> bool:
return filesize > 1024
def desc_bdx_transfer(self) -> str:
return "Test a BDX transfer with the diagnostic logs cluster"
def steps_bdx_transfer(self) -> list[TestStep]:
steps = []
base = 0
for _, _, filesize in self._intents:
steps.extend([
TestStep(base + 1, "Generate the diagnostic log file."),
TestStep(base + 2, "Set up the system to receive a BDX transfer."),
TestStep(base + 3, "Send the command to request logs."),
TestStep(base + 4, "Wait for the command's response or a BDX transfer."),
])
if TestBdxTransfer._expect_bdx(filesize):
steps.extend([
TestStep(base + 5, "Verify the init message's parameters."),
TestStep(base + 6, "Accept the transfer and obtain the data."),
TestStep(base + 7, "Verify the obtained data."),
TestStep(base + 8, "Check the command's response."),
])
else:
steps.extend([
TestStep(base + 5, "Verify inline transfer."),
TestStep(base + 6, "Obtain the data."),
TestStep(base + 7, "Check the command's response."),
TestStep(base + 8, "Verify the obtained data."),
])
base += 8
return steps
@async_test_body
async def test_bdx_transfer(self):
for i, (intent, filename, filesize) in enumerate(self._intents):
base_step = i * 8
self.step(base_step + 1)
expected_data = random.randbytes(filesize)
with open(filename, "wb") as diagnostic_file:
diagnostic_file.write(expected_data)
self.step(base_step + 2)
bdx_future: asyncio.futures.Future = self.default_controller.TestOnlyPrepareToReceiveBdxData()
self.step(base_step + 3)
file_designator = "filename"
command = Clusters.DiagnosticLogs.Commands.RetrieveLogsRequest(
intent=intent,
requestedProtocol=Clusters.DiagnosticLogs.Enums.TransferProtocolEnum.kBdx,
transferFileDesignator=file_designator
)
command_send_future = asyncio.create_task(self.default_controller.SendCommand(
self.dut_node_id,
0,
command,
responseType=Clusters.DiagnosticLogs.Commands.RetrieveLogsResponse),
)
self.step(base_step + 4)
done, pending = await asyncio.wait(
[command_send_future,
bdx_future],
return_when=asyncio.FIRST_COMPLETED,
)
self.step(base_step + 5)
if TestBdxTransfer._expect_bdx(filesize): # Sent via BDX
asserts.assert_true(bdx_future in done, "BDX transfer didn't start")
bdx_transfer: BdxTransfer.BdxTransfer = bdx_future.result()
asserts.assert_equal(bdx_transfer.init_message.TransferControlFlags,
BdxProtocol.SENDER_DRIVE, "Invalid transfer control flags")
asserts.assert_equal(bdx_transfer.init_message.MaxBlockSize, 1024, "Invalid max block size")
asserts.assert_equal(bdx_transfer.init_message.StartOffset, 0, "Invalid start offset")
asserts.assert_equal(bdx_transfer.init_message.FileDesignator,
bytes(file_designator, encoding='utf8'),
"Invalid file designator")
self.step(base_step + 6)
data = await bdx_transfer.accept_and_receive_data()
self.step(base_step + 7)
asserts.assert_equal(data, expected_data, "Transferred data doesn't match")
self.step(base_step + 8)
if command_send_future in done:
command_response = command_send_future.result()
else:
command_response = await command_send_future
asserts.assert_equal(command_response.status,
Clusters.DiagnosticLogs.Enums.StatusEnum.kSuccess,
"Invalid command response")
# Without sleep the next BDX transfer will fail.
# See https://github.com/project-chip/connectedhomeip/blob/master/src/controller/python/chip/bdx/bdx-transfer.cpp#L108
# where the session is ended after the final acknowledgement is sent, but before receipt of the StandaloneAck.
await asyncio.sleep(0.1)
else: # Sent inline
asserts.assert_true(bdx_future not in done, "BDX transfer was not expected")
self.step(base_step + 6)
command_response = await command_send_future
self.step(base_step + 7)
# Assert that the command response status is either Success or Exhausted
asserts.assert_true(
command_response.status in (
Clusters.DiagnosticLogs.Enums.StatusEnum.kSuccess,
Clusters.DiagnosticLogs.Enums.StatusEnum.kExhausted
),
f"Command failed with status {command_response.status}"
)
self.step(base_step + 8)
# Assert that inline log content is present and matches expected test data
asserts.assert_is_not_none(command_response.logContent, "No BDX and no inline logContent")
asserts.assert_equal(
command_response.logContent,
expected_data,
f"Inline response content for {intent.name} does not match expected data"
)
# Cancel the BDX receive transaction since no BDX transfer occurred (e.g., response was inline).
bdx_future.cancel()
# Clean up the temporary log file used in this iteration.
try:
os.remove(filename)
except FileNotFoundError:
pass
if __name__ == "__main__":
default_matter_test_main()