Bug 1319620 - Check that IPDL unit tests in error/ fail for the right reason. r=billm draft
authorAndrew McCreight <continuation@gmail.com>
Wed, 15 Mar 2017 10:43:51 -0700
changeset 501635 6f1960db2090f7df435656d08a116e76ea5b105d
parent 501632 8a50e75b0a942583a4cc1165d2ca9f2b859ed3e4
child 501650 1a883ab2d303c838cf29afc9efc700a52cf226bb
push id50061
push userbmo:continuation@gmail.com
push dateMon, 20 Mar 2017 18:40:38 +0000
reviewersbillm
bugs1319620, 1347527
milestone55.0a1
Bug 1319620 - Check that IPDL unit tests in error/ fail for the right reason. r=billm An IPDL unit test that is intended to fail should check that the reason the test fails matches the expected reason for failure. We have had a number of cases where some change, like renaming a keyword, causes tests to start failing for the wrong reason, which means they are no longer testing anything useful. To support this, each file in error/ must contain one or more error annotations. An error annotation is a line starting with "//error:", followed by whatever the rest of the expected error is. For every one of these annotations that a file has, the stderr output of compiling the test must contain the specified string, including the "error:". It is also an error for an error/ file to not contain an error annotation. To generate the initial set of annotations, I just copied and pasted the error that each test produced. I did some light auditing to check that the errors are reasonable, which did turn up one minor error which I fixed as part of bug 1347527. This patch does not check that every error produced by compiling the file is in the list of expected errors. I think that's less of a problem if it does occur. MozReview-Commit-ID: BrePLGPPRil
ipc/ipdl/test/ipdl/IPDLCompile.py
ipc/ipdl/test/ipdl/error/AsyncInsideSync.ipdl
ipc/ipdl/test/ipdl/error/AsyncReturn.ipdl
ipc/ipdl/test/ipdl/error/BadNestedManagee.ipdl
ipc/ipdl/test/ipdl/error/BadNestedManager.ipdl
ipc/ipdl/test/ipdl/error/ForgottenManagee.ipdl
ipc/ipdl/test/ipdl/error/ForgottenManager.ipdl
ipc/ipdl/test/ipdl/error/InsideCpowToChild.ipdl
ipc/ipdl/test/ipdl/error/IntrAsyncManagee.ipdl
ipc/ipdl/test/ipdl/error/IntrAsyncManager.ipdl
ipc/ipdl/test/ipdl/error/IntrSyncManagee.ipdl
ipc/ipdl/test/ipdl/error/IntrSyncManager.ipdl
ipc/ipdl/test/ipdl/error/ManageeForgot.ipdl
ipc/ipdl/test/ipdl/error/ManagerForgot.ipdl
ipc/ipdl/test/ipdl/error/Nullable.ipdl
ipc/ipdl/test/ipdl/error/Nullable2.ipdl
ipc/ipdl/test/ipdl/error/SyncAsyncManagee.ipdl
ipc/ipdl/test/ipdl/error/SyncAsyncManager.ipdl
ipc/ipdl/test/ipdl/error/array_Recursive.ipdl
ipc/ipdl/test/ipdl/error/asyncMessageListed.ipdl
ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl
ipc/ipdl/test/ipdl/error/compressCtor.ipdl
ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl
ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl
ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl
ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl
ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl
ipc/ipdl/test/ipdl/error/dtorReserved.ipdl
ipc/ipdl/test/ipdl/error/empty.ipdl
ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl
ipc/ipdl/test/ipdl/error/lex1.ipdl
ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl
ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl
ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl
ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl
ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl
ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl
ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl
ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl
ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl
ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl
ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl
ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl
ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh
ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl
ipc/ipdl/test/ipdl/error/parser.ipdl
ipc/ipdl/test/ipdl/error/redeclMessage.ipdl
ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl
ipc/ipdl/test/ipdl/error/redeclScope.ipdlh
ipc/ipdl/test/ipdl/error/shmem.ipdl
ipc/ipdl/test/ipdl/error/structRedecl.ipdl
ipc/ipdl/test/ipdl/error/structUnknownField.ipdl
ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl
ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl
ipc/ipdl/test/ipdl/error/tooWeakIntrAsync.ipdl
ipc/ipdl/test/ipdl/error/tooWeakIntrSync.ipdl
ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl
ipc/ipdl/test/ipdl/error/twoprotocols.ipdl
ipc/ipdl/test/ipdl/error/undeclParamType.ipdl
ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl
ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl
ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl
ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl
ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl
ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl
ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl
ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl
ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl
ipc/ipdl/test/ipdl/runtests.py
--- a/ipc/ipdl/test/ipdl/IPDLCompile.py
+++ b/ipc/ipdl/test/ipdl/IPDLCompile.py
@@ -45,31 +45,32 @@ class IPDLCompile:
 
 
     def completed(self):
         return (self.returncode is not None
                 and isinstance(self.stdout, str)
                 and isinstance(self.stderr, str))
 
 
