Bug 1309318 - Make the httpd server available to TestCase class to allow custom handlers. draft
authorHenrik Skupin <mail@hskupin.info>
Tue, 01 Nov 2016 13:06:39 +0100
changeset 432180 d0d4ff48e422174c97dd03fd8c0b4ff78534ec6b
parent 430042 3f4c3a3cabaf94958834d3a8935adfb4a887942d
child 535572 a765519b2d450997bb1c0a2f67940ee708878e78
push id34228
push userbmo:hskupin@gmail.com
push dateTue, 01 Nov 2016 12:07:19 +0000
bugs1309318
milestone52.0a1
Bug 1309318 - Make the httpd server available to TestCase class to allow custom handlers. MozReview-Commit-ID: HpazIlQG1Yq
testing/marionette/harness/marionette/marionette_test/testcases.py
testing/marionette/harness/marionette/runner/base.py
testing/marionette/harness/marionette/runner/httpd.py
testing/marionette/harness/marionette/tests/harness_unit/conftest.py
testing/marionette/harness/marionette/tests/harness_unit/test_marionette_test_result.py
testing/marionette/harness/marionette/tests/unit/test_httpd.py
testing/marionette/harness/marionette/tests/unit/unit-tests.ini
--- a/testing/marionette/harness/marionette/marionette_test/testcases.py
+++ b/testing/marionette/harness/marionette/marionette_test/testcases.py
@@ -220,17 +220,18 @@ class CommonTestCase(unittest.TestCase):
         This is done by looking for a match for the filename using cls.match_re.
         """
         if not cls.match_re:
             return False
         m = cls.match_re.match(filename)
         return m is not None
 
     @classmethod
-    def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette, testvars):
+    def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette,
+                           httpd, testvars):
         """Add all the tests in the specified file to the specified suite."""
         raise NotImplementedError
 
     @property
     def test_name(self):
         if hasattr(self, 'jsFile'):
             return os.path.basename(self.jsFile)
         else:
@@ -247,16 +248,17 @@ class CommonTestCase(unittest.TestCase):
 
     def setUp(self):
         # Convert the marionette weakref to an object, just for the
         # duration of the test; this is deleted in tearDown() to prevent
         # a persistent circular reference which in turn would prevent
         # proper garbage collection.
         self.start_time = time.time()
         self.marionette = self._marionette_weakref()
+        self.httpd = self._httpd_weakref()
         if self.marionette.session is None:
             self.marionette.start_session()
         self.marionette.reset_timeouts()
 
         super(CommonTestCase, self).setUp()
 
     def cleanTest(self):
         self._deleteSession()
@@ -419,29 +421,31 @@ if (!testUtils.hasOwnProperty("specialPo
                 raise
         self.marionette.test_name = original_test_name
 
 
 class MarionetteTestCase(CommonTestCase):
 
     match_re = re.compile(r"test_(.*)\.py$")
 
-    def __init__(self, marionette_weakref, methodName='runTest',
+    def __init__(self, marionette_weakref, httpd_weakref, methodName='runTest',
                  filepath='', **kwargs):
         self._marionette_weakref = marionette_weakref
-        self.marionette = None
+        self._httpd_weakref = httpd_weakref
         self.methodName = methodName
         self.filepath = filepath
         self.testvars = kwargs.pop('testvars', None)
 
+        self.marionette = None
+
         super(MarionetteTestCase, self).__init__(methodName, **kwargs)
 
     @classmethod
     def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette,
-                           testvars, **kwargs):
+                           httpd, testvars, **kwargs):
         # since we use imp.load_source to load test modules, if a module
         # is loaded with the same name as another one the module would just be
         # reloaded.
         #
         # We may end up by finding too many test in a module then since
         # reload() only update the module dict (so old keys are still there!)
         # see https://docs.python.org/2/library/functions.html#reload
         #
@@ -455,20 +459,21 @@ class MarionetteTestCase(CommonTestCase)
 
         for name in dir(test_mod):
             obj = getattr(test_mod, name)
             if (isinstance(obj, (type, types.ClassType)) and
                     issubclass(obj, unittest.TestCase)):
                 testnames = testloader.getTestCaseNames(obj)
                 for testname in testnames:
                     suite.addTest(obj(weakref.ref(marionette),
-                                  methodName=testname,
-                                  filepath=filepath,
-                                  testvars=testvars,
-                                  **kwargs))
+                                      weakref.ref(httpd),
+                                      methodName=testname,
+                                      filepath=filepath,
+                                      testvars=testvars,
+                                      **kwargs))
 
     def setUp(self):
         super(MarionetteTestCase, self).setUp()
         self.marionette.test_name = self.test_name
         self.marionette.execute_script("log('TEST-START: {0}:{1}')"
                                        .format(self.filepath.replace('\\', '\\\\'),
                                                self.methodName),
                                        sandbox="simpletest")
@@ -504,28 +509,34 @@ class MarionetteTestCase(CommonTestCase)
         else:
             raise TimeoutException("wait_for_condition timed out")
 
 
 class MarionetteJSTestCase(CommonTestCase):
 
     match_re = re.compile(r"test_(.*)\.js$")
 
-    def __init__(self, marionette_weakref, methodName='runTest', jsFile=None, **kwargs):
+    def __init__(self, marionette_weakref, httpd_weakref, methodName='runTest',
+                 jsFile=None, **kwargs):
         assert(jsFile)
         self.jsFile = jsFile
+        self._httpd_weakref = httpd_weakref
+        self.httpd = None
         self._marionette_weakref = marionette_weakref
         self.marionette = None
 
         super(MarionetteJSTestCase, self).__init__(methodName)
 
     @classmethod
     def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette,
-                           testvars, **kwargs):
-        suite.addTest(cls(weakref.ref(marionette), jsFile=filepath, **kwargs))
+                           httpd, testvars, **kwargs):
+        suite.addTest(cls(weakref.ref(marionette),
+                          weakref.ref(httpd),
+                          jsFile=filepath,
+                          **kwargs))
 
     def runTest(self):
         if self.marionette.session is None:
             self.marionette.start_session()
         self.marionette.execute_script(
             "log('TEST-START: {}');".format(self.jsFile.replace('\\', '\\\\')),
             sandbox="simpletest")
 
--- a/testing/marionette/harness/marionette/runner/base.py
+++ b/testing/marionette/harness/marionette/runner/base.py
@@ -992,16 +992,17 @@ class BaseMarionetteTestRunner(object):
         mod_name = os.path.splitext(os.path.split(filepath)[-1])[0]
         for handler in self.test_handlers:
             if handler.match(os.path.basename(filepath)):
                 handler.add_tests_to_suite(mod_name,
                                            filepath,
                                            suite,
                                            testloader,
                                            self.marionette,
+                                           self.httpd,
                                            self.testvars,
                                            **self.test_kwargs)
                 break
 
         if suite.countTestCases():
             runner = self.textrunnerclass(logger=self.logger,
                                           marionette=self.marionette,
                                           capabilities=self.capabilities,
--- a/testing/marionette/harness/marionette/runner/httpd.py
+++ b/testing/marionette/harness/marionette/runner/httpd.py
@@ -45,16 +45,20 @@ class FixtureServer(object):
         return self._server is not None
 
     def get_url(self, path="/"):
         if not self.alive:
             raise Exception("Server not started")
         return self._server.get_url(path)
 
     @property
+    def router(self):
+        return self._server.router
+
+    @property
     def routes(self):
         return self._server.router.routes
 
 
 @handlers.handler
 def upload_handler(request, response):
     return 200, [], [request.headers.get("Content-Type")] or []
 
--- a/testing/marionette/harness/marionette/tests/harness_unit/conftest.py
+++ b/testing/marionette/harness/marionette/tests/harness_unit/conftest.py
@@ -1,16 +1,18 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
 import pytest
 
 from mock import Mock, MagicMock
 
 from marionette_driver.marionette import Marionette
+from marionette.runner.httpd import FixtureServer
 
 
 @pytest.fixture(scope='module')
 def logger():
     """
     Fake logger to help with mocking out other runner-related classes.
     """
     import mozlog
@@ -78,16 +80,24 @@ def mach_parsed_kwargs(logger):
         'this_chunk': None,
         'timeout': None,
         'total_chunks': None,
         'verbose': None,
         'workspace': None,
         'logger': logger,
     }
 
+
+@pytest.fixture
+def mock_httpd(request):
+    """ Mock httpd instance """
+    httpd = MagicMock(spec=FixtureServer)
+    return httpd
+
+
 @pytest.fixture
 def mock_marionette(request):
     """ Mock marionette instance """
     marionette = MagicMock(spec=Marionette)
     if 'has_crashed' in request.funcargnames:
         marionette.check_for_crash.return_value = request.getfuncargvalue(
             'has_crashed'
         )
--- a/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_test_result.py
+++ b/testing/marionette/harness/marionette/tests/harness_unit/test_marionette_test_result.py
@@ -15,17 +15,18 @@ def empty_marionette_testcase():
         def test_nothing(self):
             pass
 
     return EmptyTestCase
 
 
 @pytest.fixture
 def empty_marionette_test(mock_marionette, empty_marionette_testcase):
-    return empty_marionette_testcase(lambda: mock_marionette, 'test_nothing')
+    return empty_marionette_testcase(lambda: mock_marionette, lambda: mock_httpd,
+                                     'test_nothing')
 
 
 @pytest.mark.parametrize("has_crashed", [True, False])
 def test_crash_is_recorded_as_error(empty_marionette_test,
                                     logger,
                                     has_crashed):
     """ Number of errors is incremented by stopTest iff has_crashed is true """
     # collect results from the empty test
new file mode 100644
--- /dev/null
+++ b/testing/marionette/harness/marionette/tests/unit/test_httpd.py
@@ -0,0 +1,32 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette import MarionetteTestCase
+from marionette_driver import By
+
+
+class TestHttpdServer(MarionetteTestCase):
+
+    def test_handler(self):
+        status = {"count": 0}
+
+        def handler(request, response):
+            status["count"] += 1
+
+            response.headers.set("Content-Type", "text/html")
+            response.content = "<html><body><p id=\"count\">{}</p></body></html>".format(
+                status["count"])
+
+            return ()
+
+        route = ("GET", "/httpd/test_handler", handler)
+        self.httpd.router.register(*route)
+
+        url = self.marionette.absolute_url("httpd/test_handler")
+
+        for counter in range(0, 5):
+            self.marionette.navigate(url)
+            self.assertEqual(status["count"], counter + 1)
+            elem = self.marionette.find_element(By.ID, "count")
+            self.assertEqual(elem.text, str(counter + 1))
--- a/testing/marionette/harness/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/harness/marionette/tests/unit/unit-tests.ini
@@ -135,9 +135,9 @@ skip-if = buildapp == 'b2g' || appname =
 [test_shadow_dom.py]
 
 [test_chrome.py]
 skip-if = buildapp == 'b2g' || appname == 'fennec'
 
 [test_addons.py]
 
 [test_select.py]
-
+[test_httpd.py]