| Index: testing/legion/legion_test_case.py
 | 
| diff --git a/testing/legion/legion_test_case.py b/testing/legion/legion_test_case.py
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..9514e8e19be7c57d31ec8ffff7b6e0c00cd6f906
 | 
| --- /dev/null
 | 
| +++ b/testing/legion/legion_test_case.py
 | 
| @@ -0,0 +1,135 @@
 | 
| +# Copyright 2015 The Chromium Authors. All rights reserved.
 | 
| +# Use of this source code is governed by a BSD-style license that can be
 | 
| +# found in the LICENSE file.
 | 
| +
 | 
| +"""Adds unittest-esque functionality to Legion."""
 | 
| +
 | 
| +import argparse
 | 
| +import logging
 | 
| +import sys
 | 
| +import unittest
 | 
| +
 | 
| +#pylint: disable=relative-import
 | 
| +import common_lib
 | 
| +import task_controller
 | 
| +import task_registration_server
 | 
| +
 | 
| +BANNER_WIDTH = 80
 | 
| +
 | 
| +
 | 
| +class TestCase(unittest.TestCase):
 | 
| +  """Test case class with added Legion support."""
 | 
| +
 | 
| +  _registration_server = None
 | 
| +  _initialized = False
 | 
| +
 | 
| +  @classmethod
 | 
| +  def __new__(cls, *args, **kwargs):
 | 
| +    """Initialize the class and return a new instance."""
 | 
| +    cls._InitializeClass()
 | 
| +    return super(TestCase, cls).__new__(*args, **kwargs)
 | 
| +
 | 
| +  def __init__(self, test_name='runTest'):
 | 
| +    super(TestCase, self).__init__(test_name)
 | 
| +    method = getattr(self, test_name, None)
 | 
| +    if method:
 | 
| +      # Install the _RunTest method
 | 
| +      self._TestMethod = method
 | 
| +      setattr(self, test_name, self._RunTest)
 | 
| +
 | 
| +  def _RunTest(self):
 | 
| +    """Runs the test method and provides banner info and error reporting."""
 | 
| +    self._LogInfoBanner(self._testMethodName, self.shortDescription())
 | 
| +    try:
 | 
| +      return self._TestMethod()
 | 
| +    except:
 | 
| +      exc_info = sys.exc_info()
 | 
| +      logging.error('', exc_info=exc_info)
 | 
| +      raise exc_info[0], exc_info[1], exc_info[2]
 | 
| +
 | 
| +  @classmethod
 | 
| +  def _InitializeClass(cls):
 | 
| +    """Handles class level initialization.
 | 
| +
 | 
| +    There are 2 types of setup/teardown methods that always need to be run:
 | 
| +    1) Framework level setup/teardown
 | 
| +    2) Test case level setup/teardown
 | 
| +
 | 
| +    This method installs handlers in place of setUpClass and tearDownClass that
 | 
| +    will ensure both types of setup/teardown methods are called correctly.
 | 
| +    """
 | 
| +    if cls._initialized:
 | 
| +      return
 | 
| +    cls._OriginalSetUpClassMethod = cls.setUpClass
 | 
| +    cls.setUpClass = cls._HandleSetUpClass
 | 
| +    cls._OriginalTearDownClassMethod = cls.tearDownClass
 | 
| +    cls.tearDownClass = cls._HandleTearDownClass
 | 
| +    cls._initialized = True
 | 
| +
 | 
| +  @classmethod
 | 
| +  def _LogInfoBanner(cls, method_name, method_doc=None):
 | 
| +    """Formats and logs test case information."""
 | 
| +    logging.info('*' * BANNER_WIDTH)
 | 
| +    logging.info(method_name.center(BANNER_WIDTH))
 | 
| +    if method_doc:
 | 
| +      for line in method_doc.split('\n'):
 | 
| +        logging.info(line.center(BANNER_WIDTH))
 | 
| +    logging.info('*' * BANNER_WIDTH)
 | 
| +
 | 
| +  @classmethod
 | 
| +  def CreateTask(cls, *args, **kwargs):
 | 
| +    """Convenience method to create a new task."""
 | 
| +    task = task_controller.TaskController(*args, **kwargs)
 | 
| +    cls._registration_server.RegisterTaskCallback(
 | 
| +        task.otp, task.OnConnect)
 | 
| +    return task
 | 
| +
 | 
| +  @classmethod
 | 
| +  def _SetUpFramework(cls):
 | 
| +    """Perform the framework-specific setup operations."""
 | 
| +    cls._registration_server = (
 | 
| +        task_registration_server.TaskRegistrationServer())
 | 
| +    cls._registration_server.Start()
 | 
| +
 | 
| +  @classmethod
 | 
| +  def _TearDownFramework(cls):
 | 
| +    """Perform the framework-specific teardown operations."""
 | 
| +    if cls._registration_server:
 | 
| +      cls._registration_server.Shutdown()
 | 
| +    task_controller.TaskController.ReleaseAllTasks()
 | 
| +
 | 
| +  @classmethod
 | 
| +  def _HandleSetUpClass(cls):
 | 
| +    """Performs common class-level setup operations.
 | 
| +
 | 
| +    This method performs test-wide setup such as starting the registration
 | 
| +    server and then calls the original setUpClass method."""
 | 
| +    try:
 | 
| +      common_lib.InitLogging()
 | 
| +      cls._LogInfoBanner('setUpClass', 'Performs class level setup.')
 | 
| +      cls._SetUpFramework()
 | 
| +      cls._OriginalSetUpClassMethod()
 | 
| +    except:
 | 
| +      # Make sure we tear down in case of any exceptions
 | 
| +      cls._HandleTearDownClass(setup_failed=True)
 | 
| +      exc_info = sys.exc_info()
 | 
| +      logging.error('', exc_info=exc_info)
 | 
| +      raise exc_info[0], exc_info[1], exc_info[2]
 | 
| +
 | 
| +  @classmethod
 | 
| +  def _HandleTearDownClass(cls, setup_failed=False):
 | 
| +    """Performs common class-level tear down operations.
 | 
| +
 | 
| +    This method calls the original tearDownClass then performs test-wide
 | 
| +    tear down such as stopping the registration server.
 | 
| +    """
 | 
| +    cls._LogInfoBanner('tearDownClass', 'Performs class level tear down.')
 | 
| +    try:
 | 
| +      if not setup_failed:
 | 
| +        cls._OriginalTearDownClassMethod()
 | 
| +    finally:
 | 
| +      cls._TearDownFramework()
 | 
| +
 | 
| +
 | 
| +def main():
 | 
| +  unittest.main(verbosity=0, argv=sys.argv[:1])
 | 
| 
 |