FitNesse
.
UserGuide
.
SliM
.
SlimProtocol
Edit Page:
!2 The Slim Protocol !note !4 (V0.0) {{{ instruction list +----------+ o---> +------------+ +----------+ +-----+ | FitNesse |---[socket]-->| SlimServer |----->| Fixtures |------>| SUT | +----------+ <---o +------------+ +----------+ +-----+ response list }}} !-FitNesse-! communicates with Slim over a socket (see >PortManagement). When you hit the ''Test'' button, !-FitNesse-! starts up a !-SlimServer-! process by issuing the appropriate command line. !-FitNesse-! then sends a list of instructions to the !-SlimServer-!, and expects a list of responses back. The instructions are things like ''call function f(a,b,c)'' or ''make instance of class X with arguments p,q,r''. The responses are simply the values that are returned by the instructions. Each instruction in the list is itself a list of strings. Here is a typical instruction list: {{{ [ [id_0, make, instance, fixture, argument], [id_1, call, instance, f, 3], ]}}} The first instruction in this list tells the !-SlimServer-! to create an instance of a class named ''fixture'' using the constructor argument ''argument'', and register the newly created instance under the name ''instance''. The next instruction causes the function ''f'' to be called on the instance ''instance'', passing the value ''3''. The first column in each instruction is simply an instruction id. The response to this instruction list might look like this: {{{ [ [id_0, OK], [id_1, 9] ] }}} Again, each element of the response list is itself a list of strings. The first string in each response is the id of the instruction being responded to. The second is the response value. In this case the construction in instruction ''id_0'' was successful, and the call to function ''f'' with value ''3'' in instruction ''id_1'' returned a ''9''. That's pretty much it. Lists of instructions go out. Lists of responses come back. Typically the instructions for an entire test page will be sent in one large list, yielding one large response list. There is no type information in the instructions. Each instruction is a list of strings. Each response is a list of strings. Strings and lists are the only two types in the entire protocol. It is up to the !-SlimExecutor-! to find the functions and constructors that match the instructions, and to do the necessary type conversion. !3 The Instructions There are four instructions in the Slim protocol. import, make, call, and callAndAssign. That's all. * Import: [''<id>'', ''import'', ''<path>''] This instruction causes the <path> to be added to the search path for fixtures. In java <path> gets added to the ''CLASSPATH''. In ''.NET'', the <path> would name a dll. You can send as many imports as you like. The Slim system will use all imported paths to find fixture classes. This instruction returns ''OK''. * Make: [''<id>'', ''make'', ''<instance>'', ''<class>'', ''<arg>...''] This instruction causes slim to search for a class named ''<class>'' in the imported search paths. ''<class>'' can also be fully qualified. If found slim looks for a constructor with the right number of arguments. If found, the ''<arg>'' strings are converted to the appropriate types, and the constructor is called. The newly create instance is registered under the name ''<instance>'', and the instruction returns ''OK''. * Call: [''<id>'',''call'',''<instance>'',''<function>'',''<arg>...''] This instruction causes slim to find a function named ''<function>'' in the class of the object registered under ''<instance>''. The function must have the same number of arguments as the instruction. If found, each argument in the instruction is converted to the appropriate type, and then the function is called on the instance. If the function returns a value, it is converted to a string and returned. Otherwise the instruction returns the string: ''/__VOID__/''. * !-CallAndAssign-!: [''<id>'', ''callAndAssign'', ''<symbol>'', ''<instance>'', ''<function>'', ''<arg>...''] This instruction is identical to ''call'' except that the return value is saved in a symbol named ''<symbol>''. That symbol is kept in a dictionary in the SLIM system. Symbol names may only contain letters. !4 Symbols That last one was probably puzzling. Symbols are just strings that are kept in a dictionary. The ''callAndAssign'' instruction is the only thing that can create a symbol. Symbols are used in in the ''<arg>'' strings of the ''make'', ''call'', and ''callAndAssign'' instructions. If one of those ''<arg>'' strings contains a $ followed by a symbol name (as in $V), and if the symbol has been assigned, then that string will be replaced by the value of the symbol. What this means is that the !-FitNesse-! side can tell Slim to remember a value in a symbol, and then to use that value later. !4 Strings and Lists As we will see, slim views a list as a special kind of string. Therefore functions can take and return lists as well as strings. The lists must be lists of strings, but since a list is a special kind of string, lists of lists of lists of ... are possible. The Slim executor will convert back and forth between these forms as needed. A string is encoded as six digits followed by a colon, followed by the characters of the string. The six digits are the number of characters in the string, not including the digits themselves. Thus, the empty string is "000000:". This length encoding scheme is used in other places so we'll use the token ''<length>'' to mean six digits followed by a colon. If a string is ''<null>'', then the four character string ''null'' will replace it. A list is encoded as a string that begins with a '[', followed by a ''<length>'' specifying the number of items in the list. This is followed by that many strings, each terminated by a colon, and then finally a ']' Thus, this list: ''[hello,world]'' is encoded as the following string: {{{000035:[000002:000005:hello:000005:world:]}}} Take careful note of all the colons and counts. Colons are terminators not separators. As you can see, each item of a list is a string. But since a string can encode a list, each item of a list can be another list. So we can have very deep recursive definitions. !5 !note You might think that 6 digits is plenty; but the last few decades have shown us that a megabyte isn't even close to infinite. So you'll want to keep that in mind. !4 Slim Server. So when you send a list of instructions, what you are really sending is a string. When you receive a list of responses, what you are really receiving is a string. So the high level protocol of Slim is just strings. It looks like this: 1 !-FitNesse-! invokes the Slim Server via a command line. One of the command line arguments is the port number of the socket to listen on. !-FitNesse-! opens that socket. 2 Slim Server responds with the string "Slim -- ''V<version>''\n", where ''<version>'' is the version number of the slim ''protocol''. If this protocol ever changes, ''that'' version number will change. This is the only string that is ever sent without the ''<length>'' encoding. It is terminated by the '''\n''' instead. Every other message that slim sends will be prefixed by a ''<length>'' in ''!style_red(bytes)'', followed by a colon. !style_red(NOTE:) Every other length in this document is in UTF-8 ''characters''. This one length is in bytes. 3 !-FitNesse-! sends a list of instructions encoded as a string of course. 4 Slim Server sends a list of responses similarly encoded. 5 3 and 4 repeat until !-FitNesse-! sends a ''bye'' directive. This is simply the string ''bye'' properly encoded with ''<length>''. e.g. "000003:bye". 6 Slim Server shuts down. !4 Exceptions Sometimes a function or a constructor will throw an exception in response to a ''make'', ''call'', or ''callAndAssign'' instruction. When this happens, the response value for that instruction will be: "__EXCEPTION__:''<exception string>''". The ''<exception string>'' ought to be a stack trace or some other relevant debugging information. If you want a nice yellow message to appear in one of the SLIM tables, then somewhere in the ''<<exception string>>'' put ''message:<<'' in front of the message and ''>>'' after it. e.g. !style_code(message:<<can't find constructor>>) !5 Standard exception messages There are some standard exception messages that every Slim implementation should create. |COULD_NOT_INVOKE_CONSTRUCTOR ''<some class>''|Where ''<some class>'' is the name of the class whose instructor cannot be invoked.| |NO_METHOD_IN_CLASS ''<some method>'' ''<some class>''|Where ''<some method>'' is the name of the missing method.| |NO_CONSTRUCTOR ''<some class>''|Where ''<some class>'' is the name of the class that is missing the constructor.| |NO_CONVERTER_FOR_ARGUMENT_NUMBER ''<argument type>''|Where ''<argument type>'' is the class that has no corresponding converter.| |NO_INSTANCE ''<instance name>''|Where ''<instance name>'' is the name of the missing instance.| |NO_CLASS ''<some class>''|Where ''<some class>'' is the class that could not be found.| |MALFORMED_INSTRUCTION [''instruction list'']|Where ''instruction list'' is a comma separated list of the instruction strings.| !4 Aborting a Test If a fixture throws an exception with a ''class'' name that contains "!style_code(!-StopTest-!)", then Slim should stop executing the instructions in the current batch, and return immediately. The response for this type of exception should be "!style_code(__EXCEPTION__:ABORT_SLIM_TEST:)" which may have an optional suffix of: "!style_code(message:<<''reason''>>)". !4 Type Conversions The only types in the instructions and responses are lists and strings, and since the leaves of the lists must eventually be strings, all we really have to worry about are strings. But we don't want to restrict our fixtures to use only Strings. So Slim comes with some standard type converters that allow fixtures to take more convenient data types. !see DataTypes. !4 Conclusion That's pretty much it. If you want to port Slim to a new platform, I suggest you look at the code in the fitnesse.slim package. Pay special attention to the !-ListSerializer-! and !-ListDeserializer-! classes. Also check out the logic in Statement and !-StatementExecutor-! classes. The unit tests ought to be expecially educational. You should be able to build equivalent unit tests without much fuss. Finally, take a look at the unit tests in fitnesse.responders.run.slimResponder. These should all still run with your new port (although you'll have to replace the command line that invokes the Slim Server).
Hints:
Use alt+s (Windows) or control+s (Mac OS X) to save your changes. Or, tab from the text area to the "Save" button!
Grab the lower-right corner of the text area to increase its size (works with some browsers).