140 lines
3.9 KiB
Python
140 lines
3.9 KiB
Python
# Copyright (c) 2017 Red Hat, Inc.
|
|
#
|
|
# 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 sys
|
|
|
|
import fixtures
|
|
import mock
|
|
import testtools
|
|
|
|
from ovsdbapp import api
|
|
from ovsdbapp.tests import base
|
|
|
|
try:
|
|
import eventlet
|
|
from eventlet.green import thread
|
|
|
|
sleep = eventlet.sleep
|
|
|
|
def create_thread(executable):
|
|
eventlet.spawn_n(executable)
|
|
|
|
except ImportError:
|
|
import threading
|
|
import time
|
|
|
|
sleep = time.sleep
|
|
|
|
def create_thread(executable):
|
|
thread = threading.Thread(target=executable)
|
|
thread.start()
|
|
|
|
|
|
class GreenThreadingFixture(fixtures.Fixture):
|
|
def _setUp(self):
|
|
if 'eventlet' in sys.modules:
|
|
self._orig = api.thread.get_ident
|
|
api.thread.get_ident = thread.get_ident
|
|
self.addCleanup(self.cleanup)
|
|
|
|
def cleanup(self):
|
|
api.thread.get_ident = self._orig
|
|
|
|
|
|
class FakeTransaction(object):
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, tb):
|
|
self.commit()
|
|
|
|
def commit(self):
|
|
"""Serves just for mock."""
|
|
|
|
|
|
class TestingAPI(api.API):
|
|
def create_transaction(self, check_error=False, log_errors=True, **kwargs):
|
|
txn = FakeTransaction()
|
|
mock.patch.object(txn, 'commit').start()
|
|
return txn
|
|
|
|
|
|
TestingAPI.__abstractmethods__ = set()
|
|
|
|
|
|
class TransactionTestCase(base.TestCase):
|
|
def setUp(self):
|
|
super(TransactionTestCase, self).setUp()
|
|
self.api = TestingAPI()
|
|
self.useFixture(GreenThreadingFixture())
|
|
|
|
def test_transaction_nested(self):
|
|
with self.api.transaction() as txn1:
|
|
with self.api.transaction() as txn2:
|
|
self.assertIs(txn1, txn2)
|
|
txn1.commit.assert_called_once_with()
|
|
|
|
def test_transaction_nested_false(self):
|
|
with self.api.transaction(nested=False) as txn1:
|
|
with self.api.transaction() as txn2:
|
|
self.assertIsNot(txn1, txn2)
|
|
txn1.commit.assert_called_once_with()
|
|
txn2.commit.assert_called_once_with()
|
|
|
|
def test_api_level_transaction_nested_fales(self):
|
|
api = TestingAPI(nested_transactions=False)
|
|
with api.transaction() as txn1:
|
|
with api.transaction() as txn2:
|
|
self.assertIsNot(txn1, txn2)
|
|
txn1.commit.assert_called_once_with()
|
|
txn2.commit.assert_called_once_with()
|
|
|
|
def test_transaction_no_nested_transaction_after_error(self):
|
|
class TestException(Exception):
|
|
pass
|
|
|
|
with testtools.ExpectedException(TestException):
|
|
with self.api.transaction() as txn1:
|
|
raise TestException()
|
|
|
|
with self.api.transaction() as txn2:
|
|
self.assertIsNot(txn1, txn2)
|
|
|
|
def test_transaction_nested_multiple_threads(self):
|
|
shared_resource = []
|
|
|
|
def thread1():
|
|
with self.api.transaction() as txn:
|
|
shared_resource.append(txn)
|
|
while len(shared_resource) == 1:
|
|
sleep(0.1)
|
|
shared_resource.append(0)
|
|
|
|
def thread2():
|
|
while len(shared_resource) != 1:
|
|
sleep(0.1)
|
|
with self.api.transaction() as txn:
|
|
shared_resource.append(txn)
|
|
shared_resource.append(0)
|
|
|
|
create_thread(thread1)
|
|
create_thread(thread2)
|
|
|
|
while len(shared_resource) != 4:
|
|
sleep(0.1)
|
|
|
|
txn1, txn2 = shared_resource[:2]
|
|
|
|
self.assertNotEqual(txn1, txn2)
|