Skip to content

Commit

Permalink
Simplify the implementation of run_tv_casting_test.py for easier ad…
Browse files Browse the repository at this point in the history
…dition of future test sequences that verifies that Linux tv-casting-app continues to work with Linux tv-app. (project-chip#33855)

* Simplify the implementation of `run_tv_casting_test.py` for easier addition of future test sequences that verifies that Linux tv-casting-app continues to work with Linux tv-app.

* Addressed PR comments from @sharadb-amazon.

* Addressed @sharadb-amazon PR comments. Also restructured the code to prevent circular import issues.

* Run restyle.

* Fixed typo.
  • Loading branch information
shaoltan-amazon authored and austina-csa committed Jul 10, 2024
1 parent 6438249 commit c4374a6
Show file tree
Hide file tree
Showing 3 changed files with 462 additions and 520 deletions.
107 changes: 107 additions & 0 deletions scripts/tests/linux/tv_casting_test_sequence_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env -S python3 -B

# Copyright (c) 2024 Project CHIP Authors
#
# 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 enum import Enum
from typing import List, Optional

"""
This file defines the utility classes for creating and managing test sequences to validate the casting experience between
the Linux tv-casting-app and the Linux tv-app. It includes an enumeration for the supported applications (App), a class to
represent individual steps in a test sequence (Step), and a class to represent a complete test sequence (Sequence).
Additionally, it provides helper functions to retrieve specific test sequences or all defined test sequences.
"""


class App(Enum):
"""An enumeration of the supported applications."""

TV_APP = 'tv-app'
TV_CASTING_APP = 'tv-casting-app'


class Step:
"""A class to represent a step in a test sequence for validation.
A `Step` object contains attributes relevant to a test step where each object contains:
- `app` subprocess to parse for `output_msg` or send `input_cmd`
- `timeout_sec` specified the timeout duration for parsing the `output_msg` (optional, defaults to DEFAULT_TIMEOUT_SEC)
- `output_msg` or `input_cmd` (mutually exclusive)
For output message blocks, define the start line, relevant lines, and the last line. If the last line contains trivial closing
characters (e.g., closing brackets, braces, or commas), include the line before it with actual content. For example:
`Step(subprocess_='tv-casting-app', output_msg=['InvokeResponseMessage =', 'exampleData', 'InteractionModelRevision =', '},'])`
For input commands, define the command string with placeholders for variables that need to be updated. For example:
`Step(subprocess_='tv-casting-app', input_cmd='cast request 0\n')`
"""

# The maximum default time to wait while parsing for output string(s).
DEFAULT_TIMEOUT_SEC = 10

def __init__(
self,
app: App,
timeout_sec: Optional[int] = DEFAULT_TIMEOUT_SEC,
output_msg: Optional[List[str]] = None,
input_cmd: Optional[str] = None,
):
# Validate that either `output_msg` or `input_cmd` is provided, but not both.
if output_msg is not None and input_cmd is not None:
raise ValueError(
'Step cannot contain both `output_msg` and `input_cmd`. Either `output_msg` or `input_cmd` should be provided.')
elif output_msg is None and input_cmd is None:
raise ValueError('Step must contain either `output_msg` or `input_cmd`. Both are `None`.')

# Define either `App.TV_APP` or `App.TV_CASTING_APP` on which we need to parse for `output_msg` or send `input_cmd`.
self.app = app

# Define the maximum time in seconds for timeout while parsing for the `output_msg`. If not provided, then we use the DEFAULT_TIMEOUT_SEC.
self.timeout_sec = timeout_sec

# Define the `output_msg` that we need to parse for in a list format.
self.output_msg = output_msg

# Define the `input_cmd` that we need to send to either the `App.TV_APP` or `App.TV_CASTING_APP`.
self.input_cmd = input_cmd


class Sequence:
"""A class representing a sequence of steps for testing the casting experience between the Linux tv-casting-app and the tv-app.
A Sequence object needs to be defined with an appropriate test sequence `name` along with its list of `Step` objects that will
be used for validating the casting experience.
"""

def __init__(self, name: str, steps: List[Step]):
self.name = name
self.steps = steps

@staticmethod
def get_test_sequence_by_name(test_sequences: List['Sequence'], test_sequence_name: str) -> Optional['Sequence']:
"""Retrieve a test sequence from a list of sequences by its name."""

for sequence in test_sequences:
if sequence.name == test_sequence_name:
return sequence
return None

@staticmethod
def get_test_sequences() -> List['Sequence']:
"""Retrieve all the test sequences to validate the casting experience between the Linux tv-casting-app and the Linux tv-app."""

from linux.tv_casting_test_sequences import test_sequences

return test_sequences
152 changes: 152 additions & 0 deletions scripts/tests/linux/tv_casting_test_sequences.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#!/usr/bin/env -S python3 -B

# Copyright (c) 2024 Project CHIP Authors
#
# 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 linux.tv_casting_test_sequence_utils import App, Sequence, Step

"""
In this file, we define the test sequences with the relevant steps that will be used in the `scripts/tests/run_tv_casting_test.py`
for validating the casting experience between the Linux tv-casting-app and the Linux tv-app.
At the beginning of each test sequence we need to indicate the start up of the tv-app using the `START_APP` string as the `input_cmd`
followed by the same for the tv-casting-app. On the other hand, at the end of each test sequence we need to ensure that each app will
be stopped by providing the `STOP_APP` string as the `input_cmd`. As noted in the example below of `example_test_sequence`, the first
four steps pertain to starting the apps while the last two are for signaling stopping the apps.
Note: `START_APP` and `STOP_APP` are reserved for signaling the starting and stopping of apps.
Example:
test_sequences = [
Sequence(
name='example_test_sequence',
step=[
# Signal to start the tv-app.
Step(app=App.TV_APP, input_cmd=START_APP),
# Validate that the tv-app is up and running.
Step(app=App.TV_APP, timeout_sec=APP_MAX_START_WAIT_SEC, output_msg=['Started commissioner']),
# Signal to start the tv-casting-app.
Step(app=App.TV_CASTING_APP, input_cmd=START_APP),
# Validate that the server is properly initialized in the tv-casting-app output.
Step(app=App.TV_CASTING_APP, timeout_sec=APP_MAX_START_WAIT_SEC, output_msg=['Server initialization complete']),
# Additional steps for testing the casting experience.
# Signal to stop the tv-casting-app as we finished validation.
Step(app=App.TV_CASTING_APP, input_cmd=STOP_APP),
# Signal to stop the tv-app as we finished validation.
Step(app=App.TV_APP, input_cmd=STOP_APP)
]
)
]
"""

# Signal to start the app.
START_APP = 'START'

# Signal to stop the app.
STOP_APP = 'STOP'

# The maximum amount of time to wait for the Linux tv-app or Linux tv-casting-app to start before timeout.
APP_MAX_START_WAIT_SEC = 2

# Values that identify the Linux tv-app and are noted in the 'Device Configuration' in the Linux tv-app output
# as well as under the 'Discovered Commissioner' details in the Linux tv-casting-app output.
VENDOR_ID = 0xFFF1 # Spec 7.20.2.1 MEI code: test vendor IDs are 0xFFF1 to 0xFFF4
PRODUCT_ID = 0x8001 # Test product id
DEVICE_TYPE_CASTING_VIDEO_PLAYER = 0x23 # Device type library 10.3: Casting Video Player

TEST_TV_CASTING_APP_DEVICE_NAME = 'Test TV casting app' # Test device name for identifying the tv-casting-app

# Values to verify the subscription state against from the `ReportDataMessage` in the Linux tv-casting-app output.
CLUSTER_MEDIA_PLAYBACK = '0x506' # Application Cluster Spec 6.10.3 Cluster ID: Media Playback
ATTRIBUTE_CURRENT_PLAYBACK_STATE = '0x0000_0000' # Application Cluster Spec 6.10.6 Attribute ID: Current State of Playback

test_sequences = [
Sequence(
name='commissionee_generated_passcode_test',
steps=[
# Signal to start the tv-app.
Step(app=App.TV_APP, input_cmd=START_APP),

# Validate that the tv-app is up and running.
Step(app=App.TV_APP, timeout_sec=APP_MAX_START_WAIT_SEC, output_msg=['Started commissioner']),

# Signal to start the tv-casting-app.
Step(app=App.TV_CASTING_APP, input_cmd=START_APP),

# Validate that the server is properly initialized in the tv-casting-app output.
Step(app=App.TV_CASTING_APP, timeout_sec=APP_MAX_START_WAIT_SEC, output_msg=['Server initialization complete']),

# Validate that there is a valid discovered commissioner with {VENDOR_ID}, {PRODUCT_ID}, and {DEVICE_TYPE_CASTING_VIDEO_PLAYER} in the tv-casting-app output.
Step(app=App.TV_CASTING_APP, output_msg=['Discovered Commissioner #0', f'Vendor ID: {VENDOR_ID}', f'Product ID: {PRODUCT_ID}',
f'Device Type: {DEVICE_TYPE_CASTING_VIDEO_PLAYER}', 'Supports Commissioner Generated Passcode: true']),

# Validate that we are ready to send `cast request` command to the tv-casting-app subprocess.
Step(app=App.TV_CASTING_APP, output_msg=['Example: cast request 0']),

# Send `cast request {valid_discovered_commissioner_number}\n` command to the tv-casting-app subprocess.
Step(app=App.TV_CASTING_APP, input_cmd='cast request 0\n'),

# Validate that the `Identification Declaration` message block in the tv-casting-app output has the expected values for `device Name`, `vendor id`, and `product id`.
Step(app=App.TV_CASTING_APP, output_msg=['Identification Declaration Start', f'device Name: {TEST_TV_CASTING_APP_DEVICE_NAME}',
f'vendor id: {VENDOR_ID}', f'product id: {PRODUCT_ID}', 'Identification Declaration End']),

# Validate that the `Identification Declaration` message block in the tv-app output has the expected values for `device Name`, `vendor id`, and `product id`.
Step(app=App.TV_APP, output_msg=['Identification Declaration Start', f'device Name: {TEST_TV_CASTING_APP_DEVICE_NAME}',
f'vendor id: {VENDOR_ID}', f'product id: {PRODUCT_ID}', 'Identification Declaration End']),

# Validate that we received the cast request from the tv-casting-app on the tv-app output.
Step(app=App.TV_APP,
output_msg=['PROMPT USER: Test TV casting app is requesting permission to cast to this TV, approve?']),

# Validate that we received the instructions on the tv-app output for sending the `controller ux ok` command.
Step(app=App.TV_APP, output_msg=['Via Shell Enter: controller ux ok|cancel']),

# Send `controller ux ok` command to the tv-app subprocess.
Step(app=App.TV_APP, input_cmd='controller ux ok\n'),

# Validate that pairing succeeded between the tv-casting-app and the tv-app.
Step(app=App.TV_APP, output_msg=['Secure Pairing Success']),

# Validate that commissioning succeeded in the tv-casting-app output.
Step(app=App.TV_CASTING_APP, output_msg=['Commissioning completed successfully']),

# Validate that commissioning succeeded in the tv-app output.
Step(app=App.TV_APP, output_msg=['------PROMPT USER: commissioning success']),

# Validate the subscription state by looking at the `Cluster` and `Attribute` values in the `ReportDataMessage` block in the tv-casting-app output.
Step(app=App.TV_CASTING_APP, output_msg=[
'ReportDataMessage =', f'Cluster = {CLUSTER_MEDIA_PLAYBACK}', f'Attribute = {ATTRIBUTE_CURRENT_PLAYBACK_STATE}', 'InteractionModelRevision =', '}']),

# Validate the LaunchURL in the tv-app output.
Step(app=App.TV_APP,
output_msg=['ContentLauncherManager::HandleLaunchUrl TEST CASE ContentURL=https://www.test.com/videoid DisplayString=Test video']),

# Validate the LaunchURL in the tv-casting-app output.
Step(app=App.TV_CASTING_APP, output_msg=['InvokeResponseMessage =',
'exampleData', 'InteractionModelRevision =', '},']),

# Signal to stop the tv-casting-app as we finished validation.
Step(app=App.TV_CASTING_APP, input_cmd=STOP_APP),

# Signal to stop the tv-app as we finished validation.
Step(app=App.TV_APP, input_cmd=STOP_APP)
]
)
]
Loading

0 comments on commit c4374a6

Please sign in to comment.