bug 518126: part 1 --- test the IPDL compiler in isolation. r=bsmedberg

This commit is contained in:
Chris Jones 2009-09-23 11:00:37 -05:00
parent b4b02694d7
commit 714b015109
63 changed files with 921 additions and 1 deletions

View File

@ -40,6 +40,9 @@ srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS += test
include $(topsrcdir)/config/rules.mk
IPDLDIRS = \

View File

@ -307,7 +307,7 @@ class GenerateProtocolHeader(Visitor):
enumvar = cxx.ExprVar(enumv)
unionname = 'V'+ cxxt.name
caselabel = cxx.CaseLabel(enumv)
fullcaselabel = cxx.CaseLabel(ud.decl.fullname +'::'+ enumv)
fullcaselabel = cxx.CaseLabel(ud.decl.type.fullname() +'::'+ enumv)
gettypen = 'get_'+ cxxt.name
gettypevar = cxx.ExprVar(gettypen)
returngettype = cxx.StmtReturn(cxx.ExprCall(cxx.ExprVar(gettypen)))

46
ipc/ipdl/test/Makefile.in Normal file
View File

@ -0,0 +1,46 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla Firefox.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation <http://www.mozilla.org/>.
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS += ipdl cxx
include $(topsrcdir)/config/rules.mk

10
ipc/ipdl/test/README.txt Normal file
View File

@ -0,0 +1,10 @@
There are two major categories of tests, segregated into different
top-level directories under test/.
The first category (ipdl/) is IPDL-compiler tests. These tests check
that the IPDL compiler is successfully compiling correct
specifications, and successfully rejecting erroneous specifications.
The second category (cxx/) is C++ tests of IPDL semantics. These
tests check that async/sync/rpc semantics are implemented correctly,
ctors/dtors behave as they should, etc.

View File

@ -0,0 +1,47 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla Firefox.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation <http://www.mozilla.org/>.
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
check::
echo "TODO: C++/IPDL unit tests"

View File

