async def test_browse_media_search(
    device_source_mock: DmsDeviceSource, dms_device_mock: Mock
) -> None:
    """Test async_browse_media with a search query."""
    query = 'dc:title contains "FooBar"'
    object_details = (("111", "FooBar baz"), ("432", "Not FooBar"), ("99", "FooBar"))
    dms_device_mock.async_search_directory.return_value = DmsDevice.BrowseResult(
        [
            didl_lite.DidlObject(id=ob_id, restricted="false", title=title)
            for ob_id, title in object_details
        ],
        3,
        3,
        0,
    )
    # Test that descriptors are skipped
    dms_device_mock.async_search_directory.return_value.result.insert(
        1, didl_lite.Descriptor("id", "name_space")
    )

    result = await device_source_mock.async_browse_media(f"?{query}")
    assert result.identifier == f"{MOCK_SOURCE_ID}/?{query}"
    assert result.title == "Search results"
    assert result.children

    for obj, child in zip(object_details, result.children):
        assert isinstance(child, BrowseMediaSource)
        assert child.identifier == f"{MOCK_SOURCE_ID}/:{obj[0]}"
        assert child.title == obj[1]
        assert not child.children
async def test_browse_media_root(
    device_source_mock: DmsDeviceSource, dms_device_mock: Mock
) -> None:
    """Test async_browse_media with no identifier will browse the root of the device."""
    dms_device_mock.async_browse_metadata.return_value = didl_lite.DidlObject(
        id="0", restricted="false", title="root"
    )
    dms_device_mock.async_browse_direct_children.return_value = DmsDevice.BrowseResult(
        [], 0, 0, 0
    )
    # No identifier (first opened in media browser)
    result = await device_source_mock.async_browse_media(None)
    assert result.identifier == f"{MOCK_SOURCE_ID}/:0"
    assert result.title == MOCK_DEVICE_NAME
    dms_device_mock.async_browse_metadata.assert_awaited_once_with(
        "0", metadata_filter=ANY
    )
    dms_device_mock.async_browse_direct_children.assert_awaited_once_with(
        "0", metadata_filter=ANY, sort_criteria=ANY
    )

    dms_device_mock.async_browse_metadata.reset_mock()
    dms_device_mock.async_browse_direct_children.reset_mock()
    # Empty string identifier
    result = await device_source_mock.async_browse_media("")
    assert result.identifier == f"{MOCK_SOURCE_ID}/:0"
    assert result.title == MOCK_DEVICE_NAME
    dms_device_mock.async_browse_metadata.assert_awaited_once_with(
        "0", metadata_filter=ANY
    )
    dms_device_mock.async_browse_direct_children.assert_awaited_once_with(
        "0", metadata_filter=ANY, sort_criteria=ANY
    )
async def test_can_play(
    device_source_mock: DmsDeviceSource, dms_device_mock: Mock
) -> None:
    """Test determination of playability for items."""
    protocol_infos = [
        # No protocol info for resource
        ("", True),
        # Protocol info is poorly formatted but can play
        ("http-get", True),
        # Protocol info is poorly formatted and can't play
        ("internal", False),
        # Protocol is HTTP
        ("http-get:*:audio/mpeg", True),
        # Protocol is RTSP
        ("rtsp-rtp-udp:*:MPA:", True),
        # Protocol is something else
        ("internal:*:audio/mpeg:", False),
    ]

    search_results: BrowseResultList = []
    # No resources
    search_results.append(didl_lite.DidlObject(id="", restricted="false", title=""))
    search_results.extend(
        didl_lite.MusicTrack(
            id="",
            restricted="false",
            title="",
            res=[didl_lite.Resource(uri="", protocol_info=protocol_info)],
        )
        for protocol_info, _ in protocol_infos
    )

    # Use browse_search to get multiple items at once for least effort
    dms_device_mock.async_search_directory.return_value = DmsDevice.BrowseResult(
        search_results, len(search_results), len(search_results), 0
    )

    result = await device_source_mock.async_browse_media("?query")
    assert result.children
    assert not result.children[0].can_play
    for idx, info_can_play in enumerate(protocol_infos):
        protocol_info, can_play = info_can_play
        assert result.children[idx + 1].can_play is can_play, f"Checked {protocol_info}"