Yuheng Long | 761748d | 2013-06-28 09:32:43 -0700 | [diff] [blame] | 1 | """Unittest for the pipeline_worker functions in the build/test stage. |
| 2 | |
Yuheng Long | 49358b7 | 2013-07-10 14:45:29 -0700 | [diff] [blame^] | 3 | Part of the Chrome build flags optimization. |
| 4 | |
Yuheng Long | 761748d | 2013-06-28 09:32:43 -0700 | [diff] [blame] | 5 | This module tests the helper method and the worker method. |
| 6 | """ |
| 7 | |
| 8 | __author__ = 'yuhenglong@google.com (Yuheng Long)' |
| 9 | |
| 10 | import multiprocessing |
| 11 | import random |
| 12 | import sys |
| 13 | import unittest |
| 14 | |
| 15 | import pipeline_process |
| 16 | import pipeline_worker |
| 17 | |
| 18 | |
| 19 | TESTSTAGE = 0 |
| 20 | |
| 21 | |
| 22 | def MockTaskCostGenerator(): |
| 23 | """Calls a random number generator and returns a negative number.""" |
| 24 | return random.randint(-sys.maxint - 1, -1) |
| 25 | |
| 26 | |
| 27 | class MockTask(object): |
| 28 | """This class emulates an actual task. |
| 29 | |
| 30 | It does not do the actual work, but simply returns the result as given when |
| 31 | this task is constructed. |
| 32 | """ |
| 33 | |
| 34 | def __init__(self, identifier, cost): |
| 35 | """Set up the results for this task. |
| 36 | |
| 37 | Args: |
| 38 | identifier: the identifier of this task. |
| 39 | cost: the mock cost of this task. |
| 40 | |
| 41 | The _pre_cost field stores the cost. Once this task is performed, i.e., by |
| 42 | calling the work method , the _cost field will have this cost. |
| 43 | """ |
| 44 | |
| 45 | self._identifier = identifier |
| 46 | self._pre_cost = cost |
| 47 | |
| 48 | def get_identifier(self, stage): |
| 49 | assert stage == TESTSTAGE |
| 50 | return self._identifier |
| 51 | |
| 52 | def __eq__(self, other): |
| 53 | if isinstance(other, MockTask): |
| 54 | return self._identifier == other._identifier and self._cost == other._cost |
| 55 | return False |
| 56 | |
| 57 | def set_result(self, stage, cost): |
| 58 | assert stage == TESTSTAGE |
| 59 | self._cost = cost |
| 60 | |
| 61 | def work(self, stage): |
| 62 | assert stage == TESTSTAGE |
| 63 | self._cost = self._pre_cost |
| 64 | |
| 65 | def get_result(self, stage): |
| 66 | assert stage == TESTSTAGE |
| 67 | return self._cost |
| 68 | |
| 69 | def done(self, stage): |
| 70 | """Indicates whether the task has been performed.""" |
| 71 | |
| 72 | assert stage == TESTSTAGE |
| 73 | return '_cost' in self.__dict__ |
| 74 | |
| 75 | |
| 76 | class AuxiliaryTest(unittest.TestCase): |
| 77 | """This class tests the pipeline_worker functions. |
| 78 | |
| 79 | Given the same identifier, the cost should result the same from the |
| 80 | pipeline_worker functions. |
| 81 | """ |
| 82 | |
| 83 | def testHelper(self): |
| 84 | """"Test the helper. |
| 85 | |
| 86 | Call the helper method twice, and test the results. The results should be |
| 87 | the same, i.e., the cost should be the same. |
| 88 | """ |
| 89 | |
| 90 | # Set up the input, helper and output queue for the helper method. |
| 91 | manager = multiprocessing.Manager() |
| 92 | helper_queue = manager.Queue() |
| 93 | result_queue = manager.Queue() |
| 94 | completed_queue = manager.Queue() |
| 95 | |
| 96 | # Set up the helper process that holds the helper method. |
| 97 | helper_process = multiprocessing.Process(target=pipeline_worker.helper, |
| 98 | args=(TESTSTAGE, {}, helper_queue, |
| 99 | completed_queue, |
| 100 | result_queue)) |
| 101 | helper_process.start() |
| 102 | |
| 103 | # A dictionary defines the mock result to the helper. |
| 104 | mock_result = {1: 1995, 2: 59, 9: 1027} |
| 105 | |
| 106 | # Test if there is a task that is done before, whether the duplicate task |
| 107 | # will have the same result. Here, two different scenarios are tested. That |
| 108 | # is the mock results are added to the completed_queue before and after the |
| 109 | # corresponding mock tasks being added to the input queue. |
| 110 | completed_queue.put((9, mock_result[9])) |
| 111 | |
| 112 | # The output of the helper should contain all the following tasks. |
| 113 | results = [1, 1, 2, 9] |
| 114 | |
| 115 | # Testing the correctness of having tasks having the same identifier, here |
| 116 | # 1. |
| 117 | for result in results: |
| 118 | helper_queue.put(MockTask(result, MockTaskCostGenerator())) |
| 119 | |
| 120 | completed_queue.put((2, mock_result[2])) |
| 121 | completed_queue.put((1, mock_result[1])) |
| 122 | |
| 123 | # Signal there is no more duplicate task. |
| 124 | helper_queue.put(pipeline_process.POISONPILL) |
| 125 | helper_process.join() |
| 126 | |
| 127 | while results: |
| 128 | task = result_queue.get() |
| 129 | identifier = task._identifier |
| 130 | cost = task._cost |
| 131 | self.assertTrue(identifier in results) |
| 132 | if identifier in mock_result: |
| 133 | self.assertTrue(cost, mock_result[identifier]) |
| 134 | results.remove(task._identifier) |
| 135 | |
| 136 | def testWorker(self): |
| 137 | """"Test the worker method. |
| 138 | |
| 139 | The worker should process all the input tasks and output the tasks to the |
| 140 | helper and result queue. |
| 141 | """ |
| 142 | |
| 143 | manager = multiprocessing.Manager() |
| 144 | result_queue = manager.Queue() |
| 145 | completed_queue = manager.Queue() |
| 146 | |
| 147 | # A dictionary defines the mock tasks and their corresponding results. |
| 148 | mock_work_tasks = {1: 86, 2: 788} |
| 149 | |
| 150 | mock_tasks = [] |
| 151 | |
| 152 | for flag, cost in mock_work_tasks.iteritems(): |
| 153 | mock_tasks.append(MockTask(flag, cost)) |
| 154 | |
| 155 | # Submit the mock tasks to the worker. |
| 156 | for mock_task in mock_tasks: |
| 157 | pipeline_worker.worker(TESTSTAGE, mock_task, completed_queue, |
| 158 | result_queue) |
| 159 | |
| 160 | # The tasks, from the output queue, should be the same as the input and |
| 161 | # should be performed. |
| 162 | for task in mock_tasks: |
| 163 | output = result_queue.get() |
| 164 | self.assertEqual(output, task) |
| 165 | self.assertTrue(output.done(TESTSTAGE)) |
| 166 | |
| 167 | # The tasks, from the completed_queue, should be defined in the |
| 168 | # mock_work_tasks dictionary. |
| 169 | for flag, cost in mock_work_tasks.iteritems(): |
| 170 | helper_input = completed_queue.get() |
| 171 | self.assertEqual(helper_input, (flag, cost)) |
| 172 | |
| 173 | |
| 174 | if __name__ == '__main__': |
| 175 | unittest.main() |