-    def error(self):
+    def error(self, expectedError):
         '''Return True iff compiling self.specstring resulted in an
 IPDL compiler error.'''
         assert self.completed()
 
-        return None is not re.search(r'error:', self.stderr)
+        errorRe = re.compile(re.escape(expectedError))
+        return None is not re.search(errorRe, self.stderr)
 
 
     def exception(self):
         '''Return True iff compiling self.specstring resulted in a Python
 exception being raised.'''
         assert self.completed()
 
         return None is not re.search(r'Traceback (most recent call last):',
                                      self.stderr)
 
     def ok(self):
         '''Return True iff compiling self.specstring was successful.'''
         assert self.completed()
 
         return (not self.exception()
-                and not self.error()
+                and not self.error("error:")
                 and (0 == self.returncode))
--- a/ipc/ipdl/test/ipdl/error/AsyncInsideSync.ipdl
+++ b/ipc/ipdl/test/ipdl/error/AsyncInsideSync.ipdl
@@ -1,6 +1,9 @@
 // inside_sync nested messages must be sync
 
+//error: inside_sync nested messages must be sync (here, message `Msg' in protocol `AsyncInsideSync')
+//error: message `Msg' requires more powerful send semantics than its protocol `AsyncInsideSync' provides
+
 protocol AsyncInsideSync {
 child:
     nested(inside_sync) async Msg();
 };
--- a/ipc/ipdl/test/ipdl/error/AsyncReturn.ipdl
+++ b/ipc/ipdl/test/ipdl/error/AsyncReturn.ipdl
@@ -1,6 +1,8 @@
 // Async messages are not allowed to return values.
 
+//error: asynchronous message `Msg' declares return values
+
 protocol AsyncReturn {
 child:
     async Msg() returns(int32_t aNumber);
 };
--- a/ipc/ipdl/test/ipdl/error/BadNestedManagee.ipdl
+++ b/ipc/ipdl/test/ipdl/error/BadNestedManagee.ipdl
@@ -1,7 +1,9 @@
+//error: protocol `BadNestedManagee' requires more powerful send semantics than its manager `BadNestedManager' provides
+
 include protocol BadNestedManager;
 
 nested(upto inside_sync) async protocol BadNestedManagee {
     manager BadNestedManager;
 child:
     async __delete__();
 };
--- a/ipc/ipdl/test/ipdl/error/BadNestedManager.ipdl
+++ b/ipc/ipdl/test/ipdl/error/BadNestedManager.ipdl
@@ -1,7 +1,9 @@
+//error: protocol `BadNestedManagee' requires more powerful send semantics than its manager `BadNestedManager' provides
+
 include protocol BadNestedManagee;
 
 nested(upto not) async protocol BadNestedManager {
     manages BadNestedManagee;
 parent:
     async BadNestedManagee();
 };
