# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
#
# 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 threading

import pytest

import streamlit as st
from streamlit.elements import exception
from streamlit.proto.Exception_pb2 import Exception as ExceptionProto
from streamlit.runtime.caching.cache_errors import (
    UnhashableParamError,
    UnserializableReturnValueError,
)
from tests import testutil
from tests.delta_generator_test_case import DeltaGeneratorTestCase


class CacheErrorsTest(DeltaGeneratorTestCase):
    """Make sure user-visible error messages look correct.

    These errors are a little annoying to test, but they're important! So we
    are testing them word-for-word as much as possible. Even though this
    *feels* like an antipattern, it isn't: we're making sure the codepaths
    that pull useful debug info from the code are working.
    """

    maxDiff = None

    def test_unhashable_type(self):
        @st.cache_data
        def unhashable_type_func(lock: threading.Lock):
            return str(lock)

        with pytest.raises(UnhashableParamError) as cm:
            unhashable_type_func(threading.Lock())

        ep = ExceptionProto()
        exception.marshall(ep, cm.value)

        assert ep.type == "UnhashableParamError"

        expected_message = """
Cannot hash argument 'lock' (of type `_thread.lock`) in 'unhashable_type_func'.

To address this, you can tell Streamlit not to hash this argument by adding a
leading underscore to the argument's name in the function signature:

```
@st.cache_data
def unhashable_type_func(_lock, ...):
    ...
```
                    """

        assert testutil.normalize_md(expected_message) == testutil.normalize_md(
            ep.message
        )
        assert ep.message_is_markdown
        assert not ep.is_warning

    def test_unserializable_return_value_error(self):
        @st.cache_data
        def unserializable_return_value_func():
            return threading.Lock()

        with pytest.raises(UnserializableReturnValueError) as cm:
            unserializable_return_value_func()

        ep = ExceptionProto()
        exception.marshall(ep, cm.value)

        assert ep.type == "UnserializableReturnValueError"

        assert "Cannot serialize the return value" in ep.message
        assert ep.message_is_markdown
        assert not ep.is_warning
