Module eqc_socket

This module provides functions for testing software via a socket interface.

Copyright © Quviq AB, 2007-2008

Authors: Thomas Arts.

Description

This module provides functions for testing software via a socket interface. The module eqc_socket is with advantage used together with eqc_statem. A state machine defined by eqc_statem can be used as test model for software that is accessed via a socket interface, i.e., the software undertest is accessed via a socket interface and the model used to test it is defined as a state machine.

The functions in this module provide a process interface to a socket. Instead of directly communicating to a socket, one creates a process that "owns" the socket by connect. One can send data to the socket via send and send_receive, the first for asynchronous and the second for synchronous communication. A trace of messages send from and to the socket is collected by the process "owning" the socket. This trace can be obtained by get_trace and arbitrary events can be added to the trace by event.

The process communicating with the socket is using the standard gen_tcp commands, for details on options that can be provided we refer to the documentation of gen_tcp and inet.

Example of a property using eqc_socket in combination with eqc_statem:

 ?FORALL(Cmds,commands(?MODULE),
         begin
           Socket = eqc_socket:connect("localhost",4567,[binary]),
           {H,S,Res} == run_commands(?MODULE,Cmds,[{socket,Socket}]),
           Trace = eqc_socket:get_trace(Socket),
           eqc_socket:close(Socket),
           Res==ok andalso valid(Trace)
         end)</pre>

Traces

In case you test software by communicating with it over a socket, it may be the case that there is no one-to-one map from each sent message to each message received. The application under test may sent spontaneous information to the socket or it may send no answer at all to certain messages. In case of this kind of asynchronous communication, one sends a sequence of messages and analyses the received sequence thereafter by validating whether the received sequence makes sense given the sent sequence.

The sequence of all messages sent and received together with user defined events is stored in a list, called trace. The data type for events is a record #eqc_socket_event and access functions to this record are defined in this module, viz. event_origin, event_abstract, event_content, and event_timestamp.

Abstractions

Analysis of traces is less work if there is a simple abstraction of events (or messages) present in the trace. For example, analysing a sequence add, add, modify, sub, sub on whether or not we always subtract as many items as we add is relatively easy. However, if the atoms in that sequence are textual XML messages that need a regular expression map to see whether it is an addition or subtraction, then we have to program more and perform more work.

Therefore, traces have a representation for both abstract and concrete form of each message and event. Of course, an abstraction has to be provided manually. When sending messages and events one can specify this abstraction directly. For received messages, abstraction has to be defined separately.

Function Index

close/1Closes the socket connection and terminates the process that "owned" the open connection.
connect/3Creates a process with an open socket connection.
event/2Equivalent to event(Socket, Event, Event).
event/3Adds an event to the trace created by the socket "owner".
event_abstract/1return event data: abstraction of the event.
event_content/1return event data: real data in event.
event_origin/1return event data: originator of the event.
event_timestamp/1return event data: timestamp of the event.
get_trace/2Returns messages send and received by the socket plus addional events added by event.
send/2Equivalent to send(Socket, send, Message).
send/3Send a message to the specified Socket.
send_receive/2Equivalent to send_receive(Socket, send, Message).
send_receive/3Send a message to the specified Socket and wait for a reply message.

Function Details

close/1

close(Socket::pid()) -> ok | {error, Reason::posix()}

Closes the socket connection and terminates the process that "owned" the open connection. Note that trace data cannot be collected after that the socket has been closed!

connect/3

connect(Host::ip_address(), Port::integer(), Options::[option()]) -> pid()

Creates a process with an open socket connection. The socket is connected by calling gen_tcp:connect/3. This function has a timeout of 1 second on trying to create the connection.

event/2

event(Socket::pid(), Event::term()) -> ok

Equivalent to event(Socket, Event, Event).

event/3

event(Socket::pid(), AbsEvent::term(), Event::term()) -> ok

Adds an event to the trace created by the socket "owner". The function has a timeout of 1 second in case the socket owner does not respond.

event_abstract/1

event_abstract(Eqc_socket_event::eqc_socket_event()) -> term()

return event data: abstraction of the event.

event_content/1

event_content(Eqc_socket_event::eqc_socket_event()) -> term()

return event data: real data in event.

event_origin/1

event_origin(Eqc_socket_event::eqc_socket_event()) -> term()

return event data: originator of the event.

event_timestamp/1

event_timestamp(Eqc_socket_event::eqc_socket_event()) -> term()

return event data: timestamp of the event.

get_trace/2

get_trace(Socket::pid(), Delay::int()) -> trace()

Returns messages send and received by the socket plus addional events added by event. The delay specified is time in milliseconds that the process waits before it returns all messages that it the socket has received.

Value for the delay is application and connection specific, one can start with a value of 200 and see whether the application responds sufficiently fast. One can also generate a timeout value in the property by using ?LET(TimeOut,choose(0,1000), 1000-TimeOut). This value can be used to check a simple property, e.g., that after sending a message has been received. This value will shrink to least neccessary timeout to receive a response.

send/2

send(Socket::pid(), Message::binary() | string()) -> ok | {error, posix()}

Equivalent to send(Socket, send, Message).

send/3

send(Socket::pid(), AbstractMessage::term(), Message::binary() | string()) -> ok | {error, posix()}

Send a message to the specified Socket. Make sure the message is binary in case socket expects a binary. AbstractMessage is an abstraction of the message that will be included in the trace. The value returned is the same as returned by send in gen_tcp. The function has a timeout of 1 second in case the socket owner does not respond.

send_receive/2

send_receive(Socket::pid(), Message::binary() | string()) -> {ok, Reply::binary() | string()} | {error, posix()}

Equivalent to send_receive(Socket, send, Message).

send_receive/3

send_receive(Socket::pid(), AbstractMessage::term(), Message::binary() | string()) -> {ok, Reply::binary() | string()} | {error, posix()}

Send a message to the specified Socket and wait for a reply message. Make sure the message is binary in case socket expects a binary. AbstractMessage is an abstraction of the message that will be included in the trace. The error value returned is the same as returned by send in gen_tcp. The function has a timeout of 1 second in case the socket owner or application does not respond.


Generated by EDoc, May 22 2009, 17:01:01.