Bug 739753 - Fatal handling of invalid head and tail files in xpcshell test runner; r=ted
authorGregory Szorc <gps@mozilla.com>
Wed, 28 Mar 2012 16:05:22 -0700
changeset 90577 e28a79957b4afb7cc72b43dcffdb462849e77e6c
parent 90576 520ca2f9b4b7a729bf52d10ea67b04e841eab622
child 90578 a8bebfa88961964c05f42c7035f305a6afdf2595
push id22366
push usermak77@bonardo.net
push dateThu, 29 Mar 2012 15:38:30 +0000
treeherdermozilla-central@ff3521bc6559 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted
bugs739753
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 739753 - Fatal handling of invalid head and tail files in xpcshell test runner; r=ted
testing/xpcshell/remotexpcshelltests.py
testing/xpcshell/runxpcshelltests.py
testing/xpcshell/selftest.py
toolkit/crashreporter/test/unit_ipc/xpcshell.ini
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -174,23 +174,36 @@ class XPCShellRemote(xpcshell.XPCShellTe
 
         if self.remoteDebugger:
           # for example, "/data/local/gdbserver" "localhost:12345"
           self.xpcsCmd = [
             self.remoteDebugger, 
             self.remoteDebuggerArgs, 
             self.xpcsCmd]
 
-    def getHeadFiles(self, test):
+    def getHeadAndTailFiles(self, test):
+        """Override parent method to find files on remote device."""
+        def sanitize_list(s, kind):
+            for f in s.strip().split(' '):
+                f = f.strip()
+                if len(f) < 1:
+                    continue
+
+                path = self.remoteJoin(self.remoteHere, f)
+                if not self.device.fileExists(path):
+                    raise Exception('%s file does not exist: %s' % ( kind,
+                        path))
+
+                yield path
+
         self.remoteHere = self.remoteForLocal(test['here'])
-        return [f.strip() for f in sorted(test['head'].split(' ')) if self.device.fileExists(self.remoteJoin(self.remoteHere, f))]
-    
-    def getTailFiles(self, test):
-        return [f.strip() for f in sorted(test['tail'].split(' ')) if self.device.fileExists(self.remoteJoin(self.remoteHere, f))]
-        
+
+        return (list(sanitize_list(test['head'], 'head')),
+                list(sanitize_list(test['tail'], 'tail')))
+
     def buildCmdTestFile(self, name):
         remoteDir = self.remoteForLocal(os.path.dirname(name))
         if remoteDir == self.remoteHere:
           remoteName = os.path.basename(name)
         else:
           remoteName = self.remoteJoin(remoteDir, os.path.basename(name))
         return ['-e', 'const _TEST_FILE = ["%s"];' %
                  replaceBackSlashes(remoteName)]
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -249,34 +249,39 @@ class XPCShellTests(object):
           self.testPath = self.testPath.rsplit('/', 1)
           self.singleFile = self.testPath[1]
           self.testPath = self.testPath[0]
       else:
         # Path only.
         # Simply remove optional ending separator.
         self.testPath = self.testPath.rstrip("/")
 
+  def getHeadAndTailFiles(self, test):
+      """Obtain the list of head and tail files.
 
-  def getHeadFiles(self, test):
-    """
-      test['head'] is a whitespace delimited list of head files.
-      return the list of head files as paths including the subdir if the head file exists
+      Returns a 2-tuple. The first element is a list of head files. The second
+      is a list of tail files.
+      """
+      def sanitize_list(s, kind):
+          for f in s.strip().split(' '):
+              f = f.strip()
+              if len(f) < 1:
+                  continue
 
-      On a remote system, this may be overloaded to list files in a remote directory structure.
-    """
-    return [os.path.join(test['here'], f).strip() for f in sorted(test['head'].split(' ')) if os.path.isfile(os.path.join(test['here'], f))]
+              path = os.path.normpath(os.path.join(test['here'], f))
+              if not os.path.exists(path):
+                  raise Exception('%s file does not exist: %s' % (kind, path))
 
-  def getTailFiles(self, test):
-    """
-      test['tail'] is a whitespace delimited list of head files.
-      return the list of tail files as paths including the subdir if the tail file exists
+              if not os.path.isfile(path):
+                  raise Exception('%s file is not a file: %s' % (kind, path))
 
-      On a remote system, this may be overloaded to list files in a remote directory structure.
-    """
-    return [os.path.join(test['here'], f).strip() for f in sorted(test['tail'].split(' ')) if os.path.isfile(os.path.join(test['here'], f))]
+              yield path
+
+      return (list(sanitize_list(test['head'], 'head')),
+              list(sanitize_list(test['tail'], 'tail')))
 
   def setupProfileDir(self):
     """
       Create a temporary folder for the profile and set appropriate environment variables.
       When running check-interactive and check-one, the directory is well-defined and
       retained for inspection once the tests complete.
 
       On a remote system, this may be overloaded to use a remote path structure.
@@ -632,18 +637,17 @@ class XPCShellTests(object):
         xunitResults.append(xunitResult)
         continue
 
       # Check for known-fail tests
       expected = test['expected'] == 'pass'
 
       testdir = os.path.dirname(name)
       self.buildXpcsCmd(testdir)
-      testHeadFiles = self.getHeadFiles(test)
-      testTailFiles = self.getTailFiles(test)
+      testHeadFiles, testTailFiles = self.getHeadAndTailFiles(test)
       cmdH = self.buildCmdHead(testHeadFiles, testTailFiles, self.xpcsCmd)
 
       # create a temp dir that the JS harness can stick a profile in
       self.profileDir = self.setupProfileDir()
       self.leakLogFile = self.setupLeakLogging()
 
       # The test file will have to be loaded after the head files.
       cmdT = self.buildCmdTestFile(name)
--- a/testing/xpcshell/selftest.py
+++ b/testing/xpcshell/selftest.py
@@ -207,16 +207,51 @@ tail =
         self.assertTestResult(False)
         self.assertEquals(1, self.x.testCount)
         self.assertEquals(0, self.x.passCount)
         self.assertEquals(1, self.x.failCount)
         self.assertEquals(0, self.x.todoCount)
         self.assertInLog("TEST-UNEXPECTED-FAIL")
         self.assertNotInLog("TEST-PASS")
 
+    def testMissingHeadFile(self):
+        """
+        Ensure that missing head file results in fatal error.
+        """
+        self.writeFile("test_basic.js", SIMPLE_PASSING_TEST)
+        self.writeManifest([("test_basic.js", "head = missing.js")])
+
+        raised = False
+
+        try:
+            # The actual return value is never checked because we raise.
+            self.assertTestResult(True)
+        except Exception, ex:
+            raised = True
+            self.assertEquals(ex.message[0:9], "head file")
+
+        self.assertTrue(raised)
+
+    def testMissingTailFile(self):
+        """
+        Ensure that missing tail file results in fatal error.
+        """
+        self.writeFile("test_basic.js", SIMPLE_PASSING_TEST)
+        self.writeManifest([("test_basic.js", "tail = missing.js")])
+
+        raised = False
+
+        try:
+            self.assertTestResult(True)
+        except Exception, ex:
+            raised = True
+            self.assertEquals(ex.message[0:9], "tail file")
+
+        self.assertTrue(raised)
+
     def testRandomExecution(self):
         """
         Check that random execution doesn't break.
         """
         manifest = []
         for i in range(0, 10):
             filename = "test_pass_%d.js" % i
             self.writeFile(filename, SIMPLE_PASSING_TEST)
--- a/toolkit/crashreporter/test/unit_ipc/xpcshell.ini
+++ b/toolkit/crashreporter/test/unit_ipc/xpcshell.ini
@@ -1,5 +1,5 @@
 [DEFAULT]
-head = head_crashreporter.js
-tail = 
+head =
+tail =
 
 [test_content_annotation.js]