#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
# Name:         test_player.py
# Purpose:      Pymuco Unit Tests
#
# Authors:      German Margon
#
# Copyright:    Copyright © 2022-2023 German Margon. All rights reserved.
# License:      BSD 3-Clause License, see LICENSE
# ------------------------------------------------------------------------------
import unittest
from unittest.mock import Mock

from pymuco import (
    Player,
    AudioConverter,
    MusicComputationNotation,
    NoteDuration,
    ScientificPitchNotation
)


class TestPlayer(unittest.TestCase):
    """
    Test the functionality of the Player class.

    This test suite contains test cases that verify the correct behavior of the
    methods of the Player class.

    Methods
    -------
    test_play_with_valid_sample()
        Test the play function with a valid music notation sample.
    test_play_with_invalid_sample()
        Test the play function with an invalid audio sample.

    Notes
    -----
    These test cases ensure that the play function of the Player class
    correctly plays an audio sample generated from a valid music notation and
    raises an appropriate error when an invalid sample is provided.

    Returns
    -------
    None
    """

    def test_play_with_valid_sample(self):
        """
        Test the play function with a valid music notation sample.

        Parameters
        ----------
        None

        Returns
        -------
        None

        Raises
        ------
        AssertionError
            If the audio sample is empty.

        Notes
        -----
        This test case verifies that the play function of the Player class can
        play an audio sample generated by the `convert_to_audio` method of the
        `AudioConverter` class using a valid `MusicComputationNotation` object.

        The test creates a `MusicComputationNotation` object, adds a note with
        a whole note duration to it, and generates an audio sample using an
        `AudioConverter` object. A mock object is used to simulate a valid file
        path for the audio sample. The validity of the file path is checked by
        calling the `_validate_file_path` method of the `AudioConverter`
        object, which should return None.

        The test then checks whether the `_validate_file_path` method has been
        called exactly once. If the validation is successful, the test proceeds
        to play the generated audio sample using the `play` function of the
        `Player` class.

        Finally, the test asserts that the length of the sample list of the
        `AudioConverter` object is greater than 0, which ensures that the audio
        sample has been successfully generated and played.
        """
        notation = MusicComputationNotation()
        C4 = ScientificPitchNotation("C", 4)
        whole_note = NoteDuration("WHOLE_NOTE")
        notation.add_block(C4, whole_note)
        c_sample = AudioConverter()
        c_sample._validate_file_path = Mock(return_value=None)
        c_sample.convert_to_audio(notation)

        c_sample._validate_file_path.assert_called_once()

        player = Player()
        player.play(c_sample)
        assert len(c_sample._sample_list) > 0, "Audio sample is empty"

    def test_play_with_invalid_sample(self):
        """
        Test the play function with an invalid audio sample.

        This test case verifies that the play function of the Player class
        raises a TypeError when an invalid audio sample is passed as an
        argument.

        The test creates a Player object and attempts to play a string object
        that does not represent a valid audio sample. The test uses the
        assertRaises context manager to verify that a TypeError is raised when
        the play function is called with an invalid sample.

        This test ensures that the play function of the Player class correctly
        handles invalid input and raises the appropriate error, helping to
        prevent unexpected behavior or crashes during execution.

        Returns
        -------
        None

        Raises
        ------
        AssertionError
            If the play function does not raise a TypeError when called with an
            invalid sample.
        """
        player = Player()
        with self.assertRaises(TypeError):
            player.play("invalid_sample")
