adding unit test for checking that adding milestones in one thread is reflected in other threads - possibly related to #613
git-svn-id: https://svn.apache.org/repos/asf/bloodhound/trunk@1609909 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bloodhound_multiproduct/tests/ticket/model.py b/bloodhound_multiproduct/tests/ticket/model.py
index 0ea401d..b2148af 100644
--- a/bloodhound_multiproduct/tests/ticket/model.py
+++ b/bloodhound_multiproduct/tests/ticket/model.py
@@ -33,6 +33,13 @@
from multiproduct.env import ProductEnvironment
from tests.env import MultiproductTestCase
+try:
+ import threading
+except ImportError:
+ threading = None
+from Queue import Queue
+
+
class ProductTicketTestCase(TicketTestCase, MultiproductTestCase):
def setUp(self):
@@ -146,6 +153,70 @@
self.global_env.reset_db()
self.env = self.global_env = None
+ @unittest.skipUnless(threading, 'Threading required for test')
+ def test_milestone_threads(self):
+ """ Ensure that in threaded (e.g. mod_wsgi) situations, we get
+ an accurate list of milestones from Milestone.list
+
+ The basic strategy is:
+ thread-1 requests a list of milestones
+ thread-2 adds a milestone
+ thread-1 requests a new list of milestones
+ To pass, thread-1 should have a list of milestones that matches
+ those that are in the database.
+ """
+ lock = threading.RLock()
+ results = []
+ # two events to coordinate the workers and ensure that the threads
+ # alternate appropriately
+ e1 = threading.Event()
+ e2 = threading.Event()
+
+ def task(add):
+ """the thread task - either we are discovering or adding events"""
+ with lock:
+ env = ProductEnvironment(self.global_env,
+ self.default_product)
+ if add:
+ name = 'milestone_from_' + threading.current_thread().name
+ milestone = Milestone(env)
+ milestone.name = name
+ milestone.insert()
+ else:
+ # collect the names of milestones reported by Milestone and
+ # directly from the db - as sets to ease comparison later
+ results.append({
+ 'from_t': set([m.name for m in Milestone.select(env)]),
+ 'from_db': set(
+ [v[0] for v in self.env.db_query(
+ "SELECT name FROM milestone")])})
+
+ def worker1():
+ """ check milestones in this thread twice either side of ceding
+ control to worker2
+ """
+ task(False)
+ e1.set()
+ e2.wait()
+ task(False)
+
+ def worker2():
+ """ adds a milestone when worker1 allows us to then cede control
+ back to worker1
+ """
+ e1.wait()
+ task(True)
+ e2.set()
+
+ t1, t2 = [threading.Thread(target=f) for f in (worker1, worker2)]
+ t1.start()
+ t2.start()
+ t1.join()
+ t2.join()
+
+ r = results[-1] # note we only care about the final result
+ self.assertEqual(r['from_t'], r['from_db'])
+
def test_update_milestone(self):
self.env.db_transaction("INSERT INTO milestone (name) VALUES ('Test')")