--- a/ipc/ipdl/test/ipdl/error/ForgottenManagee.ipdl
+++ b/ipc/ipdl/test/ipdl/error/ForgottenManagee.ipdl
@@ -1,8 +1,10 @@
+//error: |manager| declaration in protocol `ForgottenManagee' does not match any |manages| declaration in protocol `ManagerForgot'
+
 include protocol ManagerForgot;
 
 // This protocol says ManagerForgot manages it,
 // but ManagerForgot does not manage it.
 
 protocol ForgottenManagee {
     manager ManagerForgot;
 child:
--- a/ipc/ipdl/test/ipdl/error/ForgottenManager.ipdl
+++ b/ipc/ipdl/test/ipdl/error/ForgottenManager.ipdl
@@ -1,8 +1,10 @@
+//error: |manages| declaration in protocol `ForgottenManager' does not match any |manager| declaration in protocol `ManageeForgot'
+
 include protocol ManageeForgot;
 
 // ManageeForgot should have this protocol as its manager.
 
 protocol ForgottenManager {
     manages ManageeForgot;
 child:
     async ManageeForgot();
--- a/ipc/ipdl/test/ipdl/error/InsideCpowToChild.ipdl
+++ b/ipc/ipdl/test/ipdl/error/InsideCpowToChild.ipdl
@@ -1,6 +1,9 @@
 // inside_cpow nested parent-to-child messages are verboten
 
+//error: inside_cpow nested parent-to-child messages are verboten (here, message `Msg' in protocol `InsideCpowToChild')
+//error: message `Msg' requires more powerful send semantics than its protocol `InsideCpowToChild' provides
+
 protocol InsideCpowToChild {
 child:
     nested(inside_cpow) sync Msg();
 };
--- a/ipc/ipdl/test/ipdl/error/IntrAsyncManagee.ipdl
+++ b/ipc/ipdl/test/ipdl/error/IntrAsyncManagee.ipdl
@@ -1,7 +1,9 @@
+//error: protocol `IntrAsyncManagee' requires more powerful send semantics than its manager `IntrAsyncManager' provides
+
 include protocol IntrAsyncManager;
 
 intr protocol IntrAsyncManagee {
     manager IntrAsyncManager;
 child:
     async __delete__();
 };
--- a/ipc/ipdl/test/ipdl/error/IntrAsyncManager.ipdl
+++ b/ipc/ipdl/test/ipdl/error/IntrAsyncManager.ipdl
@@ -1,7 +1,9 @@
+//error: protocol `IntrAsyncManagee' requires more powerful send semantics than its manager `IntrAsyncManager' provides
+
 include protocol IntrAsyncManagee;
 
 async protocol IntrAsyncManager {
     manages IntrAsyncManagee;
 parent:
     async IntrAsyncManagee();
 };
--- a/ipc/ipdl/test/ipdl/error/IntrSyncManagee.ipdl
+++ b/ipc/ipdl/test/ipdl/error/IntrSyncManagee.ipdl
@@ -1,7 +1,9 @@
+//error: protocol `IntrSyncManagee' requires more powerful send semantics than its manager `IntrSyncManager' provides
+
 include protocol IntrSyncManager;
 
 intr protocol IntrSyncManagee {
     manager IntrSyncManager;
 child:
     async __delete__();
 };
--- a/ipc/ipdl/test/ipdl/error/IntrSyncManager.ipdl
+++ b/ipc/ipdl/test/ipdl/error/IntrSyncManager.ipdl
@@ -1,7 +1,9 @@
+//error: protocol `IntrSyncManagee' requires more powerful send semantics than its manager `IntrSyncManager' provides
+
 include protocol IntrSyncManagee;
 
 sync protocol IntrSyncManager {
     manages IntrSyncManagee;
 parent:
     async IntrSyncManagee();
 };
--- a/ipc/ipdl/test/ipdl/error/ManageeForgot.ipdl
+++ b/ipc/ipdl/test/ipdl/error/ManageeForgot.ipdl
@@ -1,8 +1,10 @@
+//error: |manages| declaration in protocol `ForgottenManager' does not match any |manager| declaration in protocol `ManageeForgot'
+
 include protocol ForgottenManager;
 
 // See ForgottenManager. This includes ForgottenManager to ensure that
 // loading this file fails.
 
 protocol ManageeForgot {
 child:
     async __delete__();
--- a/ipc/ipdl/test/ipdl/error/ManagerForgot.ipdl
+++ b/ipc/ipdl/test/ipdl/error/ManagerForgot.ipdl
@@ -1,8 +1,10 @@
+//error: |manager| declaration in protocol `ForgottenManagee' does not match any |manages| declaration in protocol `ManagerForgot'
+
 include protocol ForgottenManagee;
 
 // See ForgottenManagee.ipdl. This includes ForgottenManagee to
 // ensure that loading this file fails.
 
 protocol ManagerForgot {
 child:
     async Msg();
--- a/ipc/ipdl/test/ipdl/error/Nullable.ipdl
+++ b/ipc/ipdl/test/ipdl/error/Nullable.ipdl
@@ -1,4 +1,6 @@
+//error: `nullable' qualifier for type `int' makes no sense
+
 protocol Nullable {
 child:
     async Msg(nullable int i);
 };
--- a/ipc/ipdl/test/ipdl/error/Nullable2.ipdl
+++ b/ipc/ipdl/test/ipdl/error/Nullable2.ipdl
@@ -1,8 +1,10 @@
+//error: `nullable' qualifier for type `int' makes no sense
+
 union Union {
     nullable int;
 };
 
 protocol Nullable2 {
 child:
     async Msg(Union i);
 };
--- a/ipc/ipdl/test/ipdl/error/SyncAsyncManagee.ipdl
+++ b/ipc/ipdl/test/ipdl/error/SyncAsyncManagee.ipdl
@@ -1,7 +1,9 @@
+//error: protocol `SyncAsyncManagee' requires more powerful send semantics than its manager `SyncAsyncManager' provides
+
 include protocol SyncAsyncManager;
 
 sync protocol SyncAsyncManagee {
     manager SyncAsyncManager;
 child:
     async __delete__();
 };
--- a/ipc/ipdl/test/ipdl/error/SyncAsyncManager.ipdl
+++ b/ipc/ipdl/test/ipdl/error/SyncAsyncManager.ipdl
@@ -1,7 +1,9 @@
+//error: protocol `SyncAsyncManagee' requires more powerful send semantics than its manager `SyncAsyncManager' provides
+
 include protocol SyncAsyncManagee;
 
 async protocol SyncAsyncManager {
     manages SyncAsyncManagee;
 parent:
     async SyncAsyncManagee();
 };
--- a/ipc/ipdl/test/ipdl/error/array_Recursive.ipdl
+++ b/ipc/ipdl/test/ipdl/error/array_Recursive.ipdl
@@ -1,3 +1,5 @@
+//error: bad syntax near `['
+
 protocol array_Recursive {
 child: async Msg(int[][] aa);
 };
--- a/ipc/ipdl/test/ipdl/error/asyncMessageListed.ipdl
+++ b/ipc/ipdl/test/ipdl/error/asyncMessageListed.ipdl
@@ -1,4 +1,6 @@
+//error: IPC message asyncMessageListed::Msg is async, can be delisted
+
 protocol asyncMessageListed {
 parent:
     async Msg();
 };
--- a/ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl
+++ b/ipc/ipdl/test/ipdl/error/badProtocolInclude.ipdl
@@ -1,7 +1,9 @@
+//error: can't locate include file `IDONTEXIST.ipdl'
+
 include protocol IDONTEXIST;
 
 // error: nonexistent protocol ^^^
 
 protocol badProtocolInclude {
 child: Msg();
 };
--- a/ipc/ipdl/test/ipdl/error/compressCtor.ipdl
+++ b/ipc/ipdl/test/ipdl/error/compressCtor.ipdl
@@ -1,8 +1,11 @@
+//error: destructor messages can't use compression (here, in protocol `compressCtorManagee')
+//error: constructor messages can't use compression (here, in protocol `compressCtor')
+
 include protocol compressCtorManagee;
 
 intr protocol compressCtor {
     manages compressCtorManagee;
 
 parent:
     async compressCtorManagee() compress;
 };
--- a/ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl
+++ b/ipc/ipdl/test/ipdl/error/compressCtorManagee.ipdl
@@ -1,8 +1,11 @@
+//error: constructor messages can't use compression (here, in protocol `compressCtor')
+//error: destructor messages can't use compression (here, in protocol `compressCtorManagee')
+
 include protocol compressCtor;
 
 intr protocol compressCtorManagee {
     manager compressCtor;
 
 child:
     async __delete__() compress;
 };
--- a/ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl
+++ b/ipc/ipdl/test/ipdl/error/conflictProtocolMsg.ipdl
@@ -1,7 +1,9 @@
+//error: ctor for protocol `conflictProtocolMsg', which is not managed by protocol `conflictProtocolMsg'
+
 protocol conflictProtocolMsg {
 
     // it's an error to re-use the protocol name as a message ID; these
     // are reserved
 child: async conflictProtocolMsg();
 
 };
--- a/ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl
+++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Child.ipdl
@@ -1,8 +1,11 @@
+//error: cycle(s) detected in manager/manages hierarchy: `cyclecheck_Parent -> cyclecheck_Child -> cyclecheck_Grandchild -> cyclecheck_Parent'
+//error: |manages| declaration in protocol `cyclecheck_Grandchild' does not match any |manager| declaration in protocol `cyclecheck_Parent'
+
 include protocol cyclecheck_Parent;
 include protocol cyclecheck_Grandchild;
 
 protocol cyclecheck_Child {
     manager cyclecheck_Parent;
     manages cyclecheck_Grandchild;
 
 child:
--- a/ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl
+++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Grandchild.ipdl
@@ -1,8 +1,11 @@
+//error: cycle(s) detected in manager/manages hierarchy: `cyclecheck_Parent -> cyclecheck_Child -> cyclecheck_Grandchild -> cyclecheck_Parent'
+//error: |manages| declaration in protocol `cyclecheck_Grandchild' does not match any |manager| declaration in protocol `cyclecheck_Parent'
+
 include protocol cyclecheck_Child;
 include protocol cyclecheck_Parent;
 
 protocol cyclecheck_Grandchild {
     manager cyclecheck_Child;
     manages cyclecheck_Parent;
 
 child:
--- a/ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl
+++ b/ipc/ipdl/test/ipdl/error/cyclecheck_Parent.ipdl
@@ -1,8 +1,10 @@
+//error: cycle(s) detected in manager/manages hierarchy: `cyclecheck_Parent -> cyclecheck_Child -> cyclecheck_Grandchild -> cyclecheck_Parent'
+
 include protocol cyclecheck_Child;
 
 protocol cyclecheck_Parent {
     manages cyclecheck_Child;
 
 child:
     async cyclecheck_Child();
     async __delete__();
--- a/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl
+++ b/ipc/ipdl/test/ipdl/error/dtorReserved.ipdl
@@ -1,8 +1,10 @@
+//error: lexically invalid characters `~SomeMsg();
+
 protocol dtorReserved {
 
     // it's an error to use old-style dtor syntax
 
 child:
     ~SomeMsg();
 
 };
--- a/ipc/ipdl/test/ipdl/error/empty.ipdl
+++ b/ipc/ipdl/test/ipdl/error/empty.ipdl
@@ -1,1 +1,1 @@
-
+//error: bad syntax near `???'
\ No newline at end of file
--- a/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl
+++ b/ipc/ipdl/test/ipdl/error/intrMessageCompress.ipdl
@@ -1,6 +1,9 @@
+//error: message `foo' in protocol `intrMessageCompress' requests compression but is not async
+//error: message `bar' in protocol `intrMessageCompress' requests compression but is not async
+
 intr protocol intrMessageCompress {
 parent:
     intr foo() compress;
 child:
     intr bar() compress;
 };
--- a/ipc/ipdl/test/ipdl/error/lex1.ipdl
+++ b/ipc/ipdl/test/ipdl/error/lex1.ipdl
@@ -1,1 +1,2 @@
+//error: bad syntax near `slkdjfl'
 slkdjfl*&^*
--- a/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl
+++ b/ipc/ipdl/test/ipdl/error/manageSelfToplevel.ipdl
@@ -1,8 +1,10 @@
+//error: top-level protocol `manageSelfToplevel' cannot manage itself
+
 protocol manageSelfToplevel {
     manager manageSelfToplevel;
     manages manageSelfToplevel;
 
 child:
     async manageSelfToplevel();
     async __delete__();
 };
--- a/ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl
+++ b/ipc/ipdl/test/ipdl/error/managedNoCtor.ipdl
@@ -1,7 +1,9 @@
+//error: constructor declaration required for managed protocol `managedNoCtor' (managed by protocol `managerNoCtor')
+
 include protocol managerNoCtor;
 
 protocol managedNoCtor {
     manager managerNoCtor;
     // empty
 child: async __delete__();
 };
--- a/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl
+++ b/ipc/ipdl/test/ipdl/error/managedNoDtor.ipdl
@@ -1,6 +1,7 @@
+//error: destructor declaration `__delete__(...)' required for managed protocol `managedNoDtor'
 include protocol managerNoDtor;
 
 protocol managedNoDtor {
     manager managerNoDtor;
     // empty
 };
--- a/ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl
+++ b/ipc/ipdl/test/ipdl/error/managerNoCtor.ipdl
@@ -1,8 +1,10 @@
+//error: constructor declaration required for managed protocol `managedNoCtor' (managed by protocol `managerNoCtor')
+
 include protocol managedNoCtor;
 
 protocol managerNoCtor {
     manages managedNoCtor;
 
 parent:
     // error: no ctor defined
     async __delete__();
--- a/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl
+++ b/ipc/ipdl/test/ipdl/error/managerNoDtor.ipdl
@@ -1,8 +1,10 @@
+//error: destructor declaration `__delete__(...)' required for managed protocol `managedNoDtor'
+
 include protocol managedNoDtor;
 
 protocol managerNoDtor {
     manages managedNoDtor;
 
 parent:
     async managedNoDtor();
     // error: no ctor defined
--- a/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl
+++ b/ipc/ipdl/test/ipdl/error/messageNoDirection.ipdl
@@ -1,3 +1,5 @@
+//error: missing message direction
+
 protocol messageNoDirection {
     async NoDirection();
 };
--- a/ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl
+++ b/ipc/ipdl/test/ipdl/error/multimanDupMgrs.ipdl
@@ -1,8 +1,10 @@
+//error: manager `multimanDupMgrsMgr' appears multiple times
+
 include protocol multimanDupMgrsMgr;
 
 protocol multimanDupMgrs {
     manager multimanDupMgrsMgr or multimanDupMgrsMgr;
 
 child:
     async __delete__();
 };
--- a/ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl
+++ b/ipc/ipdl/test/ipdl/error/multimanDupMgrsMgr.ipdl
@@ -1,8 +1,10 @@
+//error: manager `multimanDupMgrsMgr' appears multiple times
+
 include protocol multimanDupMgrs;
 
 protocol multimanDupMgrsMgr {
     manages multimanDupMgrs;
 
 child:
     async multimanDupMgrs();
     async __delete__();
--- a/ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl
+++ b/ipc/ipdl/test/ipdl/error/multimanNonexistentMgrs.ipdl
@@ -1,7 +1,10 @@
+//error: protocol `Starsky' referenced as |manager| of `multimanNonexistentMgrs' has not been declared
+//error: protocol `Hutch' referenced as |manager| of `multimanNonexistentMgrs' has not been declared
+
 protocol multimanNonexistentMgrs {
     manager Starsky or Hutch;
 
 child:
     async Dummy();
     async __delete__();
 };
--- a/ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl
+++ b/ipc/ipdl/test/ipdl/error/mutualRecStruct.ipdl
@@ -1,8 +1,12 @@
+//error: struct `X' is only partially defined
+//error: struct `Y' is only partially defined
+//error: struct `Z' is only partially defined
+
 struct X {
     int i;
     Y[] y;
 };
 
 struct Y {
     X x;
     Z z;
--- a/ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl
+++ b/ipc/ipdl/test/ipdl/error/mutualRecStructUnion.ipdl
@@ -1,8 +1,12 @@
+//error: struct `X' is only partially defined
+//error: union `Y' is only partially defined
+//error: struct `Z' is only partially defined
+
 struct X {
     int i;
     Y[] y;
 };
 
 union Y {
     X;
     Z;
--- a/ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl
+++ b/ipc/ipdl/test/ipdl/error/noEmptyToplevel.ipdl
@@ -1,5 +1,7 @@
+//error: top-level protocol `noEmptyToplevel' cannot be empty
+
 protocol noEmptyToplevel {
 
     // it's an error for top-level protocols to be empty
 
 };
--- a/ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh
+++ b/ipc/ipdl/test/ipdl/error/noProtocolInHeader.ipdlh
@@ -1,4 +1,6 @@
+//error: can't define a protocol in a header.  Do it in a protocol spec instead.
+
 protocol noProtocolInHeader {
 child:
     async __delete__();
 };
--- a/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl
+++ b/ipc/ipdl/test/ipdl/error/oldIncludeSyntax.ipdl
@@ -1,5 +1,7 @@
+//error: bad syntax near `Foo.ipdl'
+
 include protocol "Foo.ipdl";
 
 protocol oldIncludeSyntax {
 child: async __delete__(X x);
 };
--- a/ipc/ipdl/test/ipdl/error/parser.ipdl
+++ b/ipc/ipdl/test/ipdl/error/parser.ipdl
@@ -1,1 +1,3 @@
+//error: bad syntax near `???'
+
 protocol parser {
--- a/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl
+++ b/ipc/ipdl/test/ipdl/error/redeclMessage.ipdl
@@ -1,8 +1,11 @@
+//error: message name `Msg' already declared as `MessageType'
+//error: redeclaration of symbol `Msg', first declared at
+
 protocol redeclMessage {
 
     // can't declare two messages with the same name
 
 child:
     async Msg();
     async Msg();
 
--- a/ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl
+++ b/ipc/ipdl/test/ipdl/error/redeclParamReturn.ipdl
@@ -1,7 +1,9 @@
+//error: redeclaration of symbol `f', first declared at
+
 sync protocol redeclParamReturn {
 
     // it's an error to name a parameter with the same id as a return
 
 parent: async Msg(int f) returns (bool f);
 
 };
--- a/ipc/ipdl/test/ipdl/error/redeclScope.ipdlh
+++ b/ipc/ipdl/test/ipdl/error/redeclScope.ipdlh
@@ -1,8 +1,10 @@
+//error: redeclaration of symbol `Foo', first declared at
+
 struct Foo {
   bool b;
 };
 
 struct Bar {
   // This should produce an error saying that Foo is a redeclaration,
   // even though the initial declaration was in a different frame of
   // the symbol table.
--- a/ipc/ipdl/test/ipdl/error/shmem.ipdl
+++ b/ipc/ipdl/test/ipdl/error/shmem.ipdl
@@ -1,5 +1,8 @@
+//error: redeclaration of symbol `Shmem', first declared at
+//error: redeclaration of symbol `mozilla::ipc::Shmem', first declared at
+
 using class mozilla::ipc::Shmem from "mozilla/ipc/Shmem.h";      // redeclaration
 
 protocol shmem {
 child: async Msg(Shmem s);
 };
--- a/ipc/ipdl/test/ipdl/error/structRedecl.ipdl
+++ b/ipc/ipdl/test/ipdl/error/structRedecl.ipdl
@@ -1,8 +1,10 @@
+//error: redeclaration of symbol `a', first declared at
+
 struct Redecl {
     int a;
     double a;
 };
 
 protocol structRedecl {
 child: async __delete__();
 };
--- a/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl
+++ b/ipc/ipdl/test/ipdl/error/structUnknownField.ipdl
@@ -1,7 +1,9 @@
+//error: field `i' of struct `S' has unknown type `Foobers'
+
 struct S {
     Foobers i;
 };
 
 protocol structUnknownField {
 child: async __delete__(S s);
 };
--- a/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl
+++ b/ipc/ipdl/test/ipdl/error/syncMessageCompress.ipdl
@@ -1,4 +1,6 @@
+//error: message `foo' in protocol `syncMessageCompress' requests compression but is not async
+
 sync protocol syncMessageCompress {
 parent:
     sync foo() compress;
 };
--- a/ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl
+++ b/ipc/ipdl/test/ipdl/error/syncParentToChild.ipdl
@@ -1,6 +1,8 @@
+//error: sync parent-to-child messages are verboten (here, message `Msg' in protocol `syncParentToChild')
+
 intr protocol syncParentToChild {
 
     // can't declare sync parent-to-child messages
 child: sync Msg();
 
 };
--- a/ipc/ipdl/test/ipdl/error/tooWeakIntrAsync.ipdl
+++ b/ipc/ipdl/test/ipdl/error/tooWeakIntrAsync.ipdl
@@ -1,7 +1,9 @@
+//error: message `Msg' requires more powerful send semantics than its protocol `tooWeakIntrAsync' provides
+
 protocol tooWeakIntrAsync {
 
     // it's an error to declare an async protocol with an intr message
 
 parent: intr Msg();
 
 };
--- a/ipc/ipdl/test/ipdl/error/tooWeakIntrSync.ipdl
+++ b/ipc/ipdl/test/ipdl/error/tooWeakIntrSync.ipdl
@@ -1,6 +1,8 @@
+//error: message `Msg' requires more powerful send semantics than its protocol `tooWeakIntrSync' provides
+
 sync protocol tooWeakIntrSync {
 
     // it's an error to declare a sync protocol with an interrupt message
 parent:
   intr Msg();
 };
--- a/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl
+++ b/ipc/ipdl/test/ipdl/error/tooWeakSyncAsync.ipdl
@@ -1,7 +1,9 @@
+//error: message `Msg' requires more powerful send semantics than its protocol `tooWeakSyncAsync' provides
+
 protocol tooWeakSyncAsync {
 
     // it's an error to declare an async protocol with a sync message
 
 parent:  sync Msg();
 
 };
--- a/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl
+++ b/ipc/ipdl/test/ipdl/error/twoprotocols.ipdl
@@ -1,9 +1,11 @@
 // it's an error to define two protocols in the same file
 
+//error: only one protocol definition per file
+
 protocol p1 {
 child: async Msg();
 };
 
 protocol p2 {
 child: async Msg();
 };
--- a/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl
+++ b/ipc/ipdl/test/ipdl/error/undeclParamType.ipdl
@@ -1,5 +1,7 @@
+//error: argument typename `FARGLEGARGLE' of message `Msg' has not been declared
+
 protocol undeclParamType {
 
 child: async Msg(FARGLEGARGLE p);
 
 };
--- a/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl
+++ b/ipc/ipdl/test/ipdl/error/undeclProtocol.ipdl
@@ -1,6 +1,9 @@
+//error: protocol `undeclared', managed by `undeclProtocol', has not been declared
+//error: constructor declaration required for managed protocol `undeclared' (managed by protocol `undeclProtocol')
+
 protocol undeclProtocol {
     manages undeclared;
 
 child:
     async undeclared();
 };
--- a/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl
+++ b/ipc/ipdl/test/ipdl/error/undeclReturnType.ipdl
@@ -1,5 +1,7 @@
+//error: argument typename `FARGLEGARGLE' of message `Msg' has not been declared
+
 sync protocol undeclReturnType {
 
 child:  sync Msg() returns (FARGLEGARGLE r);
 
 };
--- a/ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl
+++ b/ipc/ipdl/test/ipdl/error/undefMutualRecStruct.ipdl
@@ -1,7 +1,11 @@
+//error: struct `X' is only partially defined
+//error: struct `Y' is only partially defined
+//error: struct `Z' is only partially defined
+
 struct X { Y y; };
 struct Y { Z z; };
 struct Z { X x; };
 
 protocol undefMutualRecStruct {
 child: async __delete__(X x);
 };
--- a/ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl
+++ b/ipc/ipdl/test/ipdl/error/undefMutualRecStructUnion.ipdl
@@ -1,7 +1,11 @@
+//error: struct `X' is only partially defined
+//error: union `Y' is only partially defined
+//error: struct `Z' is only partially defined
+
 struct X { Y y; };
 union Y { Z; };
 struct Z { X x; };
 
 protocol undefMutualRecStructUnion {
 child: async __delete__(X x);
 };
--- a/ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl
+++ b/ipc/ipdl/test/ipdl/error/undefMutualRecUnion.ipdl
@@ -1,7 +1,11 @@
+//error: union `X' is only partially defined
+//error: union `Y' is only partially defined
+//error: union `Z' is only partially defined
+
 union X { Y; };
 union Y { Z; };
 union Z { X; };
 
 protocol undefMutualRecUnion {
 child: async __delete__(X x);
 };
--- a/ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl
+++ b/ipc/ipdl/test/ipdl/error/undefSelfRecStruct.ipdl
@@ -1,5 +1,7 @@
+//error: struct `X' is only partially defined
+
 struct X { X x; };
 
 protocol undefSelfRecStruct {
 child: async __delete__(X x);
 };
--- a/ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl
+++ b/ipc/ipdl/test/ipdl/error/undefSelfRecUnion.ipdl
@@ -1,5 +1,7 @@
+//error: union `X' is only partially defined
+
 union X { X; };
 
 protocol undefSelfRecUnion {
 child: async __delete__(X x);
 };
--- a/ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl
+++ b/ipc/ipdl/test/ipdl/error/unknownIntrMessage.ipdl
@@ -1,4 +1,6 @@
+//error: Unknown sync IPC message unknownIntrMessage::Msg
+
 intr protocol unknownIntrMessage {
 parent:
     intr Msg();
 };
--- a/ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl
+++ b/ipc/ipdl/test/ipdl/error/unknownSyncMessage.ipdl
@@ -1,4 +1,6 @@
+//error: Unknown sync IPC message unknownSyncMessage::Msg
+
 sync protocol unknownSyncMessage {
 parent:
     sync Msg();
 };
--- a/ipc/ipdl/test/ipdl/runtests.py
+++ b/ipc/ipdl/test/ipdl/runtests.py
@@ -9,16 +9,23 @@ class IPDLTestCase(unittest.TestCase):
         self.filename = filename
         self.compile = IPDLCompile(filename, ipdlargv)
 
     def test(self):
         self.compile.run()
         self.assertFalse(self.compile.exception(), self.mkFailMsg())
         self.checkPassed()
 
+    def mkCustomMsg(self, msg):
+        return '''
+### Command: %s
+### %s
+### stderr:
+%s''' % (' '.join(self.compile.argv), msg, self.compile.stderr)
+
     def mkFailMsg(self):
         return '''
 ### Command: %s
 ### stderr:
 %s'''% (' '.join(self.compile.argv), self.compile.stderr)
 
     def shortDescription(self):
         return '%s test of "%s"'% (self.__class__.__name__, self.filename)
@@ -37,18 +44,35 @@ The IPDL compiler should not produce err
 
 class ErrorTestCase(IPDLTestCase):
     '''An invocation of the IPDL compiler on an *invalid* specification.
 The IPDL compiler *should* produce errors but not exceptions.'''
 
     def __init__(self, ipdlargv, filename):
         IPDLTestCase.__init__(self, ipdlargv, filename)
 
+        # Look for expected errors in the input file.
+        f = open(filename, 'r')
+        self.expectedErrorMessage = []
+        for l in f:
+            if l.startswith("//error:"):
+                self.expectedErrorMessage.append(l[2:-1])
+        f.close()
+
+
     def checkPassed(self):
-        self.assertTrue(self.compile.error(), self.mkFailMsg())
+        self.assertNotEqual(self.expectedErrorMessage, [],
+                            self.mkCustomMsg("Error test should contain at least " +
+                                             "one line starting with //error: " +
+                                             "that indicates the expected failure."))
+
+        for e in self.expectedErrorMessage:
+            self.assertTrue(self.compile.error(e),
+                            self.mkCustomMsg('Did not see expected error "' +
+                                             e + '"'))
 
 
 if __name__ == '__main__':
     import sys
 
     okdir = sys.argv[1]
     assert os.path.isdir(okdir)
     errordir = sys.argv[2]