ElementaryBlock Element/en: Unterschied zwischen den Versionen

Aus expecco Wiki (Version 2.x)
Zur Navigation springen Zur Suche springen
(183 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 8: Zeile 8:
The behavior of an elementary block is defined by one of the following:
The behavior of an elementary block is defined by one of the following:
* a piece of textual program code, which executes inside expecco, and is written in one of the directly supported and builtin [[Expecco API|programming languages]] (currently: Smalltalk or a JavaScript dialect)
* a piece of textual program code, which executes inside expecco, and is written in one of the directly supported and builtin [[Expecco API|programming languages]] (currently: Smalltalk or a JavaScript dialect)
* a piece of code, written in [[Expecco API#Groovy Elementary Blocks|Groovy]], which executes in a Java Virtual Machine (JVM) on the local system, a remote system or inside the system under test (SUT) itself (requires extra Java plugin)
* a piece of code, written in [[Expecco API#Groovy Elementary Blocks|Groovy]], which executes in a Java Virtual Machine (JVM) on the local system, a remote system or inside the system under test (SUT) itself (requires a Java installation)
* a piece of code, written in [[Expecco_API#Node.js_Elementary_Blocks|JavaScript]], which executes in a Node.js interpreter on the local or a remote system (bridged)
* a piece of code, written in [[Expecco_API#Node.js_.28Bridged.29_Elementary_Blocks |JavaScript]], which executes in a Node.js interpreter on the local or a remote system (bridged)
* a piece of code, written in [[Expecco_API#Bridged_Python_Elementary_Blocks|Python]], which executes in a Python interpreter on the local or a remote system (bridged)
* a piece of code, written in [[Expecco_API#Bridged_Python_Elementary_Blocks|Python]], which executes in a Python interpreter on the local or a remote system (bridged)
* a piece of code, written in [[Expecco API#IronPython Elementary Blocks|Python (IronPathon)]], which executes in a .NET Virtual Machine (CLR) on the local system, a remote system or inside the system under test (SUT) itself (requires extra DotNET plugin)
* a piece of code, written in [[Expecco API#IronPython Elementary Blocks|Python (IronPython)]], which executes in a .NET Virtual Machine (CLR) on the local system, a remote system or inside the system under test (SUT) itself (requires a mono installation on non-Windows systems)
* a piece of code, written in [[Expecco API#CSharp Elementary Blocks|C# (CSharp)]], which executes in a .NET Virtual Machine (CLR) on the local system, a remote system or inside the system under test (SUT) itself (requires extra DotNET plugin)
* a piece of code, written in [[Expecco API#CSharp Elementary Blocks|C# (CSharp)]], which executes in a .NET Virtual Machine (CLR) on the local system, a remote system or inside the system under test (SUT) itself (requires a mono installation on non-Windows systems)
* a piece of code, written in [[Expecco API#Bridged_C_Elementary_Blocks |C or C++]], which executes in a cBridge executable on the local system, a remote system or inside the system under test (SUT) itself (requires an additional DLL to be linked or loaded in the target)
* a piece of code, written in [[Expecco API#VisualBasic Elementary Blocks|VisualBasic Script]], which executes in a separate script interpreter process (requires extra VB plugin)
* a piece of code, written in [[Expecco API#VisualBasic Elementary Blocks|VisualBasic Script]], which executes in a separate script interpreter process (requires extra VB plugin)
* a piece of code, which is executed by an external scripting language interpreter (shell, batch, node, python, ruby, gnuplot, tcl, go or any other scripting language)
* a piece of code, which is executed by an external scripting language interpreter (shell, batch, appleScript, node, python, ruby, tcl, go or any other scripting language)
* a gnuplot script to generate plots and graphs
* a gnuplot script to generate plots and graphs
* an 'R' language script; both for statistical analysis and to generate plots and graphs
* an 'R' language script; both for statistical analysis and to generate plots and graphs
Zeile 22: Zeile 23:


=== Builtin Languages ===
=== Builtin Languages ===
Expecco provides built in language interpreters and compilers for two scripting languages: the builtin Smalltalk and a builtin JavaScript-like scripting language.<br>When such an elementary block is executed, the script code runs inside the expecco process. These support high level symbolic debugging including single-step, breakpoints or inspection of variables.
Expecco provides built in language interpreters and compilers for two scripting languages: the builtin Smalltalk and a builtin JavaScript-dialect.<br>When such an elementary block is executed, the script code runs inside the expecco process. These support high level symbolic debugging including single-step, breakpoints or inspection of variables.
The debugger even allows for code changes to be done to the running program (for example, while the program is stopped at a breakpoint). These internal scripts are transparently and automatically compiled into machine code when first executed ("''hot swap''" and "''Just in Time''" (JIT) compilation).
The debugger even allows for code changes to be done to the running program (for example, while the program is stopped at a breakpoint). These internal scripts are transparently and automatically compiled into machine code when first executed ("''hot swap''" and "''Just in Time''" (JIT) compilation).
Changed code will be executed when the function/block is called the next time; thus, there is a limitation, in that a suspended function's code has to either proceed and return or be aborted for the new code to become effective (but only that particular function/elementary block - not the whole execution).
Changed code will be executed when the function/block is called the next time; thus, there is a limitation, in that a suspended function's code has to either proceed and return or be aborted for the new code to become effective (but only that particular function/elementary block - not the whole execution).
Zeile 31: Zeile 32:


=== Bridged Actions ===
=== Bridged Actions ===
In addition to "''script execution''" as described below, expecco also allows "''Bridged Execution''" of code which is loaded into and executed in an external language interpreter. In contrast to script execution, the bridge-partner is a running program which communicates with expecco and continues to run after an action's execution. Therefore, any state created there will remain alive as long as the bridge-partner and the communication channel to it are still alive. Thus, code can be defined in expecco to be executed inside a running "Java", "C#", "Node.js" or "Python" program. Object references can be exchanged between the bridge-partner and expecco. This makes it possible to get object handles from a bridge partner and pass it back to the bridge later. Typical uses are communication protocol handles or API-entries to trigger actions inside the bridge partner.
In addition to "''Script Execution''" as described below, expecco also allows "''Bridged Execution''" of code which is loaded into and executed in an external language interpreter. In contrast to script execution, the bridge-partner is a running program which communicates with expecco and continues to run after an action's execution.


Therefore, any state created there will remain alive as long as the bridge-partner and the communication channel to it are still alive. Code can be defined in expecco to be executed inside a running "Java", "C#", "Node.js", "Python" and even "C/C++" program. Object references (handles) can be exchanged between the bridge-partner and expecco. This makes it possible to get object handles from a bridge partner and pass them back to the bridge later to another action. Typical uses are communication protocol handles or API-entries to trigger actions inside the bridge partner.
Bridged action blocks provide a pin-interface similar to the above described builtin actions, where values are passed to and from the action. However, not all bridges support all datatypes; some bridge-language specific limitations may apply. Also, notice, that any objects which were allocated (instantiated) in a bridge-partner will become void, when either the bridge partner is terminated or the connection to it is broken. Of course, this also means, that bridge objects cannot be recovered from the execution log (only the printed representations will be stored in it).

Bridged action blocks provide a pin-interface similar to the above described builtin actions, where values are passed to and from the action. However, not all bridges support all datatypes - some bridge-language specific limitations may apply.

Also, notice, that handles to objects which were allocated (instantiated) inside a bridge-partner will become void, when either the bridge partner is terminated or the connection to it is broken.

Of course, this also means, that bridge objects cannot be recovered from the execution log (only the printed representations will be stored in it).


==== Groovy Language ====
==== Groovy Language ====
With the [[Java Interface Library v2 | ''Java Bridge extension plugin'']] (now included as part of the base system), elementary blocks can be written in Groovy, which is a scripting language with a syntax similar to Java. These actions are transparently (and dynamically) compiled to Java bytecode and executed inside the SUT or on the expecco host or on any other such configured machine in the local network. Groovy blocks are very useful to define Java callbacks, subclasses of existing Java classes or to create and manipulate complicated Java objects inside a tested Java application.
With the [[Java Interface Library v2 | ''Java Bridge extension plugin'']] (now included as part of the base system), elementary blocks can be written in Groovy, which is a scripting language with a syntax similar to Java. These actions are transparently (and dynamically) compiled to Java bytecode and executed inside the SUT or on the expecco host or on any other such configured machine in the local network. Groovy blocks are very useful to define Java callbacks, subclasses of existing Java classes or to create and manipulate complicated Java objects inside a tested Java application. With groovy actions, any JAR can be accessed and any code inside can be called from expecco actions.


When such a block is executed, the script code runs inside a Java VM, which can be either a local one, dedicated to script execution, or on the target system (even inside the SUT, if it is written in Java).
When such a block is executed, the script code runs inside a Java VM, which can be either a local one, dedicated to script execution, or on the target system (even inside the SUT, if it is written in Java).
More detail is found in the [[Expecco_API#Groovy_Elementary_Blocks | ''Expecco API Documentation'']].
<br>More detail is found in the [[Expecco_API#Groovy_Elementary_Blocks | ''Expecco API Documentation'']].

==== IronPython and C# Languages ====
With the [[DotNET Interface Library | ''DotNET Bridge extension plugin'']] (now included as part of the base system), elementary blocks can be written in IronPython or C#. Similar to the above Groovy code, this is transparently (and dynamically) compiled, and executed inside the SUT or on the expecco host or on any other such configured machine in the local network. DotNET blocks are useful to access internals of the SUT or to interface to systems for which DotNET bindings (libraries) are available.
More detail is found in the [[Expecco_API#DotNET_Elementary_Blocks | ''Expecco API Documentation'']].


==== Bridged NodeJS Actions ====
==== Bridged NodeJS Actions ====
Elementary blocks can also be written as bridged Node actions. These are executed in a NodeJS interpreter (either on the local or a remote machine).
Elementary blocks can also be written as bridged Node actions. These are executed in a NodeJS interpreter (local or remote).
In contrast to NodeJS-Script actions, a single NodeJS interpreter is used and objects or processes created there remain alive until the interpreter connection is closed.
In contrast to NodeJS-Script actions, a single NodeJS interpreter is used and objects or processes created there remain alive until the interpreter connection is closed.
This makes it possible to run servers or applications inside a NodeJS interpreter, and interact with it throughout the expecco session.
This makes it possible to run servers or applications inside a NodeJS interpreter, and interact with it throughout the expecco session.
More detail is found in the chapter on Node.js actions in [[Expecco_API#Node.js_.28Bridged.29_Elementary_Blocks | ''Expecco API Documentation'']].
<br>More detail is found in the chapter on Node.js actions in [[Expecco_API#Node.js_.28Bridged.29_Elementary_Blocks | ''Expecco API Documentation'']].


==== Bridged Python Actions ====
==== Bridged Python Actions ====
Similar to bridged node actions, these are executed in a Python interpreter (either on the local or a remote machine).
Similar to bridged node actions, these are executed in a Python or Jython interpreter (local or remote).
In contrast to Python-Script actions, a single Python interpreter is used and objects or processes created there remain alive until the interpreter connection is closed.
In contrast to Python-Script actions, a single Python interpreter is used and objects or processes created there remain alive until the interpreter connection is closed.
This makes it possible to run servers or applications inside a Python interpreter, and interact with it throughout the expecco session.
This makes it possible to run servers or applications inside a Python interpreter, and interact with it throughout the expecco session.
More detail is found in the chapter on Python actions in [[Expecco_API#Bridged_Python_Elementary_Blocks | ''Expecco API Documentation'']].
<br>More detail is found in the chapter on Python actions in [[Expecco_API#Bridged_Python_Elementary_Blocks | ''Expecco API Documentation'']].

==== C and C++ Languages ====
Elementary blocks can also be written in low level C or C++ (starting with expecco 19.2) . Similar to the above Groovy, Node and Python code, this is transparently (and dynamically) compiled, and executed inside a C program, given that the program has the cBridge library linked in or dynamically loaded. C/C++ blocks are useful for time critical operations or to interface to special hardware for which DLL calls are not sufficient or too complicated to implement.
<br>More detail is found in the chapter on C/C++ actions in the [[Expecco_API#Bridged C Elementary Blocks | ''Expecco API Documentation'']].

==== IronPython and C# Languages ====
With the [[DotNET Interface Library | ''DotNET Bridge extension plugin'']] (now included as part of the base system), elementary blocks can be written in IronPython or C#. Similar to the above Groovy code, this is transparently (and dynamically) compiled, and executed inside the SUT or on the expecco host or on any other such configured machine in the local network. DotNET blocks are useful to access internals of the SUT or to interface to systems for which DotNET bindings (libraries) are available.
<br>More detail is found in the [[Expecco_API#DotNET_Elementary_Blocks | ''Expecco API Documentation'']].


=== VBScript Language ===
=== VBScript Language ===
With the [[VBScript | VisualBasic Plugin]], elementary blocks can be written in the VisualBasic Script syntax and are executed in an external VisualBasic interpreter. This may run on the local host (the one on which expecco runs) or optionally on a remote host. The host on which the script engine executes must be a Windows host. For expecco users working under Unix/Linux or OSX, this means that at least a virtual (or real) machine with a Windows system must be setup and reachable in the network, and that the Windows machine serves as a script execution server for the Unix machine.
With the [[VBScript | VisualBasic Plugin]], elementary blocks can be written in VisualBasic and are executed in an external VisualBasic interpreter. This may run on the local host (the one on which expecco runs) or optionally on a remote host. The host on which the script engine executes must be a Windows host. For expecco users working under Unix/Linux or OSX, this means that at least a virtual (or real) machine with a Windows system must be setup and reachable in the network, and that the Windows machine serves as a script execution server for the Unix machine.


=== Script Languages ===
=== Script Languages ===
Many scripting languages (Ruby, Perl, TCL, Python, Node, Gnuplot etc.) can be called via external script interpreters. It is also possible to call existing command line scripts (Shell under Unix or Windows+cygwin, Batch under Windows).
Many scripting languages (Ruby, Perl, TCL, Python, Node, Gnuplot etc.) can be called via external script interpreters. It is also possible to call existing command line scripts (Shell under Unix or Windows+cygwin, Batch under Windows).
In contrast to bridged execution, these ''script executions'' will start a new interpreter for every such action invocation. Thus, it is not possible to retain state (i.e. handles or objects) from a script-action call.
In contrast to bridged execution, these ''script executions'' will start a new interpreter for every such action invocation. Thus, it is not possible to retain or pass state (i.e. handles or objects) across script-action calls. Communication in and out of such scripts must be via pin values, files or streams (stdin, stdout, stderr).


== Code Editing ==
== Code Editing ==
The source code is usually edited in the [[Code Editor/en|code editor]]. It is interpreted according to the used language’s syntax at runtime. For the builtin languages Smalltalk and JavaScript, it is possible to switch the syntax pattern for already implemented blocks, causing the code to be automatically converted to that particular syntax style. The values of [[DiagramElements-Pin/en|data pins]] can be directly accessed (read and write) in the code using getters/setters on the pin's name. The external interface of an elementary block (e.g. number of pins) is defined in the [[Scheme Editor|schema editor]].
The source code is usually edited in the [[Code Editor/en|code editor]]. It is interpreted according to the used language’s syntax at runtime. For the builtin languages Smalltalk and JavaScript, it is possible to switch the syntax pattern for already implemented blocks, causing the code to be automatically converted to that particular syntax style. The values of [[DiagramElements-Pin/en|data pins]] can be directly accessed (read and write) in the code using getters/setters on the pin's name. The external interface of an elementary block (e.g. number of pins) is defined in the [[Scheme Editor|schema editor]].


[[Datei:bulb.png|16px]] Notice that the standard library as delivered already contains a number of useful elementary blocks for a wide range of applications. So that the need to define your own elementary blocks is often limited to special interfacing or specialized processing of data. Depending on the testing domain (technical/business/UI-testing), many expecco users even do not need to write any elementary actions and can realize their tests using diagrams only - this is especially the case in the area of UI testing. On the other side, others may depend heavily on the ability to interact directly with deep internals of the SUT, for example to call Java functions with complex object interfaces. Expecco tries hard to provide a convenient framework for both ends of this spectrum, by integrating elementary code development seamless into the test development process.
[[Datei:bulb.png|16px]] Notice that the standard library as delivered already contains a number of useful elementary blocks for a wide range of applications. So that the need to define your own elementary blocks is often limited to special interfacing or specialized processing of data. Depending on the testing domain (technical/business/UI-testing), most expecco users will never need to write any elementary action and can realize their tests using diagrams only. This is especially the case in the area of UI testing.

On the other side, some users may depend heavily on the ability to interact directly with deep internals of the SUT, for example to call Java functions with complex object interfaces, or to inspect and verify internal data structures.

Expecco provides a convenient framework for both ends of this spectrum, by integrating elementary low level code development seamless into the test development process, by providing a wide range of implementation languages and code reuse, and by giving the test developer a high abstraction level with its activity diagram actions.


== Smalltalk and JavaScript Blocks ==
== Smalltalk and JavaScript Blocks ==


The following examples are meant as tutorial and introductory examples. They are written to be easy to understand, not to use the full power of the underlying languages (eg. some could be written much shorter, by using more sophisticated existing library functions).
The following examples are meant as tutorial and introductory examples. They are written to be easy to understand, not to use the full power of the underlying languages (eg. some could be written much shorter, by using more sophisticated existing library functions).


Don't worry: you will not need to go down to that low level programming to get started with expecco.
Don't worry: you will not need to go down to that low level programming to get started with expecco.
All of the functionality presented below is already present in the Standard Library and can be used "out of the box". However, it may be good to know, that there are virtually no limitations in what you can do in expecco, and that whenever you need an additional function which the Standard Library did not foresee, it can be added in a few seconds.
All of the functionality presented below is already present in the Standard Library and can be used "out of the box".

However, it may be good to know, that there are virtually no limitations in what you can do in expecco, and that whenever you need an additional function which the Standard Library did not foresee, it can be added in a few seconds.


=== Example: Simple String Processing ===
=== Example: Simple String Processing ===


The first example simply takes a string and cuts off some characters at either side, and returns some middle part of it. To make the example more interesting, the parts to be cut off shall be the first 5 characters at the left, and everything after the last separator (blank character or tab).
The first example simply takes a string and cuts off some characters at either side, and returns some middle part of it. To make the example more interesting, the parts to be cut off shall be the first 5 characters at the left, and everything after the last separator (blank character or tab).
Be reminded that a similar action is available in the standard library - the example below is here to describe how it can be done.
We first define the external interface of that action block to consist of an input-pin named "''inString''" and an output-pin named "''outString''". Both pins are declared as being of "<code>String</code>"-datatype, this ensures, that when placed into a diagram, only other String-typed pins can be connected to it.
The so called "''schema''" definition of the action block is therefore:


We first define the external interface of that action block to consist of an input-pin named "''inString''" and an output-pin named "''outString''". Both pins are declared as being of "<code>String</code>"-datatype, this ensures, that when placed into a diagram, only other String-typed pins can be connected to it.
The so called "''schema''" definition of the action block is therefore:<br>
[[Bild: string_example_elementaryBlock.png]]
[[Bild: string_example_elementaryBlock.png]]
<br>Then, write the code, either in [[Expecco_API | Smalltalk]] as:
<br>Then, write the code, either in [[Expecco_API | Smalltalk]] as:
execute
<CODE><PRE>
execute
|inStringValue indexOfLastSeparator middlePart|
|inStringValue indexOfLastSeparator middlePart|

"/ fetch the data value from the pin
<span style="color:#007F00">"/ fetch the data value from the pin</span>
<span style="color:#007F00">"/ notice that inString refers to the pin - not the value</span>
inStringValue := inString value.
inStringValue := inString value.

"/ find the last separator in the string
<span style="color:#007F00">"/ find the last separator in the string</span>
indexOfLastSeparator := inStringValue lastIndexOfSeparator.
indexOfLastSeparator := inStringValue lastIndexOfSeparator.

"/ extract the middle part
<span style="color:#007F00">"/ extract the middle part</span>
middlePart := inStringValue copyFrom:6 to:(indexOfLastSeparator-1).
middlePart := inStringValue copyFrom:6 to:(indexOfLastSeparator-1).

"/ write to the output pin
<span style="color:#007F00">"/ write to the output pin</span>
outString value: middlePart
outString value: middlePart
</PRE></CODE>


The same operation written in JavaScript as:
The same operation written in JavaScript as:
execute() {
<CODE><PRE>
execute() {
var inStringValue, indexOfLastSeparator, middlePart;
var inStringValue, indexOfLastSeparator, middlePart;

// fetch the data value from the pin
<span style="color:#007F00">// fetch the data value from the pin</span>
inStringValue = inString.value();
inStringValue = inString.value();

// find the last separator in the string
<span style="color:#007F00">// find the last separator in the string</span>
indexOfLastSeparator = inStringValue.lastIndexOfSeparator();
indexOfLastSeparator = inStringValue.lastIndexOfSeparator();

// extract the middle part
<span style="color:#007F00">// extract the middle part</span>
middlePart = inStringValue.copyFrom_to(6, indexOfLastSeparator-1);
middlePart = inStringValue.copyFrom_to(6, indexOfLastSeparator-1);
<span style="color:#007F00">// write to the output pin</span>
outString.value(middlePart);
}


// write to the output pin
outString.value(middlePart);
}
</PRE></CODE>
<!-- There is one possible pitfall, when using JavaScript:
<!-- There is one possible pitfall, when using JavaScript:
JavaScript array-indexing is 0-based, whereas Smalltalk uses 1-based indices. This applies to all standard JavaScript functions (of which "indexOf" is one). However, when Smalltalk functions are called from JavaScript (as is the case with "copyFrom_to" above), we have to adjust the index.-->
JavaScript array-indexing is 0-based, whereas Smalltalk uses 1-based indices. This applies to all standard JavaScript functions (of which "indexOf" is one). However, when Smalltalk functions are called from JavaScript (as is the case with "copyFrom_to" above), we have to adjust the index.-->
Zeile 131: Zeile 147:
=== Example: Number Reading ===
=== Example: Number Reading ===


The next example takes a string and reads three numbers from it, which it returns as a 3 element vector (tuple). This could be useful as part of a reader for test data, or to process user input. The three numbers will be separated by spaces or tabs. It has a String-typed input pin named "inString" and an Array-typed output pin named "numberVector". It will report an error if not exactly 3 numbers are contained in the string:
The next example takes a string and reads three numbers from it, which it returns as a 3 element vector (tuple).
Notice, that this example does not use any of the more sophisticated string processing features available in the system - an alternative implementation is shown below).

This could be useful as part of a reader for test data, or to process user input.
<CODE><PRE>
The three numbers will be separated by spaces or tabs. It has a String-typed input pin named "''inString''" and an Array-typed output pin named "''numberVector''".
execute
It will report an error if not exactly 3 numbers are contained in the string:
execute
|inStringValue inStream n1 n2 n3|
|inStringValue inStream n1 n2 n3|

"/ fetch the string from the pin
<span style="color:#007F00">"/ fetch the string from the pin</span>
inStringValue := inString value.
inStringValue := inString value.

"/ get a read-stream on the string's characters
<span style="color:#007F00">"/ get a read-stream on the string's characters</span>
inStream := ReadStream on:inStringValue.
inStream := ReadStream on:inStringValue.

"/ read the first number
<span style="color:#007F00">"/ read the first number</span>
n1 := Number readFrom:inStream onError:[ self error:'error reading the first number'].
n1 := Number readFrom:inStream onError:[ self error:'error reading the first number'].

"/ and so on...
<span style="color:#007F00">"/ and so on...</span>
n2 := Number readFrom:inStream onError:[ self error:'error reading the second number'].
n2 := Number readFrom:inStream onError:[ self error:'error reading the second number'].
n3 := Number readFrom:inStream onError:[ self error:'error reading the third number'].
n3 := Number readFrom:inStream onError:[ self error:'error reading the third number'].

"/ check, if the end of the stream has been reached (e.g. nothing more is there)
<span style="color:#007F00">"/ check, if the end of the stream has been reached (e.g. nothing more is there)</span>
"/ but allow additional spaces
<span style="color:#007F00">"/ but allow additional spaces</span>
inStream skipSeparators.
inStream skipSeparators.
inStream atEnd ifFalse:[
inStream atEnd ifFalse:[
self error:'garbage after the third number'
self error:'garbage after the third number'
].
].
<span style="color:#007F00">"/ create a vector and write it to the output pin</span>
numberVector value: { n1 . n2 . n3 }


The same operation written in JavaScript as:
"/ create a vector and write it to the output pin
outString value: { n1 . n2 . n3 }
</PRE></CODE>


execute() {
The same operation written in JavaScript as:
<CODE><PRE>
execute() {
var inStringValue, inStream, n1, n2, n3;
var inStringValue, inStream, n1, n2, n3;

// fetch the data value from the pin
<span style="color:#007F00">// fetch the data value from the pin</span>
inStringValue = inString.value();
inStringValue = inString.value();

// get a read-stream on the string's characters
<span style="color:#007F00">// get a read-stream on the string's characters</span>
inStream = ReadStream.on(inStringValue);
inStream = ReadStream.on(inStringValue);

// read the numbers
<span style="color:#007F00">// read the numbers</span>
n1 = Number.readFrom_onError(inStream, function() { this.error("error reading the first number");});
n1 = Number.readFrom_onError(inStream, function() { this.error("error reading the first number");});
// alternative callback definition (lambda syntax)
<span style="color:#007F00">// alternative callback definition (lambda syntax)</span>
n2 = Number.readFrom_onError(inStream, () => this.error("error reading the second number") );
n2 = Number.readFrom_onError(inStream, () => this.error("error reading the second number") );
// alternative callback definition (lambda syntax)
<span style="color:#007F00">// alternative callback definition (lambda syntax)</span>
n3 = Number.readFrom_onError(inStream, () => error("error reading the third number") );
n3 = Number.readFrom_onError(inStream, () => error("error reading the third number") );

// check, if the end of the stream has been reached (eg. nothing more is there)
<span style="color:#007F00">// check, if the end of the stream has been reached (eg. nothing more is there)</span>
// but allow additional spaces
<span style="color:#007F00">// but allow additional spaces</span>
inStream.skipSeparators();
inStream.skipSeparators();
if (! inStream.atEnd) {
if (! inStream.atEnd) {
this.error("garbage after the third number");
this.error("garbage after the third number");
}
}
<span style="color:#007F00">// create a vector and write it to the output pin</span>
numberVector.value( [ n1 , n2 , n3 ] );


// create a vector and write it to the output pin
outString.value( [ n1 , n2 , n3 ] );
</PRE></CODE>
Please notice the inner function argument in the above code, which is the error call back argument to the "<code>Number readFrom_onError</code>" call.
Please notice the inner function argument in the above code, which is the error call back argument to the "<code>Number readFrom_onError</code>" call.
In Smalltalk, this is simply written as a block "[...]", whereas in JavaScript the ugly "function() {...}" or "() => ..." syntax is needed.
In Smalltalk, this is simply written as a block "<code>[...]</code>", whereas in JavaScript the ugly "<code>function() {...}</code>" or "<code>() => ...</code>" syntax is needed.
This is one of the examples, where Smalltalk code is much easier to write and understand (at least to those who have not been conditioned to C/Java style language syntax).
<br>This is one of the examples, where Smalltalk code is much easier to write and understand (at least to those who have not been conditioned to C/Java style language syntax).

Also notice, that the underlying system (Smalltalk) includes more sophisticated facilities to parse input.
<br>Using the <code>scanf:</code> method, the same code can be written shorter as:

execute
numberVector value:('%d %d %d' scanf:(inString value))

You should also take a look at the regular expression support in Smalltalk. Using regex, you could also write:

execute
numberVector value:((inString value allRegexMatches:'[0-9]+') collect:#asNumber)


=== Example: Minimum and Maximum of an Arbitrary Function ===
=== Example: Minimum and Maximum of an Arbitrary Function ===
Zeile 201: Zeile 229:


and its code could be written in Smalltalk as:
and its code could be written in Smalltalk as:

<CODE><PRE>
execute
execute
|fn minY maxY y|
|fn minY maxY y|

fn := functionName value asSymbol.
fn := functionName value asSymbol.

(x0 value) to:(x1 value) by:(dX value) do:[:x |
(x0 value) to:(x1 value) by:(dX value) do:[:x |
y := x perform:fn.
y := x perform:fn.
Zeile 212: Zeile 240:
maxY := maxY isNil ifTrue:[y] ifFalse:[ maxY max:y ].
maxY := maxY isNil ifTrue:[y] ifFalse:[ maxY max:y ].
].
].

min value:minY.
min value:minY.
max value:maxY.
max value:maxY.
</PRE></CODE>


or in JavaScript as:
or in JavaScript as:

<CODE><PRE>
execute() {
execute() {
var fn, minY, maxY, x, y;
var fn, minY, maxY, x, y;

fn = functionName.value().asSymbol();
fn = functionName.value().asSymbol();
for (x = x0.value(); x <= x1.value(); x += dX.value()) {
for (x = x0.value(); x <= x1.value(); x += dX.value()) {
Zeile 231: Zeile 258:
max.value(maxY);
max.value(maxY);
}
}

</PRE></CODE>
(notice the use of the "perform"-function, to call a function by name)
(notice the use of the "perform"-function, to call a function by name)




Of course, as usual, there are multiple ways to write the same function; an experienced Smalltalker would write:
Of course, as usual, there are multiple ways to write the same function; an experienced Smalltalker would write:

<CODE><PRE>
execute
execute
|fn minMax|
|fn minMax|

fn := functionName value asSymbol.
fn := functionName value asSymbol.

minMax := ((x0 value) to:(x1 value) by:(dX value))
minMax := ((x0 value) to:(x1 value) by:(dX value))
collect:[:x | x perform:fn])
collect:[:x | x perform:fn])
Zeile 247: Zeile 274:
min value:minMax first.
min value:minMax first.
max value:minMax second.
max value:minMax second.
</PRE></CODE>


and a JavaScript programmer might prefer:
and a JavaScript programmer might prefer:
<CODE><PRE>
execute() {
var fn, minY, maxY, x, y;


execute() {
var fn, minY, maxY, x, y;
fn = functionName.value;
fn = functionName.value;
for (x = x0.value; x <= x1.value; x += dX.value) {
for (x = x0.value; x <= x1.value; x += dX.value) {
Zeile 263: Zeile 289:
max.value(maxY);
max.value(maxY);
}
}
</PRE></CODE>



The block could be used to calculate the min/max of the sine function in the range 0..1 as follows:
The block could be used to calculate the min/max of the sine function in the range 0..1 as follows:
Zeile 280: Zeile 304:
[[Bild:multiSetter_elementaryBlock.png|300px]]
[[Bild:multiSetter_elementaryBlock.png|300px]]


and its code could be written in Smalltalk as:
and its code could be in Smalltalk:
execute
<CODE><PRE>
execute
|dictionary|
|dictionary|

dictionary := dictionaryInPin value.
dictionary := dictionaryInPin value.
1 to: keyPin numberOfVariablePins do:[:index |
1 to: keyPin numberOfVariablePins do:[:index |
|eachKey eachValue|
|eachKey eachValue|

eachKey := keyPin valueAt:index.
eachKey := keyPin valueAt:index.
eachValue := valuePin valueAt:index.
eachValue := valuePin valueAt:index.
Zeile 294: Zeile 317:
].
].
dictionaryOutPin value:dictionary.
dictionaryOutPin value:dictionary.
</PRE></CODE>


or in JavaScript as:
or in JavaScript:
execute() {
<CODE><PRE>
execute() {
var dictionary;
var dictionary;

dictionary = dictionaryInPin.value;
dictionary = dictionaryInPin.value;
for (var index = 1; index <= keyPin. numberOfVariablePins(); index++) {
for (var index = 1; index <= keyPin. numberOfVariablePins(); index++) {
var eachKey, eachValue;
var eachKey, eachValue;

eachKey = keyPin.valueAt(index);
eachKey = keyPin.valueAt(index);
eachValue = valuePin.valueAt(index);
eachValue = valuePin.valueAt(index);
Zeile 311: Zeile 332:
dictionaryOutPin.value( dictionary );
dictionaryOutPin.value( dictionary );
}
}
</PRE></CODE>


A step to set 5 key-value pairs would look as follows (input pin group replicated 4 more times):
A step to set 5 key-value pairs would look as follows (input pin group replicated 4 more times):
Zeile 320: Zeile 340:


Both bridge actions and script actions are executed in an external program or language interpreter.
Both bridge actions and script actions are executed in an external program or language interpreter.

In contrast to script actions which are described below,
However, in contrast to script actions which are described below,
a bridge partner is started once (or an already running program),
a bridge partner is started once (either by expecco or is already running),
and expecco communicates with this running program via a communication mechanism (typically a socket connection).
and expecco communicates with this running program via a communication mechanism (typically a socket connection).
The bridge partner is kept alive between bridge action calls.
The bridge partner is kept alive between bridge action calls.<br>Thus, bridge actions can refer to state and definitions which were created in previous calls, whereas script actions cannot.


On the other hand, script actions are independent actions, where the language interpreter is in a fresh state for every call.
On the other hand, script actions are independent actions, where the language interpreter is in a fresh state for every call.


Also, script actions take much longer to execute than bridge actions, because the script interpreter's startup and initialization actions are performed for every step, whereas bridged actions only need this when the very first step is executed (if not already running, the bridge is started and then kept alive). Depending on the startup time of the script language interpreter, the startup time can be in the range of seconds, whereas bridged functions are called in the order of milliseconds.<br>Of course, both are much slower than builtin Smalltalk or JavaScript actions, which are called within some hundreds of nanoseconds.
Thus, bridge actions can refer to state and definitions which were created in previous calls, whereas script actions cannot.

Also, script actions take much longer to execute than bridge actions, because the script interpreter's startup and initialization actions are performed for every step, whereas bridged actions only need this when the very first step is executed (the bridge is started and kept alive).


== Groovy Blocks ==
== Groovy Blocks ==
Zeile 338: Zeile 357:


Similar to Groovy actions, these are bridged actions, executed inside a .NET Virtual Machine (CLR), either locally, on a remote system or inside the system under test (SUT). It is possible to define new classes, new functions and to create objects which persist between C# calls. It is also possible to define callbacks and other functions which lead back into expecco. C# actions are described in more detail in the [[ Expecco_API | Expecco API documentation ]] .
Similar to Groovy actions, these are bridged actions, executed inside a .NET Virtual Machine (CLR), either locally, on a remote system or inside the system under test (SUT). It is possible to define new classes, new functions and to create objects which persist between C# calls. It is also possible to define callbacks and other functions which lead back into expecco. C# actions are described in more detail in the [[ Expecco_API | Expecco API documentation ]] .

C# source code entry will be available with the 20.1 version. However the basic mechanism to call .NET functions and exchange objects is already present and used in various places. The code is currently written in Smallalk or JavaScript, using the call forwarding mechanisms provided by Smalltalk. An alternative mechanism to interface to CLR-assemplies is via IronPython as described below.


== Node.js Blocks (Bridged) ==
== Node.js Blocks (Bridged) ==
Zeile 349: Zeile 370:


[[Datei:bulb.png|16px]] Notice, that both '''bridged Node.js''' actions and '''scripted Node.js''' actions are supported by expecco, and that they behave quite differently.
[[Datei:bulb.png|16px]] Notice, that both '''bridged Node.js''' actions and '''scripted Node.js''' actions are supported by expecco, and that they behave quite differently.

== NodeRED Blocks (Bridged) ==
These behave exactly like Node.js actions, but may be configured to use a different NODE_MODULES path and a different execution directory.
This is required because NodeRED must be executed inside ist own .node-red Folder, using ist own node_modules folder.


== Python Blocks (Bridged) ==
== Python Blocks (Bridged) ==


A bridged Python block's code is executed in a Python interpreter; either locally or on a remote system. These are similar to Groovy or Node actions, but the function is executed by Python. You have to ensure that an appropriate [https://nodejs.org python3 interpreter] is installed on the machine (it is not part of the expecco delivery package).
A bridged Python block's code is executed in a Python interpreter; either locally or on a remote system. These are similar to Groovy or Node actions, but the function is executed by Python. You have to ensure that an appropriate [https://python.org python interpreter] is installed on the machine (it is not part of the expecco delivery package). You can download either python 2.x or python3.x (or both). However due to python2 to be soon obsoleted, a python3 installation is preferred (see [https://www.python.org/downloads python download page]).


The python interpreter path can either be left unspecified, or specified in the expecco settings ("''external tools''"), or via an expecco environment variable named "<code>PYTHON_SHELL</code>"). The environment variable can be one found in the current execution scope, in the global test suite environment, or the shell environment.
The python interpreter path can either be left unspecified, or specified in the expecco settings ("''external tools''"), or via an expecco environment variable named "<code>PYTHON_SHELL</code>"). The environment variable can be one found in the current execution scope, in the global test suite environment, or the shell environment.
If left unspecified, or without an absolute path, the node interpreter should be found along the PATH.
If left unspecified, or without an absolute path, the python interpreter should be found along the PATH.


The bridged Python API is described in the [[Expecco API#Python_.28Bridged.29_Elementary_Blocks|Expecco API documentation on Python]].
The bridged Python API is described in the [[Expecco_API#Bridged_Python_Elementary_Blocks|Expecco API documentation on Python]].


[[Datei:bulb.png|16px]] Notice, that both '''bridged Python''' actions and '''scripted Python''' actions are supported by expecco, and that they behave quite differently.
[[Datei:bulb.png|16px]] Notice, that both '''bridged Python''' actions and '''scripted Python''' actions are supported by expecco, and that they behave quite differently.

==== Jython Blocks (Bridged) ====

These behave roughly the same as python 2.x actions, but are executed by Jython, which is a python interpreter running inside a Java VM. Jython actions have full access to Java classes and are useful if you need access to both python code and java objects within a single interpreter, or to call into scripts written for it.

The API is the same as for python actions and described in the [[Expecco_API#Bridged_Python_Elementary_Blocks|Expecco API documentation on Python]].

[[Datei:bulb.png|16px]] Notice that Jython implements the older 2.7 language and library version.

==== IronPython Blocks (Bridged) ====

These behave roughly the same as python 2.x or jython actions, but are executed by IronPython, which is a python interpreter running inside a .NET CLR (Windows). IronPython actions have full access to .NET assemblies and are especially useful to interface to CLR functionality under Windows.

The API is the same as for python actions and described in the [[Expecco_API#Bridged_Python_Elementary_Blocks|Expecco API documentation on Python]].

[[Datei:bulb.png|16px]] Notice that IronPython implements the older 2.7 language and library version.

== C Blocks (Bridged) ==

These execute a piece of C-code which is compiled dynamically.

The API is described in the [[Expecco_API#Bridged_C_Elementary_Blocks|"Expecco API documentation on Bridged C Actions"]].

== Smalltalk Blocks (Bridged) ==

As opposed to "''normal''" Smalltalk elementary actions, which execute inside expecco, bridged Smalltalk actions are executed by a separate Smalltalk process, either on the local machine, or on a remote machine. As with the other bridge implementations, the code is dynamically transported to the target virtual machine and executed there.

Currently, bridged Smalltalk execution is supported for the ST/X Smalltalk implementation, but versions for VA-Smalltalk (VisualAge) and VW-Smalltalk (VisualWorks) are being developed.

The API is described in the [[Expecco_API#Bridged_Smalltalk_Elementary_Blocks|"Expecco API documentation on Bridged Smalltalk Actions"]].


== VisualBasic Blocks ==
== VisualBasic Blocks ==


A VisualBasic Script block's code is executed by a scripting host (see Microsoft documentation on Visual Basic). It is described in more detail in the [[ Expecco_API#VisualBasic Elementary Blocks | ''Expecco API documentation on VisualBasic'' ]] and the [[ VBScript|''VisualBasic Plugin'' ]] documentation.
A VisualBasic Script block's code is executed by a scripting host (see Microsoft documentation on Visual Basic). It is described in more detail in the <!-- [[ Expecco_API#VisualBasic Elementary Blocks | ''Expecco API documentation on VisualBasic'' ]] and the --> [[ VBScript/en|''VisualBasic Plugin'' ]] documentation.


== Script Action Blocks ==
== Script Action Blocks ==
Zeile 373: Zeile 428:
=== Script Execution in General ===
=== Script Execution in General ===


The following sections describe the mechanism of script blocks in general, all of this is valid for Shell, Batch and all the other external scripting language action blocks (i.e. python, ruby, tcl, etc.).
The following sections describe the mechanism of script blocks in general, all of this is valid for Shell, Batch and all the other external scripting language action blocks (i.e. python, ruby, tcl, etc.). Be reminded that there is a fundamental difference between ''script actions'' and ''bridged actions'' as described above.


==== Standard Input / Standard Output Handling ====
==== Standard Input / Standard Output Handling ====
Zeile 379: Zeile 434:
Script blocks provide the standard output (stdout) and standard error (stderr) of the executed command
Script blocks provide the standard output (stdout) and standard error (stderr) of the executed command
at corresponding output pins as text, so that it can be read and analyzed further by other expecco actions.
at corresponding output pins as text, so that it can be read and analyzed further by other expecco actions.
These pins can be configured to either provide the whole output as one big string, or likewise.
These pins can be configured to either provide the whole output as one big string, or linewise either as individual lines or as chunks of multiple lines.

Notice, that those pins are hidden by default, you have to add them via the step's context menu.


===== Buffered vs. Unbuffered Stdout and Stderr =====
===== Buffered vs. Unbuffered Stdout and Stderr =====
Zeile 389: Zeile 446:
* if you are interested in the output, either during the execution, or in the error case.
* if you are interested in the output, either during the execution, or in the error case.


One typical situation when you want to read the output, is when a server program is started, which takes a while to start and send some "I am ready" prompt after a while, and the stdout text is processed by other actions.
One typical situation when you want to read the output, is when a server program is started, which takes a while to start and sends some "I am ready" prompt after a while, and the stdout text is processed by other actions.


Then you should change the "''Buffered''" attribute to "Unbuffered". The output will then be read linewise and passed on to the output pin whenever multiples of the ''NLines'' parameters are received. With an ''NLines'' value of 1 (which is the default), every line will be sent individually.
Then you should change the "''Buffered''" attribute to "''Unbuffered''". The output will then appear linewise at the output pin whenever multiples of the ''NLines'' parameters are received. With an ''NLines'' value of 1 (which is the default), every line will be sent individually.


By default, the ''stderr'' pin is unbuffered, with an ''NLines'' value of 1.
By default, the ''stderr'' pin is unbuffered, with an ''NLines'' value of 1.
Zeile 397: Zeile 454:
===== End of Line Conventions (Windows Operating System Specific) =====
===== End of Line Conventions (Windows Operating System Specific) =====
Windows uses a different End-of-Line (EOL) convention than Unix/Linux: every line sent to stdout/stderr is terminated with a CRNL (carriage-return + newline) sequence, whereas under Unix, a single NL is used as line terminator.
Windows uses a different End-of-Line (EOL) convention than Unix/Linux: every line sent to stdout/stderr is terminated with a CRNL (carriage-return + newline) sequence, whereas under Unix, a single NL is used as line terminator.

If your stdout/stderr pins are configured to generate likewise output, there is no difference, as expecco will
If your stdout/stderr pins are configured to generate linewise output, there is no difference, as expecco will
care for it and strip off the EOL character(s) to provide the same line-strings on both systems.
care for it and strip off the EOL character(s) to provide the same line-strings on both systems.

If the pin has an ''NLines'' parameter of zero, the whole output is presented as one big string,
If the pin has an ''NLines'' parameter of zero, the whole output is presented as one big string,
and expecco will post process the output and
and expecco will post-process the output and
if executing under Windows, any CRNL sequences will be replaced with N.
if executing under Windows, any &lt;CRNL&gt; sequences will be replaced with &lt;NL&gt;.
So here as well will the generated string be compatible among different operating systems.
This is usually ok, if the output consists of readable (ascii-) text.
There may be rare situations, when you want the "raw" (i.e. unprocessed) output. For this, add the special pin "''rawOutput''" and give it a true value (eg., when the output consist of binary data which is to be processed unchanged) .


So here as well will the generated output string be compatible among different operating systems.
Be reminded that this has no effect on Unix/Linux/OSX systems.
This is usually ok, if the output consists of readable (ascii-) text.
There may be rare situations, when you want the "raw" (i.e. unprocessed) output. For this, add the special pin "''rawOutput''" and give it a true value (eg., when the output consist of binary data which is to be processed unchanged) .


==== Showing stdin/stderr on the Transcript ====
==== Showing stdin/stderr on the Transcript ====
Zeile 421: Zeile 479:
==== Command Line Arguments ====
==== Command Line Arguments ====


To pass command-line arguments into the script, you can list them in a StringCollection and connect it to the "''args''" input pin. The individual arguments are then accessible with the usual means of your script interpreter, e.g. in a shell/bash as $1, $2 etc.
To pass command-line arguments into the script, list them in a StringCollection and connect it to the "''args''" input pin. The individual arguments are then accessible with the usual means of your script interpreter, e.g. in a shell/bash as $1, $2 etc.
For shell- and batch actions, arguments can also be provided by the "arguments" field (found in the action's code editor). That value is used, if no args pin is present or it has no value (technically, this creates a pin-freeze value with the value from the editor).
For shell- and batch actions, arguments can also be provided by the "arguments" field (found in the action's code editor). That value is used, if no args pin is present or it has no value (technically, this creates a pin-freeze value with the value from the editor).


==== Execution Directory ====
==== Execution Directory ====


By default, the scripting interpreter is executing in the executor's temporary "''execution directory''". The script finds any attachments in a folder named "$(Attachments)" or "%(Attachments)" and can refer to attachments by "<code>%(Attachments)/file</code>" (or "<code>%(Attachments)\file</code>" on windows), where "''file''" is the attachment's filename.
By default, the scripting interpreter is executing in the executor's temporary "''execution directory''" which is created for the execution and vanishes afterwards. This is done to Prevent conflicts, if multiple tests are executed in parallel (see below on how to overwrite that default).
Be reminded that this is not required to be the same as the attachment's name in the tree. However, it is a good idea, to use the same name for both.


The script finds any attachments in a folder named "$(Attachments)" (or "%(Attachments)" and can refer to attachments by "<code>%(Attachments)/file</code>", where "''file''" is the attachment's filename (on a Windows system, this would be "<code>%(Attachments)\file</code>").
If the script must run somewhere else, you can provide the path at the block's "''execDir''" input pin (or for shell scripts, you can also specify it in the editor, which is effectively defining the directory as a freeze value).
Be reminded that the attachment's filename is not strictly required to be the same as the attachment's element-name in the tree. However, it is a good idea, to use the same name for both.


The execution directory pathname may contain placeholders in the form "<code>$(xxx)</code>", where "xxx" is either a shell environment variable, or one of the special builtin placeholder variables:
If the script must run somewhere else, you can provide the path at the block's "''execDir''" input pin (or for shell scripts, you can also specify it in the editor, which is effectively defining the directory as a freeze value). The execution directory pathname (provided at the pin) may contain placeholders in the form "<code>$(xxx)</code>", where "xxx" is either a shell environment variable, or one of the special builtin placeholder variables:
* <code>$(AttachmentsDirectory)</code> - the directory, where all attachments of the suite are located.
* <code>AttachmentsDirectory</code> - the directory, where all attachments of the suite are located.
* <code>$(Attachments)</code> - the same, but a little shorter
* <code>Attachments</code> - the same, but a little shorter
* <code>$(ExecutionDirectory)</code> - a temporary directory, which vanishes after the execution
* <code>ExecutionDirectory</code> - a temporary directory, which vanishes after the execution
* <code>$(ExpeccoInstallationDirectory)</code> - the directory, where expecco was installed
* <code>ExpeccoInstallationDirectory</code> - the directory, where expecco was installed
* <code>$(ExpeccoPluginDirectory)</code> - the directory, where expecco's plugins are installed
* <code>ExpeccoPluginDirectory</code> - the directory, where expecco's plugins are installed
* <code>$(ProjectDirectory)</code> - the temporary project directory
* <code>ProjectDirectory</code> - the temporary project directory
* <code>$(HomeDirectory)</code> - the user's home directory
* <code>HomeDirectory</code> - the user's home directory
* <code>$(TmpDirectory)</code> - a temporary directory (subfolder of the system's tmp directory)
* <code>TmpDirectory</code> - a temporary directory (subfolder of the system's tmp directory)
* <code>$(CurrentDirectory)</code> - the "current" directory (see below)
* <code>CurrentDirectory</code> - the "current" directory (see below)
* <code>$(DocumentsDirectory)</code> - the "documents" directory
* <code>DocumentsDirectory</code> - the "documents" directory
* <code>$(DesktopDirectory)</code> - the "desktop" directory
* <code>DesktopDirectory</code> - the "desktop" directory
* <code>$(LoginName)</code> - your login name (username under Unix/Linux/OSX)
* <code>LoginName</code> - your login name (username under Unix/Linux/OSX)
* <code>$(UserName)</code> - your full name (if known, otherwise the login name)
* <code>UserName</code> - your full name (if known, otherwise the login name)


Notice that the expansion at the pin-value is using $(...), whereas inside the script, you must use %(...).
Especially "<code>$(AttachmentDirectory)</code>" is useful, if your script refers to other scripts or files which are attached to the test suite.

Especially "<code>AttachmentDirectory</code>" is useful, if your script refers to other scripts or files which are attached to the test suite.
Then, the script sees all attachments in its current directory. The attachment files are NOT write protected, so be careful to not overwrite or remove attachment files in the script (unless you want to create new attachments and add them to the suite later).
Then, the script sees all attachments in its current directory. The attachment files are NOT write protected, so be careful to not overwrite or remove attachment files in the script (unless you want to create new attachments and add them to the suite later).


Zeile 469: Zeile 529:
==== Script Expansion ====
==== Script Expansion ====


You can add additional input pins, and their values will be inserted into the script via "%(<''pinName''>)".
You can add additional input pins, and their values will be inserted into the script via
%(<''pinName''>)
<br>For example, the following (unix-) shell script will list the contents of the folder defined by a pin named "''folderName''":
For example, the following (unix-) shell script will list the contents of the folder defined by a pin named "''folderName''":

ls -l %(folderName)
ls -l %(folderName)

This script expansion is applied to all scripting languages (shell, ruby, python, etc.) and especially useful with gnuplot and 'R' scripts. Notice that the expansion does not care for or look at any syntactical constraints as defined by the concrete language. This means, that string values will usually have to be embedded in extra quotes. It also means, that the script expansion might result in a syntax error, which is not detected by the editor before execution.
This script expansion is applied to all scripting languages (shell, ruby, python, etc.) and especially useful with gnuplot and 'R' scripts. Notice that the expansion does not care for or look at any syntactical constraints as defined by the concrete language. This means, that string values will usually have to be embedded in extra quotes. It also means, that the script expansion might result in a syntax error, which is not detected by the editor before execution.

==== Output Pins ====

You can define additional output pins and write to them by writing a special character sequence to stderr (i.e. file descriptor 2 on unix):
:out:<nameOfPin>:<text>

==== Log Messages ====
You can generate log-messages (which will also be printed in the generated report), by sending a special character sequence to stderr:
:logInfo:<message>
:logWarning:<message>
:logError:<message>
:logFail:<message>
:Transcript:<message>


=== Shell Script Blocks ===
=== Shell Script Blocks ===


Shell Script blocks are trivially supported under Unix operating systems (incl. Linux, Solaris, MacOSX etc.). On Windows, an additional shell interpreter must be installed (and defined in the expecco settings). Thus, on windows, you should install Cygwin or a similar shell, in order to execute shell-script blocks.
Shell Script blocks are trivially supported under Unix operating systems (incl. Linux, Solaris, MacOSX etc.). On Windows, an additional shell interpreter must be installed and defined in the expecco settings. Thus, on Windows, you should install Cygwin or a similar shell, in order to execute shell-script blocks (of course, hard-core Windows users would probably rather use PowerShell actions, which are described elsewhere).


As an example, here is a script to read its input, filter all lines beginning with some pattern,
As an example, here is a script to read its input, filter all lines beginning with some pattern,
Zeile 501: Zeile 573:
sleep 60 >/dev/null 2>&1 &
sleep 60 >/dev/null 2>&1 &


==== Different Shell Interpreters ====
Under Windows, make sure that you have installed an appropriate shell emulator, and that it is either found along the PATH, or that you have specified the sh-path in the expecco-settings dialog (in "''External Script Interpreters''" - "''Shell Path''").
Under Windows, make sure that you have installed an appropriate shell emulator, and that it is either found along the PATH, or that you have specified the sh-path in the expecco-settings dialog (in "''External Script Interpreters''" - "''Shell Path''").
You can also define an environment variable named "<code>SHELL</code>", containing the path to a shell interpreter. The environment variable can be in the global project environment, the test suite environment, or a compound action's variable in the scope of the executed shell action.


You can also define an environment variable named "<code>SHELL</code>", containing the path to a shell interpreter. The environment variable can be in the global project environment, the test suite environment, or a compound action's variable in the scope of the executed shell action.
This is convenient, if the shell path is to be determined dynamically, or if another language interpreter is to be used.
This is convenient, if the shell path is to be determined dynamically, or if another language interpreter is to be used.

For example, by defining a local environment variable (in the surrounding compound action), and setting it to "<code>/bin/ksh</code>",
For example, by defining a local environment variable (in the surrounding compound action), and setting it to "<code>/bin/ksh</code>",
the script will be passed to a "<code>ksh</code>" instead of the default "<code>bash</code>", which is useful if you have to run shell scripts written by different teams for different shell interpreters (although, we'd consider that "bad style")
the script will be passed to a "<code>ksh</code>" instead of the default "<code>bash</code>", which is useful if you have to run shell scripts written by different teams for different shell interpreters (although, we'd consider that "bad style")


==== Additional Input Pin Values ====
==== Additional Input Pins ====
You can add additional input pins to shell action blocks, and refer to the value(s) as "%(pinName)" inside the script, as described in the above section on [[#Script_Expansion | "Script Expansion"]].
You can add additional input pins to shell action blocks, and refer to the value(s) as "%(''pinName'')" inside the script, as described in the above section on [[#Script_Expansion | "Script Expansion"]].
<br>Thus, assuming that your block has two inputs named "in1" and "in2", of Stringtype,
<br>Thus, assuming that your block has two inputs named "''in1''" and "''in2''", with String-type,
the script:
the script:
echo "you gave me %(in1) and %(in2)"
echo "you gave me %(in1) and %(in2)"
would output those values on the standard output.
would output those values on the standard output.

==== Additional Output Pins ====
Starting with expecco 19.2, you can also add additional output pins to shell actions, and write values to them via:
echo :out:''nameOfPin'':''valueToBeWritten'' 1&gt;&amp;2

This feature uses the stderr file-descriptor (2) to send specially encoded commands to expecco.

Assuming that your block has two Integer typed outputs named "''numFiles''" and "''numCFiles''",
the script:
nF=`ls | wc -l`
echo :out:numFiles:$(nF) 1&gt;&amp;2
nC=`ls *.c | wc -l`
echo :out:numCFiles:$(nC) 1&gt;&amp;2
will provide the number of files and the number of C-files of the current directory as integer values at the output pins.

==== Messages to the expecco Console ====
To send a message to the expecco console, write it to stderr, prefixed by ":Transcript:", as in:
echo :Transcript:''someInfo'' 1&gt;&amp;2

==== Generating Log Entries ====
Use:
echo :logInfo:''someInfo'' 1&gt;&amp;2
echo :logWarning:''someInfo'' 1&gt;&amp;2
echo :logError:''someInfo'' 1&gt;&amp;2
echo :logFail:''someInfo'' 1&gt;&amp;2
to add corresponding messages to the activity log.


=== Batch Script Blocks ===
=== Batch Script Blocks ===
Zeile 521: Zeile 621:


As an example, the code below would check out some source code from a CVS repository and start a build:
As an example, the code below would check out some source code from a CVS repository and start a build:

<code><pre>
<code><pre>
REM
REM
Zeile 532: Zeile 631:
make
make
</pre></code>
</pre></code>
Like Shell-Script actions, Batch-Script Actions provide the standard output and standard error at corresponding output pins as text, so you can to read and analyze that further. Also, the above sections on buffering, argument expansion, command line arguments, script expansion and output pin writing also apply to Batch Script actions. For details and examples, please refer to the "[[ #Script Execution in General |Script Execution in General]]" section above and read the paragraph on buffering the output, the execution directory, the environment and prompt filtering.


For logging and output pin writing, the above described shell script mechanism and syntax is used.
Like Shell Script action blocks, Batch Script blocks provide the standard output and standard error at corresponding output pins as text, so you can to read and analyze that further. Also, the above sections on buffering, argument expansion, command line arguments and script expansion also apply to Batch Script actions. For details and examples, please refer to the "[[ #Script Execution in General |Script Execution in General]]" section above and read the paragraph on buffering the output, the execution directory, the environment and prompt filtering.

=== Combined Shell/Batch Script Blocks ===

New in rel. 20.1

These combine the functionality of shell and batch script actions. The definition includes both a shell script and a batch script. When executed under Windows, the batch code is executed, when executed under Unix, the shell code is executed.
Although no new functionality is provided by these actions (they can be simulated via a compound action, which checks for the operating system and runs either a shell or a batch script action), they make it easier to write tests which are to run on both Unix and Windows systems.<br>These are very useful for simple "<Code>pip install foo</Code>"-kind of scripts.

=== AppleScript Blocks ===

Starting with expecco 20.1, AppleScript actions are supported on OSX (Mac) systems. The script is executed by the "osascript" interpreter.
These cannot be executed on other Unix or Windows systems, so your tests become OSX-dependent, if you use them.

As an example, the code below would open the TextEdit application and bring it to the foreground:
<code><pre>
tell application id "com.apple.TextEdit"
make new document
end tell
activate application "TextEdit"
</pre></code>
Like other script action blocks, AppleScript blocks provide the standard output and standard error at corresponding output pins as text, so you can to read and analyze that further. Also, the above sections on buffering, argument expansion, command line arguments, script expansion and output pin writing also apply to AppleScript actions. For details and examples, please refer to the "[[ #Script Execution in General |Script Execution in General]]" section above.

To send a value to an output pin, use log, which writes to stderr:
log ":out:<pinName>:valueString
To add entries to the activity log, use:
log ":logInfo:<message>"
log ":logError:<message>"
etc.

AppleScript is documented eg. in [https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html https://developer.apple.com/library/archive/documentation/AppleScript]


=== Ruby Script Blocks ===
=== Ruby Script Blocks ===
Zeile 548: Zeile 678:
For details and examples, please read to the "[[ #Script Execution in General |Script Execution in General]]" section above.
For details and examples, please read to the "[[ #Script Execution in General |Script Execution in General]]" section above.


To write to an output pin, or generate log entries, use "STDERR.puts".
Ruby can be downloaded from eg. [https://www.ruby-lang.org https://www.ruby-lang.org]
For example:
STDERR.puts ":out:pinName:"+someString
to write to an output pin.
Notice, that <someString> needs to be a string. If it is not (i.e. numbers, booleans etc.),
you should use "to_s" as in:
STDERR.puts ":out:pinName:"+count.to_s

To generate a log entry, use
STDERR.puts ":logInfo:some info message from ruby"


Ruby can be downloaded from eg. "[https://www.ruby-lang.org https://www.ruby-lang.org]"


=== Python Script Blocks ===
=== Python Script Blocks ===


Please do not confuse "''Python Script''" action blocks with "''Bridged Python''" action blocks. The following describes script actions, whereas bridged Python actions are described [[ #Python_Blocks_.28Bridged.29 | elsewhere]].
These are similar to shell scripts, but the script is executed by a python interpreter, instead of the sh (or bash).

These are similar to shell scripts, but the script is executed by a python or jython interpreter, instead of the sh (or bash).
You have to ensure that an appropriate python interpreter is installed on the machine (it is not part of the expecco delivery package).
You have to ensure that an appropriate python interpreter is installed on the machine (it is not part of the expecco delivery package).


Zeile 560: Zeile 704:
If left unspecified, or without an absolute path, the python interpreter should be found along the PATH.
If left unspecified, or without an absolute path, the python interpreter should be found along the PATH.


Python Script blocks provide the standard output and standard error at their output pins as text, so you may have to read and analyze that further.
Python Script blocks provide the standard output and standard error at their output pins as text, so you may have to read and analyze that further. You can also send values to output pins or messages to the activity log by sending an appropriate string to stderr (as described above). Use "<Code>print( <message> , file=sys.stderr)</Code>" to do that.
For details and examples, please refer to the "[[ #Script Execution in General |Script Execution in General]]" section above.
<br>For details and examples, please refer to the "[[ #Script Execution in General |Script Execution in General]]" section above.


Python can be downloaded from [https://www.python.org/downloads www.python.org/downloads].
Python can be downloaded from "[https://www.python.org/downloads www.python.org/downloads]";<br>Jython from "[https://jython.org/downloads.html https://jython.org/downloads.html]".


===== Special Note on Python Releases =====
===== Special Note on Python Releases =====
Due to some bad planning in the python community, there were a number of changes between python2.x and python3.x, which may (and usually do) make scripts written for either version incompatible with the other.
Due to some bad planning in the python community, there were a number of changes between python2.x and python3.x, which may (and usually do) make scripts written for either version incompatible with the other.
For example, the following print expression from python2:
<br>For example, the following print expression from python2:
print "Hello World from a Python2 script\n"
print "Hello World from a Python2 script\n"
needs parentheses in python3:
needs parentheses in python3:
Zeile 585: Zeile 729:
===== Specifying the Python Version in the Script =====
===== Specifying the Python Version in the Script =====


If the very first line of your script is either:
If the very first line of your script is (exactly as written, without any whitespace at the left or in between the hash and the identifier) one of:
#PYTHON2
#PYTHON2
or:
#PYTHON3
#PYTHON3
(exactly as written, without any whitespace at the left or in between the hash and the identifier),
#JYTHON
<br>then the script will be executed with that python version, ignoring any environment values.
#IRONPYTHON
then the script will ignore any environment values and executed with that python version's setup values (see Python settings).


===== Specifying the Python Version in an Environment Variable =====
===== Specifying the Python Version in an Environment Variable =====
For this, expecco contains 3 setting fields for the python interpreter:
* "Python Path" in the settings or the "<code>PYTHON_SHELL</code>" shell environment variable,<br>are used when no explicit version is required, or if all of your script require the same python version,
* "Python2 Path" in the settings (or the "<code>PYTHON2_SHELL</code>" shell variable)<br>are used for scripts which have been marked as explicitly requiring python2, and
* "Python3 Path" in the settings (or the "<code>PYTHON3_SHELL</code>" shell variable)<br>for scripts which are marked to require python3.


The python version requirements are defined in the environment of the suite or imported library in which the action is defined.
The python version requirements are defined in the environment of the suite/imported library in which the action is defined: a variable named "PYTHON_VERSION", which should be an integer with either "<code>2</code>" or "<code>3</code>" as value specifies the version to be used for ALL of the python actions within that library. Notice, that the environment of the project from which the action was imported is relevant here - not the active environment at execution time, or the suite's top environment. This version number then determines, which of the above setting is to be used (i.e. PYTHON2_SHELL/path or PYTHON3_SHELL/path).
A variable named "PYTHON_VERSION" should be defined, which holdes either a digit "<code>2</code>" or "<code>3</code>" or the string "jython" or "ironPython". This value specifies the version to be used for ALL of the python actions within that library. Notice, that the environment of the project from which the action was imported is relevant here - not the active environment at execution time, or the suite's top environment. This version number then determines, which of the settings is to be used (i.e. "PYTHON2_SHELL/path", "PYTHON3_SHELL/path", "JYTHON_SHELL/path" or "IRONPYTHON_SHELL/path").

The Settings or a corresponding variable then define the python interpreter (in "''Settings''" -> "''Execution''" -> "''External Language Interpreters''"):
* "Python Path" in the settings or the "<code>PYTHON_SHELL</code>" shell environment variable,<br>are used when no explicit version is required, or if all of your scripts require the same python version,
* "Python2 Path" in the settings (or the "<code>PYTHON2_SHELL</code>" shell variable)<br>are used for scripts which have been marked as explicitly requiring python2,
* "Python3 Path" in the settings (or the "<code>PYTHON3_SHELL</code>" shell variable)<br>for scripts which are marked to require python3
* "Jython Path" in the settings (or the "<code>JYTHON_SHELL</code>" shell variable)<br>for scripts which are marked to require jython.
* "IronPython Path" in the settings (or the "<code>IRONPYTHON_SHELL</code>" shell variable)<br>for scripts which are marked to require ironPython.


Using that double-indirect mechanism, it is still possible to:
Using that double-indirect mechanism, it is still possible to:
Zeile 605: Zeile 756:


A concrete demo importing libraries which require different python interpreter versions is found in "<code>d46_Suite_using_both_Python2_and_Python3_actions.ets</code>" in the demo folder.
A concrete demo importing libraries which require different python interpreter versions is found in "<code>d46_Suite_using_both_Python2_and_Python3_actions.ets</code>" in the demo folder.

===== Jython and IronPython Version =====
Jython and IronPython are currently available as 2.7 version only (at the time this document was last edited).


=== Node.js-Script Blocks ===
=== Node.js-Script Blocks ===


Please do not confuse "Node.js Script" action blocks with "Bridged Node.js" action blocks. The following describes script actions, whereas bridged Node.js actions are described [[ #Node.js_Blocks_.28Bridged.29 | elsewhere]].
Please do not confuse "''Node.js Script''" action blocks with "''Bridged Node.js''" action blocks. The following describes script actions, whereas bridged Node.js actions are described [[ #Node.js_Blocks_.28Bridged.29 | elsewhere]].


Node.js script actions are similar to shell script actions, but the script is executed by a Node.js interpreter, instead of the sh (or bash).
Node.js script actions are similar to shell script actions, but the script is executed by a Node.js interpreter, instead of the sh (or bash).
Zeile 618: Zeile 772:
If left unspecified, or without an absolute path, the node interpreter should be found along the PATH.
If left unspecified, or without an absolute path, the node interpreter should be found along the PATH.


Node.js-Script blocks provide the standard output and standard error at their output pins as text, so you may have to read and analyze that further.
Node.js-Script blocks provide the standard output and standard error at their output pins as text, so you may have to read and analyze that further. You can also write to output pins or create log entries by writing appropriate Messages to stdder (use <code>"console.error(…)</code>" to do that).
For details and examples, please read the "[[ #Script Execution in General |Script Execution in General]]" section above.
<br>For details and examples, please read the "[[ #Script Execution in General |Script Execution in General]]" section above.


Notice that in older expecco (pre 18.1) versions, a shell/batch script action block, with appropriate command and command line options can be used to execute node.js scripts.
Notice that in older expecco (pre 18.1) versions, a shell/batch script action block, with appropriate command and command line options can be used to execute node.js scripts.
Zeile 650: Zeile 804:
More information, step-by-step guidance and examples are found in "[[R_Action_Blocks |Using R Action Blocks]]".
More information, step-by-step guidance and examples are found in "[[R_Action_Blocks |Using R Action Blocks]]".
Concrete examples are found in the library "<code>R_Demo_Suite.ets</code>", which is found in the examples folder.
Concrete examples are found in the library "<code>R_Demo_Suite.ets</code>", which is found in the examples folder.

=== GNU Octave Action Blocks ===

With expecco rel20.1, the new GNU Octave action type was added. Octave is very similar to Matlab, and most Matlab scripts are drop-in compatible. Octave is a powerful mathematics oriented programming language with builtin plotting and visualisation tools. Similar to Gnuplot and R actions, this can be used to add graphs of execution times, response times, quality data etc. to the generated report.

As a prequisite, you have to install gnu octave (-> [https://www.gnu.org/software/octave Octave download Page]) and it must be found along your shell-path, or in an environment variable named "<code>GNUOCTAVE_SHELL</CODE>" or configured in the "''External Tools''" settings.

The "[[#Script Execution in General |general information]]" on script actions also applies to gnu octave actions.
<!-- In addition, more detailed information, step-by-step guidance and examples are found in "[[Using Gnuplot Action Blocks]]".
Concrete examples are found in the library "<code>Plot_Graph_Library.ets</code>",
which is found in the gnuplot plugin or the demos folder. -->


=== PowerShell Script Blocks ===
=== PowerShell Script Blocks ===


As a prerequisite, install powershell and ensure that it is either found along your shell PATH or configured in the settings
On Unix systems, install powershell and ensure that it is either found along your shell PATH or configured in the settings
(powershell is now also available for MacOSX and Linux)
(powershell is now also available for MacOSX and Linux). Notice, that the command is named either "<code>powershell</code>" or "<code>pwsh</code>", depending on the operating system you use.

Notice, that the command is named either "<code>powershell</code>" or "<code>pwsh</code>", depending on the operating system you use.
On Windows, powershell typically comes preinstalled with the operating system.
Please read the previous chapter on [[#Script Execution in General | "Script Execution in General"]] on how to get the output and/or pass parameters from input pins to the script.


=== TCL, Perl and Go Script Actions ===
=== TCL, Perl and Go Script Actions ===
Zeile 830: Zeile 997:
== See Also ==
== See Also ==


[[Code Editor]]
<br>
For a list of already existing blocks, see [[Standard Library]]
For a list of already existing blocks, see [[Standard Library]]
<br>
<br>

Version vom 4. Mai 2020, 12:39 Uhr

Inhaltsverzeichnis

Introduction[Bearbeiten]

Elementary blocks provide the basic low-level functionality of an expecco testsuite. They are used for operations which are not broken down into more elementary subactivities, and which are therefore not representable as an activity diagram ("Compound action"). Typically, operations described as elementary action block are numeric calculations, low level string, stream or collection operations, interfaces to protocols or to external devices. Especially domain specific low-level interfaces to local or remote processes, document converters, databases and hardware devices are implemented as elementary block.

Also, elementary actions are typically executed much faster than compound (diagram) actions. Therefore, performance critical operations are sometimes written as elementary blocks.

Languages[Bearbeiten]

The behavior of an elementary block is defined by one of the following:

  • a piece of textual program code, which executes inside expecco, and is written in one of the directly supported and builtin programming languages (currently: Smalltalk or a JavaScript dialect)
  • a piece of code, written in Groovy, which executes in a Java Virtual Machine (JVM) on the local system, a remote system or inside the system under test (SUT) itself (requires a Java installation)
  • a piece of code, written in JavaScript, which executes in a Node.js interpreter on the local or a remote system (bridged)
  • a piece of code, written in Python, which executes in a Python interpreter on the local or a remote system (bridged)
  • a piece of code, written in Python (IronPython), which executes in a .NET Virtual Machine (CLR) on the local system, a remote system or inside the system under test (SUT) itself (requires a mono installation on non-Windows systems)
  • a piece of code, written in C# (CSharp), which executes in a .NET Virtual Machine (CLR) on the local system, a remote system or inside the system under test (SUT) itself (requires a mono installation on non-Windows systems)
  • a piece of code, written in C or C++, which executes in a cBridge executable on the local system, a remote system or inside the system under test (SUT) itself (requires an additional DLL to be linked or loaded in the target)
  • a piece of code, written in VisualBasic Script, which executes in a separate script interpreter process (requires extra VB plugin)
  • a piece of code, which is executed by an external scripting language interpreter (shell, batch, appleScript, node, python, ruby, tcl, go or any other scripting language)
  • a gnuplot script to generate plots and graphs
  • an 'R' language script; both for statistical analysis and to generate plots and graphs
  • a call to a DLL (shared library) function (which is often, but not required to be) written in C or C++
  • a call to an external program
  • a remote procedure or service call (SOAP, REST, XML-RPC, Sun RPC)

Builtin Languages[Bearbeiten]

Expecco provides built in language interpreters and compilers for two scripting languages: the builtin Smalltalk and a builtin JavaScript-dialect.
When such an elementary block is executed, the script code runs inside the expecco process. These support high level symbolic debugging including single-step, breakpoints or inspection of variables. The debugger even allows for code changes to be done to the running program (for example, while the program is stopped at a breakpoint). These internal scripts are transparently and automatically compiled into machine code when first executed ("hot swap" and "Just in Time" (JIT) compilation). Changed code will be executed when the function/block is called the next time; thus, there is a limitation, in that a suspended function's code has to either proceed and return or be aborted for the new code to become effective (but only that particular function/elementary block - not the whole execution).

These builtin languages can use the full power of the underlying class library, (part of) which is described in "Expecco_API".

Additional builtin scripting languages (Ruby and Scheme) are being prepared and planned for future releases.

Bridged Actions[Bearbeiten]

In addition to "Script Execution" as described below, expecco also allows "Bridged Execution" of code which is loaded into and executed in an external language interpreter. In contrast to script execution, the bridge-partner is a running program which communicates with expecco and continues to run after an action's execution.

Therefore, any state created there will remain alive as long as the bridge-partner and the communication channel to it are still alive. Code can be defined in expecco to be executed inside a running "Java", "C#", "Node.js", "Python" and even "C/C++" program. Object references (handles) can be exchanged between the bridge-partner and expecco. This makes it possible to get object handles from a bridge partner and pass them back to the bridge later to another action. Typical uses are communication protocol handles or API-entries to trigger actions inside the bridge partner.

Bridged action blocks provide a pin-interface similar to the above described builtin actions, where values are passed to and from the action. However, not all bridges support all datatypes - some bridge-language specific limitations may apply.

Also, notice, that handles to objects which were allocated (instantiated) inside a bridge-partner will become void, when either the bridge partner is terminated or the connection to it is broken.

Of course, this also means, that bridge objects cannot be recovered from the execution log (only the printed representations will be stored in it).

Groovy Language[Bearbeiten]

With the Java Bridge extension plugin (now included as part of the base system), elementary blocks can be written in Groovy, which is a scripting language with a syntax similar to Java. These actions are transparently (and dynamically) compiled to Java bytecode and executed inside the SUT or on the expecco host or on any other such configured machine in the local network. Groovy blocks are very useful to define Java callbacks, subclasses of existing Java classes or to create and manipulate complicated Java objects inside a tested Java application. With groovy actions, any JAR can be accessed and any code inside can be called from expecco actions.

When such a block is executed, the script code runs inside a Java VM, which can be either a local one, dedicated to script execution, or on the target system (even inside the SUT, if it is written in Java).
More detail is found in the Expecco API Documentation.

Bridged NodeJS Actions[Bearbeiten]

Elementary blocks can also be written as bridged Node actions. These are executed in a NodeJS interpreter (local or remote). In contrast to NodeJS-Script actions, a single NodeJS interpreter is used and objects or processes created there remain alive until the interpreter connection is closed. This makes it possible to run servers or applications inside a NodeJS interpreter, and interact with it throughout the expecco session.
More detail is found in the chapter on Node.js actions in Expecco API Documentation.

Bridged Python Actions[Bearbeiten]

Similar to bridged node actions, these are executed in a Python or Jython interpreter (local or remote). In contrast to Python-Script actions, a single Python interpreter is used and objects or processes created there remain alive until the interpreter connection is closed. This makes it possible to run servers or applications inside a Python interpreter, and interact with it throughout the expecco session.
More detail is found in the chapter on Python actions in Expecco API Documentation.

C and C++ Languages[Bearbeiten]

Elementary blocks can also be written in low level C or C++ (starting with expecco 19.2) . Similar to the above Groovy, Node and Python code, this is transparently (and dynamically) compiled, and executed inside a C program, given that the program has the cBridge library linked in or dynamically loaded. C/C++ blocks are useful for time critical operations or to interface to special hardware for which DLL calls are not sufficient or too complicated to implement.
More detail is found in the chapter on C/C++ actions in the Expecco API Documentation.

IronPython and C# Languages[Bearbeiten]

With the DotNET Bridge extension plugin (now included as part of the base system), elementary blocks can be written in IronPython or C#. Similar to the above Groovy code, this is transparently (and dynamically) compiled, and executed inside the SUT or on the expecco host or on any other such configured machine in the local network. DotNET blocks are useful to access internals of the SUT or to interface to systems for which DotNET bindings (libraries) are available.
More detail is found in the Expecco API Documentation.

VBScript Language[Bearbeiten]

With the VisualBasic Plugin, elementary blocks can be written in VisualBasic and are executed in an external VisualBasic interpreter. This may run on the local host (the one on which expecco runs) or optionally on a remote host. The host on which the script engine executes must be a Windows host. For expecco users working under Unix/Linux or OSX, this means that at least a virtual (or real) machine with a Windows system must be setup and reachable in the network, and that the Windows machine serves as a script execution server for the Unix machine.

Script Languages[Bearbeiten]

Many scripting languages (Ruby, Perl, TCL, Python, Node, Gnuplot etc.) can be called via external script interpreters. It is also possible to call existing command line scripts (Shell under Unix or Windows+cygwin, Batch under Windows). In contrast to bridged execution, these script executions will start a new interpreter for every such action invocation. Thus, it is not possible to retain or pass state (i.e. handles or objects) across script-action calls. Communication in and out of such scripts must be via pin values, files or streams (stdin, stdout, stderr).

Code Editing[Bearbeiten]

The source code is usually edited in the code editor. It is interpreted according to the used language’s syntax at runtime. For the builtin languages Smalltalk and JavaScript, it is possible to switch the syntax pattern for already implemented blocks, causing the code to be automatically converted to that particular syntax style. The values of data pins can be directly accessed (read and write) in the code using getters/setters on the pin's name. The external interface of an elementary block (e.g. number of pins) is defined in the schema editor.

Bulb.png Notice that the standard library as delivered already contains a number of useful elementary blocks for a wide range of applications. So that the need to define your own elementary blocks is often limited to special interfacing or specialized processing of data. Depending on the testing domain (technical/business/UI-testing), most expecco users will never need to write any elementary action and can realize their tests using diagrams only. This is especially the case in the area of UI testing.

On the other side, some users may depend heavily on the ability to interact directly with deep internals of the SUT, for example to call Java functions with complex object interfaces, or to inspect and verify internal data structures.

Expecco provides a convenient framework for both ends of this spectrum, by integrating elementary low level code development seamless into the test development process, by providing a wide range of implementation languages and code reuse, and by giving the test developer a high abstraction level with its activity diagram actions.

Smalltalk and JavaScript Blocks[Bearbeiten]

The following examples are meant as tutorial and introductory examples. They are written to be easy to understand, not to use the full power of the underlying languages (eg. some could be written much shorter, by using more sophisticated existing library functions).

Don't worry: you will not need to go down to that low level programming to get started with expecco. All of the functionality presented below is already present in the Standard Library and can be used "out of the box".

However, it may be good to know, that there are virtually no limitations in what you can do in expecco, and that whenever you need an additional function which the Standard Library did not foresee, it can be added in a few seconds.

Example: Simple String Processing[Bearbeiten]

The first example simply takes a string and cuts off some characters at either side, and returns some middle part of it. To make the example more interesting, the parts to be cut off shall be the first 5 characters at the left, and everything after the last separator (blank character or tab). Be reminded that a similar action is available in the standard library - the example below is here to describe how it can be done.

We first define the external interface of that action block to consist of an input-pin named "inString" and an output-pin named "outString". Both pins are declared as being of "String"-datatype, this ensures, that when placed into a diagram, only other String-typed pins can be connected to it. The so called "schema" definition of the action block is therefore:
String example elementaryBlock.png
Then, write the code, either in Smalltalk as:

execute
   |inStringValue indexOfLastSeparator middlePart|

   "/ fetch the data value from the pin
   "/ notice that inString refers to the pin - not the value
   inStringValue := inString value.

   "/ find the last separator in the string
   indexOfLastSeparator := inStringValue lastIndexOfSeparator.

   "/ extract the middle part
   middlePart := inStringValue copyFrom:6 to:(indexOfLastSeparator-1).

   "/ write to the output pin
   outString value: middlePart

The same operation written in JavaScript as:

execute() {
   var inStringValue, indexOfLastSeparator, middlePart;

   // fetch the data value from the pin
   inStringValue = inString.value();

   // find the last separator in the string
   indexOfLastSeparator = inStringValue.lastIndexOfSeparator();

   // extract the middle part
   middlePart = inStringValue.copyFrom_to(6, indexOfLastSeparator-1);

   // write to the output pin
   outString.value(middlePart);
}


You should always provide a little test and demo of your elementary actions in the Test/Demo page. There, place at least one instance of the block as a step, give it its required inputs and execute it there. For example:

String example elementaryBlock test.png

Example: Number Reading[Bearbeiten]

The next example takes a string and reads three numbers from it, which it returns as a 3 element vector (tuple). Notice, that this example does not use any of the more sophisticated string processing features available in the system - an alternative implementation is shown below). This could be useful as part of a reader for test data, or to process user input. The three numbers will be separated by spaces or tabs. It has a String-typed input pin named "inString" and an Array-typed output pin named "numberVector". It will report an error if not exactly 3 numbers are contained in the string:

execute
   |inStringValue inStream n1 n2 n3|

   "/ fetch the string from the pin
   inStringValue := inString value.

   "/ get a read-stream on the string's characters
   inStream := ReadStream on:inStringValue.

   "/ read the first number
   n1 := Number readFrom:inStream onError:[ self error:'error reading the first number'].

   "/ and so on...
   n2 := Number readFrom:inStream onError:[ self error:'error reading the second number'].
   n3 := Number readFrom:inStream onError:[ self error:'error reading the third number'].

   "/ check, if the end of the stream has been reached (e.g. nothing more is there)
   "/ but allow additional spaces
   inStream skipSeparators.
   inStream atEnd ifFalse:[
       self error:'garbage after the third number'
   ].

   "/ create a vector and write it to the output pin
   numberVector value: { n1 . n2 . n3 }

The same operation written in JavaScript as:

execute() {
   var inStringValue, inStream, n1, n2, n3;

   // fetch the data value from the pin
   inStringValue = inString.value();

   // get a read-stream on the string's characters
   inStream = ReadStream.on(inStringValue);

   // read the numbers
   n1 = Number.readFrom_onError(inStream, function() { this.error("error reading the first number");});
   // alternative callback definition (lambda syntax)
   n2 = Number.readFrom_onError(inStream, () => this.error("error reading the second number") );
   // alternative callback definition (lambda syntax)
   n3 = Number.readFrom_onError(inStream, () => error("error reading the third number") );

   // check, if the end of the stream has been reached (eg. nothing more is there)
   // but allow additional spaces
   inStream.skipSeparators();
   if (! inStream.atEnd) {
       this.error("garbage after the third number");
   }

   // create a vector and write it to the output pin
   numberVector.value( [ n1 , n2 , n3 ] );

Please notice the inner function argument in the above code, which is the error call back argument to the "Number readFrom_onError" call. In Smalltalk, this is simply written as a block "[...]", whereas in JavaScript the ugly "function() {...}" or "() => ..." syntax is needed.
This is one of the examples, where Smalltalk code is much easier to write and understand (at least to those who have not been conditioned to C/Java style language syntax).

Also notice, that the underlying system (Smalltalk) includes more sophisticated facilities to parse input.
Using the scanf: method, the same code can be written shorter as:

execute
   numberVector value:('%d %d %d' scanf:(inString value))

You should also take a look at the regular expression support in Smalltalk. Using regex, you could also write:

execute
   numberVector value:((inString value allRegexMatches:'[0-9]+') collect:#asNumber)

Example: Minimum and Maximum of an Arbitrary Function[Bearbeiten]

Here is a block to compute the minimum and maximum values of a mathematical function over a range of x-values. It is especially flexible, as the name of the function can be passed as an argument (for example: 'sin', 'cos' or 'arcTan'). The block's definition is:

Min max elementaryBlock.jpg

and its code could be written in Smalltalk as:

execute
   |fn minY maxY y|

   fn := functionName value asSymbol.

   (x0 value) to:(x1 value) by:(dX value) do:[:x |
       y := x perform:fn.
       minY := minY isNil ifTrue:[y] ifFalse:[ minY min:y ].
       maxY := maxY isNil ifTrue:[y] ifFalse:[ maxY max:y ].
   ].

   min value:minY.
   max value:maxY.

or in JavaScript as:

execute() {
   var fn, minY, maxY, x, y;

   fn = functionName.value().asSymbol();
   for (x = x0.value(); x <= x1.value(); x += dX.value()) {
       y = x.perform(fn);
       minY = (minY.isNil()) ? y : minY.min(y);
       maxY = (maxY.isNil()) ? y : maxY.max(y);
   }
   min.value(minY);
   max.value(maxY);
}

(notice the use of the "perform"-function, to call a function by name)


Of course, as usual, there are multiple ways to write the same function; an experienced Smalltalker would write:

execute
   |fn minMax|

   fn := functionName value asSymbol.

   minMax := ((x0 value) to:(x1 value) by:(dX value))
                 collect:[:x | x perform:fn])
                     minMax.
   min value:minMax first.
   max value:minMax second.

and a JavaScript programmer might prefer:

execute() {
   var fn, minY, maxY, x, y;

   fn = functionName.value;
   for (x = x0.value; x <= x1.value; x += dX.value) {
       y = x.perform(fn.asSymbol());
       minY = (minY == null) ? y : Math.min(minY,y);
       maxY = (maxY == null) ? y : Math.max(maxY,y);
   }
   min.value(minY);
   max.value(maxY);
}

The block could be used to calculate the min/max of the sine function in the range 0..1 as follows:

Min max in use with sine.jpg

Example: Multi-Setter with Variable Number of Input Pins[Bearbeiten]

The following defines a block to set multiple key-value associations in a dictionary object. In the schema, the last two input pins were defined as a variable-pin-group (using the second to last pin's "Special Attribute" menu). Therefore, the last two pins can be replicated an arbitrary number of times in a step. The block's elementary code must of course be prepared for this, and loop over the step's number of input pin values.
The block's definition is:

MultiSetter elementaryBlock.png

and its code could be in Smalltalk:

execute
   |dictionary|

   dictionary := dictionaryInPin value.
   1 to: keyPin numberOfVariablePins do:[:index |
       |eachKey eachValue|

       eachKey := keyPin valueAt:index.
       eachValue := valuePin valueAt:index.
       dictionary at:eachKey put:eachValue.
   ].
   dictionaryOutPin value:dictionary.

or in JavaScript:

execute() {
   var dictionary;

   dictionary = dictionaryInPin.value;
   for (var index = 1; index <= keyPin. numberOfVariablePins(); index++) {
       var eachKey, eachValue;

       eachKey = keyPin.valueAt(index);
       eachValue = valuePin.valueAt(index);
       dictionary[eachKey] = eachValue;
   }
   dictionaryOutPin.value( dictionary );

}

A step to set 5 key-value pairs would look as follows (input pin group replicated 4 more times):

MultiSetter use with 5 groups.png

Bridge Action Blocks vs. Script Action Blocks[Bearbeiten]

Both bridge actions and script actions are executed in an external program or language interpreter.

However, in contrast to script actions which are described below, a bridge partner is started once (either by expecco or is already running), and expecco communicates with this running program via a communication mechanism (typically a socket connection). The bridge partner is kept alive between bridge action calls.
Thus, bridge actions can refer to state and definitions which were created in previous calls, whereas script actions cannot.

On the other hand, script actions are independent actions, where the language interpreter is in a fresh state for every call.

Also, script actions take much longer to execute than bridge actions, because the script interpreter's startup and initialization actions are performed for every step, whereas bridged actions only need this when the very first step is executed (if not already running, the bridge is started and then kept alive). Depending on the startup time of the script language interpreter, the startup time can be in the range of seconds, whereas bridged functions are called in the order of milliseconds.
Of course, both are much slower than builtin Smalltalk or JavaScript actions, which are called within some hundreds of nanoseconds.

Groovy Blocks[Bearbeiten]

A Groovy action is a bridge action and its code is executed in a Java Virtual Machine (JVM), either locally, on a remote system or inside the system under test (SUT). It is possible to define new classes, new functions and to create objects which persist between Groovy calls. It is also possible to define callbacks and other functions which lead back into expecco. Groovy actions are described in more detail in the Expecco API documentation on Groovy .

C# Action Blocks[Bearbeiten]

Similar to Groovy actions, these are bridged actions, executed inside a .NET Virtual Machine (CLR), either locally, on a remote system or inside the system under test (SUT). It is possible to define new classes, new functions and to create objects which persist between C# calls. It is also possible to define callbacks and other functions which lead back into expecco. C# actions are described in more detail in the Expecco API documentation .

C# source code entry will be available with the 20.1 version. However the basic mechanism to call .NET functions and exchange objects is already present and used in various places. The code is currently written in Smallalk or JavaScript, using the call forwarding mechanisms provided by Smalltalk. An alternative mechanism to interface to CLR-assemplies is via IronPython as described below.

Node.js Blocks (Bridged)[Bearbeiten]

A bridged Node.js block's code is executed in a Node.js interpreter; either locally or on a remote system. These are similar to Groovy actions, but the function is executed by Node instead of a JVM. You have to ensure that an appropriate node interpreter is installed on the machine (it is not part of the expecco delivery package).

The node interpreter path can either be left unspecified, or specified in the expecco settings ("external tools"), or via an expecco environment variable named "NODEJS_SHELL"). The environment variable can be one found in the current execution scope, in the global test suite environment, or the shell environment. If left unspecified, or without an absolute path, the node interpreter should be found along the PATH.

The Node.js API is described in the Expecco API documentation on Node.js.

Bulb.png Notice, that both bridged Node.js actions and scripted Node.js actions are supported by expecco, and that they behave quite differently.

NodeRED Blocks (Bridged)[Bearbeiten]

These behave exactly like Node.js actions, but may be configured to use a different NODE_MODULES path and a different execution directory. This is required because NodeRED must be executed inside ist own .node-red Folder, using ist own node_modules folder.

Python Blocks (Bridged)[Bearbeiten]

A bridged Python block's code is executed in a Python interpreter; either locally or on a remote system. These are similar to Groovy or Node actions, but the function is executed by Python. You have to ensure that an appropriate python interpreter is installed on the machine (it is not part of the expecco delivery package). You can download either python 2.x or python3.x (or both). However due to python2 to be soon obsoleted, a python3 installation is preferred (see python download page).

The python interpreter path can either be left unspecified, or specified in the expecco settings ("external tools"), or via an expecco environment variable named "PYTHON_SHELL"). The environment variable can be one found in the current execution scope, in the global test suite environment, or the shell environment. If left unspecified, or without an absolute path, the python interpreter should be found along the PATH.

The bridged Python API is described in the Expecco API documentation on Python.

Bulb.png Notice, that both bridged Python actions and scripted Python actions are supported by expecco, and that they behave quite differently.

Jython Blocks (Bridged)[Bearbeiten]

These behave roughly the same as python 2.x actions, but are executed by Jython, which is a python interpreter running inside a Java VM. Jython actions have full access to Java classes and are useful if you need access to both python code and java objects within a single interpreter, or to call into scripts written for it.

The API is the same as for python actions and described in the Expecco API documentation on Python.

Bulb.png Notice that Jython implements the older 2.7 language and library version.

IronPython Blocks (Bridged)[Bearbeiten]

These behave roughly the same as python 2.x or jython actions, but are executed by IronPython, which is a python interpreter running inside a .NET CLR (Windows). IronPython actions have full access to .NET assemblies and are especially useful to interface to CLR functionality under Windows.

The API is the same as for python actions and described in the Expecco API documentation on Python.

Bulb.png Notice that IronPython implements the older 2.7 language and library version.

C Blocks (Bridged)[Bearbeiten]

These execute a piece of C-code which is compiled dynamically.

The API is described in the "Expecco API documentation on Bridged C Actions".

Smalltalk Blocks (Bridged)[Bearbeiten]

As opposed to "normal" Smalltalk elementary actions, which execute inside expecco, bridged Smalltalk actions are executed by a separate Smalltalk process, either on the local machine, or on a remote machine. As with the other bridge implementations, the code is dynamically transported to the target virtual machine and executed there.

Currently, bridged Smalltalk execution is supported for the ST/X Smalltalk implementation, but versions for VA-Smalltalk (VisualAge) and VW-Smalltalk (VisualWorks) are being developed.

The API is described in the "Expecco API documentation on Bridged Smalltalk Actions".

VisualBasic Blocks[Bearbeiten]

A VisualBasic Script block's code is executed by a scripting host (see Microsoft documentation on Visual Basic). It is described in more detail in the VisualBasic Plugin documentation.

Script Action Blocks[Bearbeiten]

Script actions are executed by an external language interpreter. Beside the basic Shell- and Batch-Script (Windows) actions, a number of script languages are specially supported. Although any external script in any script language can be called by using appropriate command line arguments (i.e. using a Shell-Script action with a command which calls the language interpreter), these specially supported languages present themselves with a special icon, support syntax highlighting, and provide better error diagnostics (in particular: error-line highlighting).

A demo suite with various script actions is found in the examples/demos folder named "d45_ExternalScriptElementaryActionsDemo.ets".

Script Execution in General[Bearbeiten]

The following sections describe the mechanism of script blocks in general, all of this is valid for Shell, Batch and all the other external scripting language action blocks (i.e. python, ruby, tcl, etc.). Be reminded that there is a fundamental difference between script actions and bridged actions as described above.

Standard Input / Standard Output Handling[Bearbeiten]

Script blocks provide the standard output (stdout) and standard error (stderr) of the executed command at corresponding output pins as text, so that it can be read and analyzed further by other expecco actions. These pins can be configured to either provide the whole output as one big string, or linewise either as individual lines or as chunks of multiple lines.

Notice, that those pins are hidden by default, you have to add them via the step's context menu.

Buffered vs. Unbuffered Stdout and Stderr[Bearbeiten]

The stdout pin is buffered by default. This means that all of the output is collected and only passed to the "stdout" pin if the command finishes with an "OK" execution status (i.e. a zero exit status). Of course, this has the consequence that expecco waits for the command to finish and only writes that pin at the end, and only if the exit status is 0 (i.e. "No Error").

This may be inconvenient in two situations:

  • if the called program/script interpreter returns an invalid exit status (some programs return a non-zero status even if everything worked out ok)
  • if you are interested in the output, either during the execution, or in the error case.

One typical situation when you want to read the output, is when a server program is started, which takes a while to start and sends some "I am ready" prompt after a while, and the stdout text is processed by other actions.

Then you should change the "Buffered" attribute to "Unbuffered". The output will then appear linewise at the output pin whenever multiples of the NLines parameters are received. With an NLines value of 1 (which is the default), every line will be sent individually.

By default, the stderr pin is unbuffered, with an NLines value of 1.

End of Line Conventions (Windows Operating System Specific)[Bearbeiten]

Windows uses a different End-of-Line (EOL) convention than Unix/Linux: every line sent to stdout/stderr is terminated with a CRNL (carriage-return + newline) sequence, whereas under Unix, a single NL is used as line terminator.

If your stdout/stderr pins are configured to generate linewise output, there is no difference, as expecco will care for it and strip off the EOL character(s) to provide the same line-strings on both systems.

If the pin has an NLines parameter of zero, the whole output is presented as one big string, and expecco will post-process the output and if executing under Windows, any <CRNL> sequences will be replaced with <NL>.

So here as well will the generated output string be compatible among different operating systems. This is usually ok, if the output consists of readable (ascii-) text. There may be rare situations, when you want the "raw" (i.e. unprocessed) output. For this, add the special pin "rawOutput" and give it a true value (eg., when the output consist of binary data which is to be processed unchanged) .

Showing stdin/stderr on the Transcript[Bearbeiten]

In addition, the settings dialog contains a "Show Stdout/Stderr on Transcript" flag setting in the "Execution-Tracing" section. If this flag is set, all output from external script execution is also shown in the Transcript window (after version 2.10.1, there is an additional flag named "Show Executed Commands", which logs the commands as they are executed, but not their output).

Notice, that there is another flag setting in the "Execution-Logging" section, to enable log-entries in the activity log (which will be also in the generated report).

Matching stdin/stderr against a Pattern[Bearbeiten]

Starting with expecco 18.1, two additional pins are provided for script actions: "prompt_optional" and "prompt_detected". The "prompt_optional" input pin, can provide a matchpattern (GLOB pattern), which will be matched against every line of stdout and stderr. When a matching line is detected, the "prompt_detected" output will receive that line. This can be used to wait for a server program to come up and indicate its ready status via a prompt (in earlier expecco versions, you had to write a little filter for this).

Command Line Arguments[Bearbeiten]

To pass command-line arguments into the script, list them in a StringCollection and connect it to the "args" input pin. The individual arguments are then accessible with the usual means of your script interpreter, e.g. in a shell/bash as $1, $2 etc. For shell- and batch actions, arguments can also be provided by the "arguments" field (found in the action's code editor). That value is used, if no args pin is present or it has no value (technically, this creates a pin-freeze value with the value from the editor).

Execution Directory[Bearbeiten]

By default, the scripting interpreter is executing in the executor's temporary "execution directory" which is created for the execution and vanishes afterwards. This is done to Prevent conflicts, if multiple tests are executed in parallel (see below on how to overwrite that default).

The script finds any attachments in a folder named "$(Attachments)" (or "%(Attachments)" and can refer to attachments by "%(Attachments)/file", where "file" is the attachment's filename (on a Windows system, this would be "%(Attachments)\file"). Be reminded that the attachment's filename is not strictly required to be the same as the attachment's element-name in the tree. However, it is a good idea, to use the same name for both.

If the script must run somewhere else, you can provide the path at the block's "execDir" input pin (or for shell scripts, you can also specify it in the editor, which is effectively defining the directory as a freeze value). The execution directory pathname (provided at the pin) may contain placeholders in the form "$(xxx)", where "xxx" is either a shell environment variable, or one of the special builtin placeholder variables:

  • AttachmentsDirectory - the directory, where all attachments of the suite are located.
  • Attachments - the same, but a little shorter
  • ExecutionDirectory - a temporary directory, which vanishes after the execution
  • ExpeccoInstallationDirectory - the directory, where expecco was installed
  • ExpeccoPluginDirectory - the directory, where expecco's plugins are installed
  • ProjectDirectory - the temporary project directory
  • HomeDirectory - the user's home directory
  • TmpDirectory - a temporary directory (subfolder of the system's tmp directory)
  • CurrentDirectory - the "current" directory (see below)
  • DocumentsDirectory - the "documents" directory
  • DesktopDirectory - the "desktop" directory
  • LoginName - your login name (username under Unix/Linux/OSX)
  • UserName - your full name (if known, otherwise the login name)

Notice that the expansion at the pin-value is using $(...), whereas inside the script, you must use %(...).

Especially "AttachmentDirectory" is useful, if your script refers to other scripts or files which are attached to the test suite. Then, the script sees all attachments in its current directory. The attachment files are NOT write protected, so be careful to not overwrite or remove attachment files in the script (unless you want to create new attachments and add them to the suite later).

If the script is executed outside the expecco folder hierarchy (i.e. if you specify another execution directory via the "execDir" input pin), the script can refer to the attachments via "%(AttachmentsDirectory)/foo.dat" (unix) or "%(AttachmentsDirectory)\foo.dat" (windows).

Variables are searched in any block-local environment, the test-suite environment, or the shell environment. Thus, you can also refer to shell variables such as "HOME" via "$(HOME)".

Bulb.png Notice, if you have imported many scripts which refer to attached expecco configuration or data files without a directory prefix, you can change the execution directory globally (i.e. for all scripts) via the "Settings" - "Execution" - "Execute Scripts in Attachment Folder" flag. However, as this is a global flag, it may break imported script actions, which are not prepared for this.

Bulb.png Also notice, that the above $-expansion of the "execDir" value is a speciality of the "execDir" pin. Other pins which expect filenames will not perform this expansion. However, inside scripts, those values are expanded and can be referred to via %(...).

Environment (Shell-) Variables[Bearbeiten]

You can define shell environment variables for the script by providing a Dictionary containing key-value pairs. These are added or replace the values from the current shell environment. (i.e. these are merged with expecco's own shell environment). To pass a variable as empty (i.e. unset), place a nil value into the dictionary.

In addition to any bindings-Directory provided at the "environment" pin, the above listed directory values and additional pin values are also present in the script's shell environment. Thus, you can add a pin named "foo" and refer to its value inside the shell script with "$foo".

Script Expansion[Bearbeiten]

You can add additional input pins, and their values will be inserted into the script via

 %(<pinName>)

For example, the following (unix-) shell script will list the contents of the folder defined by a pin named "folderName":

ls -l %(folderName)

This script expansion is applied to all scripting languages (shell, ruby, python, etc.) and especially useful with gnuplot and 'R' scripts. Notice that the expansion does not care for or look at any syntactical constraints as defined by the concrete language. This means, that string values will usually have to be embedded in extra quotes. It also means, that the script expansion might result in a syntax error, which is not detected by the editor before execution.

Output Pins[Bearbeiten]

You can define additional output pins and write to them by writing a special character sequence to stderr (i.e. file descriptor 2 on unix):

   :out:<nameOfPin>:<text>

Log Messages[Bearbeiten]

You can generate log-messages (which will also be printed in the generated report), by sending a special character sequence to stderr:

   :logInfo:<message>
   :logWarning:<message>
   :logError:<message>
   :logFail:<message>
   :Transcript:<message>

Shell Script Blocks[Bearbeiten]

Shell Script blocks are trivially supported under Unix operating systems (incl. Linux, Solaris, MacOSX etc.). On Windows, an additional shell interpreter must be installed and defined in the expecco settings. Thus, on Windows, you should install Cygwin or a similar shell, in order to execute shell-script blocks (of course, hard-core Windows users would probably rather use PowerShell actions, which are described elsewhere).

As an example, here is a script to read its input, filter all lines beginning with some pattern, sort them, and pass them on as output:

   grep "^xx" | sort

Of course, arbitrary commands can be called (even programs with their own GUI).

When executing a shell script block, expecco waits until the following conditions are met:

  1. the shell process which is executing the script has terminated
  2. when subprocesses are still running, it is guaranteed that no more data will be received via Stdout and Stderr.

This script will wait for a minute, although the sleep command is running in the background:

 echo Hello World from Shell Script
 sleep 60 &

So, when you want to fork a subprocess runing in the background, you must close Stdin and Stdout. The following script will return immediately to expecco:

 echo Hello World from Shell Script
 sleep 60 >/dev/null 2>&1 &

Different Shell Interpreters[Bearbeiten]

Under Windows, make sure that you have installed an appropriate shell emulator, and that it is either found along the PATH, or that you have specified the sh-path in the expecco-settings dialog (in "External Script Interpreters" - "Shell Path").

You can also define an environment variable named "SHELL", containing the path to a shell interpreter. The environment variable can be in the global project environment, the test suite environment, or a compound action's variable in the scope of the executed shell action. This is convenient, if the shell path is to be determined dynamically, or if another language interpreter is to be used.

For example, by defining a local environment variable (in the surrounding compound action), and setting it to "/bin/ksh", the script will be passed to a "ksh" instead of the default "bash", which is useful if you have to run shell scripts written by different teams for different shell interpreters (although, we'd consider that "bad style")

Additional Input Pins[Bearbeiten]

You can add additional input pins to shell action blocks, and refer to the value(s) as "%(pinName)" inside the script, as described in the above section on "Script Expansion".
Thus, assuming that your block has two inputs named "in1" and "in2", with String-type, the script:

echo "you gave me %(in1) and %(in2)"

would output those values on the standard output.

Additional Output Pins[Bearbeiten]

Starting with expecco 19.2, you can also add additional output pins to shell actions, and write values to them via:

echo :out:nameOfPin:valueToBeWritten 1>&2 

This feature uses the stderr file-descriptor (2) to send specially encoded commands to expecco.

Assuming that your block has two Integer typed outputs named "numFiles" and "numCFiles", the script:

nF=`ls | wc -l`
echo :out:numFiles:$(nF) 1>&2
nC=`ls *.c | wc -l`
echo :out:numCFiles:$(nC) 1>&2

will provide the number of files and the number of C-files of the current directory as integer values at the output pins.

Messages to the expecco Console[Bearbeiten]

To send a message to the expecco console, write it to stderr, prefixed by ":Transcript:", as in:

echo :Transcript:someInfo 1>&2

Generating Log Entries[Bearbeiten]

Use:

echo :logInfo:someInfo 1>&2
echo :logWarning:someInfo 1>&2
echo :logError:someInfo 1>&2
echo :logFail:someInfo 1>&2

to add corresponding messages to the activity log.

Batch Script Blocks[Bearbeiten]

These are similar to shell scripts, but use the Windows-batch file syntax. These cannot be executed on Unix systems, so your tests become OS-dependent, if you use them (we are not aware of any batch emulator running under Unix, although it is not unlikely, that such a thing exists).

As an example, the code below would check out some source code from a CVS repository and start a build:

REM
REM batch file script (Windows only)
REM
echo Checking out...
cvs co myProgram
cd myProgram
echo Building...
make

Like Shell-Script actions, Batch-Script Actions provide the standard output and standard error at corresponding output pins as text, so you can to read and analyze that further. Also, the above sections on buffering, argument expansion, command line arguments, script expansion and output pin writing also apply to Batch Script actions. For details and examples, please refer to the "Script Execution in General" section above and read the paragraph on buffering the output, the execution directory, the environment and prompt filtering.

For logging and output pin writing, the above described shell script mechanism and syntax is used.

Combined Shell/Batch Script Blocks[Bearbeiten]

New in rel. 20.1

These combine the functionality of shell and batch script actions. The definition includes both a shell script and a batch script. When executed under Windows, the batch code is executed, when executed under Unix, the shell code is executed. Although no new functionality is provided by these actions (they can be simulated via a compound action, which checks for the operating system and runs either a shell or a batch script action), they make it easier to write tests which are to run on both Unix and Windows systems.
These are very useful for simple "pip install foo"-kind of scripts.

AppleScript Blocks[Bearbeiten]

Starting with expecco 20.1, AppleScript actions are supported on OSX (Mac) systems. The script is executed by the "osascript" interpreter. These cannot be executed on other Unix or Windows systems, so your tests become OSX-dependent, if you use them.

As an example, the code below would open the TextEdit application and bring it to the foreground:

tell application id "com.apple.TextEdit"
  make new document
end tell
activate application "TextEdit"

Like other script action blocks, AppleScript blocks provide the standard output and standard error at corresponding output pins as text, so you can to read and analyze that further. Also, the above sections on buffering, argument expansion, command line arguments, script expansion and output pin writing also apply to AppleScript actions. For details and examples, please refer to the "Script Execution in General" section above.

To send a value to an output pin, use log, which writes to stderr:

   log ":out:<pinName>:valueString

To add entries to the activity log, use:

   log ":logInfo:<message>"
   log ":logError:<message>"
   etc.

AppleScript is documented eg. in https://developer.apple.com/library/archive/documentation/AppleScript

Ruby Script Blocks[Bearbeiten]

These are similar to shell scripts, but the script is executed by a ruby interpreter, instead of the sh (or bash). You have to ensure that an appropriate ruby interpreter is installed on the machine (it is not part of the expecco delivery package).

Similar to shell scripts, the ruby interpreter path either be left unspecified, or specified in the expecco settings ("External Tools"), or via an expecco environment variable named "RUBY_SHELL"). The environment variable can be the global project environment, the global test suite environment, or a compound action's variable in the scope of the executed ruby action. If left unspecified, or without an absolute path, the ruby interpreter should be found along the PATH.

Ruby Script blocks provide the standard output and standard error at corresponding output pins as text, so you can to read and analyze that further. For details and examples, please read to the "Script Execution in General" section above.

To write to an output pin, or generate log entries, use "STDERR.puts". For example:

STDERR.puts ":out:pinName:"+someString

to write to an output pin. Notice, that <someString> needs to be a string. If it is not (i.e. numbers, booleans etc.), you should use "to_s" as in:

STDERR.puts ":out:pinName:"+count.to_s

To generate a log entry, use

STDERR.puts ":logInfo:some info message from ruby"


Ruby can be downloaded from eg. "https://www.ruby-lang.org"

Python Script Blocks[Bearbeiten]

Please do not confuse "Python Script" action blocks with "Bridged Python" action blocks. The following describes script actions, whereas bridged Python actions are described elsewhere.

These are similar to shell scripts, but the script is executed by a python or jython interpreter, instead of the sh (or bash). You have to ensure that an appropriate python interpreter is installed on the machine (it is not part of the expecco delivery package).

Similar to shell scripts, the python interpreter path can be either be left unspecified, or specified in the expecco settings ("external tools"), or via an expecco environment variable named "PYTHON_SHELL"). The environment variable can be in any environment which is visible in the scope of the python action. If left unspecified, or without an absolute path, the python interpreter should be found along the PATH.

Python Script blocks provide the standard output and standard error at their output pins as text, so you may have to read and analyze that further. You can also send values to output pins or messages to the activity log by sending an appropriate string to stderr (as described above). Use "print( <message> , file=sys.stderr)" to do that.
For details and examples, please refer to the "Script Execution in General" section above.

Python can be downloaded from "www.python.org/downloads";
Jython from "https://jython.org/downloads.html".

Special Note on Python Releases[Bearbeiten]

Due to some bad planning in the python community, there were a number of changes between python2.x and python3.x, which may (and usually do) make scripts written for either version incompatible with the other.
For example, the following print expression from python2:

print "Hello World from a Python2 script\n"

needs parentheses in python3:

print ("Hello World from a Python2 script\n")

and will lead to a syntax error if executed in python3.

Thus, it may be necessary to specify an explicit python interpreter version to be used for python script actions.

If all of your actions depend on the same python interpreter version, it is sufficient to ensure that either the "PYTHON_SHELL" variable is set correctly, or the "Python Path" is defined in the settings to point to the correct interpreter. However, if you have mixed version scripts (for example, when importing python actions written by another team, which used a different version), you have to specify the python interpreter explicitly for some of the actions.

This can be done either by specifying the version explicitly in the script itself, or indirectly, via an environment variable.

Specifying the Python Version in the Script[Bearbeiten]

If the very first line of your script is (exactly as written, without any whitespace at the left or in between the hash and the identifier) one of:

#PYTHON2

#PYTHON3

#JYTHON

#IRONPYTHON

then the script will ignore any environment values and executed with that python version's setup values (see Python settings).

Specifying the Python Version in an Environment Variable[Bearbeiten]

The python version requirements are defined in the environment of the suite or imported library in which the action is defined. A variable named "PYTHON_VERSION" should be defined, which holdes either a digit "2" or "3" or the string "jython" or "ironPython". This value specifies the version to be used for ALL of the python actions within that library. Notice, that the environment of the project from which the action was imported is relevant here - not the active environment at execution time, or the suite's top environment. This version number then determines, which of the settings is to be used (i.e. "PYTHON2_SHELL/path", "PYTHON3_SHELL/path", "JYTHON_SHELL/path" or "IRONPYTHON_SHELL/path").

The Settings or a corresponding variable then define the python interpreter (in "Settings" -> "Execution" -> "External Language Interpreters"):

  • "Python Path" in the settings or the "PYTHON_SHELL" shell environment variable,
    are used when no explicit version is required, or if all of your scripts require the same python version,
  • "Python2 Path" in the settings (or the "PYTHON2_SHELL" shell variable)
    are used for scripts which have been marked as explicitly requiring python2,
  • "Python3 Path" in the settings (or the "PYTHON3_SHELL" shell variable)
    for scripts which are marked to require python3
  • "Jython Path" in the settings (or the "JYTHON_SHELL" shell variable)
    for scripts which are marked to require jython.
  • "IronPython Path" in the settings (or the "IRONPYTHON_SHELL" shell variable)
    for scripts which are marked to require ironPython.

Using that double-indirect mechanism, it is still possible to:

  • import multiple libraries with different python-versions requirements
  • specify the paths independent of where the python interpreters are installed (i.e. the machine on which the final suite is executed may have different installation paths than the machine on which the imported libraries were developed).

A concrete demo importing libraries which require different python interpreter versions is found in "d46_Suite_using_both_Python2_and_Python3_actions.ets" in the demo folder.

Jython and IronPython Version[Bearbeiten]

Jython and IronPython are currently available as 2.7 version only (at the time this document was last edited).

Node.js-Script Blocks[Bearbeiten]

Please do not confuse "Node.js Script" action blocks with "Bridged Node.js" action blocks. The following describes script actions, whereas bridged Node.js actions are described elsewhere.

Node.js script actions are similar to shell script actions, but the script is executed by a Node.js interpreter, instead of the sh (or bash). You have to ensure that an appropriate node interpreter is installed on the machine (it is not part of the expecco delivery package).

Similar to shell scripts, the node interpreter path can be either be left unspecified, or specified in the expecco settings ("External tools"), or via an expecco environment variable named "NODEJS_SHELL"). The environment variable can be the global project environment, or the global test suite environment. If left unspecified, or without an absolute path, the node interpreter should be found along the PATH.

Node.js-Script blocks provide the standard output and standard error at their output pins as text, so you may have to read and analyze that further. You can also write to output pins or create log entries by writing appropriate Messages to stdder (use "console.error(…)" to do that).
For details and examples, please read the "Script Execution in General" section above.

Notice that in older expecco (pre 18.1) versions, a shell/batch script action block, with appropriate command and command line options can be used to execute node.js scripts.

Node can be downloaded from nodejs.org.

Plot/Graph Action Blocks[Bearbeiten]

With expecco rel18.1, a new action type was added, which makes it very easy and convenient to generate graphs of measured or generated statistical data. It is now trivial to add graphs of execution times, response times, quality data etc. to the generated report.

As a prequisite, you need to install gnuplot (-> Gnuplot on Sourceforge) and it must be found along your shell-path, or in an environment variable named "GNUPLOT_SHELL" or configured in the "External Tools" settings.

The "general information" on script actions also applies to gnu plot actions. In addition, more detailed information, step-by-step guidance and examples are found in "Using Gnuplot Action Blocks". Concrete examples are found in the library "Plot_Graph_Library.ets", which is found in the gnuplot plugin or the demos folder.

R Action Blocks[Bearbeiten]

With expecco rel18.1, another new action type was added for 'R' action blocks. These are evaluated using an 'R' language script interpreter, a language especially suited for statistical analysis, bulk data processing and graph plotting. Similar to Plot/Graph actions, these can also generate figures and graphs to be attached to the final report and/or log.

As a prequisite, you need to install R and it must be found along your shell PATH or configured in the "external Tools" settings. Notice that the 'R' interpreter is named 'r' (lowercase) on some systems, and 'R' (uppercase) on others.

The "general information" on script actions also applies to RScript actions. More information, step-by-step guidance and examples are found in "Using R Action Blocks". Concrete examples are found in the library "R_Demo_Suite.ets", which is found in the examples folder.

GNU Octave Action Blocks[Bearbeiten]

With expecco rel20.1, the new GNU Octave action type was added. Octave is very similar to Matlab, and most Matlab scripts are drop-in compatible. Octave is a powerful mathematics oriented programming language with builtin plotting and visualisation tools. Similar to Gnuplot and R actions, this can be used to add graphs of execution times, response times, quality data etc. to the generated report.

As a prequisite, you have to install gnu octave (-> Octave download Page) and it must be found along your shell-path, or in an environment variable named "GNUOCTAVE_SHELL" or configured in the "External Tools" settings.

The "general information" on script actions also applies to gnu octave actions.

PowerShell Script Blocks[Bearbeiten]

On Unix systems, install powershell and ensure that it is either found along your shell PATH or configured in the settings (powershell is now also available for MacOSX and Linux). Notice, that the command is named either "powershell" or "pwsh", depending on the operating system you use.

On Windows, powershell typically comes preinstalled with the operating system. Please read the previous chapter on "Script Execution in General" on how to get the output and/or pass parameters from input pins to the script.

TCL, Perl and Go Script Actions[Bearbeiten]

Same as above.

Any other Scripting Language via Shell Blocks[Bearbeiten]

Any external interpreter for any scripting language can be called via a regular shell/batch block. For this, either place your script into a well defined folder or add it as attachments to the suite. Then define corresponding compound blocks, which consist of a simple shell/batch call to the interpreter, and pass the script file's name plus additional parameters as command-line arguments.

Although now somewhat outdated due to the now available Python script action blocks, an example to call a python script via a shell block is found in the demo suite "d43_Call_Python_Script_via_Shell.ets", which looks like:

  CallPythonViaShellExample.png  

and a python script (in the "test.py" attachment):

print( "Hello World from a python script (1)" )

DLL-Calls[Bearbeiten]

A DLL-call block defines an interface to a function which is contained inside a DLL (Dynamic Link Library), also called "Shared Library" or "Shared Object" in the Unix world). Typically, these are C or C++ functions. For a dll-call to be possible, some of the function's attributes have to be entered into the dll-call editor.

These are:

  • name of the dll (for example: "user32.dll"). If the DLL-name has no extension (".dll"/".so"), an OS-specific extension is added by expecco. For portability of your suite, it is better to only provide the base name here and let expecco choose the extension.
  • the name of the function within the dll
  • Windows operating system only: the calltype (WINAPI or C-Call).
  • the number and types of the arguments
  • the type of the return value.

In addition, it is possible to specify that the dll-call is to be executed by a separate API-call thread. This avoids a blocking of the expecco user interface and other executing activities, for long running dll-calls, or called functions which stay in a blocking wait or a busy polling loop.

If your testsuite is planned to be portable across operating systems (i.e. it should run under both Windows- and Unix test machines), you will need both a windows DLL ("foo.dll") and a Unix shared library object ("foo.so" or "foo.dylib"). Because the filename extensions are OS-specific, it is recommended to enter the basename without suffix into the "DLL name" field. Expecco will fill in the extension as used by the OS (however, if your dll uses a non-standard extension, you will have to provide it).

If the shared library file names are different among different operating systems, you can either define appropriate dll-name mappings as described below, or alternatively define separate call blocks as per operating system, and choose dynamically at runtime, which one to call. (e.g. by connecting the trigger input of each to an action which checks for the OS-type at runtime).

In seldom situations, the name of the dll is different in a 64bit operating system. Under WIndows, the 64bit variant sometimes has a '64' suffix. If this is the case, and your suite needs to run on both 32 and 64 bit operating systems, specify the 64-bit name int the extra "DLL (64bit)" field.

Argument and Return Types[Bearbeiten]

Before the call, incoming argument values are read from the input pin(s) and converted to corresponding C arguments, as specified by the "argumentTypes" field in the DLL-call editor. The argument type list consists of a comma-separated list of C types, the return type of a single individual type name, each of which being one of:

   char, short, int, long, longlong,
   uchar, ushort, uint, ulong, ulonglong,
   float, double,
   pointer, HANDLE,
   char*, short*, int*, long*, uchar*, ushort*, uint*, ulong*,
   float*, double*

Notice that the exact argument type is not absolutely required for the call to succeed. For example, the callout mechanism (which is based on the ffi foreign function call interface) does not really need the type of structure or union to which a pointer points to. Thus, the generic "pointer" type is usually all you need to specify, when a structure or array or character pointer is passed to a C function.

However, some argument types might be passed in specialized registers (especially: floats, doubles and structs), and wrong argument values might be passed if the declaration is wrong.

For a comprehensive list, consult the "ExternalLibraryFunction >> ffiTypeSymbolForType:" method.

Calling Conventions[Bearbeiten]

The term "calling convention" refers to the way arguments and the return value are passed to/from the called function. For most function calls, the default (C-API) is to be used. However, for historic reasons, functions in Windows32 may be defined in two flavours: WINAPI and CDECL (C-API)

The WINAPI (or API-) calltype specifies that a different argument passing scheme is to be used. This is specific to MS-Windows 32bit DLLs, and even there, only to a subset of all libraries.

Unless you have the C-header file parser plugin/extension installed, expecco has no builtin mechanism to figure out, which call convention is to be used for a particular DLL function. On Windows, if you do not have the source code of your called function at hand, consult the C header file, where the called function's declaration is found. There, look for a "WINAPI" or "API" prefix. In general, the WINAPI interface is a historic leftover from ancient times, when Windows was written in Pascal. However, they are still used (for backward compatibility) in many system DLLs - especially in the Kernel, User and GDI dlls.

On Unix- and 64bit Windows systems, the calltype is ignored and the C-calling convention is used. Thus, unless the name or parameter types are different, the same DLL-call action can be used for both architectures (given that corresponding DLLs are available).

C++ Name Mangling[Bearbeiten]

If the called function is a C++ function or C++ wrapper function, the compiler may have arbitrarily mangled the function's name (-> Wikipedia: "name mangling"); usually a prefix or suffix, or both are added to the plain function's name. As this name mangling is very OS and compiler-specific, no automatic name generation can be done by expecco, and you will have to find out the name yourself, and paste it into the "C++ Name" field. Use any of your system's symbol-table listing tool ("nm" on Unix and "dllWalker" on Windows) to find out that name.

It is recommended to use C-wrapper functions ("extern C" declaration) as entry points which forward calls to corresponding C++ functions. This gives you constant entry points and thus reduces the amount of maintenance later, when a different compiler or different version mangles names differently.

DLL Mapping[Bearbeiten]

Expecco (actually Smalltalk) maintains a DLL-name mapping feature, which translates DLL-library names on an individual per-DLL basis. Thus, it is possible to control which DLL is actually used by DLL call blocks. The mapping is very useful, if multiple versions of a DLL are present (e.g. use "foo" in the DLL call block, and define a mapping of "foo" to "foo.vsnX") or to control from which location a library is to be loaded (e.g. define a mapping from "foo" to "/mylibs/libraries/foo.so").

This mapping can be defined in the "Settings" -> "Project Management" -> "DLL Mapping" dialog, and is stored in your local user settings.

In addition, the StandardLibrary contains a block "Define DLL Mapping", which can be used to modify this mapping table dynamically by the suite itself. However, DLL-mapping is an issue depending on the underlying execution machine - not of the test suite. In order to get portable suites, it is recommended to define the mapping in the user-preferences of the execution machine, and leave any dependencies on paths or DLL-file names out of the suite.  

DLL Path[Bearbeiten]

In addition, expecco needs to know the location of the dll. You can define this as an absolute path or a relative path (in the DLL-call action block). You may use absolute paths if the dll is part of the tested system and the folder in which the dll is found is well known and relatively stable. It is of course your responsibility to ensure that the dll is present when the action block is invoked.

Alternatively, you can give only the name of the file (without the folder name prefix) in the DLL-call action block, and either place the dll as attachment right into the test suite, or alternatively define the dll search path in the expecco settings dialog.

The dll-path settings are saved as part of your personal (per system and per user) settings - not in the ets file as project parameters.

DLL Example[Bearbeiten]

You should find an example folder containing a simple DLL (source plus makefiles) and an expecco suite which calls functions from that DLL in the "expecco/projects/examples/sampleDLL" folder.

SOAP-Calls[Bearbeiten]

These blocks define a remote procedure call via the SOAP protocol. SOAP blocks are normally generated automatically, by importing a WSDL service description. For more information, please refer to the WSDL Import Plugin description.  

REST-Calls[Bearbeiten]

These blocks define a remote procedure call via the REST protocol. REST blocks are created in the navigation tree via the "New Action"-"Services"-"REST Action" menu item. They are to be configured with a service endpoint (i.e. URL) and a sub-path to address/identify the object under that endpoint.

REST actions have a predefined scheme, which looks like:

REST scheme sample.png

Part of its parameters are given dynamically by input pins, whereas others are predefined in the declaration tab:

REST declaration sample.png

The final HTTP URL is constructed by concatenating these path components (endpoint URL and object locator sub-path), of which either may be empty. I.e. it is possible to specify the resulting full path completely statically via the declaration and leave the pin unconnected, or to specify it completely dynamically, by leaving the declaration empty. In addition, the endpoint URL can be specified by a separate input pin named "__url__", or an environment variable named "REST_URL".

In most situations you would define the service endpoint in the environment variable (the target host plus REST-service prefix), and the object-path via a possibly frozen input pin. Of course, it is also possible to provide the object locator from another action block. The best way depends on your concrete setup and/or personal preferences.

The "__decode__" input specifies if the response text should be decoded (as JSON- or XML-DOM object) and provided at the responseObject output pin. If the REST service does provide one of them as answer, it is convenient to get the object already decoded at this output. Otherwise, you will have to fetch the value from the "responseText" output pin, and decode the value yourself (in this case, it will usually be plain text or encoded in a non-standard format, for which you will have to provide a decoder yourself).

The "httpBody" input should receive a string to be sent with the request, if the REST-service requires so. Typically, this is a JSON- or XML-encoded object to be stored or passed as argument (e.g. in a PUT/POST request). However, there are also REST calls, which do not require any input data. The value passed to this pin should be a string and thus already encoded from whatever object is to be sent. If required, use on of the JSON-encode or XML-generate action blocks. Or generate the string using one of the String-format or String-concatenation blocks.

The "encodeUTF8" and "decodeUTF8" flags are new with expecco 2.10.1. In previous releases, the given httpBody and returned responseData where transmitted unchanged (i.e. you had to encode/decode them explicitly, by using one of the EncodeUTF/DecodeUTF actions. Now, this can be done automatically for you, by checking one of those flags. For backward compatibility, the default is "false"; if your suite already provides UTF-8-encoded data (or there is no need to do so, because it only contains ASCII text), it will run unchanged in 2.10.1.

When structured objects are returned (as JSON or XML), it is recommended to create another compound action, which performs the required decoding and isolates the details from the rest of the suite. For example, if a XML-DOM is returned, a wrapper action may extract relevant fields and look like:

REST wrapper sample.png


Take a look at the examples in the "w09_RESTcallDemo.ets" file, which is found in the examples folder. More details on REST actions are also found in ElementaryBlock Element RESTCall/en .

XML-RPC-Calls[Bearbeiten]

These blocks define XML-RPC remote procedure calls. XML-RPC is a very light-weight and low-overhead XML-based protocol, which is supported by many service oriented applications (among them, expecco and expecco ALM themselves). For an XML-RPC call, the following information is mandatory:

There are 3 alternative ways, to specify the URL:

  • hardwired as a string in the blocks specification (not recommended)
  • via an environment variable
  • via a pin value (freeze or dynamic)
Hardwired Service URL[Bearbeiten]

Because this is very inflexible, it should be only used for very constant URLs, or while exploring a service interface. For this, type the URL into the block's specification form.

Specifying the Service URL via an Environment Variable[Bearbeiten]

Use a macro-replacement pattern such as "%(RPC_HOST)" in the URL field (as above). This will be replaced by a corresponding variable name from the reachable environment variable named accordingly. You can either specify a full URL (i.e. host, port and path) or a partial URL using this mechanism. For example, you can also provide the URL via 3 separate environment variables, by entering: "%(RPC_HOST):%(RPC_PORT)/%(RPC_PATH)". A runtime error will be reported, if the variable expansion leads to an invalid URL. However, if a missing expansion leads to a valid URL, no error is reported. This allows for optional fields to be added to the URL, for example as in: "%(JIRA_HOST)%(OPTIONAL_RPC_PORT)/jira/rpc/xmlrpc".

Providing the URL via an Input Pin[Bearbeiten]

Add an input pin with the (obligatory) name "url", and deliver it a string value. This is the most flexible way, as the URL value can be dynamically generated by any other block, or be provided via a pin-freeze value. If a url-pin is present, but not connected, it is ignored and the above variable mechanism is used. Otherwise, the pin-value takes precedence over any value in the specification.


See Also[Bearbeiten]

Code Editor
For a list of already existing blocks, see Standard Library
For the programmer API for the built in languages see: Expecco API
For the scripting API for the built in languages see: Expecco Scripting API
For information on the functions available in the built-in classes, please refer to the Smalltalk/X Online Documentation and especially the Class Reference. For example, some of the mathematical functions are described here and String processing functions are found here and here.



Copyright © 2014-2024 eXept Software AG