#!/usr/bin/python
#
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import mock
import unittest

import common

from autotest_lib.database import database_connection
from autotest_lib.frontend import setup_django_environment
from autotest_lib.frontend.afe import readonly_connection
from autotest_lib.server import utils as server_utils
from autotest_lib.scheduler import scheduler_lib
from django.db import utils as django_utils


class ConnectionManagerTests(unittest.TestCase):
    """Connection manager unittests."""

    def setUp(self):
        self.connection_manager = None
        readonly_connection.set_globally_disabled = mock.MagicMock()
        setup_django_environment.enable_autocommit = mock.MagicMock()
        server_utils.Singleton._instances = {}


    def tearDown(self):
        readonly_connection.set_globally_disabled.reset_mock()
        setup_django_environment.enable_autocommit.reset_mock()


    def testConnectionDisconnect(self):
        """Test connection and disconnecting from the database."""
        # Test that the connection manager only opens a connection once.
        connection_manager = scheduler_lib.ConnectionManager()
        connection_manager.open_connection = mock.MagicMock()
        connection = connection_manager.get_connection()
        connection_manager.open_connection.assert_called_once_with()
        connection_manager.open_connection.reset_mock()
        connection = connection_manager.get_connection()
        self.assertTrue(
                connection_manager.open_connection.call_count == 0)
        connection_manager.open_connection.reset_mock()

        # Test that del on the connection manager closes the connection
        connection_manager.disconnect = mock.MagicMock()
        connection_manager.__del__()
        connection_manager.disconnect.assert_called_once_with()


    def testConnectionReconnect(self):
        """Test that retries don't destroy the connection."""
        database_connection._DjangoBackend.execute = mock.MagicMock()
        database_connection._DjangoBackend.execute.side_effect = (
                django_utils.DatabaseError('Database Error'))
        connection_manager = scheduler_lib.ConnectionManager()
        connection = connection_manager.get_connection()
        self.assertRaises(django_utils.DatabaseError,
                          connection.execute, *('', None, True))
        self.assertTrue(
                database_connection._DjangoBackend.execute.call_count == 2)
        database_connection._DjangoBackend.execute.reset_mock()
        self.assertTrue(connection_manager.db_connection ==
                        connection_manager.get_connection())


    def testConnectionManagerSingleton(self):
        """Test that the singleton works as expected."""
        # Confirm that instantiating the class applies global db settings.
        connection_manager = scheduler_lib.ConnectionManager()
        readonly_connection.set_globally_disabled.assert_called_once_with(True)
        setup_django_environment.enable_autocommit.assert_called_once_with()

        readonly_connection.set_globally_disabled.reset_mock()
        setup_django_environment.enable_autocommit.reset_mock()

        # Confirm that instantiating another connection manager doesn't change
        # the database settings, and in fact, returns the original manager.
        connection_manager_2 = scheduler_lib.ConnectionManager()
        self.assertTrue(connection_manager == connection_manager_2)
        self.assertTrue(
                readonly_connection.set_globally_disabled.call_count == 0)
        self.assertTrue(
                setup_django_environment.enable_autocommit.call_count == 0)

        # Confirm that we don't open the connection when the class is
        # instantiated.
        self.assertTrue(connection_manager.db_connection is None)


if __name__ == '__main__':
    unittest.main()