@ -0,0 +1,75 @@
import copy, re, os, subprocess, sys, tempfile
# We test the compiler indirectly, rather than reaching into the ipdl/
# module, to make the testing framework as general as possible.
class IPDLCompile:
def __init__(self, specfilename, ipdlargv=[ 'python', 'ipdl.py' ]):
self.argv = copy.deepcopy(ipdlargv)
self.specfilename = specfilename
self.stdout = None
self.stderr = None
self.returncode = None
def run(self):
'''Run |self.specstring| through the IPDL compiler.'''
assert self.returncode is None
tmpoutdir = tempfile.mkdtemp(prefix='ipdl_unit_test')
try:
self.argv.extend([
'-d', tmpoutdir,
self.specfilename
])
proc = subprocess.Popen(args=self.argv,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
self.stdout, self.stderr = proc.communicate()
self.returncode = proc.returncode
assert self.returncode is not None
finally:
for root, dirs, files in os.walk(tmpoutdir, topdown=0):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
os.rmdir(tmpoutdir)
if proc.returncode is None:
proc.kill()
def completed(self):
return (self.returncode is not None
and isinstance(self.stdout, str)
and isinstance(self.stderr, str))
def error(self):
'''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)
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 (0 == self.returncode))

View File

@ -0,0 +1,55 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla Firefox.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation <http://www.mozilla.org/>.
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
OKTESTS := $(wildcard $(srcdir)/ok/*.ipdl)
ERRORTESTS := $(wildcard $(srcdir)/error/*.ipdl)
check::
@$(PYTHON) $(srcdir)/runtests.py \
$(srcdir)/ok $(srcdir)/error \
$(PYTHON) $(topsrcdir)/config/pythonpath.py \
-I$(topsrcdir)/other-licenses/ply \
$(topsrcdir)/ipc/ipdl/ipdl.py \
OKTESTS $(OKTESTS) \
ERRORTESTS $(ERRORTESTS)

View File

@ -0,0 +1,9 @@
protocol actorparam_badState {
child:
Msg(actorparam_badState:FARGEL p);
state S1:
send Msg goto S1;
};

View File

@ -0,0 +1,7 @@
include protocol "I.DONT.EXIST.ipdl";
// error: nonexistent protocol ^^^
protocol badProtocolInclude {
child: Msg();
};

View File

@ -0,0 +1,7 @@
protocol conflictProtocolMsg {
// it's an error to re-use the protocol name as a message ID; these
// are reserved
child: conflictProtocolMsg();
};

View File

@ -0,0 +1,8 @@
protocol dtorReserved {
// it's an error to use dtor syntax for non-dtor messages
child:
~SomeMsg();
};

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
slkdjfl*&^*

View File

@ -0,0 +1,6 @@
include protocol "managerNoCtor.ipdl";
protocol managedNoCtor {
manager managerNoCtor;
// empty
};

View File

@ -0,0 +1,6 @@
include protocol "managerNoDtor.ipdl";
protocol managedNoDtor {
manager managerNoDtor;
// empty
};

View File

@ -0,0 +1,9 @@
include protocol "managedNoCtor.ipdl";
protocol managerNoCtor {
manages managedNoCtor;
parent:
// error: no ctor defined
~managedNoCtor();
};

View File

@ -0,0 +1,9 @@
include protocol "managedNoDtor.ipdl";
protocol managerNoDtor {
manages managedNoDtor;
parent:
managedNoDtor();
// error: no ctor defined
};

View File

@ -0,0 +1,5 @@
protocol noEmptyToplevel {
// it's an error for top-level protocols to be empty
};

View File

@ -0,0 +1 @@
protocol parser {

View File

@ -0,0 +1,29 @@
protocol race_OverlappingMultiOut {
child:
Msg1();
Msg1_();
parent:
Msg2();
Msg2_();
// *** ERROR: send/recv of Msg1/Msg2 in state S10 goes to overlapping
// sets { S11, S12 }, { S12, S13 } and thus can't be unidirectional
start state S10:
send Msg1 goto S11 or S12;
recv Msg2 goto S12 or S13;
state S11:
recv Msg2 goto S14;
state S12:
send Msg1 goto S14;
recv Msg2 goto S14;
state S13:
send Msg1 goto S14;
state S14:
send Msg1 goto S14;
recv Msg2 goto S14;
};

View File

@ -0,0 +1,25 @@
protocol race_ViolateSameDirection {
child:
Msg1();
Msg1_();
parent:
Msg2();
Msg2_();
// *** ERROR: state S7 doesn't have all-same-direction
start state S6:
send Msg1 goto S7;
recv Msg2 goto S8;
state S7:
recv Msg2 goto S9;
send Msg1 goto S9;
state S8:
send Msg1 goto S9;
state S9:
send Msg1 goto S9;
recv Msg2 goto S9;
};

View File

@ -0,0 +1,9 @@
protocol redeclMessage {
// can't declare two messages with the same name
child:
Msg();
Msg();
};

View File

@ -0,0 +1,7 @@
sync protocol redeclParamReturn {
// it's an error to name a parameter with the same id as a return
parent: Msg(int f) returns (bool f);
};

View File

@ -0,0 +1,11 @@
protocol redefState {
// error: redefining state in state machine
child:
Msg();
state S1: send Msg goto S1;
state S1: send Msg goto S1;
};

View File

@ -0,0 +1,12 @@
protocol repeatedOutState {
child: Msg();
// error: S2 repeated in multi-out set
state S1:
send Msg goto S2 or S2 or S4;
state S2: send Msg goto S2;
state S3: send Msg goto S3;
state S4: send Msg goto S4;
};

View File

@ -0,0 +1,6 @@
rpc protocol syncParentToChild {
// can't declare sync parent-to-child messages
child: sync Msg();
};

View File

@ -0,0 +1,7 @@
protocol tooWeakRPCAsync {
// it's an error to declare an async protocol with an rpc message
parent: rpc Msg();
};

View File

@ -0,0 +1,7 @@
protocol tooWeakSyncAsync {
// it's an error to declare an async protocol with a sync message
parent: sync Msg();
};

View File

@ -0,0 +1,9 @@
protocol trans_WrongDirection {
child:
Msg();
state S1:
recv Msg goto S1;
};

View File

@ -0,0 +1,9 @@
protocol trans_WrongDirection2 {
parent:
Msg();
state S1:
send Msg goto S1;
};

View File

@ -0,0 +1,9 @@
sync protocol trans_WrongDirection3 {
parent:
sync Msg();
state S1:
send Msg goto S1;
};

View File

@ -0,0 +1,9 @@
rpc protocol trans_WrongDirection4 {
child:
rpc Msg();
state S1:
answer Msg goto S1;
};

View File

@ -0,0 +1,9 @@
rpc protocol trans_WrongDirection5 {
parent:
rpc Msg();
state S1:
call Msg goto S1;
};

View File

@ -0,0 +1,9 @@
protocol trans_WrongName {
child:
Msg();
state S1:
call Msg goto S1;
};

View File

@ -0,0 +1,9 @@
protocol trans_WrongName2 {
parent:
Msg();
state S1:
answer Msg goto S1;
};

View File

@ -0,0 +1,9 @@
sync protocol trans_WrongName3 {
parent:
sync Msg();
state S1:
answer Msg goto S1;
};

View File

@ -0,0 +1,9 @@
rpc protocol trans_WrongName4 {
child:
rpc Msg();
state S1:
send Msg goto S1;
};

View File

@ -0,0 +1,9 @@
rpc protocol trans_WrongName5 {
parent:
rpc Msg();
state S1:
recv Msg goto S1;
};

View File

@ -0,0 +1,9 @@
// it's an error to define two protocols in the same file
protocol p1 {
child: Msg();
};
protocol p2 {
child: Msg();
};

View File

@ -0,0 +1,5 @@
protocol undeclParamType {
child: Msg(FARGLEGARGLE p);
};

View File

@ -0,0 +1,7 @@
protocol undeclProtocol {
manages undeclared;
child:
undeclared();
~undeclared();
};

View File

@ -0,0 +1,5 @@
sync protocol undeclReturnType {
child: sync Msg() returns (FARGLEGARGLE r);
};

View File

@ -0,0 +1,5 @@
protocol actorparam {
child: Msg(actorparam p);
};

View File

@ -0,0 +1,9 @@
protocol actorparam_state {
child:
Msg(actorparam_state:S1 p);
state S1:
send Msg goto S1;
};

View File

@ -0,0 +1,6 @@
sync protocol actorreturn {
parent:
sync Msg(actorreturn p) returns (actorreturn r);
};

View File

@ -0,0 +1,21 @@
protocol builtins {
// sanity-check that "essential" builtins are being declared
child: Msg(bool b,
char c,
int i,
long l,
float f,
double d,
int8_t i8t,
uint8_t u8t,
int16_t i16t,
uint16_t u16t,
int32_t i32t,
uint32_t u32t,
int64_t i64t,
uint64_t u64t);
};

View File

@ -0,0 +1,3 @@
protocol empty {
child: Msg();
};

View File

@ -0,0 +1,6 @@
include protocol "managerProtocol.ipdl";
protocol managedProtocol {
manager managerProtocol;
// empty
};

View File

@ -0,0 +1,12 @@
include protocol "managedProtocol.ipdl";
// sanity check of managed/manager protocols
protocol managerProtocol {
manages managedProtocol;
parent:
managedProtocol(int i);
~managedProtocol(int i);
};

View File

@ -0,0 +1,12 @@
protocol multiOutStates {
child: Msg();
// sanity check that multi-out-states are being processed correctly
state S1:
send Msg goto S2 or S3 or S4;
state S2: send Msg goto S2;
state S3: send Msg goto S3;
state S4: send Msg goto S4;
};

View File

@ -0,0 +1,11 @@
protocol multiStartState {
child: Msg();
start state S1:
send Msg goto S1;
start state S2:
send Msg goto S2;
};

View File

@ -0,0 +1,12 @@
namespace basic {
// sanity check of namespaced protocols
protocol namespace_Basic {
child:
Msg();
};
} // namespace basic

View File

@ -0,0 +1,9 @@
protocol noRedeclCrossMessage {
// each message has its own scope for param/return names
child:
Msg1(int f);
Msg2(int f);
};

View File

@ -0,0 +1,27 @@
protocol race_DiamondRule1 {
child:
Msg1();
Msg1_();
parent:
Msg2();
Msg2_();
// OK: this state machine is one of the simplest that follows the
// Diamond Rule
start state S1:
send Msg1 goto S2;
recv Msg2 goto S3;
state S2:
recv Msg2 goto S4;
recv Msg2_ goto S2;
state S3:
send Msg1 goto S4;
send Msg1_ goto S3;
state S4:
send Msg1 goto S4;
};

View File

@ -0,0 +1,61 @@
protocol race_KitchenSink {
child:
Msg1();
Msg1_();
parent:
Msg2();
Msg2_();
// concatenation of a few other state machines, should be OK
start state S1:
send Msg1 goto S2;
recv Msg2 goto S3;
state S2:
recv Msg2 goto S4;
recv Msg2_ goto S2;
state S3:
send Msg1 goto S4;
send Msg1_ goto S3;
state S4:
send Msg1 goto S4;
start state S5:
send Msg1 goto S5;
recv Msg2 goto S5;
start state S15:
send Msg1 goto S16 or S17;
recv Msg2 goto S18 or S19;
state S16:
recv Msg2 goto S20;
recv Msg2_ goto S18;
state S17:
recv Msg2 goto S20;
recv Msg2_ goto S15;
state S18:
send Msg1 goto S20;
send Msg1_ goto S15;
state S19:
send Msg1 goto S20;
send Msg1_ goto S16;
state S20:
send Msg1 goto S20;
send Msg1_ goto S20;
recv Msg2 goto S20;
recv Msg2_ goto S20;
};

View File

@ -0,0 +1,35 @@
protocol race_MultiOut {
child:
Msg1();
Msg1_();
parent:
Msg2();
Msg2_();
start state S15:
send Msg1 goto S16 or S17;
recv Msg2 goto S18 or S19;
state S16:
recv Msg2 goto S20;
recv Msg2_ goto S18;
state S17:
recv Msg2 goto S20;
recv Msg2_ goto S15;
state S18:
send Msg1 goto S20;
send Msg1_ goto S15;
state S19:
send Msg1 goto S20;
send Msg1_ goto S16;
state S20:
send Msg1 goto S20;
send Msg1_ goto S20;
recv Msg2 goto S20;
recv Msg2_ goto S20;
};

View File

@ -0,0 +1,16 @@
protocol race_Stateless {
// manages Child;
child:
Msg1();
Msg1_();
parent:
Msg2();
Msg2_();
// OK: this is trivial stateless protocol, so race-free "by definition"
start state S5:
send Msg1 goto S5;
recv Msg2 goto S5;
};

View File

@ -0,0 +1,13 @@
rpc protocol rpcProtocol {
// sanity check of RPC protocols
child:
AsyncMsg();
parent:
sync SyncMsg(int i) returns (int r);
both:
rpc RPCMsg(int x) returns (int y);
};

View File

@ -0,0 +1,11 @@
sync protocol syncProtocol {
// sanity check of sync protocols
child:
AsyncMsg();
parent:
sync SyncMsg() returns (int i);
};

View File

@ -0,0 +1,13 @@
protocol threeDirections {
// sanity check that the three direction specifiers are being accepted
child:
ChildMsg();
parent:
ParentMsg();
both:
BothMsg();
};

View File

@ -0,0 +1,11 @@
union Basic {
int;
double;
};
sync protocol union_Basic {
parent:
sync Msg(Basic p) returns (Basic r);
};

View File

@ -0,0 +1,18 @@
namespace kitties {
union Socks {
int;
double;
};
} // namespace kitties
namespace puppies {
protocol union_Namespaced {
child:
Msg(Socks s);
};
} // namespace puppies

View File

@ -0,0 +1,75 @@
import os, unittest
from IPDLCompile import IPDLCompile
class IPDLTestCase(unittest.TestCase):
def __init__(self, ipdlargv, filename):
unittest.TestCase.__init__(self, 'test')
self.filename = filename
self.compile = IPDLCompile(filename, ipdlargv)
def test(self):
self.compile.run()
self.assertFalse(self.compile.exception(), self.mkFailMsg())
self.checkPassed()
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)
class OkTestCase(IPDLTestCase):
'''An invocation of the IPDL compiler on a valid specification.
The IPDL compiler should not produce errors or exceptions.'''
def __init__(self, ipdlargv, filename):
IPDLTestCase.__init__(self, ipdlargv, filename)
def checkPassed(self):
self.assertTrue(self.compile.ok(), self.mkFailMsg())
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)
def checkPassed(self):
self.assertTrue(self.compile.error(), self.mkFailMsg())
if __name__ == '__main__':
import sys
okdir = sys.argv[1]
assert os.path.isdir(okdir)
errordir = sys.argv[2]
assert os.path.isdir(errordir)
ipdlargv = [ ]
oksuite = unittest.TestSuite()
errorsuite = unittest.TestSuite()
oktests, errortests = 0, 0
for arg in sys.argv[3:]:
if errortests:
errorsuite.addTest(ErrorTestCase(ipdlargv+ [ '-I', errordir ],
arg))
elif oktests:
if 'ERRORTESTS' == arg: errortests = 1; continue
oksuite.addTest(OkTestCase(ipdlargv+ [ '-I', okdir ],
arg))
else:
if 'OKTESTS' == arg: oktests = 1; continue
ipdlargv.append(arg)
(unittest.TextTestRunner()).run(
unittest.TestSuite([ oksuite, errorsuite ]))