Expecco API/en: Unterschied zwischen den Versionen

Aus expecco Wiki (Version 2.x)
Zur Navigation springen Zur Suche springen
 
(823 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 8: Zeile 8:
== expecco API ==
== expecco API ==


The expecco API provides functions and access to the underlying class library for the use in [[Elementary Block|elementary blocks]] written in Smalltalk and JavaScript (i.e. for code which is executed inside expecco itself). This API is not available for elementary blocks written in other languages which are executed by external script engines (Shell, Batch, Python, Node.js etc.) or inside a different Java or CLR Virtual Machine (Groovy, VBScript, IronPython).
The expecco API provides functions and access to the underlying class library for the use in [[Elementary Block|elementary blocks]] written in Smalltalk and JavaScript (i.e. for code which is executed inside expecco itself). This API is not available for elementary blocks written in other languages which are executed by external script engines (Shell, Batch, Python, Node.js etc.) or inside a different Java or CLR Virtual Machine (Groovy, VBScript, IronPython). However, a subset of the functions are supported as RPC (remote procedure calls) in bridged elementary actions.


For a short introduction to the Smalltalk programming language,
For a short introduction to the Smalltalk programming language,
Zeile 17: Zeile 17:
<br>The API for bridged Node.js actions is described [[#Node.js_.28Bridged.29_Elementary_Blocks|here]].
<br>The API for bridged Node.js actions is described [[#Node.js_.28Bridged.29_Elementary_Blocks|here]].
<br>The API for bridged Python and Jython actions is described [[#Bridged Python Elementary Blocks|here]].
<br>The API for bridged Python and Jython actions is described [[#Bridged Python Elementary Blocks|here]].
<br>The API for bridged Ruby actions is described [[#Bridged Ruby Elementary Blocks|here]].
<br>The API for bridged C actions is described [[#Bridged C Elementary Blocks|here]].


==<span id="Smalltalk_Elementary_Blocks"></span><span id="JavaScript_Elementary_Blocks"></span>JavaScript and Smalltalk Elementary Blocks ==
==<span id="Smalltalk_Elementary_Blocks"></span><span id="JavaScript_Elementary_Blocks"></span>JavaScript and Smalltalk Elementary Blocks ==


The JavaScript and the Smalltalk API consist of the same functions - both call into the underlying [http://www.smalltalk-x.de Smalltalk/X] system, for which extensive documentation is available as
The expecco JavaScript and the Smalltalk API consist of the same functions - both call into the underlying [http://www.smalltalk-x.de Smalltalk/X] system, for which extensive documentation is available as
[http://live.exept.de/doc/online/english/TOP.html Online Documentation] and
[http://live.exept.de/doc/online/english/TOP.html Online Documentation] and
as [http://live.exept.de/ClassDoc Class Reference].
as [http://live.exept.de/ClassDoc Class Reference].


The Smalltalk/X language and class library are [http://wiki.squeak.org/squeak/uploads/172/standard_v1_9-indexed.pdf ANSI compatible]; therefore the official ANSI documents and Smalltalk literature are also valuable sources of information.
The Smalltalk/X language and class library are [http://wiki.squeak.org/squeak/uploads/172/standard_v1_9-indexed.pdf ANSI compatible]; therefore the official ANSI documents, Smalltalk literature and tutorials are also valuable sources of information (for example, the online tutorials you may find in youtube).


:If you wonder "why Smalltalk?", you should know that [https://insights.stackoverflow.com/survey/2017#most-loved-dreaded-and-wanted Smalltalk ranked nr. 2 in a 2017 survey of "most loved languages"] - admittedly, this ranking may be subjective (in that Smalltalk programmers might be more loyal), but it should at least raise your eyebrows.
JavaScript and Smalltalk only differ slightly in their syntax; semantically they are very similar:
:It is not among the most used languages - but that decision is usually made by people who do not even know Smalltalk or who think that language does not matter.
{| Border
:If you think it is outdated, be reminded that Smalltalk has the most consistent object model and reflection facilities of all OO languages, and that modern clones like Ruby and Python only provide a subset of Smalltalk's facilities. If you don't know it, don't judge it.
:The underlying virtual machine (VM) is feature rich and provides many mechanisms which are not possible in most 'modern' languages. For example, in most (if not all) of the current languages it is not possible to interrupt a thread which blocks in a system call (eg. in a read). In none of them, are exceptions proceedable. In none of them can you interrupt other threads in any situation (again: especially not if it is inside a blocking read).
:The set of integrated tools is massive in Smalltalk: full access to classes, threads, semaphores, any object's internals are all provided by graphical tools which can be opened by double clicks or via menus.

Expecco's JavaScript is implemented by compiling JavaScript syntax to the underlying Smalltalk bytecode. It is not fully compatible with a "real" JavaScript: it is class based, and the underlying object model and class library are actually the Smalltalk/X object model and class library. Thus, expecco's JavaScript can be seen as "<I>a Smalltalk with JavaScript syntax</I>".

Please be not confused by two JavaScript-based elementary actions: the builtin JavaScript described here is executed within expecco itself whereas Node-actions are executed in a separate Node.js virtual machine. These Node actions are "real JavaScript" and are described in a separate [[#Node.js_.28Bridged.29_Elementary_Blocks |chapter below]].

===Smalltalk / JavaScript Short Syntax Overview===
Expecco's builtin JavaScript and Smalltalk only differ in their syntax - semantically they are very similar:
{| class="wikitable"
! JavaScript
! JavaScript
! Smalltalk
! Smalltalk
!
!
|-
|-
| this
|style="width: 15em"|this
| self
|style="width: 15em"|self
| the current activity
| the current activity (inside an elementary action)
|-
|-
| this.functionName()
|this.<I>functionName</I>()
| self functionName
|self <I>functionName</I>
| a call without arguments
| a call without arguments
|-
|-
| this.functionName(''arg'')
|this.''functionName''(''arg'')
| self functionName:''arg''
|self ''functionName'':''arg''
| a call with one argument
| a call with one argument
|-
|-
| this.namePart1_part2(''arg1'',''arg2'')
|this.''namePart1_part2''(''arg1'',''arg2'')
| self namePart1:''arg1'' part2:''arg2''
|self ''namePart1'':''arg1'' ''part2'':''arg2''
| two arguments<br>Notice that in Smalltalk, the arguments are ''sliced'' into the name parts, and that the concatenation of the parts is the actual name (incl. the colons).<br>Thus, in "<code>x at:5 put:10</code>" the name of the called method is "at:put:" and it gets two arguments: 5 and 10. Whereas "<code>(x at:5) put:10</code>" would first send an "at:" message, then send a"put:" message to the object returned from "at:".<br>In JavaScript, ":" is not a value character in a function name, and a translation mechanism is applied which replaces ":" by underline, and drops the final colon.
| two arguments
|-
|-
| functionName(...)
| ''functionName''(...)
| self functionName...
| self ''functionName''...
| implicit this/self receiver
| implicit ''this'' receiver in JS<br>explicit in Smalltalk
|-
|-
| accessor
| ''accessor''
| self accessor
| self ''accessor''
| slot access - implicit this/self send
| slot access - implicit this receiver in JS<br>explicit in Smalltalk
|-
|-
| stat1 ; stat2
| ''stat1'' ; ''stat2'' ;
| stat1 . stat2
| ''stat1'' . ''stat2''
| statement terminator / separator<br>in ST: required to separate statements<br>in JS: required to terminate every statement.<br>Thus, in Smalltalk, the last period "." inside a method or block can be and often is omitted.
| statement terminator / separator<br>in ST: to '''separate''' statements<br>in JS: to '''terminate''' a statement.<br>In Smalltalk, the last period "." inside a method or block can be and often is omitted.
|-
|-
| if (cond) {<br>&nbsp;&nbsp;ifStats<br>} else {&nbsp;&nbsp;<br>&nbsp;&nbsp;elseStats<br>}
| if (''cond'') {<br>&nbsp;&nbsp;&nbsp;&nbsp;''ifStats''<br>} else {<br>&nbsp;&nbsp;&nbsp;&nbsp;''elseStats''<br>}
| cond ifTrue:[<br>&nbsp;&nbsp;ifStats<br>] ifFalse:[<br>&nbsp;&nbsp;elseStats<br>]
| ''cond'' ifTrue:[<br>&nbsp;&nbsp;&nbsp;&nbsp;''ifStats''<br>] ifFalse:[<br>&nbsp;&nbsp;&nbsp;&nbsp;''elseStats''<br>]
| conditional execution
| conditional execution.<br>Notice the square brackets in Smalltalk
|-
|-
| while (cond) {<br>&nbsp;&nbsp;stats<br>}
| while (''cond'') {<br>&nbsp;&nbsp;&nbsp;&nbsp;''stats''<br>}
| [ cond ] whileTrue:[<br>&nbsp;&nbsp;stats<br>]
| [ ''cond'' ] whileTrue:[<br>&nbsp;&nbsp;&nbsp;&nbsp;''stats''<br>]
| while-loop. Notice the extra brackets in Smalltalk
| while-loop.<br>Notice the brackets around the condition in Smalltalk
|-
|-
| for (i=''start''; i&lt;=''end''; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;''stats using i''<br>}
| function () { stats }
| ''start'' to:''end'' do:[:i &#124;<br>&nbsp;&nbsp;&nbsp;&nbsp;''stats using i''<br>]
| [ stats ]
| counting-loop.<br>Seldom used in Smalltalk
| an anonymous (inner) function (called "block" in ST)
|-
|-
| foreach (''el'' in ''collection'') {<br>&nbsp;&nbsp;&nbsp;&nbsp;''stats using el''<br>}
| function (a1, a2,...) { stats }
| [:a1 :a2 ...&#124; stats ]
| ''collection'' do:[:el &#124;<br>&nbsp;&nbsp;&nbsp;&nbsp;''stats using el''<br>]
| loop over collection elements.<br>Very often used in Smalltalk
|-
| function () { ''stats'' }
| [ ''stats'' ]
| an anonymous (inner) function (called "block" in ST).<br>JS: return value via explicit return<br>ST: return value is last expression's value
|-
| function (''a1'', ''a2'',...) { ''stats'' }
| [:''a1'' :''a2'' ...&#124; ''stats'' ]
| in ST: blocks are references to anonymous function
| in ST: blocks are references to anonymous function
|-
|-
| var v1, v2, ... ;
| var ''v1'', ''v2'', ... ;
| &#124; v1 v2 ... &#124;
| &#124; ''v1'' ''v2'' ... &#124;
| local variables inside a function (or block)
| local variables inside a function (or block). Semantically, these are "let" variables (i.e. only seen in the current scope)
|-
|-
| return expr;
| return ''expr'';
| ^ expr
| ^ ''expr''
| return a value.<br>In ST: a return from within a block returns the enclosing top-level function<br>In JS: a return returns from the inner function
| return a value.<br>In ST: a return from within a block returns the enclosing '''top-level''' method<br>In JS: a return returns from the '''inner''' function
|-
|-
|
| "..." or '...'
| '...'
| '...'
| String constant
| String constant without c-escapes
|-
|-
| "..." or '...'
|
| c'...'
| c'...'
| String constant with c-escapes (\n, \t, etc.)
| String constant with c-escapes (\n, \t, etc.)
|-
|
| e'..{ expr1 } .. { expr2 } ...'
| String with sliced in expressions (exprs will be converted to string)
|-
|-
| /* ... */
| /* ... */
Zeile 99: Zeile 124:
| "/ ...
| "/ ...
| end of line comment
| end of line comment
|-
|
| "<<TOKEN<br> ... <br>TOKEN
| token comment
|}
|}


==== Smalltalk Operator Precedence ====
Smalltalk beginners may be irritated by the missing binary operator precedence rules. In Smalltalk, all operators have the same precedence and are evaluated left to right. Thus, "<code>a + b * 5</code>" in JavaScript means "<code>a + (b * 5)</code>", whereas in Smalltalk, it is evaluated as "<code>(a + b) * 5</code>" (i.e. left to right).
Smalltalk beginners may be irritated by the missing binary operator precedence rules: in Smalltalk, all operators have the same precedence and are evaluated left to right.<br>Thus:
a + b * 5
in JavaScript means:
a + (b * 5)
whereas in Smalltalk, it is evaluated as:
(a + b) * 5
(i.e. left to right).

Therefore, as a guideline (and actually a good convention), all Smalltalk binary expressions with more than one operator should be parenthesized to make the intention clear. It should be done even if the left-to-right order matches the mathematical precedences.

==== Smalltalk Operators ====
Another uncommon feature of the Smalltalk language is that there are almost no keywords in the language
(except "<code>self</code>", "<code>super</code>", "<code>true</code>", "<code>false</code>", "<code>nil</code>" and "<code>thisContext</code>").
Every other word is either the name of a variable or the name of a message to be sent to an object (aka a "''virtual function call''" or "''method invocation''").

Every sequence of special characters (except ":", ".", "(", ")", "[", "]", ":=" and ";") is interpreted as operators.
Thus, the sequences "->", "=", "=>", "<-" and especially the comma (",") are operators which are treated like "+", "-" etc.


Finally, message sends ("''function calls''") with arguments are always written with keyword arguments. I.e. the arguments are prefixed by a keyword (which is an identifier with a colon). Smalltalk does not allow positional arguments in a message send.
Therefore, as a guideline (and actually a strict convention), all Smalltalk expressions should be parenthesized to make the intention clear. It should be done even if the left-to-right order matches the mathematical precedences.


==== Calling Functions and Invoking Methods (aka "Sending Messages") ====
All functions are actually implemented in Smalltalk and follow the standard Smalltalk naming conventions. The same function names are used for JavaScript. As seen above, this scheme works well for functions without or with a single argument, but requires a name translation for functions with more than one argument. This translation is done by the JavaScript compiler by replacing every colon (:) of the Smalltalk name by an underline (_) character, except for the last colon. Thus for example, the Smalltalk name "<code>at:put:</code>" will be replaced to "<code>at_put</code>" in JavaScript.
All functions are actually implemented in Smalltalk and follow the standard Smalltalk naming conventions. The same function names are used for JavaScript. As seen above, this scheme works well for functions without or with a single argument, but requires a name translation for functions with more than one argument. This translation is done by the JavaScript compiler by replacing every colon (:) of the Smalltalk name by an underline (_) character, except for the last colon. Thus for example, the Smalltalk name "<code>at:put:</code>" will be named "<code>at_put</code>" in JavaScript.


In JavaScript, a function-name alone (i.e. without explicit receiver) is translated into a self-send; thus "<code>this.foo()</code>" and "<code>foo()</code>" are equivalent.
In JavaScript, a function-name alone (i.e. without explicit receiver) is translated into a self-send; thus "<code>this.foo()</code>" and "<code>foo()</code>" are equivalent.
Zeile 114: Zeile 161:


For example, the JavaScript call:
For example, the JavaScript call:
this.'''environmentAt'''("foo")
<br>
this.environmentAt("foo")</PRE></CODE>
is written in Smalltalk as:
is written in Smalltalk as:
self environmentAt:'foo'</PRE></CODE>
self '''environmentAt:''''foo'


and, to demonstrate the multi-argument translation rule,
and, to demonstrate the multi-argument translation rule, the Smalltalk code:
self '''environmentAt:''''foo' '''put:'''1234
the Smalltalk code:
<br>
self environmentAt:'foo' put:1234</PRE></CODE>
is written in JavaScript as:
is written in JavaScript as:
this.environmentAt_put("foo", 1234)</PRE></CODE>
this.'''environmentAt_put'''("foo", 1234)


=== Syntax ===
=== Syntax Summary ===
For a formal specification of the JavaScript and Smalltalk languages, see the appendixes below.
For a formal specification of the JavaScript and Smalltalk languages, see the appendixes below.
The following gives a rough overview over the most common syntactic constructs.
The following gives a rough overview over the most common syntactic constructs.


{| class="wikitable"
{| Border
! JavaScript
! JavaScript
! Smalltalk
! Smalltalk
! Notes
! Notes
|-
|-
| this
|style="width: 10em"| this
| self
|style="width: 10em"| self
| the current activity
| the current activity
|-
|-
Zeile 145: Zeile 189:
| ;
| ;
| .
| .
| statement terminator/separator<br>In JavaScript it is a terminator, meaning that every statement (except brace-blocks) must be terminated by a semicolon.<br>In Smalltalk, it is a separator, meaning that the last statement in a block or method does not need one.
| Statement Terminator/Separator
|-
|-
| "..."
| "..."
| '...'
| '...'
| a String constant (JavaScript allows single quotes too)
| a String constant (JavaScript allows single quotes too);<br>no C-escapes in Smalltalk
|-
|
| c'...'
| a Smalltalk String constant with c-escapes
|-
|
| e'..{ expr } ..'
| a Smalltalk String with embedded expressions (sliced-in)
|-
|-
|
|
| #'...'
| #'...'
| a Symbol constant (not available in JavaScript)
| a Symbol constant<br>(not available in JavaScript; use "xxx".asSymbol() in JS)
|-
|-
|
|
| #...
| #...
| also a Symbol constant (not available in JavaScript)
| also a Symbol constant<br>(not available in JavaScript; use "xxx".asSymbol() in JS)
|-
|-
| [ el1 , el2 , ... ]
| [ el1 , el2 , ... ]
| #( el1 el2 ... )
| #( el1 el2 ... )
| an Array constant<br>(elements must be constant literals)<br>Notice the separating comma in JS, but space-separated elements in Smalltalk.<br>In Smalltalk, the array is created at compile time (i.e. runtime cost is zero); the array is immutable,<br>whereas in JS it is created mutable at runtime
| an Array constant (elements must be constants)
|-
|-
| [ expr1 , expr2 , ... ]
| [ expr1 , expr2 , ... ]
| { expr1 . expr2 . ... }
| { expr1 . expr2 . ... }
| a computed Array (notice the expression terminators in ST, and that "," (comma) is an operator in ST)
| a computed Array<br>(elements are expressions)<br>notice the expression terminators in ST (periods),<br>and that "," (comma) is an operator in ST.<br>The array is created at runtime; the array is mutable
|-
|-
|
|
| #[ el1 el2 ... ]
| #[ el1 el2 ... ]
| a ByteArray constant (not available in JavaScript)
| an immutable ByteArray constant<br>(not available in JavaScript)
|-
| 1234
| 1234
| an Integer constant<br>(arbitrary precision)
|-
| 0x1234
| 0x1234 or 16r1234
| a hexadecimal Integer constant (radix is 16)<br>(arbitrary precision)
|-
| 0b10001010
| 0b10001010 or 2r10001010
| a binary Integer constant (radix is 2)<br>(arbitrary precision)
|-
|
| 3r10001010
| a ternary Integer constant (radix is 3);<br>not available in JavaScript
|-
|
| &lt;N&gt;rxxxx
| an N-ary Integer constant (radix is N);<br>not available in JavaScript
|-
|-
|
|
| ( num / den )
| ( num / den )
| a Fraction constant (not available in JavaScript)
| a Fraction constant (not available in JavaScript);<br>both numerator and denominator must be integers
|-
| 1.234<br>1e5
| 1.234<br>1e5
| Floating Point constants<br>(double precision)
|-
| v = expression
| v := expression
| assignment to a variable
|-
| expr1 == expr2
| expr1 = expr2
| compare for equal value
|-
| expr1 === expr2
| expr1 == expr2
| compare for identity<br>be careful: 1.0 (the float) is NOT identical to 1 (the integer)<br>However, they are equal in value.
|-
|-
| rcvr.f ()
| rcvr.f ()
Zeile 185: Zeile 273:
| rcvr.a_b (arg1, arg2)
| rcvr.a_b (arg1, arg2)
| rcvr a: arg1 b: arg2
| rcvr a: arg1 b: arg2
| with 2 args<br>notice the different names of the function<br>"a_b" vs."a:b:"
| with args<br>notice the different names of the function<br>"a_b" vs."a:b:"
|-
|-
| return;
| return;
| ^ self
| ^ self
| without return value (in ST: from outer function; in JS: from current function)
| without return value<br>ST: from enclosing method;<br>JS: from current function
|-
|-
| return expr;
| return expr;
| ^ expr
| ^ expr
| with return value (in ST: from outer function; in JS: from current function)
| with return value<br>ST: from enclosing method;<br>JS: from current function
|-
|-
| return from execute;
| return from execute;
| ^ self
| ^ self
| return outer from inner function
| return from execute activity from within an inner function
|-
|-
| ! expr
| ! expr
Zeile 205: Zeile 293:
| e1 && e2
| e1 && e2
| (e1 and:[ e2 ])
| (e1 and:[ e2 ])
| non evaluating and (e2 is not evaluated if e1 is false)
| non evaluating and<br>(e2 is not evaluated if e1 is false)
|-
|-
| e1 &#124;&#124; e2
| e1 &#124;&#124; e2
| (e1 or:[ e2 ])
| (e1 or:[ e2 ])
| non evaluating or (e2 is not evaluated if e1 is true)
| non evaluating or<br>(e2 is not evaluated if e1 is true)
|-
|
| pin <- value
| only for output pins: writes value to the pin (same as "pin value(val)")
|}
|}


Zeile 215: Zeile 307:


Some syntactic constructs of the JavaScript language are implemented as library functions in Smalltalk. Among others, most noteworthy are conditional execution (if-the-else) and loops.
Some syntactic constructs of the JavaScript language are implemented as library functions in Smalltalk. Among others, most noteworthy are conditional execution (if-the-else) and loops.

<br>The JavaScript syntax is mapped to corresponding Smalltalk library functions as follows:
Non-Smalltalkers should be aware that in Smalltalk all control structures are implemented as library functions (i.e. method invocations) to corresponding receiver objects. They get a block (=closure or anonymous function) as argument, which is then possibly evaluated. The Smalltalk block syntax "<code>[...]</code>" makes this extremely convenient and easy to use.
{| Border

It is the responsibility of the Smalltalk compiler and runtime system, to care for optimized execution of such code (typically by inlining the code). However, it is still possible to put a block into a variable, to pass it as argument, or to return it as value from a method or block. Thus, in Smalltalk, variables or computed blocks may also be used as arguments to messages like "<code>ifTrue:</code>", "<code>whileTrue:</code>", "<code>do:</code>" etc.

<br>The JavaScript syntax for control structures is mapped to corresponding Smalltalk library functions as follows:
{| class="wikitable"
! JavaScript
! JavaScript
! Smalltalk
! Smalltalk
Zeile 222: Zeile 319:
|
|
|-
|-
|if (expr) { .. }
|style="width: 15em"|if (''expr'') {<br>...<br>}
| (expr) ifTrue:[ ... ]
|style="width: 15em"| (''expr'') ifTrue:[<br>...<br>]
| Notice the square brackets
|-
|if (! ''expr'') {<br>...<br>}
| (''expr'') ifFalse:[<br>...<br>]
|
|
|-
|-
|if (! expr) { .. }
| if (''expr'') {<br>...<br>} else {<br>...<br>}
| (expr) ifFalse:[ ... ]
| (''expr'') ifTrue:[<br>...<br>] ifFalse:[<br>...<br>]
|
|
|-
|-
| if (expr) { .. }<BR>else { ... }
| if (!''expr'') {<br>..<br>} else {<br>...<br>}
| (expr) ifTrue:[ ... ]<BR> ifFalse:[...]
| (''expr'') ifFalse:[<br>...<br>] ifTrue:[<br>...<br>]
|
|
|-
|-
| while (expr) { .. }
| while (''expr'') {<br>..<br>}
| [expr] whileTrue:[ ... ]
| [''expr''] whileTrue:[<br>...<br>]
| Notice the square brackets
| Notice the square brackets around the condition
|-
|-
| for (expr1;expr2;expr3) { .. }
| for (''expr1'';''expr2'';''expr3'') {<br>..<br>}
| expr; [expr2] whileTrue:[ ... expr3 ]
| ''expr1''. [''expr2''] whileTrue:[<br>... .<br>''expr3''<br>]
|
|
|-
|-
| for (var i=start; i <= stop; i += incr) { .. }
| for (var i=''start''; i<=''stop''; i++) {<br>..<br>}
| start to: stop by: incr do:[:i &#124; ... ]
| ''start'' to:''stop'' do:[:i &#124;<br>...<br>]
|
|
|-
|-
| for (var i=''start''; i<=''stop''; i+=''incr'') {<br>..<br>}
| try { .. } finally {...}
| ''start'' to:''stop'' by:''incr'' do:[:i &#124;<br>...<br>]
| [...] ensure:[...]
|
|
|-
|-
| try { .. } catch(e) {...}
| try {<br>..<br>} finally {<br>...<br>}
| [...] on:Error do:[:e &#124; ...]
| [<br>...<br>] ensure:[<br>...<br>]
|
|
|-
|-
| try { .. } catch(e) {...} finally {...}
| try {<br>..<br>} catch(e) {<br>...<br>}
| [ [...] on:Error do:[:e &#124; ...] ] ensure:[ ... ]
| [<br>...<br>] on:Error do:[:e &#124;<br>...<br>]
|
|
|-
|-
| try {<br>..<br>} catch(e) {<br>...<br>} finally {<br>...<br>}
| var++
| [<br>...<br>] on:Error do:[:e &#124;<br>...<br>] ensure:[<br>...<br>]
| var := var + 1
|
|
|-
|-
| var--
| ''var''++
| var := var - 1
| ''var'' := ''var'' + 1
|
|
|-
|-
| ''var''--
| arr[ idx ]
| ''var'' := ''var'' - 1
| arr at: idx
|
| Array and Dictionary indexed read<br>Notice that JavaScript uses 0-based indexing, whereas Smalltalk is 1-based.
|-
| ''arr''[ ''idx'' ]
| ''arr'' at:''idx''<br>''arr''[ ''idx'' ]
| Array and Dictionary indexed fetch<br>Notice that real JavaScript uses 0-based indexing, whereas Smalltalk and expecco JavaScript is 1-based.<br>Smalltalk/X also allows the bracket notation for indexed access.
|-
|-
| arr[ idx ] = expr
| ''arr''[ ''idx'' ] = ''expr''
| arr at: idx put: expr
| ''arr'' at:''idx'' put:''expr''<br>''arr''[ ''idx'' ] := ''expr''
| Array and Dictionary indexed write
| Array and Dictionary indexed store<br>Smalltalk/X also allows the bracket notation for indexed access.
|}
|}


Zeile 282: Zeile 387:
|-
|-
| %
| %
| \
| \\<br>%
| modulu operator
| modulu operator
|-
|-
| &lt;&lt;
| &lt;&lt;
| bitShift:
| leftShift:
| left-shift; negative shift count is right-shift
| left-shift<br>negative shift count is right-shift
|-
|-
| &gt;&gt;
| &gt;&gt;
| rightShift:
| rightShift:
| right-shift; negative shift count is left-shift
| right-shift<br>negative shift count is left-shift
|-
|
| bitShift:
| left or right shift;<br>negative shift count is right-shift<br>positive is left shift<br>(not available in JavaScript)
|}
|}


More details are in the [[Smalltalk Syntax Cheat Sheet]].


=== Array Indexing ===
=== Array Indexing ===


A big potential pitfall is the different indexing base used in Smalltalk vs. JavaScript: in Smalltalk, array indices start at 1 and end at the array's size. In JavaScript, indices start at 0 and end at the array's size minus 1.
A big potential pitfall is the different indexing base used in Smalltalk vs. (standard) JavaScript: in Smalltalk, array indices start at 1 and end at the array's size. In (standard) JavaScript, indices start at 0 and end at the array's size minus 1.


This also affects some character- and substring search functions, which return 0 (zero) in Smalltalk, whereas some of the standard JavaScript functions return -1 if nothing is found.
This also affects some character- and substring search functions, which return 0 (zero) in Smalltalk, whereas some of the standard JavaScript functions return -1 if nothing is found.


The following functions expect or return 0-based indices if used in JavaScript:
The following functions expect or return 0-based indices if used in JavaScript:
* ''collection''.<code>indexOf</code>(''element'')<br>attention: there exists a corresponding Smalltalk method, which returns a 1-based index
* ''collection''.<code>'''indexOf'''</code>(''element'')<br>attention: there exists a corresponding Smalltalk method, which returns a 1-based index
* ''collection''.<code>lastIndexOf</code>(''element'')<br>attention: there exists a corresponding Smalltalk method, which returns a 1-based index
* ''collection''.<code>'''lastIndexOf'''</code>(''element'')<br>attention: there exists a corresponding Smalltalk method, which returns a 1-based index


* ''collection''.<code>indexOf</code>(''element'', ''startIndex'')<br>the corresponding Smalltalk method "<code>indexOf:</code>''element'' <code>startingAt:</code>''startIndex''" uses 1-based indices
* ''collection''.<code>'''indexOf'''</code>(''element'', ''startIndex'')<br>the corresponding Smalltalk method "<code>indexOf:</code>''element'' <code>startingAt:</code>''startIndex''" uses 1-based indices
* ''collection''.<code>lastIndexOf</code>(''element'', ''startIndex'')<br>ditto
* ''collection''.<code>'''lastIndexOf'''</code>(''element'', ''startIndex'')<br>ditto




Zeile 323: Zeile 433:
It is possible to create functions within the elementary block's code. This example shows how to do so:
It is possible to create functions within the elementary block's code. This example shows how to do so:
execute() {
execute() {
<span style="color:#007F00">...</span>
...
function f(arg1, arg2) {
function f(arg1, arg2) {
return(arg1 + arg2);
return(arg1 + arg2);
}
}
<span style="color:#007F00">...</span>
...
}
}
A inner function f is created with two arguments (arg1, arg2), which returns the sum of both. The function is called like this:
An inner function "f" is created with two arguments ("arg1" and "arg2"), which returns the sum of both. The function is called like this:
f(3,5)
f(3,5)
Inner functions can be used for example to filter specific elements from collections, as in:
Inner functions can be used for example to filter specific elements from collections, as in:
Zeile 340: Zeile 450:
execute() {
execute() {
var f;
var f;
<span style="color:#007F00">...</span>
...
f = function (arg1, arg2) {
f = function (arg1, arg2) {
return(arg1 + arg2);
return(arg1 + arg2);
}
}
<span style="color:#007F00">...</span>
...
}
}
(notice the missing function name after the "function" keyword).
(notice the missing function name after the "function" keyword).




Notice, that there is a difference in the behavior of the return statement between JavaScript inner functions
Notice, that there is a difference in the behavior of the return statement between JavaScript inner functions and Smalltalk blocks:
and Smalltalk blocks: a JavaScript "return" statement inside an inner function returns from that inner function only, whereas a return in Smalltalk always returns from the outer-most method.
a JavaScript "return" statement inside an inner function returns from that inner function only, whereas a return in Smalltalk always returns from the outer-most method.

To return from the outermost function in JavaScript, use the "<CODE>return from &lt;''fnName''&gt;</CODE>" statement form.
To return from the outermost function in JavaScript, use the "<CODE>return from &lt;''fnName''&gt;</CODE>" statement form.


In expecco, where the outermost function is always named "''execute''", write:
In expecco, where the outermost function is always named "''execute''", write:
execute
execute() {
...
<span style="color:#007F00">...</span>
function myFunction() {
function myFunction() {
...
<span style="color:#007F00">...</span>
return from execute;
return from execute;
}
}
<span style="color:#007F00">...</span>
...
}
(notice that the execute function is not supposed to return a value)
(notice that the execute function is not supposed to return a value)


Zeile 387: Zeile 499:
In Smalltalk, inner functions are called "''block''", and are defined as:
In Smalltalk, inner functions are called "''block''", and are defined as:
f := [:arg1 :arg2 | arg1 + arg2 ].
f := [:arg1 :arg2 | arg1 + arg2 ].
This block can be called by sending at a "<code>value:value:</code>" message (one "<code>value:</code>" for each argument):
This block can be called by sending at a "<code>value:value:</code>" message (i.e. one "<code>value:</code>" for each argument) <sup>(1)</sup>:
f value:3 value:5
f value:3 value:5
The returned value is the value of the last expression inside the block. Thus, blocks can contain multiple expressions separated by a period:
f := [:arg1 :arg2 |
Transcript showCR: e'my first arg is: {arg1}'.
Transcript showCR: e'my second arg is: {arg2}'.
arg1 + arg2 "/ the block's return value
].
This is in contrast to a JavaScript inner function, where an explicit "return" statement is needed. The return value of JavaScript inner function without an explicit return will be void.
Therefore, the above inner function in JavaScript has to have a return:
f = function(arg1, arg2) {
Transcript.showCR('my first arg is: '+arg1);
Transcript showCR('my second arg is: '+arg2);
return (arg1 + arg2); // the function's return value
};

Blocks without argument are defined as:
Blocks without argument are defined as:
f := [ Transcript showCR:'blabla: Euler was great' ].
f := [ Transcript showCR:'blabla: Euler was great' ].
and invoked with a simple "<code>value</code>" message.
and invoked with a simple "<code>value</code>" message.


In both JavaScript and Smalltalk code, it is an error to invoke an inner function/block with a wrong number of arguments.
In both JavaScript and Smalltalk code, it is an error to invoke an inner function/block with a wrong number of arguments <sup>(2)</sup>.


Smalltalk blocks and JavaScript inner functions can be used interchangeable - i.e. it is possible to pass a JS inner function to a collection method such as "<code>collect:</code>" or "<code>select:</code>". It is also possible, to pass either via input/output pins to other activities (which is not considered good style, as it could make the program quite hard to understand, if not used with caution).
Smalltalk blocks and JavaScript inner functions can be used interchangeable - i.e. it is possible to pass a JS inner function to a collection method such as "<code>collect:</code>" or "<code>select:</code>". It is also possible, to pass either via input/output pins to other activities (which is not considered good style, as it could make the program quite hard to understand, if not used with caution).


Notice again, that the behavior of the Smalltalk return ("^") is different from a JavaScript return ("''return-Statement''") inside inner functions. The JavaScript return returns a value from the inner function, whereas the Smalltalk return forces a return from the containing method (the block's "execute" method). The Smalltalk return always behaves like the "<I>return from execute</I>" JavaScript special form.
Notice again, that the behavior of the Smalltalk return ("^") is different from a JavaScript return ("''return-Statement''") inside inner functions. The JavaScript return returns a value from the inner function, whereas the Smalltalk return forces a return from the containing method (the block's "execute" method). The Smalltalk return always behaves like the "<I>return from execute</I>" JavaScript special form.

<sup>1)</sup> Smalltalk/X allows for a non-standard block evaluation message using the "normal" parentheses notation. I.e. you can call a block with its arguments in parentheses as in "<code>f(1, 2, 3)</code>. This is syntactic sugar for the standard <code>f value:1 value:2 value:3</code>.

<sup>2)</sup> in Smalltalk, you can define "''varargs blocks''" with the "<code>asVarArgBlock</code>" message. The block will then receive its arguments as a vector, and fetch them using collection protocol (i.e. "size", "at:" or enumeration messages like "do:"). For example,
b := [:args | Transcript showCR: e'my arguments are {args}' ] asVarArgBlock
can be called with any number of arguments.


==== Example uses for Inner Functions ====
==== Example uses for Inner Functions ====


In Smalltalk, blocks are very often used when enumerating collections.
In Smalltalk, blocks are very often used when enumerating collections.
For example, code corresponding to a C# 3.0 collection "select (where(x => ...))" is written in Smalltalk as:
<br>For example, code corresponding to a C# 3.0 collection "select (where(x => ...))" is written in Smalltalk as:
|names namesStartingWithA|
|names namesStartingWithA|
Zeile 413: Zeile 545:
names = [ "Alice" , "Ann" , "Bob" , "Mallory" ];
names = [ "Alice" , "Ann" , "Bob" , "Mallory" ];
namesStartingWithA = names.select( function(n) { n.startsWith("A"); } );
namesStartingWithA = names.select( function(n) { n.startsWith("A"); } );
or alternative using lambda expressions as:
var names, namesStartingWithA;
names = [ "Alice" , "Ann" , "Bob" , "Mallory" ];
namesStartingWithA = names.select( n => n.startsWith("A") );


To find the first element in a collection, for which some condition is true, use:
To find the first element in a collection, for which some condition is true, use:
Zeile 425: Zeile 562:
names = [ "Alice" , "Ann" , "Bob" , "Mallory" ];
names = [ "Alice" , "Ann" , "Bob" , "Mallory" ];
firstNameContainingAnO = names.detect( function(n) { n.includesString("o"); } );
firstNameContainingAnO = names.detect( function(n) { n.includesString("o"); } );

For more information on the Smalltalk syntax, see [[http://live.exept.de/doc/online/english/getstart/tut_2.html Smalltalk Basics]] in the [[http://live.exept.de/doc/online/english/getstart/tutorial.html Smalltalk Online Tutorial]].
For more information on the Smalltalk syntax, see [[http://live.exept.de/doc/online/english/getstart/tut_2.html Smalltalk Basics]] in the [[http://live.exept.de/doc/online/english/getstart/tutorial.html Smalltalk Online Tutorial]].


Zeile 438: Zeile 576:
Although very very rarely required in practice,
Although very very rarely required in practice,
values can be converted explicitly, via one of the "<code>asXXX</code>" messages:
values can be converted explicitly, via one of the "<code>asXXX</code>" messages:
* <code>asFloat64()</code> - to convert to a double precision IEEE 64bit float
* asFloat()
* <code>asFloat32()</code> - to convert to a single precision IEEE 32bit float
* asInteger()
* <code>asFloat80()</code> - to convert to an extended precision IEEE 80bit float
* asFraction()
* <code>asInteger()</code>
* asFixedPoint(scale)
* <code>asFraction()</code>
* <code>asScaledDecimal(scale)</code>
the older API is still supported, but less self explanatory:
* <code>asFloat()</code> - to convert to a double precision IEEE 64bit float
* <code>asShortFloat()</code> - to convert to a single precision IEEE 32bit float
* <code>asLongFloat()</code> - to convert to an extended precision IEEE 80bit float


Thus, if you want to avoid fractions (why would you?), process the result of an integer division using one of the <code>asFloat()</code>, <code>truncated()</code>, <code>floor()</code>, <code>ceiling()</code> or <code>rounded()</code> functions. But be aware that fractions provide an exact result, whereas float arithmetic is inherently imprecise, rounds on the the last bit and that such rounding errors accumulate.
Thus, if you want to avoid fractions (why would you?), process the result of an integer division using one of the <code>asFloat()</code>, <code>truncated()</code>, <code>floor()</code>, <code>ceiling()</code> or <code>rounded()</code> functions. But be aware that fractions provide an exact result, whereas float arithmetic is inherently imprecise, rounds on the the last bit and that such rounding errors accumulate.
Zeile 459: Zeile 603:
41463976156518286253697920827223758251185210916864000000000000000000000000
41463976156518286253697920827223758251185210916864000000000000000000000000


Small integer values (<32bit or <64bit, depending in the underlying hardware's native word length) are stored more efficiently than large integers - however, the conversion and representation as used by the system is transparent and automatic.
Small integer values (smaller than 32 or 64bit, depending in the underlying hardware's native word length) are stored more efficiently than large integers - however, the conversion and representation as used by the system is transparent and automatic.
The same is true for mixed mode arithmetic between small and large integers.
The same is true for mixed mode arithmetic between small and large integers.


Occasionally it is required to perform modulo arithmetic in 32 or 64bit, for example to reproduce or check values as generated by C or Java programs. For this, use methods from the "''special modulo''" category, such as "<code>add_32:</code>", "<code>add_32u:</code>", "<code>mul_32:</code>" etc. Use a class browser to find those methods.
Occasionally it is required to perform modulo arithmetic in 32 or 64bit; for example to reproduce or check values as generated by C or Java programs. For this, use methods from the "''special modulo''" category, such as "<code>add_32()</code>", "<code>add_32u()</code>", "<code>mul_32()</code>" etc. Use a [[Tools_ClassBrowser|class browser]] to find those methods.


Integer constants (of arbitrary size) can be given in hexadecimal (Smalltalk: 16rxxx, JavaScript 0xXXXX), octal (Smalltalk: 8rxxx, JavaScript 0XXXX), and binary (Smalltalk: 2rxxx, JavaScript 0bXXXX). In fact, Smalltalk supports any base from 2 to 36 written as &lt;b&gt;rxxx, where &lt;b&gt; is the base. Thus 3r100 is the ternary representation of the integer 9.
Integer constants (of arbitrary size) can be given in hexadecimal (Smalltalk: 16rxxx, JavaScript 0xXXXX), octal (Smalltalk: 8rxxx, JavaScript 0XXXX), and binary (Smalltalk: 2rxxx, JavaScript 0bXXXX). In fact, Smalltalk supports any base from 2 to 36 written as &lt;b&gt;rxxx, where &lt;b&gt; is the base. Thus 3r100 is the ternary representation of the integer 9.
Zeile 473: Zeile 617:


Float represents double precision IEEE floating point numbers (64bit).
Float represents double precision IEEE floating point numbers (64bit).
Although seldom needed, conversion to single precision (32bit) and high precision (80bit/128bit, depending on the underlying hardware) is possible via the ''asShortFloat()'' and ''asLongFloat()'' conversion functions. Notice that the actual precision of longFloat numbers depends on the CPU: x86 and x86_64 provide 80bits, Sparc and a few other CPUs provide 128 bits.
Although seldom needed, conversion to single precision (32bit) and high precision (80bit/128bit, depending on the underlying hardware) is possible via the ''asShortFloat()'' and ''asLongFloat()'' conversion functions. Notice that the actual precision of longFloat numbers depends on the CPU: x86 and x86_64 provide 80bits, Sparc and a few other CPUs provide 128 bits.<br>For more precision, two additional classes called "<code>QDouble</code>" and "<code>LargeFloat</code>" are provided: QDouble provides roughly 200 bits (70 digits) of precision, and LargeFloat offers an arbitrary (configurable) precision. Both are described in the [[Numeric Limits/en|Numeric Limits]] and [[Number_API_Functions|Number API Functions]] documents.


<br>[[Number_API_Functions|Number API Functions]]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/numeric.html#FLOAT More Info]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/numeric.html#FLOAT More Info] in the Smalltalk/X online documentation
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Float Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Float Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,ShortFloat ShortFloat],
[http://live.exept.de/ClassDoc/classDocOf:,LongFloat LongFloat],
[http://live.exept.de/ClassDoc/classDocOf:,QDouble QDouble]
<!-- [http://live.exept.de/ClassDoc/classDocOf:,LargeFloat LargeFloat ] -->
Class Documentation


====== Float Rounding Errors ======
====== Float Rounding Errors ======
Be reminded that floating point arithmetic is inherently inaccurate: there will almost always be rounding errors on the last bit. The reason is inherent in that most rational numbers simply cannot be represented as the sum of powers of two. For example, the 0.3 is one of them: no sum of powers of two (1/2, 1/4, 1/8, ...) is able to represent this number exactly.
Be reminded that floating point arithmetic is inherently inaccurate: there will almost always be rounding errors on the last bit. The reason is inherent in that most rational numbers simply cannot be represented as the sum of powers of two. For example, 0.1 and 0.3 are two of them: no sum of powers of two (1/2, 1/4, 1/8, ...) is able to represent these numbers exactly. Thus, there will definitely be an error in the last bit of the stored float value.


Typically, print functions (which convert floating point values to a string representation) will "cheat" and round on the last bit. However, if you operate with such numbers (i.e. add, multiply, etc.) these rounding errors will accumulate and may finally add up to more than one bit of error. When this happens, you will get the typical ".999999" result strings.
Typically, print functions (which convert floating point values to a string representation) will "cheat" and round on the last bit. However, if you operate with such numbers (i.e. add, multiply, etc.) these rounding errors will accumulate and may finally add up to more than one bit of error. When this happens, the print function will no longer round up, and you will get the typical ".999999" result strings.


As an example, try adding 0.3 a hundred times, as in:
As an example, try adding 0.3 a thousand times, as in:


|t|
|t|
Zeile 489: Zeile 639:
1000 timesRepeat:[t := t + 0.3].
1000 timesRepeat:[t := t + 0.3].
t print
t print
may give "300.300000000006" as a result.


As a consequence, NEVER ever compare floating point values for equality. In the above case, the comparison with "300.3" would return false!
which may give "300.300000000006" as a result.


Floating point numbers should always be compared by giving a range (i.e. between x-delta and x+delta) or by giving a number of valid digits or bits, by which to compare.
As a consequence, NEVER compare floating point values for equality. In the above case, the comparison with "300.3" would return false!
expecco provides a number of action blocks for this kind of "''almost-equal''" comparisons.


Of course, there are also similar functions in the Float class, in case you need this in elementary Smalltalk or JavaScript code.
And also: NEVER compute monetary values using floating point - or your legal advisor will blame you for missing Cents in the invoice.


And also: NEVER ever compute monetary values using floating point - or your legal advisor will blame you for missing Cents in the invoice. Monetary values should never be processed with floats (expecco provides a ScaledDecimal number type for that, as described below).
Floating point numbers should always be compared by giving a range (i.e. between x-delta and x+delta) or by giving a number of valid digits, by which to compare.

expecco provides a number of action blocks for this kind of "almost-equal" comparisons.
Problems with numbers are discussed in detail in "[[Numeric Limits/en|Numeric Limits]]".
Of course, there are also functions in the Float class, in case you need this in elementary Smalltalk or JavaScript code.


===== Fraction =====
===== Fraction =====
Zeile 514: Zeile 666:
<p>
<p>
Be aware that fractional arithmetic is done by software, whereas floating point arithmetic is done by CPU instructions.
Be aware that fractional arithmetic is done by software, whereas floating point arithmetic is done by CPU instructions.
Therefore, floating point arithmetic is usually much faster.
Therefore, floating point arithmetic is usually faster.
<p>
<p>
Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/numeric.html#FRACTION More Info]
Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/numeric.html#FRACTION More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Fraction Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Fraction Class Documentation]


===== FixedPoint=====
===== ScaledDecimal =====

ScaledDecimal (also called "''FixedPoint''") numbers are decimals, with a configurable number of post-decimal digits. They are typically used when dealing with money. Like fractions, they avoid rounding errors. When printed, they round in their last digit, however, the exact information is always kept for further processing. ScaledDecimal numbers are perfect to represent money and other fractional data, which must be represented with a fixed number of post-decimal digits. Internally, fixed point numbers are represented as fractions with a power-of-10 denominator.
<br>
ScaledDecimal numbers are also great to print tabular data; for example: <code>someNumber as ScaledDecimal:3</code> will print the number with and rounded to 3 post-decimal digits.


FixedPoint numbers are decimals, with a configurable number of post-decimal digits. They are typically used when dealing with money. Like fractions, they avoid rounding errors. However, when printed, they round in their last digit (however, the exact information is always kept for further processing). FixedPoint numbers are perfect to represent money and other fractional data, which must be represented with a fixed number of post-decimal digits. Internally, fixed point numbers are represented as fractions with a power-of-10 denominator.
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/numeric.html#FIXEDPOINT More Info]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/numeric.html#FIXEDPOINT More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,FixedPoint Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,FixedPoint Class Documentation]

===== Complex=====

Complex numbers with real and imaginary part are sometimes used in technical/physical applications.
They can be read as "<code>a * b i</code>", for example: "<code>5 + 3i</code>" represents the complex number with 5 as real and 3 as imaginary part.

Complex numbers can result from math functions such as sqrt and log (by default, these raise an error, which can be ignored for a complex result)
<!-- <br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/numeric.html#COMPLEX More Info] -->
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Complex Class Documentation]


===== Number =====
===== Number =====


The Number type is an abstract union type which includes all of the above types. Thus, a variable or pin declared as being of "<code>Number</code>" type can take any of the above values. Due to the polymorphic implementation of the underlying numeric class library, most operations can be passed any of the above values (even mixing arguments is possible). For example, the "Add" elementary block, which is implemented by simply calling the Smalltalk "+" operation, is able to deal with any combination of argument types.
The Number type is an abstract union type which includes all of the above types. Thus, a variable or pin declared as being of "<code>Number</code>" type can take any of the above values. Due to the polymorphic implementation of the underlying numeric class library, most operations can be passed any of the above values (even mixing arguments is possible). For example, the "[Add]" elementary block, which is implemented by simply calling the Smalltalk "+" operation, is able to deal with any combination of argument types.
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/numeric.html#NUMBER More Info]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/numeric.html#NUMBER More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Number Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Number Class Documentation]
Zeile 537: Zeile 701:
[[Datei:bulb.png|16px]] Notice that ASCII (0..127) and ISO 8859-1 (0..255) are proper subsets of the Unicode set.
[[Datei:bulb.png|16px]] Notice that ASCII (0..127) and ISO 8859-1 (0..255) are proper subsets of the Unicode set.
Do not confuse Unicode with UTF-8: Unicode is the numeric value associated to a character and used everywhere within expecco.
Do not confuse Unicode with UTF-8: Unicode is the numeric value associated to a character and used everywhere within expecco.
UTF-8 is an encoding format for data interchange of Unicode characters. UTF-8 is NOT used inside expecco, only for data exchange with the external world.
UTF-8 is an encoding format for data interchange of Unicode characters. UTF-8 is NOT used inside expecco, only for data exchange with the external world (see "[[String_API_Functions#Encoding_.2F_Decoding|Encoding and Decoding]]" in the String API doc).


In most situations, you do not need to care for the actual number of bits required to store the characters, and expecco/Smalltalk takes care of the actual representation in memory. However, when dealing with the external world (files, sockets, devices), it may be required to perform explicit conversions to/from wider formats or different encodings (UTF-8, UTF-16, etc.)
In most situations, you do not need to care for the actual number of bits required to store the characters, and expecco/Smalltalk takes care of the actual representation in memory. However, when dealing with the external world (files, sockets, devices), it may be required to perform explicit conversions to/from wider formats or different encodings (UTF-8, UTF-16, etc.)
Zeile 544: Zeile 708:


This is used to represent character strings, where each individual character has a one-byte encoding. I.e. the values are 0..255.
This is used to represent character strings, where each individual character has a one-byte encoding. I.e. the values are 0..255.
Individual characters can be accessed by an integral index starting with 1 in both Smalltalk and JavaScript.
Individual characters of a string can be accessed by an integral index starting with 1 in both Smalltalk and JavaScript. The String class provides many useful functions for substring searching, matching, concatenation/replacing etc.
Also, because String inherits all functions from the Collection superclass hierarchy, all enumeration, mapping, filtering functions are also applicable to strings.
<br>See also: [[String API Functions | Most Useful String Functions]] and [[Collection API Functions | Most Useful Collection Functions]]
<br>See also: [[String API Functions | ''Most Useful String Functions'']] and [[Collection API Functions | ''Most Useful Collection Functions'']]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/collections.html#STRING More Info]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/collections.html#STRING More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,String Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,String Class Documentation]
Zeile 558: Zeile 723:


===== Character =====
===== Character =====
Individual characters (as extracted from a string or possibly read from a file) are represented as instances of the Character class. Queries are available to ask the character for its type (isLetter, isDigit etc.) or its Unicode encoding (code point). There is virtually no limit and every character from the full Unicode set (e.g. even above 0xFFFF) can be represented by instances of the Character class.
Individual characters (as extracted from a string or possibly read from a file) are represented as instances of the Character class. Queries are available to ask the character for its type (isLetter, isDigit etc.) or its Unicode encoding (codePoint). There is virtually no limit and every character from the full Unicode set (e.g. even above 0xFFFF) can be represented by instances of the Character class.
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/misc.html#CHARACTER More Info]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/misc.html#CHARACTER More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Character Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Character Class Documentation]
Zeile 571: Zeile 736:


The following paragraphs list the most used collection classes.
The following paragraphs list the most used collection classes.
Notice, that Smalltalk provides many more useful container classes, so please take a look at the online documentation or take a life tour with the browser.
Notice, that Smalltalk provides many more container classes, so please take a look at the online documentation or take a life tour with the browser.


===== Array =====
===== Array =====


Ordered, fixed size, indexed by an integral index starting with 1 in both Smalltalk and JavaScript.
Ordered, fixed size, indexed by an integral index starting with 1 in both Smalltalk and JavaScript.
Can store any type of object, and storage requirements are 1 machine word per element. As the size is fixed (when created), adding and removing of
Can store any type of object, and storage requirements are 1 machine word per element (i.e. arrays store pointers to the elements). As the size is fixed (when created), adding and removing of
elements is not possible. Use one of the other collections (OrderedCollection, Set or Dictionary) if the number
elements is not possible. Use one of the other collections (OrderedCollection, Set or Dictionary) if the number
of elements needs to change later or is not known in advance.
of elements needs to change later or is not known in advance.
Zeile 586: Zeile 751:
Ordered, fixed size, indexed by an integral index starting with 1.
Ordered, fixed size, indexed by an integral index starting with 1.
<br>ByteArrays can only store very small byte-valued unsigned integers (0..255), and allocate 1 byte per element. Access time and complexity are like with ordinary arrays, but the required memory is much less. ByteArrays are used to represent the contents of binary data, binary files, bitmap images, protocol elements (data packets) or low level memory dumps.
<br>ByteArrays can only store very small byte-valued unsigned integers (0..255), and allocate 1 byte per element. Access time and complexity are like with ordinary arrays, but the required memory is much less. ByteArrays are used to represent the contents of binary data, binary files, bitmap images, protocol elements (data packets) or low level memory dumps.
<br>UInt8Array is an alias for ByteArray.
<br> Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/collections.html#BYTEARRAY More Info]
<br> Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/collections.html#BYTEARRAY More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,ByteArray Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,ByteArray Class Documentation]
Zeile 592: Zeile 758:
Ordered, fixed size, indexed by an integral index starting with 1.
Ordered, fixed size, indexed by an integral index starting with 1.
<br>These store very small 8bit signed integer values (-128..127), requiring 1 byte of storage per element.
<br>These store very small 8bit signed integer values (-128..127), requiring 1 byte of storage per element.
<br>Int8Array is an alias for SignedByteArray.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,SignedByteArray Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,SignedByteArray Class Documentation]


Zeile 608: Zeile 775:
<br>FloatArrays store short IEEE floats (32bit) and allocate 4 bytes per element.
<br>FloatArrays store short IEEE floats (32bit) and allocate 4 bytes per element.
Access times and complexity are like those of regular arrays, but they require much less memory. FloatArrays are often used for data interchange with C language functions or in data acquisition scenarios (series of measurement values).
Access times and complexity are like those of regular arrays, but they require much less memory. FloatArrays are often used for data interchange with C language functions or in data acquisition scenarios (series of measurement values).
<br>Float32Array is an alias for FloatArray, and preferable, as it makes the precision explicit, und thus the code more readable.
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/collections.html#FLOATARRAY More Info]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/collections.html#FLOATARRAY More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,FloatArray Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,FloatArray Class Documentation]
Zeile 615: Zeile 783:
<br>DoubleArrays store IEEE doubles (64bit) and allocate 8 bytes per element.
<br>DoubleArrays store IEEE doubles (64bit) and allocate 8 bytes per element.
These are used in similar situations as FloatArrays.
These are used in similar situations as FloatArrays.
<br>Float64Array is an alias for DoubleArray, and preferable, as it makes the precision explicit, und thus the code more readable.
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/collections.html#FLOATARRAY More Info]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/collections.html#FLOATARRAY More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,DoubleArray Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,DoubleArray Class Documentation]
Zeile 621: Zeile 790:
Ordered, fixed size, indexed by an integral index starting with 1.
Ordered, fixed size, indexed by an integral index starting with 1.
<br>HalfFloatArray can only store IEEE half floats (16bit) and allocate 2 bytes per element.
<br>HalfFloatArray can only store IEEE half floats (16bit) and allocate 2 bytes per element.
<br>Notice that half floats have a very low precision and are seldom used. Exceptions are game programs and 3D graphics accelerators which preserve memory by using this very compact format for depth information, texture attributes and some audio formats. It is also used with some AI programs in neural networks.
<br>[[Datei:bulb.png|16px]] Notice that half floats have a very low precision and are seldom used.
<br>Exceptions are game programs and 3D graphics accelerators which preserve memory by using this very compact format for depth information, texture attributes and some audio formats.
<br>They are also used with some AI programs in neural networks.
<br>Float16Array is an alias for HalfFloatArray , and preferable, as it makes the precision explicit, und thus the code more readable.


===== IntegerArray =====
===== IntegerArray =====
Ordered, fixed size, indexed by an integral index starting with 1.
Ordered, fixed size, indexed by an integral index starting with 1.
<br>These store small 32bit unsigned integer values (0..0xFFFFFFFF) and require 4 bytes of storage per element.
<br>These store small 32bit unsigned integer values (0..0xFFFFFFFF) and require 4 bytes of storage per element.
<br>UInt32Array is an alias for IntegerArray, and preferable, as it makes the precision explicit, und thus the code more readable.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,IntegerArray Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,IntegerArray Class Documentation]


Zeile 631: Zeile 804:
Ordered, fixed size, indexed by an integral index starting with 1.
Ordered, fixed size, indexed by an integral index starting with 1.
<br>They store small 32bit signed integer values (-0x800000000..0x7FFFFFFF), requiring 4 bytes of storage per element.
<br>They store small 32bit signed integer values (-0x800000000..0x7FFFFFFF), requiring 4 bytes of storage per element.
<br>Int32Array is an alias for SignedIntegerArray, and preferable, as it makes the precision explicit, und thus the code more readable.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,SignedIntegerArray Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,SignedIntegerArray Class Documentation]

===== WordArray =====
Ordered, fixed size, indexed by an integral index starting with 1.
<br>They store small 16bit unsigned integer values (0..0xFFFF), allocating 2 bytes per element.
<br>UInt16Array is an alias for WordArray, and preferable, as it makes the precision explicit, und thus the code more readable.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,WordArray Class Documentation]

===== SignedWordArray =====
Ordered, fixed size, indexed by an integral index starting with 1.
<br>They store small 16bit signed integer values (-0x8000..0x7FFF), allocating 2 bytes per element.
<br>Int16Array is an alias for SignedWordArray, and preferable, as it makes the precision explicit, und thus the code more readable.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,SignedWordArray Class Documentation]


===== LongIntegerArray =====
===== LongIntegerArray =====
Ordered, fixed size, indexed by an integral index starting with 1.
Ordered, fixed size, indexed by an integral index starting with 1.
<br>They store small 64bit unsigned integer values (0..0xFFFFFFFFFFFFFF), allocating 8 bytes per element.
<br>They store small 64bit unsigned integer values (0..0xFFFFFFFFFFFFFF), allocating 8 bytes per element.
<br>UInt64Array is an alias.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,LongIntegerArray Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,LongIntegerArray Class Documentation]


Zeile 641: Zeile 828:
Ordered, fixed size, indexed by an integral index starting with 1.
Ordered, fixed size, indexed by an integral index starting with 1.
<br>These store small 64bit signed integer values (-0x80000000000000000..0x7FFFFFFFFFFFFFFF), allocating 8 bytes per element.
<br>These store small 64bit signed integer values (-0x80000000000000000..0x7FFFFFFFFFFFFFFF), allocating 8 bytes per element.
<br>Int64Array is an alias.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,SignedLongIntegerArray Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,SignedLongIntegerArray Class Documentation]

===== ComplexFloatArray and ComplexDoubleArray =====
Ordered, fixed size, indexed by an integral index starting with 1.
<br>These store complex numbers as pairs of IEEE floats (32bit) or doubles (64bit) and allocate 8/16 bytes per element.
These are used in similar situations as FloatArrays and DoubleArrays.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,ComplexFloatArray] [http://live.exept.de/ClassDoc/classDocOf:,ComplexDoubleArray]


===== OrderedCollection =====
===== OrderedCollection =====
Zeile 647: Zeile 841:
<br>OrderedCollections can store any type of object and are specifically tuned for adding/removing elements at either end.
<br>OrderedCollections can store any type of object and are specifically tuned for adding/removing elements at either end.
<br>Access time to elements by index has a time complexity of O(1).
<br>Access time to elements by index has a time complexity of O(1).
<br>Insertion and remove at either end approaches O(1), but is O(n) for inner elements, because the elements are moved inside a linear container.
<br>Insertion and remove at either end approach O(1), but is O(n) for inner elements, because the elements are moved inside a linear container.
When searching for an element in an OrderedCollection (using <code>includes:</code>, <code>indexOf:</code> or similar methods), time complexity is O(n) (because a linear search is done).
When searching for an element in an OrderedCollection (using <code>includes:</code>, <code>indexOf:</code> or similar methods), time complexity is O(n) (because a linear search is done).


Java users will recognize the similarity to "''ArrayLists''".
Java users will recognize the similarity to "''ArrayLists''".


Note: For very big or lightly sparse collections, you can use a [http://live.exept.de/ClassDoc/classDocOf:,SegmentedOrderedCollection SegmentedOrderedCollection]. This uses a hierarchy of buckets and does not need storage for big unused regions inside the collection. However, due to the additional computations, the break even in performance compared to regular OrderedCollections is usually at around 100K to 1M elements.
Note: For very big or lightly sparse collections, you can use a [http://live.exept.de/ClassDoc/classDocOf:,SegmentedOrderedCollection SegmentedOrderedCollection]. This uses a hierarchy of buckets and does not need storage for big unused regions inside the collection. However, due to the additional computations, the breakeven in performance compared to regular OrderedCollections is usually at around 100K to 1M elements.


Note: For very very big collections (millions of elements) which are very lightly filled, use a [http://live.exept.de/ClassDoc/classDocOf:,SparseArray SparseArray]. This only keeps the filled slots, and is great, if you have only a few valid elements inside a virtually huge index space.
Note: For very very big collections (millions of elements) which are very lightly filled, use a [http://live.exept.de/ClassDoc/classDocOf:,SparseArray SparseArray]. This only keeps the filled slots, and is great, if you have only a few valid elements inside a virtually huge index space.
Zeile 725: Zeile 919:


There is also a non-balancing BinaryTree, which is slightly faster (by not balancing), but its worst case access times may degenerate to O(N), if elements are added non-randomly. <br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,BinaryTree Class Documentation]
There is also a non-balancing BinaryTree, which is slightly faster (by not balancing), but its worst case access times may degenerate to O(N), if elements are added non-randomly. <br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,BinaryTree Class Documentation]

===== Interval and GeometricSeries =====

This represent ranges of numbers as in "<CODE>(1 to:5)</CODE>" or "<CODE>(1 to:10 by:2)</CODE>" or "<CODE>(7 to:4 by:-1)</CODE>", which represent collections of numbers [1,2,3,4,5], [1,3,5,7,9] and [7,6,5,4] respectively.

GeometricSeries are similar, but have a constant factor between elements, instead of a constant difference. For example, the "<CODE>(GeometricSeries from:1 to:64 byFactor:2)</CODE>" represents the collection of [1,2,4,8,16,32,64]".

Intervals and GeometricSeries can be used wherever a readonly collection can be used.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Interval Interval Class Documentation] and [http://live.exept.de/ClassDoc/classDocOf:,GeometricSeries GeometricSeries Class Documentation]


==== Stream Types ====
==== Stream Types ====
Zeile 730: Zeile 933:
Streams are accessors for either internal collections or external files, sockets and other character oriented I/O devices.
Streams are accessors for either internal collections or external files, sockets and other character oriented I/O devices.
They are created by either "''opening''" a file or device or by creation "''on''" a numerical indexable collection object (String, Array, OrderedCollection etc.).
They are created by either "''opening''" a file or device or by creation "''on''" a numerical indexable collection object (String, Array, OrderedCollection etc.).
Once created, read streams deliver the next element via a "''next''()" operation, write streams append elements via the "''nextPut(e)''" operation.
Once created, readstreams deliver the next element via a "''next''()" operation, writestreams append elements via the "''nextPut(e)''" operation.
In addition to the above elementary functions, bulk read/write and linewise read/write and various other functionality is provided.
In addition to the above elementary functions, bulk read/write and linewise read/write and various other functionality is provided.


Zeile 753: Zeile 956:
Provides low level bytewise or linewise access to a communication socket. Typically, but not limited to TCP/IP connections.
Provides low level bytewise or linewise access to a communication socket. Typically, but not limited to TCP/IP connections.
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/streams.html#FILESTREAM More Info]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/streams.html#FILESTREAM More Info]

<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Socket Class Documentation]
Reference: [http://live.exept.de/ClassDoc/classDocOf:,Socket Class Documentation]


There are a number of useful companion classes which deal with host addresses:
There are a number of useful companion classes which deal with host addresses:
Zeile 762: Zeile 966:
===== PipeStream =====
===== PipeStream =====
These are used to read/write from/to another program's standard input or standard output. For details, consult a Unix programmer's manual.
These are used to read/write from/to another program's standard input or standard output. For details, consult a Unix programmer's manual.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,PipeStream Class Documentation]


==== Other useful Types ====
==== Other useful Types ====


===== Date =====
===== Date =====
Represents a calendar date. Instances can be created with "Date today", by giving day/month/year or a calendar day. Instances offer a large number of queries, such as weekday, dayInWeek, leapYear etc.
Represents a calendar date. Instances can be created with eg. "<code>Date today</code>", by giving day/month/year or a calendar day. Instances offer a large number of queries, such as weekday, dayInWeek, leapYear etc.
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/misc.html#DATE More Info]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/misc.html#DATE More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Date Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Date Class Documentation]


===== Time =====
===== Time =====
Represents a time-of-day in second resolution.
Represents a time-of-day in second resolution. Instances are created with "<code>Time now</CODE>" or by giving hour/minute/seconds.
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/misc.html#TIME More Info]
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/misc.html#TIME More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Time Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Time Class Documentation]


===== Timestamp (Date and Time) =====
===== Timestamp (Date and Time) =====
Represents a timestamps. With the 18.1 release, the resolution has been improved to sub-nanosecond resolution, although internal timestamps as generated by the operating system will usually not generate such fine grained values (some systems will only generate, 1 millisecond resolution stamps).
Represents a timestamp. With the 18.1 release, the resolution has been improved to sub-nanosecond resolution, although internal timestamps as generated by the operating system will usually not generate such fine grained values (some systems will only generate, 1 millisecond resolution stamps).

<br>For backward compatibility, the default resolution is 1 millisecond for internally generated stamps, and whatever resolution is provided when stamps are read from an external source. When printed, the default print format includes 3 millisecond digits, but a separate print generator can produce higher resolution strings (in 18.1).
Update: now the resolution supported by Timestamps is 1 picosecond, and microsecond times are also supported on Windows systems.

For backward compatibility, the default resolution is 1 millisecond for internally generated stamps, and whatever resolution is provided when stamps are read from an external source. When printed, the default print format includes 3 millisecond digits, but a separate print generator can produce higher resolution strings (in 18.1).


The Timestamp class has been enhanced to support dates before 1.1.1970 and after 2036, which are typical restrictions of systems which represent times as "''seconds since the epoch''" in a 32bit integer. Expecco timestamps handle values before and after those dates, although no calendar adjustments are done for timestamps before the julian/gregorian switch (i.e. this affects weekdays, leap years, etc. before 1582, which are very unlikely to be encountered in technical environments ;-) ).
The Timestamp class has been enhanced to support dates before 1.1.1970 and after 2036, which are typical restrictions of systems which represent times as "''seconds since the epoch''" in a 32bit integer. Expecco timestamps handle values before and after those dates, although no calendar adjustments are done for timestamps before the julian/gregorian switch (i.e. this affects weekdays, leap years, etc. before 1582, which are very unlikely to be encountered in technical environments ;-) ).
Zeile 784: Zeile 992:


===== TimeDuration =====
===== TimeDuration =====
Represents an amount of time, to represent time intervals in millisecond resolution (18.1: sub-millisecond resolution). When reading, a unit-specifier character is allowed to specify milliseconds (ms), seconds (s), minutes (m), hours (h) or days (d).
Represents an amount of time, to represent time intervals in picosecond resolution (before rel18.1: millisecond resolution).


For example, "1h 20s" specifies 1 hour and 20 seconds.
When reading from a string or stream, a unit-specifier character is allowed to specify milliseconds (ms), seconds (s), minutes (m), hours (h) or days (d). For example, "<code>1h 20s</code>" specifies 1 hour and 20 seconds.


As a programmer, you'd write (in Smalltalk): "<code>1 hours + 20 seconds</code>"
<br>Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/misc.html#TIMEDURATION More Info]

Overview: [http://live.exept.de/doc/online/english/overview/basicClasses/misc.html#TIMEDURATION More Info]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,TimeDuration Class Documentation]
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,TimeDuration Class Documentation]


===== Delay =====
===== Delay =====
Utility providing delay functions. Most useful are (in Smalltalk):
Delay is a utility providing timed delay functions. Most useful are (written in Smalltalk):
Delay waitForSeconds: n
Delay waitForSeconds: <i>n</i>. (arg is a number)
and
and:
Delay waitFor: <i>n</i> seconds. (arg is a time duration)
Delay waitUntil: timestamp
and:
Delay waitUntil: <i>timestamp</i>. (arg is a timestamp)

or (written in JavaScript):
"<code>Delay.waitForSeconds(n)</code>";
"<code>Delay.waitFor(n.seconds())</code>";
and
"<code>Delay.waitUntil(timestamp)</code>";


Reference: [http://live.exept.de/ClassDoc/classDocOf:,Delay Class Documentation]
or (in JavaScript) <code>"Delay.waitForSeconds(n)"</code> and <code>"Delay.waitUntil(timestamp)"</code>.
<br>Reference: [http://live.exept.de/ClassDoc/classDocOf:,Delay Class Documentation]


===== Filename =====
===== Filename =====
Zeile 811: Zeile 1.028:
for example, to read an ISO 8859-7 encoded string,
for example, to read an ISO 8859-7 encoded string,
use:
use:
decodedString := (CharacterEncoder encoderFor:#'ISO8859-7') decodeFrom:encodedString.
decodedString :=
(CharacterEncoder encoderFor:#'ISO8859-7')
decodeFrom:encodedString.
and vice versa:
and vice versa:
encodedString := (CharacterEncoder encoderFor:#'ISO8859-7') encodeFrom:originalString.
encodedString :=
(CharacterEncoder encoderFor:#'ISO8859-7')
encodeFrom:originalString.


By the time of writing, the following encodings are supported (many are aliases of others):
By the time of writing, the following encodings are supported (many are aliases of others):
Zeile 842: Zeile 1.063:
==== CTypes ====
==== CTypes ====


These types describe C-data structures. They are needed when data is exchanged with DLL functions or is to be read/written in binary from/to a file.
These types describe C-data structures. They are needed when data is exchanged with DLL functions or is to be read/written in binary from/to a file or a communication channel.


CTypes are usually parsed from a string using the C Parser, which is invoked by expecco when a type definition starts with a comment like "/* C: */".
CTypes are usually parsed from a string using the C Parser, which is invoked by expecco when a type definition starts with a comment like "/* C: */".
But of course, the C-Parser can also be invoked explicitly via a workspace or programmatically via an elementary block.
But of course, the C-Parser can also be invoked explicitly in a workspace or programmatically via an elementary block.
The following example would create a C-type from a structure definition string (in JavaScript code):
The following example would create a C-type from a structure definition string (in JavaScript code):
t = CParser.parseDeclaration("
t = CParser.parseDeclaration("
Zeile 862: Zeile 1.083:
");
");
the type in variable "<code>t</code>" can then be instantiated by sending it a "<code>new</code>" message:
the type in variable "<code>t</code>" can then be instantiated by sending it a "<code>new</code>" message:
datum = t.new();
cDatum = t.new();
or with:
or with:
datum = t.malloc();
cDatum = t.malloc();
The difference is that "<code>new</code>" allocates the bytes in the regular garbage collected expecco memory (which may move objects),
The difference is that "<code>new</code>" allocates the bytes in the regular garbage collected expecco memory (which may move objects),
whereas "<code>malloc</code>" allocates in pinned (not moving) memory on the C heap. Thus references to such malloc'd memory can be passed to DLL code which wants to keep a reference to it, whereas C-data allocated with <code>new</code> can only be used temporarily by C code and will vanish sooner or later after the call returns.
whereas "<code>malloc</code>" allocates in pinned (not moving) memory on the C heap. Thus references to such malloc'd memory can be passed to DLL code which wants to keep a reference to it, whereas C-data allocated with <code>new</code> can only be used temporarily by C code and will vanish sooner or later after the call returns.


You can inspect the created instance via the inspector (evaluate: "<code>datum.inspect()</code>").
You can inspect the created instance via the inspector (evaluate: "<code>cDatum.inspect()</code>").


Individual fields of the c-datum are accessed via the following functions:
Individual fields of the c-datum are accessed via the following functions:
* <code>datum.memberAt(fieldName)</code><br>retrieves a field. If the field is a composite datum (array or structure), a new datum is extracted (i.e. a structure copy is made).
* <code>cDatum.memberAt(fieldName)</code><br>retrieves a field. If the field is a composite datum (array or structure), a new datum is extracted (i.e. a structure copy is made).


* <code>datum.at(index0Based)</code><br>retrieves an indexed array element as a copy.
* <code>cDatum.at(index0Based)</code><br>retrieves an indexed array element as a copy.


* <code>datum.memberAtPut(fieldName, value)</code><br>stores a field. If the field is a composite datum (array or structure), the whole value is copied.
* <code>cDatum.memberAtPut(fieldName, value)</code><br>stores a field. If the field is a composite datum (array or structure), the whole value is copied.


* <code>datum.atPut(index0Based)</code><br>stores an indexed array element.
* <code>cDatum.atPut(index0Based)</code><br>stores an indexed array element.


* <code>datum.refMemberAt(fieldName)</code><br>creates a pointer to a field. This allows for subfields to be modified.
* <code>CDatum.refMemberAt(fieldName)</code><br>creates a pointer to a field. This allows for subfields to be modified.


* <code>datum.refAt(index0Based)</code><br>creates a pointer to an array element.
* <code>cDatum.refAt(index0Based)</code><br>creates a pointer to an array element.


notice that in JavaScript, indexed access can be written more convenient as "<code>datum[index0Based]</code>" and that member field access can be written as "<code>datum.fieldName</code>".
notice that in JavaScript, indexed access can be written more convenient as "<code>cDatum[index0Based]</code>" and that member field access can be written as "<code>cDatum.fieldName</code>".


For the full protocol of CDatum, please refer to the class code in the
For the full protocol of CDatum, please refer to the class code in the
Zeile 891: Zeile 1.112:
=== Additional Standard Objects for JavaScript ===
=== Additional Standard Objects for JavaScript ===


In order for a "well known" environment to be provided to those who know JavaScript, but do not know Smalltalk, mimicri classes (Math) and mimicri protocol has been added to the underlying Smalltalk system. These functions simply call corresponding Smalltalk functions and only exist to provide a JavaScript compatible API which is described below. Notice that this represents only a small fraction (less than 1/100th) of the real functionality of the base system. In order to find out more about all existing classes, you should open a class browser and navigate through the system yourself.
In order for a "''well known''" environment to be provided to those who know JavaScript, but do not know Smalltalk, mimicri classes (Math) and mimicri protocol has been added to the underlying Smalltalk system. These functions simply call corresponding Smalltalk functions and only exist to provide a JavaScript compatible API which is described below.

There is also a more detailed introduction to the basic classes found in the
Notice that this represents only a small fraction (less than 1/100th) of the real functionality of the base system. In order to find out more about all existing classes, you should open a class browser and navigate through the system yourself (eg. Number and subclasses).<br>There is also a more detailed introduction to the basic classes found in the
[http://live.exept.de/doc/online/english/overview/basicClasses/TOP.html Smalltalk/X Base Classes Documentation].
[http://live.exept.de/doc/online/english/overview/basicClasses/TOP.html Smalltalk/X Base Classes Documentation].


Zeile 899: Zeile 1.121:
===== Constants =====
===== Constants =====


*'''E'''<br>Euler's constant, the base of the natural logarithm (approximately 2.718).<br>Same as "<code>Float e</code>" in Smalltalk.
*'''E'''<br>Euler's constant, the base of the natural logarithm (approximately 2.718).<br>Same as "<code>Float e</code>" in Smalltalk (or "<code>Math E</code>").


*'''LN10'''<br>Natural logarithm of 10 (approximately 2.302). In Smalltalk, you can use "<code>Math LN10</code>".
*'''LN10'''<br>Natural logarithm of 10 (approximately 2.302).<br>In Smalltalk, you can use "<code>Float ln10</code>" (or "<code>Math LN10</code>").


*'''LN2'''<br>Natural logarithm of 2 (approximately 0.693).<br>Same as "Float ln2" in Smalltalk.
*'''LN2'''<br>Natural logarithm of 2 (approximately 0.693).<br>Same as "<code>Float ln2</code>" in Smalltalk.


*'''LOG10E'''<br>Base 10 logarithm of E (approximately 0.434)
*'''LOG10E'''<br>Base 10 logarithm of E (approximately 0.434)
Zeile 909: Zeile 1.131:
*'''LOG2E'''<br>Base 2 logarithm of E (approximately 1.442)
*'''LOG2E'''<br>Base 2 logarithm of E (approximately 1.442)


*'''PI'''<br>Pi (approximately 3.14159).<br>Same as "Float pi" in Smalltalk.
*'''PI'''<br>Pi (approximately 3.14159).<br>Same as "<code>Float pi</code>" in Smalltalk.


*'''SQRT1_2'''<br>Square root of 1/2 (approximately 0.707)
*'''SQRT1_2'''<br>Square root of 1/2 (approximately 0.707)


*'''SQRT2'''<br>Square root of 2 (approximately 1.414).<br>Same as "Float sqrt2" in Smalltalk.
*'''SQRT2'''<br>Square root of 2 (approximately 1.414).<br>Same as "<code>Float sqrt2</code>" in Smalltalk.


===== Min & Max =====
===== Min & Max =====


*'''max''' (''number1 , number2,...'')<br>Returns the largest of 0 to 5 arguments
*'''max''' (''number1 , number2,...'')<br>Returns the largest of up to 6 arguments


*'''min''' (''number1, number2,...'')<br>Returns the smallest of 0 to 5 arguments
*'''min''' (''number1, number2,...'')<br>Returns the smallest of up to 6 arguments


===== Miscellaneous =====
===== Miscellaneous =====
Zeile 929: Zeile 1.151:
*'''ceil''' (''aNumber'')<br>Returns the smallest integer greater than or equal to aNumber
*'''ceil''' (''aNumber'')<br>Returns the smallest integer greater than or equal to aNumber


*'''exp''' (''aNumber'')<br>Returns E<sup>aNumber</sup>, where aNumber is the argument, and E is Euler's constant, the base of the natural logarithm
*'''exp''' (''aNumber'')<br>Returns e<sup>aNumber</sup>, where aNumber is the argument, and e is Euler's constant, the base of the natural logarithm


*'''fac''' (''aNumber'')<br>Returns the factorial of aNumber
*'''fac''' (''aNumber'')<br>Returns the factorial of aNumber
Zeile 952: Zeile 1.174:


*'''sqrt''' (''aNumber'')<br>Returns the square root of aNumber
*'''sqrt''' (''aNumber'')<br>Returns the square root of aNumber

*'''cbrt''' (''aNumber'')<br>Returns the cubic root of aNumber


===== Trigonometric =====
===== Trigonometric =====


*'''acos''' (''aNumber'')<br>Returns the arccosine (in radians) of aNumber
*'''sin''' (''aNumber'')<br>Returns the sine of aNumber (given in radians)


*'''asin''' (''aNumber'')<br>Returns the arcsine (in radians) of aNumber
*'''cos''' (''aNumber'')<br>Returns the cosine of aNumber (given in radians)


*'''atan2''' (''x, y'')<br>Returns the arctangent of the quotient of its arguments (in radians)
*'''tan''' (''aNumber'')<br>Returns the tangent of aNumber (given in radians)


*'''atan''' (''aNumber'')<br>Returns the arctangent (in radians) of aNumber
*'''cot''' (''aNumber'')<br>Returns the cotangent of aNumber (given in radians)


*'''cos''' (''aNumber'')<br>Returns the cosine of aNumber (given in radians)
*'''csc''' (''aNumber'')<br>Returns the cosecant of aNumber (given in radians)


*'''sin''' (''aNumber'')<br>Returns the sine of aNumber (given in radians)
*'''sec''' (''aNumber'')<br>Returns the secant of aNumber (given in radians)


*'''tan''' (''aNumber'')<br>Returns the tangent of aNumber (given in radians)
*'''arcSin''' (''aNumber'')<br>Returns the arcsine (in radians) of aNumber

*'''arcCos''' (''aNumber'')<br>Returns the arccosine (in radians) of aNumber

*'''arcTan''' (''aNumber'')<br>Returns the arctangent (in radians) of aNumber

*'''arcTan2''' (''x, y'')<br>Returns the arctangent of the quotient of its arguments (in radians)

*'''sinh''' / '''cosh''' / '''tanh''' / '''arSinh''' / '''arCosh''' etc.<br>Hyperbolic versions of above


==== Number ====
==== Number ====
===== Properties =====
===== Properties =====


*'''MAX_VALUE'''<br>The largest representable number. The returned number is 0x7FFFFFFFFFFFFFFF, to mimicri a 64 limit on numbers for JS programs ported from other systems. In fact, expecco does not impose any such limit in integer values.
*'''MAX_VALUE'''<br>The largest representable number. The returned number is 0x7FFFFFFFFFFFFFFF, to mimicri a 64 bit limit on numbers for JS programs ported from other systems. In fact, expecco does not impose any such limit in integer values.


*'''MIN_VALUE'''<br>The smallest representable number. The returned number is -0x8000000000000000, to mimicri a 64 limit on numbers for JS programs ported from other systems. In fact, expecco does not impose any such limit in integer values.
*'''MIN_VALUE'''<br>The smallest representable number. The returned number is -0x8000000000000000, to mimicri a 64 bit limit on numbers for JS programs ported from other systems. In fact, expecco does not impose any such limit in integer values.


*'''NEGATIVE_INFINITY'''<br>Returns the special 'negative infinity' value
*'''NEGATIVE_INFINITY'''<br>Returns the special 'negative infinity' value
Zeile 984: Zeile 1.216:
===== Methods =====
===== Methods =====


*'''asFloat''' ()<br>Returns a floating-point version of the receiver object. For example: "1.asFloat()" yields the floating point number: "1.0".
*''aNumber''.'''asFloat''' ()<br>Returns a floating-point version of the receiver object.<br>For example: "<code>1.asFloat()</code>" yields the floating point number: "1.0".


*'''asInteger''' ()<br>Returns an integer version of the receiver object (truncating). For example: "1.0.asInteger()" yields the integer number: "1". Alternatives are ceiling(), floor() and rounded().
*''aNumber''.'''asInteger''' ()<br>Returns an integer version of the receiver object (truncating).<br>For example: "<code>1.0.asInteger()</code>" yields the integer number: "1". Alternatives are ceiling(), floor() and rounded().


*'''ceiling''' ()<br>For non-integral values, ceiling returns the smallest integer which is larger than the receiver. For integers, the original value is returned. For example: "1.4.ceiling()" yields the integer number: "2", whereas "1.ceiling()" returns "1". See also: floor() and rounded().
*''aNumber''.'''ceiling''' ()<br>For non-integral values, ceiling returns the smallest integer which is larger than the receiver. For integers, the original value is returned.<br>For example: "<code>1.4.ceiling()</code>" yields the integer number: "2", whereas "<code>1.ceiling()</code>" returns "1". See also: floor() and rounded().


*'''floor''' ()<br>For non-integral values, floor returns the largest integer which is smaller than the receiver. For integers, the original value is returned. For example: "1.4.floor()" yields the integer number: "1", whereas "1.floor()" returns "1". See also: ceiling() and rounded().
*''aNumber''.'''floor''' ()<br>For non-integral values, floor returns the largest integer which is smaller than the receiver. For integers, the original value is returned.<br>For example: "<code>1.4.floor()</code>" yields the integer number: "1", whereas "<code>1.floor()</code>" returns "1". See also: ceiling() and rounded().


*'''rounded''' ()<br>For non-integral values, rounded returns the nearest integer from rounding. For integers, the original value is returned. For example: "1.4.rounded()" yields the integer number: "1", "1.6.rounded()" yields the integer number: "2" and whereas "1.rounded()" returns "1". See also: ceiling() and floor().
*''aNumber''.'''rounded''' ()<br>For non-integral values, rounded returns the nearest integer from rounding. For integers, the original value is returned.<br>For example: "<code>1.4.rounded()</code>" yields the integer number: "1", "<code>1.6.rounded()</code>" yields the integer number: "2" and whereas "<code>1.rounded()</code>" returns "1". See also: ceiling() and floor().


*'''roundTo''' (''r'')<br>Rounds towards the nearest multiple of r. For example, "1.543.roundedTo(0.01)" returns "1.54" and "1.567.roundedTo(0.01)" returns "1.57". See also: ceiling(), floor() and rounded().
*''aNumber''.'''roundTo''' (''r'')<br>Rounds towards the nearest multiple of r.<br>For example, "<code>1.543.roundedTo(0.01)</code>" returns "1.54" and "<code>1.567.roundedTo(0.01)</code>" returns "1.57". See also: ceiling(), floor() and rounded().


*'''toExponential''' (''nDigits'')<br>Returns a string representing the number in exponential notation, where nDigits is the number of digits to appear after the decimal point
*''aNumber''.'''toExponential''' (''nDigits'')<br>Returns a string representing the number in exponential notation, where nDigits is the number of digits to appear after the decimal point


*'''toExponential''' (''nDigits, nDigitsAfter'')<br>Returns a string representing the number in exponential notation
*''aNumber''.'''toExponential''' (''nDigits, nDigitsAfter'')<br>Returns a string representing the number in exponential notation


*'''toFixed''' (''nDigits'')<br>Returns a string representing the number in fixed notation, where nDigits is the number of digits to appear after the decimal point
*''aNumber''.'''toFixed''' (''nDigits'')<br>Returns a string representing the number in fixed notation, where nDigits is the number of digits to appear after the decimal point


==== Random ====
==== Random ====


A built in random generator, which generates linear congruential pseudo random numbers itself. On Unix/Linux operating systems, an additional class called "RandomGenerator" uses the operating system's random generator (<code>/dev/rand</code>). In addition, alternative generators are available as "RandomParkMiller" and "RandomTT800". For most applications, the default generator named Random should provide a reasonable random sequence.
A built in random generator, which generates linear congruential pseudo random numbers itself. On Unix/Linux operating systems, an additional class called "RandomGenerator" uses the operating system's random generator (<code>/dev/rand</code>). In addition, alternative generators are available as "<code>RandomParkMiller</code>" and "<code>RandomTT800</code>". For most applications, the default generator named "<code>Random</code>" should provide a reasonable random sequence (this will be the "/dev/rand"-based generator on Unix/Linux, and a Cryptographic Hash-based generator on other systems).


*'''next ()'''<br>Returns a random number between 0 and 1 (float)
*''Random''.'''next ()'''<br>Returns a random number between 0 and 1 (float)


*'''next (''n'')'''<br>Returns n random numbers between 0 and 1 (float) as an array
*''Random''.'''next (''n'')'''<br>Returns n random numbers between 0 and 1 (float) as an array


*'''nextBoolean ()'''<br>Randomly returns either true or false
*''Random''.'''nextBoolean ()'''<br>Randomly returns either true or false


*'''nextBetween_and_''' (''min, max'')<br>Returns a random float between min and max
*''Random''.'''nextBetween_and_''' (''min, max'')<br>Returns a random float between min and max


*'''nextIntegerBetween_and_''' (''min, max'')<br>Returns a random integer between min and max (use 1 and 6, to simulate a dice)
*''Random''.'''nextIntegerBetween_and_''' (''min, max'')<br>Returns a random integer between min and max (use 1 and 6, to simulate a dice)


==== Time / Date ====
==== Time / Date ====
Zeile 1.020: Zeile 1.252:
Time and Date instances provide a rich protocol for all kinds of queries. The list below is only a short extract of the most used methods:
Time and Date instances provide a rich protocol for all kinds of queries. The list below is only a short extract of the most used methods:


*'''getDate'''<br>Returns the day of the month (1..31)
*'''Time now'''<br>Returns the current time

*'''Date today'''<br>Returns the current date

*''aDate''.'''getDate'''()<br>Returns the day of the month (1..31)


*'''getDay'''<br>Returns the day of the week (0..6); Sunday is 0
*''aDate''.'''getDay'''()<br>Returns the day of the week (0..6); Sunday is 0


*'''getFullYear'''<br>Returns the year
*''aDate''.'''getMonth'''()<br>Returns the day of the month (1..12)


*'''getHours<br>'''Returns the hours (0..24)
*''aDate''.'''getFullYear'''()<br>Returns the year


*'''getMinutes'''<br>Returns the minutes (0..60)
*''aTime''.'''getHours'''()<br>Returns the hours (0..24)


*'''getMonth'''<br>Returns the day of the month (1..12)
*''aTime''.'''getMinutes'''()<br>Returns the minutes (0..60)


==== String ====
==== String ====


Be reminded that the String class inherits from CharacterArray, ByteArray, SequentialCollection, Collection and Object. Thus all of those methods are also available (and useful) with strings. Use the browser or take a look at the online documentation for the full protocol.
Be reminded that the String class inherits from CharacterArray, ByteArray, SequentialCollection, Collection and Object. Thus all of those methods are also available (and useful) with strings. Use the browser or take a look at the online documentation for the full protocol (which is much larger than what is listed below).


*'''charAt0''' (''index'')<br>Returns the n'th character as a Character instance object, using a 0-based indexing scheme
*''aString''.'''charAt0''' (''index'')<br>Returns the n'th character as a Character instance object, using a 0-based indexing scheme


*'''charAt1''' (''index'')<br>Returns the n'th character as a Character instance object, using a 1-based indexing scheme
*''aString''.'''charAt1''' (''index'')<br>Returns the n'th character as a Character instance object, using a 1-based indexing scheme


*'''charCodeAt0''' (''index'')<br>Returns the code of the n'th character, using a 0-based indexing scheme
*''aString''.'''charCodeAt0''' (''index'')<br>Returns the code of the n'th character, using a 0-based indexing scheme


*'''charCodeAt1''' (''index'')<br>Returns the code of the n'th character, using a 1-based indexing scheme
*''aString''.'''charCodeAt1''' (''index'')<br>Returns the code of the n'th character, using a 1-based indexing scheme


*'''indexOf0''' (''aCharacter'')<br>Returns the index of aCharacter, using a 0-based indexing scheme; -1 if not found
*''aString''.'''indexOf0''' (''aCharacter'')<br>Returns the index of aCharacter, using a 0-based indexing scheme; -1 if not found


*'''indexOf1''' (''aCharacter'')<br>Returns the index of aCharacter, using a 1-based indexing scheme; 0 if not found
*''aString''.'''indexOf1''' (''aCharacter'')<br>Returns the index of aCharacter, using a 1-based indexing scheme; 0 if not found


*'''lastIndexOf0''' (''aCharacter'')<br>Returns the last index of aCharacter, using a 0-based indexing scheme; -1 if not found
*''aString''.'''lastIndexOf0''' (''aCharacter'')<br>Returns the last index of aCharacter, using a 0-based indexing scheme; -1 if not found


*'''lastIndexOf1''' (''aCharacter'')<br>Returns the last index of aCharacter, using a 1-based indexing scheme; 0 if not found
*''aString''.'''lastIndexOf1''' (''aCharacter'')<br>Returns the last index of aCharacter, using a 1-based indexing scheme; 0 if not found


*'''quote()'''<br>Wraps the receiver into double quotes (")
*''aString''.'''quote()'''<br>Wraps the receiver into double quotes (")


*'''split''' (''separator'')<br>Splits the string into a collection of substrings using the separator. Warning: the JavaScript split(xxx) function and the Smalltalk split: method have different semantics (and argument order)
*''aString''.'''split''' (''separator'')<br>Splits the string into a collection of substrings using the separator. Warning: the JavaScript split(xxx) function and the Smalltalk split: method have different semantics (and argument order)


*'''substr0''' (''index, count'')<br>Extracts a substring starting at the index with the given length, using a 0-based indexing scheme
*''aString''.'''substr0''' (''index, count'')<br>Extracts a substring starting at the index with the given length, using a 0-based indexing scheme


*'''substr1''' (''index, count'')<br>Extracts a substring starting at the index with the given length, using a 1-based indexing scheme
*''aString''.'''substr1''' (''index, count'')<br>Extracts a substring starting at the index with the given length, using a 1-based indexing scheme


*'''substring0''' (''index1'')<br>Extracts a substring starting at the index, using a 0-based indexing scheme
*''aString''.'''substring0''' (''index1'')<br>Extracts a substring starting at the index, using a 0-based indexing scheme


*'''substring0''' (''index1, index2'')<br>Extracts a substring between the given indices, using a 0-based indexing scheme
*''aString''.'''substring0''' (''index1, index2'')<br>Extracts a substring between the given indices, using a 0-based indexing scheme


*'''substring1''' (''index1'')<br>Extracts a substring starting at the index, using a 1-based indexing scheme
*''aString''.'''substring1''' (''index1'')<br>Extracts a substring starting at the index, using a 1-based indexing scheme


*'''substring1''' (''index1, index2'')<br>Extracts a substring between the given indices, using a 1-based indexing scheme
*''aString''.'''substring1''' (''index1, index2'')<br>Extracts a substring between the given indices, using a 1-based indexing scheme


*'''toLowerCase'''<br>Returns a copy of the receiver with all chars in lower case
*''aString''.'''toLowerCase'''<br>Returns a copy of the receiver with all chars in lower case


*'''toUpperCase'''<br>Returns a copy of the receiver with all chars in upper case
*''aString''.'''toUpperCase'''<br>Returns a copy of the receiver with all chars in upper case


*'''trim'''<br>Returns a copy of the receiver with all leading and trailing white space removed
*''aString''.'''trim'''<br>Returns a copy of the receiver with all leading and trailing white space removed


*'''trimLeft'''<br>Returns a copy of the receiver with all leading white space removed
*''aString''.'''trimLeft'''<br>Returns a copy of the receiver with all leading white space removed


*'''trimRight'''<br>Returns a copy of the receiver with all trailing white space removed
*''aString''.'''trimRight'''<br>Returns a copy of the receiver with all trailing white space removed
&nbsp;
&nbsp;


Zeile 1.083: Zeile 1.319:
Notice that Array, OrderedCollection, ByteArray, String and others are also inheriting the Collection protocol. Thus the below listed messages can also be applied to instances of those.
Notice that Array, OrderedCollection, ByteArray, String and others are also inheriting the Collection protocol. Thus the below listed messages can also be applied to instances of those.


*'''concat''' (''aCollection'')<br>Returns a new collection consisting of the concatenation of the receiver and the argument
*''aCollection''.'''concat''' (''aCollection'')<br>Returns a new collection consisting of the concatenation of the receiver and the argument


*'''every''' (''filterFunction'')<br>Returns true, if "filterFunction" returns true for all elements
*''aCollection''.'''every''' (''filterFunction'')<br>Returns true, if "filterFunction" returns true for all elements


*'''filter''' (''filterFunction'')<br>Selects elements for which "filterFunction" returns true
*''aCollection''.'''filter''' (''filterFunction'')<br>Selects elements for which "filterFunction" returns true


*'''forEach''' (''function'')<br>Applies "function" for each element
*''aCollection''.'''forEach''' (''function'')<br>Applies "function" for each element


*'''join''' (''seperator'')<br>Joins the strings of the receiver into a new single string using the separator
*''aCollection''.'''join''' (''seperator'')<br>Joins the strings of the receiver into a new single string using the separator


*'''map''' (''function'')<br>Returns a new collection collecting the results of applying "function" to each element in sequence
*''aCollection''.'''map''' (''function'')<br>Returns a new collection collecting the results of applying "function" to each element in sequence


*'''pop'''<br>Removes and returns the last element of the collection. Modifies the receiver collection as a side effect.
*''aCollection''.'''pop'''<br>Removes and returns the last element of the collection. Modifies the receiver collection as a side effect.


*'''push''' (''value'')<br>Adds an element to the end of the collection. Modifies the receiver collection as a side effect.
*''aCollection''.'''push''' (''value'')<br>Adds an element to the end of the collection. Modifies the receiver collection as a side effect.


*'''reduce0''' (''filterFunction'')<br>Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 0-based indices to the filter.
*''aCollection''.'''reduce0''' (''filterFunction'')<br>Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 0-based indices to the filter.


*'''reduce0''' (''filterFunction, initialValue'')<br>Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 0-based indices to the filter.
*''aCollection''.'''reduce0''' (''filterFunction, initialValue'')<br>Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 0-based indices to the filter.


*'''reduce1''' (''filterFunction'')<br>Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 1-based indices to the filter.
*''aCollection''.'''reduce1''' (''filterFunction'')<br>Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 1-based indices to the filter.


*'''reduce1''' (''filterFunction, initialValue'')<br>Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 1-based indices to the filter.
*''aCollection''.'''reduce1''' (''filterFunction, initialValue'')<br>Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 1-based indices to the filter.


*'''shift'''<br>Removes and returns the first element of the collection. Same as ''removeFirst''. Notice that this modifies the receiver collection as a side effect.
*''aCollection''.'''shift'''<br>Removes and returns the first element of the collection. Same as ''removeFirst''. Notice that this modifies the receiver collection as a side effect.


*'''slice0''' (''index1, index2'')<br>Extracts a subcollection, using a 0-based indexing scheme
*''aCollection''.'''slice0''' (''index1, index2'')<br>Extracts a subcollection, using a 0-based indexing scheme


*'''slice1''' (''index1, index2'')<br>Extracts a subcollection, using a 1-based indexing scheme
*''aCollection''.'''slice1''' (''index1, index2'')<br>Extracts a subcollection, using a 1-based indexing scheme


*'''some''' (''filterFunction'')<br>Returns true, if "filterFunction" returns true for any element
*''aCollection''.'''some''' (''filterFunction'')<br>Returns true, if "filterFunction" returns true for any element


*'''unshift''' (''arg'')<br>Adds an element to the beginning of the collection. Same as ''addFirst''. Notice that this modifies the receiver collection as a side effect.
*''aCollection''.'''unshift''' (''arg'')<br>Adds an element to the beginning of the collection. Same as ''addFirst''. Notice that this modifies the receiver collection as a side effect.
&nbsp;
&nbsp;


Zeile 1.122: Zeile 1.358:
The global variable "<code>Transcript</code>" refers to either the Transcript information window (if it is open) or the standard error stream. As such, it implements most of the stream interface functions. Most noteworthy are:
The global variable "<code>Transcript</code>" refers to either the Transcript information window (if it is open) or the standard error stream. As such, it implements most of the stream interface functions. Most noteworthy are:


*'''cr''' ()<br>Adds a linebreak (i.e. followup text will be shown on the next line)
*Transcript.'''cr''' ()<br>Adds a linebreak (i.e. followup text will be shown on the next line)


*'''show''' (''arg'')<br>Adds a textual representation of the argument, which can be a string, number or any other object.
* Transcript.'''show''' (''arg'')<br>Adds a textual representation of the argument, which can be a string, number or any other object.


*'''show''' (''fmt'', ''arg'',...)<br>Like show, but "%i" (i=1..8) sequences in the format argument are expanded by corresponding argument strings. Up to 8 arguments are allowed.
* Transcript.'''show''' (''fmt'', ''arg'',...)<br>Like show, but "%i" (i=1..8) sequences in the format argument are expanded by corresponding argument strings. Up to 8 arguments are allowed.


*'''showCR''' (''arg'')<br>A combination of show(), followed by a linebreak.
* Transcript.'''showCR''' (''arg'')<br>A combination of show(), followed by a linebreak.


*'''showCR''' (''fmt'', ''arg'',...)<br>Like showCR, but "%i" (i=1..8) sequences in the format argument are expanded by corresponding argument strings. Up to 8 arguments are allowed.
* Transcript.'''showCR''' (''fmt'', ''arg'',...)<br>Like showCR, but "%i" (i=1..8) sequences in the format argument are expanded by corresponding argument strings. Up to 8 arguments are allowed.


and from the inherited write stream interface:
and from the inherited write stream interface:


*'''nextPut''' (''aChar'')<br>Sends a single character
* Transcript.'''nextPut''' (''aChar'')<br>Sends a single character


*'''nextPutAll''' (''aString'')<br>Sends a string
* Transcript.'''nextPutAll''' (''aString'')<br>Sends a string


*'''nextPutLine''' (''aString'')<br>Sends a string followed by a linebreak
* Transcript.'''nextPutLine''' (''aString'')<br>Sends a string followed by a linebreak
&nbsp;


&nbsp;
==== Logger ====
==== Logger ====


The global variable "<code>Logger</code>" refers to an object which will handle system log messages. This is the underlying framework's logging mechanism, not to be confused with expecco's ''ActivityLog''.
The global variable "<code>Logger</code>" refers to an object which will handle system log messages. This is the underlying Smalltalk framework's logging mechanism, not to be confused with expecco's ''ActivityLog''.

The Logger will filter messages according to their ''severity'', which is one of {<code>debug, info, warn, error, fatal</code>}. Severities are ordered as <code>debug < info < warn < error < fatal</code>.
The Logger will filter messages according to their ''severity'', which is one of {<code>debug, info, warn, error, fatal</code>}. Severities are ordered as <code>debug < info < warn < error < fatal</code>.
For filtering, a severityThreshold can be set, and only messages with a higher severity will be reported.
For filtering, a severityThreshold can be set, and only messages with a higher severity will be reported.
Zeile 1.152: Zeile 1.389:
<br>The Logger supports the following protocol:
<br>The Logger supports the following protocol:


*'''debug''' (''message'')<br>logs a debug message
*Logger.'''debug''' (''message'')<br>logs a debug message


*'''info''' (''message'')<br>logs an info message
*Logger.'''info''' (''message'')<br>logs an info message


*'''warn''' (''message'')<br>logs an warning message
*Logger.'''warn''' (''message'')<br>logs an warning message


*'''error''' (''message'')<br>logs an error message
*Logger.'''error''' (''message'')<br>logs an error message


*'''fatal''' (''message'')<br>logs a fatal message
*Logger.'''fatal''' (''message'')<br>logs a fatal message
&nbsp;
&nbsp;


Zeile 1.177: Zeile 1.414:
*'''error''' () <br>Report a defect (in the test). Stops execution.
*'''error''' () <br>Report a defect (in the test). Stops execution.


*'''error''' (''infoString'') <br>Report a defect (in the test). Stops execution.
*'''error''' (''infoString'') <br>Report a defect (in the test) with infoString to be shown in the activity log. Stops execution.


*'''fail''' () <br>Report a failure (in the SUT). Stops execution.
*'''fail''' ( [''infoString''] ) <br>Report a failure (in the SUT) with optional infoString to be shown in the activity log. Stops execution.


*'''fail''' (''infoString'') <br>Report a failure (in the SUT). Stops execution.
*'''inconclusive''' ( [''infoString''] ) <br>Report an inconclusive test. Stops execution.


*'''success''' ( [''infoString''] ) OBSOLETE - see activitySuccess below<br>Finishes the current activity with success.
*'''inconclusive''' ()<br>Report an inconclusive test. Stops execution.


*'''activitySuccess''' ( [''infoString''] )<br>Finishes the current activity with success. Actually not needed, because simply returning from an action's execute method is semantically equivalent (however, this allows for early successful finish). Due to the more descriptive name, use this function instead of "<code>success()</code>".
*'''inconclusive''' (''infoString'') <br>Report an inconclusive test. Stops execution.


*'''testPass''' ( [''infoString''] )<br>Finishes the current test case with success. Notice: this aborts the current activity and all of the callers up to the test case level. This behavior is different from activitySuccess's behavior.
*'''success''' () OBSOLETE - see activitySuccess below<br>Finishes the current activity with success.


*'''success''' (''infoString'') OBSOLETE - see activitySuccess below<br>Finishes the current activity with success.
*'''pass''' ( [''infoString''] )<br>Same as testPass (backward compatibility).

*'''activitySuccess''' ()<br>Finishes the current activity with success. Actually not needed, because simply returning from an action's execute method is semantically equivalent (however, this allows for early successful finish). Due to the more descriptive name, use this function instead of "<code>success()</code>".

*'''activitySuccess''' (''infoString'')<br>Finishes the current activity with success.

*'''pass''' ()<br>Finishes the current test case with success. Notice: this aborts the current activity and all of the callers up to the test case level. This behavior is different from activitySuccess's behavior.

*'''pass''' (''infoString'')<br>Finishes the current test case with success.
&nbsp;
&nbsp;


Zeile 1.220: Zeile 1.449:
*'''logImageInfo''' (''messageString'', image) <br>Adds an info message with an image (screendump) to the activity log. There is an option in the report generator to include those in the generated pdf-report.
*'''logImageInfo''' (''messageString'', image) <br>Adds an info message with an image (screendump) to the activity log. There is an option in the report generator to include those in the generated pdf-report.


*'''alert''' (''messageString'') (1.8.1) <br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "Execution - Log -Settings" dialog (by default, it is disabled).
*'''alert''' (''messageString'') <br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "[[Settings_LoggingSettings/en|Execution-Log-Settings]]" dialog (by default, it is disabled).


*'''warn''' (''messageString'') (1.8.1) <br>Same as ''alert()'' (for Smalltalk compatibility).
*'''warn''' (''messageString'') <br>Same as ''alert()'' (for Smalltalk protocol compatibility).


Notice the similarity and difference between "error()" and "logError()": both actually create a log-data entry, but "error()" stops the execution, whereas "logError()" proceeds (maybe, the naming is a bit confusing here...)
Notice the similarity and difference between "error()" and "logError()": both actually create a log-data entry, but "error()" stops the execution, whereas "logError()" proceeds (maybe, the naming is a bit confusing here...)
Zeile 1.329: Zeile 1.558:
*'''blockDescription''' () <br>The definition of the activity (i.e. the tree definition item of the action's step)
*'''blockDescription''' () <br>The definition of the activity (i.e. the tree definition item of the action's step)


*'''executor''' () <br>Returns the current executor. See [[#Executor Functions|"Executor Functions"]] below.
*'''environment''' () <br>The currently valid environment variables of the compound step which contains this action, or (if none) of the test plan in which this action executes (see "[[#Environment Access|Environment Access]]" below)

*'''environmentAt''' (''anEnvironmentVarName'') <br>The value of an environment variable (in the above environment)

*'''environmentAt_put''' (''anEnvironmentVarName'', ''value'') <br>Changing the value of an environment variable (in the above environment)

*'''executor''' () <br>Returns the current executor (see "[[#Executor Functions|Executor Functions]]" below).


*'''inputValueForPin''' (''pin'')<br>The value of a certain pin
*'''inputValueForPin''' (''pin'')<br>The value of a certain pin
Zeile 1.352: Zeile 1.575:


*'''step''' () <br>The corresponding step of the activity
*'''step''' () <br>The corresponding step of the activity

*'''stepLocalStorageAt''' ("<varName>") <br>Retrieves a step's local storage value.

*'''stepLocalStorageAt_put''' ("<varName>", ''value'') <br>Defines a step's local storage value.
:Step local storage can be seen as a kind of static variable, which is only visible inside the code of a single execute function, but with the live time of the step. This means that the value is still present, when the step is executed the next time. However, it will vanish, whenever the diagram which contains the step is edited. Also, its value is not copied, when the step is copied. It is also not made persistent. The value will be reset to nil with every edit operation on the diagram, or via a menu function in the project's "More" menu, or the step's menu.
:Step local storage can be used to count the number of invocations, or to implement static state which is to be preserved between step executions.
:This is an experimental feature, only to be used by advanced users.
&nbsp;
&nbsp;


Zeile 1.358: Zeile 1.588:
Starting at every compound activity, a nested stack of outer environments is present, ending in a top project environment. The following activity methods provide access to some of them. This can be used to automatically setup environment variables from a configuration or when autodetecting a system-under-test's configuration and leaving the values in a defined outer environment. Depending on the intended lifetime of those values, one of the following environments should be chosen as target of such operations:
Starting at every compound activity, a nested stack of outer environments is present, ending in a top project environment. The following activity methods provide access to some of them. This can be used to automatically setup environment variables from a configuration or when autodetecting a system-under-test's configuration and leaving the values in a defined outer environment. Depending on the intended lifetime of those values, one of the following environments should be chosen as target of such operations:


* '''environment''' () <br> the environment of the compound in which the current elementary action is executing (as a step)
*'''environment''' () <br>The currently valid environment variables of the compound step which contains this action, or (if none) of the test plan in which this action executes (see "[[#Environment Access|Environment Access]]" below)

*'''environmentAt''' (''anEnvironmentVarName'') <br>The value of an environment variable (in the environment as described above)

*'''environmentAt_put''' (''anEnvironmentVarName'', ''value'') <br>Changing the value of an environment variable (in the environment as describe above)



* '''executorEnvironment''' () <br> an environment which is only present during a single testplan execution
* '''executorEnvironment''' () <br> an environment which is only present during a single testplan execution
Zeile 1.366: Zeile 1.601:
* '''browserEnvironment''' () <br> an environment which is present while the expecco application is running (i.e. the current session). (it is not made persistent)
* '''browserEnvironment''' () <br> an environment which is present while the expecco application is running (i.e. the current session). (it is not made persistent)


None of the following can be made persistent directly. However, every test suite, test plan and compound block provide a description of initial values and how to compute them in their environment description, which is edited using the [[ Environment Editor ]].
None of the following are made persistent directly (i.e. the values will not saved with the ".ets"file). However, every test suite, test plan and compound block provides a description of initial values and how to compute them in their environment description, which is edited using the [[ Environment Editor ]] or which can be modified with actions from the reflection library.
Also, such descriptions can be saved and loaded separately from the test suite via the "''Load/Save Parameter Set''" menu functions of the "''File''" menu.
Also, such descriptions can be saved and loaded separately from the test suite via the "''Load/Save Parameter Set''" menu functions of the "''File''" menu or via the "--parameters" [[Starting_expecco_via_Command_Line/en#Test_Parameters | command line argument]].


&nbsp;
&nbsp;

===== Event Sending =====
===== Event Sending =====
Events can be pushed onto an event queue from either actions found in the standard library or via
Events can be pushed onto an event queue from either actions found in the standard library or via
Zeile 1.419: Zeile 1.655:
Projects, Test Suites and Libraries are all the same kind of object (i.e. any project can be used as a test suite or imported into another as imported library).
Projects, Test Suites and Libraries are all the same kind of object (i.e. any project can be used as a test suite or imported into another as imported library).


*'''documentation''' () <br>The documentation or nil
*''aProject''.'''documentation''' () <br>The documentation or nil


*'''functionalId''' () <br>A unique ID which defines this testSuite independent of its name (remains constant after change)
*''aProject''.'''functionalId''' () <br>A unique ID which defines this testSuite independent of its name (remains constant after change)


*'''projectsWorkingDirectory''' () <br>Project directory - i.a. all attachments. This directory is created when a project file is loaded and is deleted when expecco is closed. Do not use it for permanently needed files.
*''aProject''.'''projectsWorkingDirectory''' () <br>Project directory - i.a. all attachments. This directory is created when a project file is loaded and is deleted when expecco is closed. Do not use it for permanently needed files.


*'''modelLanguageStringFor''' (''aString'')<br>translates a string according to the model-language translation table
*''aProject''.'''modelLanguageStringFor''' (''aString'')<br>translates a string according to the model-language translation table


*'''versionId''' () <br>A unique ID which defines this testSuite in exactly this version. (changed with any edit operation)
*''aProject''.'''versionId''' () <br>A unique ID which defines this testSuite in exactly this version. (changed with any edit operation)


*'''elementWithName''' (''aString'') <br>Retrieve an element by name
*''aProject''.'''elementWithName''' (''aString'') <br>Retrieve an element by name


*'''elementWithId''' (''aUUID'') <br>Retrieve an element by its id
*''aProject''.'''elementWithId''' (''aUUID'') <br>Retrieve an element by its version id


*'''elementWithFunctionalId''' (''aUUID'')<br>Retrieve an element by its version id
*''aProject''.'''elementWithFunctionalId''' (''aUUID'')<br>Retrieve an element by its functional id

*'''typeNamed''' (''aTypeName'')<br>Retrieve a datatype. To instantiate a type, write "<code>(self project typeNamed:'Foo') new</code>".


*''aProject''.'''typeNamed''' (''aTypeName'')<br>Retrieve a datatype. To instantiate a type, write<br>"<code>(self project typeNamed:'Foo') new</code>".
&nbsp;
&nbsp;


==== Testplan Functions ====
==== Testplan Functions ====


*'''documentation''' () <br>The documentation or nil
*''aTestPlan''.'''documentation''' () <br>The documentation or nil


*'''name''' () <br>The name of the Testplan
*''aTestPlan''.'''name''' () <br>The name of the Testplan


*'''tags''' () <br>A collection of tags of the testplan
*''aTestPlan''.'''tags''' () <br>A collection of tags of the testplan


*'''functionalId''' () <br>A unique ID which defines this testplan independent of its name (remains constant after change)
*''aTestPlan''.'''functionalId''' () <br>A unique ID which defines this testplan independent of its name (remains constant after change)


*'''versionId''' () <br>A unique ID which defines this testplan in exactly this version. (changed with any edit operation)
*''aTestPlan''.'''versionId''' () <br>A unique ID which defines this testplan in exactly this version. (changed with any edit operation)
&nbsp;
&nbsp;


==== Step Functions ====
==== Step Functions ====


*'''name''' () <br>The name of the Step
*''aStep''.'''name''' () <br>The name of the Step


*'''tags''' () <br>A collection of tags of the Step
*''aStep''.'''tags''' () <br>A collection of tags of the Step


*'''inputPins'''() <br>A collection with all input pins
*''aStep''.'''inputPins'''() <br>A collection with all input pins


*'''inputPinForPinName'''(''inputPinName'') <br>The corresponding input pin. Raises an error, if no such input pin exists.
*''aStep''.'''inputPinForPinName'''(''inputPinName'') <br>The corresponding input pin. Raises an error, if no such input pin exists.


*'''inputPinForPinName_ifAbsent'''(''inputPinName'', ''ifAbsentValue'') <br>The corresponding input pin. Returns ''ifAbsentValue'', if no such input pin exists.
*''aStep''.'''inputPinForPinName_ifAbsent'''(''inputPinName'', ''ifAbsentValue'') <br>The corresponding input pin. Returns ''ifAbsentValue'', if no such input pin exists.


*'''outputPins'''() <br>A collection with all output pins
*''aStep''.'''outputPins'''() <br>A collection with all output pins


*'''outputPinForPinName'''(''outputPinName'') <br>The corresponding output pin. Raises an error, if no such output pin exists.
*''aStep''.'''outputPinForPinName'''(''outputPinName'') <br>The corresponding output pin. Raises an error, if no such output pin exists.


*'''outputPinForPinName_ifAbsent'''(''outputPinName'', ''ifAbsentValue'') <br>The corresponding output pin. Returns ''ifAbsentValue'', if no such output pin exists.
*''aStep''.'''outputPinForPinName_ifAbsent'''(''outputPinName'', ''ifAbsentValue'') <br>The corresponding output pin. Returns ''ifAbsentValue'', if no such output pin exists.
&nbsp;
&nbsp;


==== Pin Functions ====
==== <span id="Smalltalk_PinFunctions>Pin Functions</span> ====


The input and output pins can be referenced in elementary code directly by their name.
The input and output pins can be referenced in elementary code directly by their name.
The name refers to the pin - '''NOT''' the passed value (this allows for the code to check, if a value is present). For indexed pins (i.e. in steps with a variable number of pins), which is a new feature in v2.1, the collection of pins is referred to by the name, and pins are accessed via an index (1..). See more about how to handle variable pins in the section below.
The name refers to the pin - '''NOT''' the passed value (this allows for the code to check, if a value is present). For indexed pins (i.e. in steps with a variable number of pins), the collection of pins is referred to by the name, and pins are accessed via an index (1..). See more about variable pins in the section below.


===== Common to all Pins: =====
===== Common to all Pins: =====
*'''datatype''' () <br>Returns the data type of the pin (Reflection-API)
*''pinName''.'''datatype''' () <br>Returns the data type of the pin (Reflection-API)


===== Input Pins: =====
===== Input Pins: =====


*'''hasValue''' () <br>Returns true if the pin has received a value (useful to determine if there is a value if the trigger condition is "AndConnected" or "Or")
*''pinName''.'''hasValue''' () <br>Returns true if the pin has received a value (useful to determine if there is a value if the trigger condition is "AndConnected" or "Or")


*'''hasEnvironmentFreezeValue''' () <br>Returns true if the pin has a freezeValue from an environment
*''pinName''.'''hasEnvironmentFreezeValue''' () <br>Returns true if the pin has a freezeValue from an environment


*'''hasFreezeValue''' () <br>Returns true if the pin has a freezeValue
*''pinName''.'''hasFreezeValue''' () <br>Returns true if the pin has a freezeValue


*'''isConnected''' () <br>Returns true if the pin is connected (but not frozen)
*''pinName''.'''isConnected''' () <br>Returns true if the pin is connected (but not frozen)


*'''value''' () <br>Returns the value of the pin. This is either the value received via a connection, or a constant freeze value, or an environment variable freeze value. Raises an error if the pin did not receive any value.
*''pinName''.'''value''' () <br>Returns the value of the pin. This is either the value received via a connection, or a constant freeze value, or an environment variable freeze value. Raises an error if the pin did not receive any value.


*'''valueIfPresent''' () <br>Returns the value of a pin or nil if the pin did not receive any value. Similar to "<code>value()</code>", but avoids the exception.
*''pinName''.'''valueIfPresent''' () <br>Returns the value of a pin or nil if the pin did not receive any value. Similar to "<code>value()</code>", but avoids the exception.


*'''valueIfAbsent''' (''alternativeValue'') <br>Returns the value of a pin or the alternativeValue, if the pin did not receive any value. Similar to "<code>value()</code>", but avoids the exception.
*''pinName''.'''valueIfAbsent''' (''alternativeValue'') <br>Returns the value of a pin or the alternativeValue, if the pin did not receive any value. Similar to "<code>value()</code>", but avoids the exception.


*'''waitForValue''' () <br>The pin has to be a mailbox pin. The execution of this action will be suspended until the pin receives a value.
*''mbxPinName''.'''waitForValue''' () - ''Mailbox pins only''<br>The pin has to be a mailbox pin. The execution of this action will be suspended until the pin receives a value.


*'''waitForValueWithTimeout''' (''seconds'')<br>Like above, but the execution will only wait for the specified time limit, given in seconds. The parameter is typically an integer, but can also be a fraction (e.g. 1/3) or a floating-point number (e.g. 0.3).
*''mbxPinName''.'''waitForValueWithTimeout''' (''seconds'') - ''Mailbox pins only''<br>Like above, but the execution will only wait for the specified time limit, given in seconds. The parameter is typically an integer, but can also be a fraction (e.g. 1/3) or a floating-point number (e.g. 0.3).

*''mbxPinName''.'''consumeValue''' () - ''Mailbox pins only''<br>Consumes the current value which is present at the mailbox input pin. If there is another pending input value, <code>hasValue()</code> will continue to return true, and <code>value()</code> will return the next pending value.


===== Output Pins: =====
===== Output Pins: =====


*'''isBuffered''' () <br>True if the pin is buffered
*''outPinName''.'''isBuffered''' () <br>True if the pin is buffered (only generates a value if the action completes successful)


*'''isConnected''' () <br>Returns true if the pin is connected
*''outPinName''.'''isConnected''' () <br>Returns true if the pin is connected. This can be used to not perform expensive computations in an elementary action, iff no-one is interested in that value.


*'''value''' (''data'' ) <br>Writes the value. If the output pin is unbuffered and connected to another step's input pin, the other step's action may be triggered (if the other step's trigger condition is fulfilled). If the output pin is buffered, the datum will be passed to the connected input, when the action has finished with success.
*''outPinName''.'''value''' (''data'' ) <br>Writes the value. If the output pin is unbuffered and connected to another step's input pin, the other step's action may be triggered (if the other step's trigger condition is fulfilled). If the output pin is buffered, the datum will be passed to the connected input, when the action has finished with success.


===== Variable Input Pins =====
===== Variable Input Pins =====


Starting with expecco v2.0, steps can have a variable number of input pins, if the pin's schema has the "variable pin" attribute set. To change the number of pins of a step in the diagram editor, pull the pin-handle of a placed step, to add or remove pins. In elementary code, the following API call entries (to the variable pin) are provided to deal with this situation:
Elementary steps can have a variable number of input pins, if the pin's schema has the "variable pin" attribute set. To change the number of pins of a step in the diagram editor, pull the pin-handle of a placed step, to add or remove pins. In elementary code, the following API call entries (to the variable pin) are provided to deal with this situation:


*'''pinName''' <br> now refers to a collection of pins, representing the set of pins actually present at the step. In contrast regular pins, where pinName refers to a single pin.
*''pinName'' <br> now refers to a collection of pins, representing the set of pins actually present at the step. In contrast to regular pins, where pinName refers to a single pin.


*'''pinName.numberOfVariablePins''' () <br> returns the number of actual pins present in the step for the (variable) schema pin named "pinName"
*''varPinName''.'''numberOfVariablePins''' () <br> returns the number of actual pins present in the step for the (variable) schema pin named "pinName"


*'''pinName.size''' () <br> same as the above "numberOfVariablePins"
*''varPinName''.'''size''' () <br> same as the above "numberOfVariablePins"


*'''pinName.inputPinAt''' (''idx'') <br> returns the idx'th actual pin of the schema pin named "pinName". Notice this retrieves the pin for reflection queries, NOT the pin value. To get value or query if it has a value, call ''valueAt:''() / ''hasValueAt''(). Pin indexing starts with 1.
*''varPinName''.'''inputPinAt''' (''idx'') <br> returns the idx'th actual pin of the schema pin named "pinName". Notice this retrieves the pin for reflection queries, NOT the pin value. To get value or query if it has a value, call ''valueAt:''() / ''hasValueAt''(). Pin indexing starts with 1.


*'''pinName.at''' (''idx'') <br> same as inputPinAt(''idx'')
*''varPinName''.'''at''' (''idx'') <br> same as inputPinAt(''idx'')


*'''pinName.hasValueAt''' (''idx'') <br> true if the idx'th variable pin is connected and has received a value. Indices start with 1.
*''varPinName''.'''hasValueAt''' (''idx'') <br> true if the idx'th variable pin is connected and has received a value. Same as <code>hasValue()</code> for variable input pins. Indices start with 1.


*'''pinName.values''' () <br>Retrieves the values passed to the pins as an array. The array contains nil entries for disconnected pins and those without a value
*''varPinName''.'''values''' () <br>Retrieves the values passed to the pins as an array. The array contains nil entries for disconnected pins and those without a value


*'''pinName.valuesPresent''' () <br>Retrieves the values as present; skips unconnected pins or those without a value.
*''varPinName''.'''valuesPresent''' () <br>Retrieves the values as present; skips unconnected pins or those without a value.


*'''pinName.valueAt''' (''idx'') <br> to get the idx'th pin's value; raises an error if no value was given or pin is not connected
*''varPinName''.'''valueAt''' (''idx'') <br> to get the idx'th pin's value; raises an error if no value was given or pin is not connected. Same as <code>value()</code> for variable input pins.


*'''pinName.valueIfPresentAt''' (''idx'') <br> to get the idx'th pin's value, or nil if no value was given or pin is not connected
*''varPinName''.'''valueIfPresentAt''' (''idx'') <br> to get the idx'th pin's value, or nil if no value was given or pin is not connected

*''varMbxPinName''.'''waitForValueAt''' (''index'') -''Variable mailbox pins only''<br>Same as <code>waitForValue()</code> for variable input mailbox pins.

*''varMbxPinName''.'''waitForValueAt_withTimeout''' (''index'', ''seconds'') - ''Variable mailbox pins only''<br>Same as <code>waitForValue()</code> for variable input mailbox pins.

*''varMbxPinName''.'''consumeValueAt''' (''index'') - ''Variable mailbox pins only''<br>Same as <code>consumeValue()</code> for variable input mailbox pins.


===== Variable Output Pins =====
===== Variable Output Pins =====
Zeile 1.535: Zeile 1.778:
Starting with expecco v2.1, steps can also have a variable number of output pins, if the schema has the "variable output pin" attribute set. To change the number of pins, pull the pin-handle of a placed step, to add or remove pins. In elementary code, the following API call entries (to the variable pin) are provided to deal with this situation:
Starting with expecco v2.1, steps can also have a variable number of output pins, if the schema has the "variable output pin" attribute set. To change the number of pins, pull the pin-handle of a placed step, to add or remove pins. In elementary code, the following API call entries (to the variable pin) are provided to deal with this situation:


*'''pinName.numberOfVariablePins''' () <br> returns the number of actual pins present in the step for the (variable) schema pin named "pinName".
*''varPinName''.'''numberOfVariablePins''' () <br> returns the number of actual pins present in the step for the (variable) schema pin named "pinName".


*'''pinName.outputPinAt_value''' (''idx'', ''value'') <br> write a value to the idx'th output pin. Index starts with 1.
*''varPinName''.'''outputPinAt_value''' (''idx'', ''value'') <br> write a value to the idx'th output pin. Index starts with 1.


*'''pinName.outputPinAt''' (''idx'') <br> returns the idx'th actual pin of the schema pin named "pinName". Notice that this retrieves the pin; to write an output value to it, call ''value''(v) on the returned pin.
*''varPinName''.'''outputPinAt''' (''idx'') <br> returns the idx'th actual pin of the schema pin named "pinName". Notice that this retrieves the pin; to write an output value to it, call ''value''(v) on the returned pin.


*'''pinName.at''' (''idx'') <br> same as outputPinAt(''idx'')
*''varPinName''.'''at''' (''idx'') <br> same as outputPinAt(''idx'')


*'''pinName.outputPins''' () <br> returns a collection of all actual pins present in the step for the (variable) schema pin named "pinName".
*''varPinName''.'''outputPins''' () <br> returns a collection of all actual pins present in the step for the (variable) schema pin named "pinName".


&nbsp;
&nbsp;
Zeile 1.552: Zeile 1.795:
This is sometimes useful in elementary code, in order to instantiate a new value (given the pin) or to ask for details of the type.
This is sometimes useful in elementary code, in order to instantiate a new value (given the pin) or to ask for details of the type.


*'''name''' () <br>The name of the Datatype
*''aType''.'''name''' () <br>The name of the Datatype


*'''isAnyType''' () <br>true if the datatype is the builtin "Any" type
*''aType''.'''isAnyType''' () <br>true if the datatype is the builtin "Any" type


*'''isArrayType''' () <br>true if the datatype is an "Array" type (with any element type)
*''aType''.'''isArrayType''' () <br>true if the datatype is an "Array" type (with any element type)


*'''isCompoundType''' () <br>true if the datatype is a compound (structure) type
*''aType''.'''isCompoundType''' () <br>true if the datatype is a compound (structure) type


*'''isEnumType''' () <br>true if the datatype is an enumeration type
*''aType''.'''isEnumType''' () <br>true if the datatype is an enumeration type


*'''isPrimaryType''' () <br>true if the datatype is any builtin primary type
*''aType''.'''isPrimaryType''' () <br>true if the datatype is any builtin primary type


*'''isRangeType''' () <br>true if the datatype is a subrange of another type
*''aType''.'''isRangeType''' () <br>true if the datatype is a subrange of another type


*'''isTupleType''' () <br>true if the datatype is a tuple type
*''aType''.'''isTupleType''' () <br>true if the datatype is a tuple type


*'''isUnionType''' () <br>true if the datatype is a union type
*''aType''.'''isUnionType''' () <br>true if the datatype is a union type


*'''isStringPrimaryType''' () <br>true if the datatype is the builtin "String" primary type
*''aType''.'''isStringPrimaryType''' () <br>true if the datatype is the builtin "String" primary type


==== Enum Type Functions ====
==== Enum Type Functions ====


*'''values''' () <br>returns an array of enum-value names (Symbolic)
*''anEnumType''.'''values''' () <br>returns an array of enum-value names (symbolic)


*'''integerValueOf''' (''elementName'') <br>returns the associated integer value. (new in expecco 2.8)
*''anEnumType''.'''integerValueOf''' (''elementName'') <br>returns the associated integer value.


==== Compound Type Functions ====
==== Compound Type Functions ====


*'''namedFieldDescriptions''' () <br>returns an array of descriptions of the named fields (if any)
*''aCompoundType''.'''namedFieldDescriptions''' () <br>returns an array of descriptions of the named fields (if any)


*'''indexedFieldDescriptins''' () <br>returns a description of indexed fields (if any)
*''aCompoundType''.'''indexedFieldDescriptins''' () <br>returns a description of indexed fields (if any)


Each field description can be asked for the "fieldName()", "fieldType()" and "defaultValue()".
Each field description can be asked for the "fieldName()", "fieldType()" and "defaultValue()".
Zeile 1.588: Zeile 1.831:
==== Primary Type Functions ====
==== Primary Type Functions ====


*'''typeClass''' () <br>returns the underlying Smalltalk class, of which values are instances of.
*''aPrimaryType''.'''typeClass''' () <br>returns the underlying Smalltalk class, of which values are instances of.


==== Range Type Functions ====
==== Range Type Functions ====


*'''baseType''' () <br>returns the underlying base type. Typically this will be the Integer primary type.
*''aRangeType''.'''baseType''' () <br>returns the underlying base type. Typically this will be the Integer primary type.


*'''minValue''' () <br>returns the minimum value of the range
*''aRangeType''.'''minValue''' () <br>returns the minimum value of the range

*'''maxValue''' () <br>returns the maximum value of the range


*''aRangeType''.'''maxValue''' () <br>returns the maximum value of the range
&nbsp;
&nbsp;


Zeile 1.604: Zeile 1.846:
These include: BlockDescriptions, Types, TestPlans, TestCases, Steps, Connections, Pins, Annotations etc.
These include: BlockDescriptions, Types, TestPlans, TestCases, Steps, Connections, Pins, Annotations etc.


*'''versionId''' <br>A globally unique identifier (GUID or UUID) which is changed with every modification of the item
*''object''.'''versionId''' <br>A globally unique identifier (GUID or UUID) which is changed with every modification of the item


*'''functionalId''' <br>A globally unique identifier (GUID or UUID) which is assigned once-and-for-all to the item
*''object''.'''functionalId''' <br>A globally unique identifier (GUID or UUID) which is assigned once-and-for-all to the item


*'''id''' <br>alias for versionId().
*''object''.'''id''' <br>alias for versionId().


*'''tags''' <br>A collection of tags.
*''object''.'''tags''' <br>A collection of tags.


*'''isAnnotation'''<br>true, if it is an annotation (in a diagram)
*''object''.'''isAnnotation'''<br>true, if it is an annotation (in a diagram)


*'''isStep'''<br>true, if it is a step (in a diagram)
*''object''.'''isStep'''<br>true, if it is a step (in a diagram)


*'''isConnection'''<br>true, if it is a connection (in a diagram)
*''object''.'''isConnection'''<br>true, if it is a connection (in a diagram)


*'''isBlockDescription'''<br>true, if it is a block description (in the tree)
*''object''.'''isBlockDescription'''<br>true, if it is a block description (in the tree)


*'''isAttachment'''<br>true, if it is any attachment (in the tree)
*''object''.'''isAttachment'''<br>true, if it is any attachment (in the tree)


*'''isFileAttachment'''<br>true, if it is a file attachment (in the tree)
*''object''.'''isFileAttachment'''<br>true, if it is a file attachment (in the tree)


*'''taggedValueAt''' (''aKey'') <br>To retrieve a tagged value. These are usually preserved when objects are transported to/from other systems.
*''object''.'''taggedValueAt''' (''aKey'') <br>To retrieve a tagged value. These are usually preserved when objects are transported to/from other systems.


*'''taggedValueAt_put''' (''aKey'', ''anObject'') <br>to change a tagged value. Only a limited set of datatypes are supported: anObject must be a '''String''' or a '''Number'''.
*''object''.'''taggedValueAt_put''' (''aKey'', ''anObject'') <br>to change a tagged value. Only a limited set of datatypes are supported: anObject must be a '''String''' or a '''Number'''.


*'''propertyAt''' (''aKey'')<br>to retrieve an expecco property. These are expecco-internal properties. These are usually not recognized by other systems.
*''object''.'''propertyAt''' (''aKey'')<br>to retrieve an expecco property. These are expecco-internal properties. These are usually not recognized by other systems.

*'''propertyAt_put''' (''aKey'', ''anObject'')<br>to set an expecco property. These are usually not recognized by other systems. Only a limited set of datatypes are supported: anObject must be a '''String''' or a '''Number'''.


*''object''.'''propertyAt_put''' (''aKey'', ''anObject'')<br>to set an expecco property. These are usually not recognized by other systems. Only a limited set of datatypes are supported: anObject must be a '''String''' or a '''Number'''.
&nbsp;
&nbsp;


Zeile 1.657: Zeile 1.898:
Expecco's JavaScript actions are actually alternative syntactic forms of Smalltalk actions. When executed, they run inside the expecco executable and have access to any class or object inside expecco itself, or those which are passed in via input pins or environment variables.
Expecco's JavaScript actions are actually alternative syntactic forms of Smalltalk actions. When executed, they run inside the expecco executable and have access to any class or object inside expecco itself, or those which are passed in via input pins or environment variables.


On the other hand, Groovy actions are "''injected''" (eg. downloaded) into a Java VM and execute in that object space. They cannot directly access expecco classes or objects, but instead can access all public classes and objects within the system under test directly. By means of proxy objects (which are forwarding operation-calls), expecco can deal with remote objects almost transparently: these can be returned from a Groovy action to expecco, and later be used again as arguments to other Groovy actions; i.e. they can be passed around via output- and input pins, stored in variables and inspected.
On the other hand, Groovy actions are "''injected''" (eg. downloaded) into a Java VM and execute in that object space. They cannot directly access expecco classes or objects, but instead can access all public classes and objects within the JVM in which they run, which can be the SUT (System Under Test). By means of proxy objects (which are forwarding operation-calls), expecco can deal with remote objects almost transparently: these can be returned from a Groovy action to expecco, and later be used again as arguments to other Groovy actions; i.e. they can be passed around via output- and input pins, stored in variables and inspected.


=== What is the Difference between a Groovy Block and a Bridged Node Block? ===
=== What is the Difference between a Groovy Block and a Bridged Node Block? ===
Zeile 1.663: Zeile 1.904:
Groovy blocks execute inside a JVM (Java Virtual Machine) and can access any Java code (eg. classes inside a java application or inside a dynamically loaded jar-library).
Groovy blocks execute inside a JVM (Java Virtual Machine) and can access any Java code (eg. classes inside a java application or inside a dynamically loaded jar-library).


In contrast, Node actions are executed inside a Node interpreter (which is not a JVM).
In contrast, Node actions are executed inside a Node (aka "Node.js") interpreter (which is not a JVM).


There are also slight differences in the syntax: Groovy is more Java-like, whereas Node blocks use pure JavaScript.
There are also slight differences in the syntax: Groovy is more Java-like, whereas Node blocks use pure JavaScript.
Zeile 1.692: Zeile 1.933:
To use input pins, you have to create corresponding pins manually, and the pin names MUST be "java" / "groovy".
To use input pins, you have to create corresponding pins manually, and the pin names MUST be "java" / "groovy".
You cannot use these two reserved names as regular input pin names.
You cannot use these two reserved names as regular input pin names.

=== Choosing another JVM Version ===
By default, the Groovy interpreter runs in the default Java virtual machine. That is the JWM which would be executed when
entering "<CODE>java</CODE>" on the command line. To verify the version, open a command shell (or cmd.exe window) and enter
java -version

To specify another JVM, go to "''Extras''" &#8594; "''Settings''" &#8594; "''Plugins''" &#8594; "''Java Bridge''",
and enter the path to a JRE (Java Runtime Environment) folder into the "''Java Installation Path''" field.
You can also enter a JDK (Java Development Kit) folder name into that field.<br>See also [[Settings_JavaBridgeSettings]].


=== Additional JAR Class Libraries ===
=== Additional JAR Class Libraries ===
Zeile 2.085: Zeile 2.335:
==== Attention / Warning (Groovy) ====
==== Attention / Warning (Groovy) ====


Unless explicitly prevented by the "<code>mayStillDoExpeccoCalls()</code>" function ([[#Special_Functions |described below]]),
Unless explicitly prevented by the "<code>mayStillDoExpeccoCalls()</code>" function ([[#Special_Functions (Groovy) |described below]]),
all called expecco functions listed below will ONLY work as expected WHILE the Groovy action is still ACTIVE (i.e. while inside the Groovy "<code>execute()</code>" function).
all called expecco functions listed below will ONLY work as expected WHILE the Groovy action is still ACTIVE (i.e. while inside the Groovy "<code>execute()</code>" function).


Zeile 2.100: Zeile 2.350:


==== The Current Activity (Groovy) ====
==== The Current Activity (Groovy) ====
The current activity instance is accessed as "''this''" inside the Groovy action code (like in an elementary JavaScript block). For every executed action, a new activity object is instantiated. It is usually alive during the execution only (i.e. it is destroyed and its memory reused automatically, after the block's action has finished). In Groovy (as in JavaScript or Java), if no receiver is given for a function call, "''this''" is the implicit receiver. Thus the statements "<CODE>this.logInfo("hello")</CODE>" and "<CODE>logInfo("hello")</CODE>" are equivalent.
The current activity instance is accessed as "''this''" inside the Groovy action code (like in an elementary JavaScript block). For every executed action, a new activity object is instantiated. It is usually alive during the execution only (i.e. it is destroyed and its memory reused automatically, after the block's action has finished).

In Groovy (as in JavaScript or Java), if no receiver is given for a function call, "''this''" is the implicit receiver. Thus the statements "<CODE>this.logInfo("hello")</CODE>" and "<CODE>logInfo("hello")</CODE>" are equivalent.


==== Objects are passed by Reference ====
==== Objects are passed by Reference ====
Except for Strings and Numbers, objects are passed from Groovy to expecco by reference. These references can later be sent back to other Groovy actions transparently. Notice, that these references will keep the Groovy object (which is actually a Java object) alive until the reference object is garbage collected in expecco. Be aware that such references may have a very long life time, if kept in an activity log. Therefore, output pins which receive such references should either be marked as non-logging, or you should set the "''Log Strings of Bridge Objects''" flag in the "''Settings''" - "''Execution''" - "''Logging''" dialog.
Except for Strings and Numbers, objects are passed from Groovy to expecco by reference. These references can later be sent back to other Groovy actions transparently. Notice, that these references will keep the Groovy object (which is actually a Java object) alive until the reference object is garbage collected in expecco. Be aware that such references may have a very long life time, if kept in an activity log. Therefore, output pins which receive such references should either be marked as non-logging, or you should set the "''Log Strings of Bridge Objects''" flag in the [[Settings_LoggingSettings/en||"''Settings''" - "''Execution''" - "''Logging''" dialog]].


==== Reporting (Groovy) ====
==== Reporting (Groovy) ====
Zeile 2.109: Zeile 2.361:
*'''error''' () <br>Report a defect (in the test). Stops execution.
*'''error''' () <br>Report a defect (in the test). Stops execution.


*'''error''' (''infoString'') <br>Report a defect (in the test). Stops execution.
*'''error''' (''infoString'') <br>Report a defect (in the test). Stops execution. The infoString argument will be shown in the activity log.

*'''fail''' () <br>Report a failure (in the SUT). Stops execution.

*'''fail''' (''infoString'') <br>Report a failure (in the SUT). Stops execution.

*'''inconclusive''' ()<br>Report an inconclusive test. Stops execution.


*'''inconclusive''' (''infoString'') <br>Report an inconclusive test. Stops execution.
*'''fail''' ([''infoString'']) <br>Report a failure (in the SUT) with optional infoString. Stops execution.


*'''inconclusive''' ([''infoString'']) <br>Report an inconclusive test (with optional infoString). Stops execution.
*'''activitySuccess''' ()<br>Finishes the current activity with success.


*'''activitySuccess''' (''infoString'')<br>Finishes the current activity with success.
*'''activitySuccess''' ([''infoString''])<br>Finishes the current activity with success.


*'''pass''' ()<br>Finishes the current testCase with success.
*'''pass''' ([''infoString''])<br>Finishes the current testCase with success. The optional infoString argument will be shown in the activity log.


*'''pass''' (''infoString'')<br>Finishes the current testCase with success.
*'''testPass''' ([''infoString''])<br>Same as pass(); for compatibility with bridged languages where "pass" is a reserved keyword (i.e. python).
&nbsp;
&nbsp;


Zeile 2.142: Zeile 2.388:
*'''logInfo''' (''messageString'', detail) <br>Adds an info message to the activity log.
*'''logInfo''' (''messageString'', detail) <br>Adds an info message to the activity log.


*'''alert''' (''messageString'')<br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "Execution - Log -Settings" dialog (by default it is disabled).
*'''alert''' (''messageString'')<br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "[[Settings_LoggingSettings/en|Execution-Log-Settings]]" dialog (by default it is disabled).


*'''warn''' (''messageString'')<br>Same as ''alert()'' (for Smalltalk compatibility).
*'''warn''' (''messageString'')<br>Same as ''alert()'' (for Smalltalk protocol compatibility).
&nbsp;
&nbsp;


Zeile 2.176: Zeile 2.422:


*'''mayStillDoExpeccoCalls''' (''boolean'') <br>Ensures that the API-functions described here will still be callable (by Java callback functions from other threads) even after the step has finished execution. In other words, it prevents releasing the observer object which is responsible for the transfer of information between the two systems. Be aware that this object remains and will never be released, until the bridge connection is closed. I.e. there is a potential for a memory leak inside the Java VM here.
*'''mayStillDoExpeccoCalls''' (''boolean'') <br>Ensures that the API-functions described here will still be callable (by Java callback functions from other threads) even after the step has finished execution. In other words, it prevents releasing the observer object which is responsible for the transfer of information between the two systems. Be aware that this object remains and will never be released, until the bridge connection is closed. I.e. there is a potential for a memory leak inside the Java VM here.
:Even with mayStillDoExpeccoCalls(true), only functions which are not depending on the step's activity may be called after the activity has finished. This includes event, logging and transcript/stderr functions, but no pin access or verdict reporting functions.
&nbsp;
&nbsp;


Zeile 2.226: Zeile 2.473:


Node.js is an open source interpreter for JavaScript. For details on the language and libraries, visit [https://www.w3schools.com/nodejs "NodeJS Tutorial"] .
Node.js is an open source interpreter for JavaScript. For details on the language and libraries, visit [https://www.w3schools.com/nodejs "NodeJS Tutorial"] .
You have to download and install "node" separately - the interpreter is not part of the expecco installation procedure (see [https://nodejs.org/en/download/ https://nodejs.org/en/download] ).
<br>You have to download and install "node" separately - the interpreter is not part of the expecco installation procedure (see [[Installing_additional_Frameworks/en | "Installing additional Frameworks"]]) and [https://nodejs.org/en/download/ https://nodejs.org/en/download] ).


Node.js code execution is supported in expecco via 2 different mechanisms:
Node.js code execution in expecco is supported via 2 different mechanisms:
* Node.js Bridged Action Blocks - these look at the outside like regular action blocks with typed input and output pins, which are available inside the action's code via "<code>.value()</code>" APIs. The bridge-partner in which the code is to be executed is started once and will remain an active process until terminated. Any state (data) created inside the bridge remains alive while the partner is running and the connection is alive.
* Node.js Bridged Action Blocks - these look at the outside like regular action blocks with typed input and output pins, which are available inside the action's code via "<code>.value()</code>" APIs. The bridge-partner in which the code is to be executed is started once and will remain an active process until terminated. Any state (data) created inside the bridge remains alive while the partner is running and the connection is alive.
* [[ElementaryBlock_Element/en#Node.js-Script_Blocks | Node.js-Script Action Blocks]] - these look like shell-script blocks, in that the standard input and output is used to interact with the script. For every individual script action, the script interpreter is started anew. No state is alive between and across actions (the script-interpreter is terminated after each action)
* [[ElementaryBlock_Element/en#Node.js-Script_Blocks | Node.js-Script Action Blocks]] - these look like shell-script blocks, in that the standard input and output is used to interact with the script. For every individual script action, the script interpreter is started anew. No state is alive between and across actions (the script-interpreter is terminated after each action)
Zeile 2.237: Zeile 2.484:


=== NodeJS Datatype Limitations ===
=== NodeJS Datatype Limitations ===
Because expecco objects and Node objects live in different processes,
no direct object access is possible between the two partners.
When objects are passed via an input pin from expecco to Node, or via an output pin from Node to expecco, the object is either converted to JSON on the sender side and reconstructed on the receiver side, or it is passed by reference, in that the sender transmits a "handle" (which is some unique number) to the partner.

Since the class system / object model is different, not all objects are exactly representable on the other side, and some information may be lost. If required, additional information must be explicitly passed (eg. by generating a string representation ''manually'').


==== Limited NodeJS Object Conversion ====
==== Limited NodeJS Object Conversion ====
Zeile 2.300: Zeile 2.552:
==== Limited Error Reporting ====
==== Limited Error Reporting ====
Notice, that arithmetic errors are usually not reported by Node.
Notice, that arithmetic errors are usually not reported by Node.
Division by zero or taking the logarithm of a negative number deliver a Nan ("Not a Number") instead of raising an error. Thus you have to explicitly check the result from such computations in your code.
Dividing by zero or taking the logarithm of a negative number will deliver a Nan ("Not a Number") instead of raising an error. Thus you have to explicitly check the result from such computations in your code (this is different in expecco's builtin JavaScript actions, where an error is reported).


=== Debugging NodeJS Actions ===
=== Debugging NodeJS Actions ===
Zeile 2.322: Zeile 2.574:
are sometimes off-by-one; this means, that the line reported by the node-debugger is wrong and
are sometimes off-by-one; this means, that the line reported by the node-debugger is wrong and
therefore also shown wrong in the debugger. This is a problem of the particular node version,
therefore also shown wrong in the debugger. This is a problem of the particular node version,
and may or may not appear in your concrete installation.
and may or may not appear in your concrete installation (i.e. node interpreter version).


Sorry, but we cannot currently provide a workaround for this problem, as we have not yet figured out,
Sorry, but we cannot currently provide a workaround for this problem, as we have not yet figured out,
what constellation of source-code/statement/situation leads to this problem.
what constellation of source-code/statement/situation leads to this problem.
(this problem was also reported by other node users in the internet forums)
(this problem was also reported by other node users in the internet forums).


=== NodeJS Code API ===
=== NodeJS Code API ===


NodeJS code supports a '''subset''' of the above activity functions, which is intended provide an interface similar to the JavaScript and Smalltalk elementary code API. Of course, technically for every API function below, the code executing in the remote Node interpreter has to make a remote procedure call back to expecco. On the Node side, this is done very similar to the above described remote message mechanism, when messages are sent from Smalltalk to a bridge.
NodeJS code supports a '''subset''' of the above activity functions, which is intended provide an interface similar to the JavaScript and Smalltalk elementary code API. Of course, technically for every API function below, the code executing in the remote Node interpreter has to make a remote procedure call back to expecco. On the Node side, this is done very similar to the above described remote message mechanism, when messages are sent from expecco to a bridge.
Also notice, that due to the single threaded nature of Node programs, all functions which ask for a value from expecco (eg. "<code>environmentAt</code>") will take a callback argument, which is called when the return value arrives.
Also notice, that due to the single threaded nature of Node programs, all functions which ask for a value from expecco (eg. "<code>environmentAt</code>") will take a callback argument, which is called when the return value arrives.

Be remonded that the objects described below are proxy objects inside node, which will forward messages back to expecco when functions listed below are called on them. Due to the roundtrip times, these will be relatively slow (in the order of milliseconds).


==== Variables seen by the Executed Node Function ====
==== Variables seen by the Executed Node Function ====
Zeile 2.346: Zeile 2.600:
==== Reporting (NodeJS) ====
==== Reporting (NodeJS) ====


*'''error''' (''infoString'') <br>Report a defect (in the test). Stops execution.
*'''error''' (''infoString'',...) <br>Report a defect (in the test). Stops execution. <br>If the infoString argument contains "%i" placeholders ("%1","%2",...), these will be expanded by the remaining arguments.<br>(eg. <code>logFail("foo:%1 bar:%2", 123, 234.0)</code> will generate the failure string "foo:123 bar:234.0").


*'''fail''' (''infoString'') <br>Report a failure (in the SUT). Stops execution.
*'''fail''' (''infoString'',...) <br>Report a failure (in the SUT). Stops execution.


*'''inconclusive''' (''infoString'') <br>Report an inconclusive test. Stops execution.
*'''inconclusive''' (''infoString'',...) <br>Report an inconclusive test. Stops execution.


*'''activitySuccess''' (''infoString'')<br>Finishes the current activity with success (same as "success()").
*'''activitySuccess''' (''infoString'',...)<br>Finishes the current activity with success (same as "success()").


*'''success''' (''infoString'',...)<br>Finishes the current activity with success (same as "activitySuccess()"). Please use "activitySuccess" ("success" is not supported in all bridges due to name conflictswith other libraries. For readability, it is better to use the same name in all bridges: "activitySuccess", which is supported on all bridges)
*'''pass''' (''infoString'')<br>Finishes the current testCase with success.

*'''pass''' (''infoString'',...)<br>Finishes the current testCase with success.

*'''testPass''' (''infoString'',...)<br>Same as pass(); for compatibility with bridged languages where "pass" is a reserved keyword (i.e. python).


==== Logging (NodeJS) ====
==== Logging (NodeJS) ====


*'''logFail''' (''messageString'') <br>Adds a fail message to the activity log.
*'''logFail''' (''messageString''...) <br>Adds a fail message to the activity log.<br>Execution continues, but the test will be marked as failed.<br>If the messageString argument contains "%i" placeholders ("%1","%2",...), these will be expanded by the remaining arguments.<br>(eg. <code>logFail("foo:%1 bar:%2", 123, 234.0)</code> will generate the failure string "foo:123 bar:234.0").


*'''logError''' (''messageString'') <br>Adds a error message to the activity log.
*'''logError''' (''messageString''...) <br>Adds a error message to the activity log.<br>Execution continues, but the test will be marked as erroneous.


*'''logWarning''' (''messageString'') <br>Adds a warning to the activity log.
*'''logWarning''' (''messageString''...) <br>Adds a warning to the activity log.<br>Execution continues.


*'''logInfo''' (''messageString'') <br>Adds an info message to the activity log.
*'''logInfo''' (''messageString''...) <br>Adds an info message to the activity log.<br>Execution continues


*'''alert''' (''messageString'')<br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "''Execution''" - "''Log -Settings''" dialog (by default it is disabled).
*'''alert''' (''messageString'')<br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in "''Extras''" &#8594; "''Settings''" &#8594; "''Execution''" &#8594; "''Log -Settings''" (by default it is enabled).<br>Notice that the code in the bridge is suspended until the box is confirmed.


*'''warn''' (''messageString'')<br>Same as ''alert()'' (for Smalltalk compatibility).
*'''warn''' (''messageString''...)<br>Same as ''alert()'' (for Smalltalk protocol compatibility).


==== Reflection, Information, Queries and Accessing (NodeJS) ====
==== Reflection, Information, Queries and Accessing (NodeJS) ====
Zeile 2.386: Zeile 2.644:
*'''evalJS''' (''javascriptCodeString'') <br>Evaluate a piece of JavaScript code inside expecco.
*'''evalJS''' (''javascriptCodeString'') <br>Evaluate a piece of JavaScript code inside expecco.
-->
-->
*'''call'''(&lt;actionName&gt;, &lt;arg1&gt;, &lt;arg2&gt;, ... &lt;argN&gt;, function(rslt...) {...});<br>Executes any other expecco action (inside expecco - not inside the node interpreter), and finally calls the callBack function, when the action has finished.<br>Notice that this is an asynchronous callback; you should not perform any further actions after the call, but instead continue inside the callback (and signal final completion via "<code>success() / error()</code>" call from there).<br>The callback may expect multiple rslt-arguments, to get the output values of more than one pin. However, it is currently not possible to get the values of pins which are written multiple times (i.e. an error will be reported, if that is the case)<br>See example below.
*'''call'''(&lt;actionName&gt;, &lt;arg1&gt;, &lt;arg2&gt;, ... &lt;argN&gt;, function(rslt...) {...});<br>Executes any other expecco action (inside expecco - not inside the node interpreter), and finally calls the callBack function, when the action has finished.<br>Notice that this is an asynchronous callback; you should not perform any further actions after the call, but instead continue inside the callback (and signal final completion via "<code>activitySuccess() / error()</code>" call from there).<br>The callback may expect multiple rslt-arguments, to get the output values of more than one pin. However, it is currently not possible to get the values of pins which are written multiple times (i.e. an error will be reported, if that is the case)<br>See example below.

==== Event Sending (NodeJS) ====
==== Event Sending (NodeJS) ====
* '''pushEvent''' (''payloadData'') <br> pushes an event onto the global event handler's event queue. The payload is packed into an Event object with eventType "#default. Raises an error, if no global event handler process is running.
* '''pushEvent''' (''payloadData'') <br> pushes an event onto the global event handler's event queue. The payload is packed into an Event object with eventType "#default. Raises an error, if no global event handler process is running.
Zeile 2.394: Zeile 2.653:
==== Environment Access (NodeJS) ====
==== Environment Access (NodeJS) ====


*'''environmentAt'''(&lt;varName&gt;, function(err, rslt) {...});<br>Fetches a value from the expecco environment which is in scope of the current activity, and finally calls the callBack function, when the action has finished.<br>Notice that this is an asynchronous callback; you should not perform any further actions after the call, but instead continue inside the callback (and signal final completion via "<code>success() / error()</code>" call from there).<br>You can only read simple objects (numbers and strings) from node actions.
*'''environmentAt'''(''varName'', function(err, rslt) {...});<br>Fetches a value from the expecco environment which is in scope of the current activity, and finally calls the callBack function, when the value has been retrieved (passing the retrieved value as argument).<br>Notice that this is an asynchronous callback; you should not perform any further actions after the call, but instead continue inside the callback (and signal final completion via "<code>activitySuccess() / error()</code>" call from there).<br>You can only read simple objects (numbers, booleans and strings) from node actions.


*'''environmentAtPut'''(&lt;varName&gt;, &lt;newValue&gt;, function(err) {...});<br>Writes a value into the expecco environment which is in scope of the current activity, and finally calls the callBack function, when the action has finished.<br>Notice that this is an asynchronous callback; you should not perform any further actions after the call, but instead continue inside the callback (and signal final completion via "<code>success() / error()</code>" call from there).<br>You can only write simple objects (numbers and strings) from node actions.
*'''environmentAtPut'''(''varName'', ''newValue'', function(err) {...});<br>Writes a value into the expecco environment which is in scope of the current activity, and finally calls the callBack function, when the value has been stored.<br>Notice that this is an asynchronous callback; you should not perform any further actions after the call, but instead continue inside the callback (and signal final completion via "<code>activitySuccess() / error()</code>" call from there).<br>The ''varName'' argument must be a string and ''newValue'' a simple object (number, boolean or string) from node actions.


==== Special Functions (NodeJS) ====
==== Special Functions (NodeJS) ====
Zeile 2.403: Zeile 2.662:
*'''releaseRef''' (&lt;nodeObjectReference&gt;) <br>removes the reference from the registery inside the node interpreter.<br>See example below.
*'''releaseRef''' (&lt;nodeObjectReference&gt;) <br>removes the reference from the registery inside the node interpreter.<br>See example below.


*'''wait''' () <br>Tells expecco to wait until one of the "<code>success/error/fail/inconclusive</code>" functions is called.<br>Use this if the action's execution is not finished when the execute() function returns, but instead will explicitly notify the finish from within a callback. This is also to be used if a pin value has to be written later by a callback function.<br>See example and description of callbacks/async functions below.
*'''wait''' () <br>Tells expecco to wait until one of the "<code>activitySuccess/error/fail/inconclusive</code>" functions is called.<br>Use this if the action's execution is not finished when the execute() function returns, but instead will explicitly notify the finish from within a callback. This is also to be used if a pin value has to be written later by a callback function.<br>See example and description of callbacks/async functions below.
*'''waitForPin''' (''pinName'')<br>Tells expecco to wait until the given output pin receives a value. This is needed for async/await (promise resolved) pin values.<br>See example and description of callbacks/async functions below.
*'''waitForPin''' (''pinName'')<br>Tells expecco to wait until the given output pin receives a value. This is needed for async/await (promise resolved) pin values.<br>See example and description of callbacks/async functions below.
<!--
<!--
Zeile 2.410: Zeile 2.669:


==== Pin Functions (NodeJS) ====
==== Pin Functions (NodeJS) ====
Currently, Node.js blocks do not support a variable number of input or output pins.


Attention:
Attention:
<br>Javascript uses many more reserved keywords for syntax than Smalltalk. These keywords cannot be used as pin names, and you will get a syntax error ("<foo> token not expected") if you try. Be careful to not name your pins as any of: "<code>in</code>", "<code>return</code>", "<code>class</code>", "<code>private</code>", "<code>public</code>", etc. As a proven best practice, add a "''Pin''" suffix to your pin names (i.e. name it "''inPin''", instead of "''in''").
<br>Javascript uses many more reserved keywords for syntax than Smalltalk. These keywords cannot be used as pin names, and you will get a syntax error ("<foo> token not expected") if you try. Be careful to not name your pins as any of: "<code>in</code>", "<code>return</code>", "<code>class</code>", "<code>private</code>", "<code>public</code>", etc.<br>As a proven best practice, add a "''Pin''" suffix to your pin names (i.e. name it "''inPin''", instead of "''in''").


===== Input Pins (NodeJS) =====
===== Input Pins (NodeJS) =====


Currently, NodeJS blocks do not support a variable number of input or output pins.
*'''hasValue''' () <br>Returns true if the pin has received a value


*'''isConnected''' () <br>Returns true if the pin is connected
*''pinName''.'''hasValue''' () <br>Returns true if the pin has received a value


*'''value''' () <br>Returns the value at the pin (the datum). Raises an error if the pin did not receive any value.
*''pinName''.'''isConnected''' () <br>Returns true if the pin is connected


*'''refValue''' () <br>Returns the object referenced by the value at the pin. The pin must have received a reference. Raises an error if the pin did not receive any value. Use this to pass back the original reference object to expecco. The input pin must have received a reference to a Node object as generated previously by "<code>makeRef</code>". Read below about reference passing vs. value passing.
*''pinName''.'''value''' () <br>Returns the value at the pin (the datum). Raises an error if the pin did not receive any value.

*''pinName''.'''refValue''' () <br>Returns the object referenced by the value at the pin. The pin must have received a reference. Raises an error if the pin did not receive any value. Use this to pass back the original reference object to expecco. The input pin must have received a reference to a Node object as generated previously by "<code>makeRef</code>". Read below about reference passing vs. value passing.
<!--
<!--
*'''valueIfAbsent''' (''alternativeValue'') <br>Returns the value of a pin or the alternativeValue if the pin did not receive any value.
*''pinName''.'''valueIfAbsent''' (''alternativeValue'') <br>Returns the value of a pin or the alternativeValue if the pin did not receive any value.
-->
-->
*'''valueIfPresent''' () <br>Returns the pin's datum if it has one, null otherwise. Similar to "<code>value()</code>", but avoids the exception.
*''pinName''.'''valueIfPresent''' () <br>Returns the pin's datum if it has one, null otherwise. Similar to "<code>value()</code>", but avoids the exception.
*'''valueIfAbsent''' (''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>value()</code>", but avoids the exception.
*''pinName''.'''valueIfAbsent''' (''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>value()</code>", but avoids the exception.


===== Output Pins (NodeJS) =====
===== Output Pins (NodeJS) =====


Currently, NodeJS blocks do not support a variable number of input or output pins.
<!--*'''isBuffered''' () <br>True if the pin is buffered

<!--*''pinName''.'''isBuffered''' () <br>True if the pin is buffered
-->
-->
<!-- *'''isConnected'''() <br>Returns true if the pin is connected
*''pinName''.'''isConnected'''() <br>Returns true if the pin is connected (this can be used to prevent writing output values and thus speed up the execution, especially if big arrays of data values are generated)

-->
*'''value''' (''data'') <br>Writes the value.
*''pinName''.'''value''' (''data'') <br>Writes the value.


==== Transcript, Stderr and Stdout (NodeJS) ====
==== Transcript, Stderr and Stdout (NodeJS) ====
Zeile 2.463: Zeile 2.725:


The default mechanism is "''pass by value''". To pass a reference, use "<code>makeRef(obj)</code>" and pass the generated reference to an output pin.
The default mechanism is "''pass by value''". To pass a reference, use "<code>makeRef(obj)</code>" and pass the generated reference to an output pin.
==== Example ====
==== Example (NodeJS) ====
The following code snippet sends an object's reference to an output pin:
The following code snippet sends an object's reference to an output pin:


Zeile 2.471: Zeile 2.733:
}
}


the returned handle can later be sent via an input pin to another Node action, and use transparently there:
the returned handle can later be sent via an input pin to another Node action, and used transparently there:


function execute() {
function execute() {
Zeile 2.507: Zeile 2.769:
=== Asynchronous and Callback Functions ===
=== Asynchronous and Callback Functions ===
A common pattern in node is "''continuation passing style''" control flow. That means, that many functions expect a function as callback argument, which is called later whenever the requested operation has finished. This is used especially with I/O and protocol related operations (such as socket connect, http requests, client connect setup etc.).
A common pattern in node is "''continuation passing style''" control flow. That means, that many functions expect a function as callback argument, which is called later whenever the requested operation has finished. This is used especially with I/O and protocol related operations (such as socket connect, http requests, client connect setup etc.).
A similar situation arises with async function, which somehow get a promise as value, and the promise is resolved via an await.
A similar situation arises with async functions, which somehow get a promise as value, and the promise is resolved via an await.


If your "<code>execute</code>" function calls any of those or is an async function which awaits on a promise, it has to tell expecco that the node-action is effectively still active when the execute function returns and that expecco should wait for an explicit finished-notification. This is done by either calling either "<code>wait()</code>" or "<code>waitForPin()</code>" somewhere within the execute function.
If your "<code>execute</code>" function calls any of those or is an async function which awaits on a promise, it has to tell expecco that the node-action is effectively still active when the execute function returns and that expecco should wait for an explicit finished-notification. This is done by either calling either "<code>wait()</code>" or "<code>waitForPin()</code>" somewhere within the execute function.
Zeile 2.513: Zeile 2.775:
Then expecco will continue to wait for the action to be finished until the node code calls one of the "<code>fail()</code>", "<code>error()</code>", "<code>success()</code>", "<code>pass()</code>", "<code>inconclusive()</code>" functions (listed above) or - in case of a previous <code>waitForPin()</code>, until that pin receives a value.
Then expecco will continue to wait for the action to be finished until the node code calls one of the "<code>fail()</code>", "<code>error()</code>", "<code>success()</code>", "<code>pass()</code>", "<code>inconclusive()</code>" functions (listed above) or - in case of a previous <code>waitForPin()</code>, until that pin receives a value.


Example 1:<br>the following code fragment represents a typical callback situation: the "myProtocolConnect()" call gets a callback, which will be called later (asynchronously), when a connection is established. However, "myProtocolConnect()" will return immediately. Without the "wait()" at the end, expecco would assume that the action has finished and would proceed with other actions (possibly without any output being written to the output pin (actually, it would later detect a value being written by an already finished action, and write a warning message to the console).
Example 1:<br>the following code fragment represents a typical callback situation: the "myProtocolConnect()" call gets a callback, which will be called later (asynchronously), when a connection is established. However, "myProtocolConnect()" will return immediately. Without the "wait()" at the end, expecco would assume that the action has finished and would proceed with other actions (possibly without any output being written to the output pin (actually, it would later detect a value being written by an already finished action, and write a warning message to the console and the log).


function execute() {
function execute() {
Zeile 2.554: Zeile 2.816:
sessionOut.value(s);
sessionOut.value(s);
}
}

Example 3:<br>The following example demonstrates what happens if the "wait()" call is missing. The code below installs a callback which writes to an output pin 1 second AFTER the finished action:

function execute() {
output.value("Value1");
setTimeout(function(err) {
output.value("Value2");
}, 1000);
}
the above code will lead to an error.

The correct version is:

function execute() {
output.value("Value1");
setTimeout(function(err) {
output.value("Value2");
success();
}, 1000);
wait();
}

Up to version 23.2, this situation was not always detected and could have lead to an output value being written to a same-named output pin of a followup action (iff another bridged action was triggered which was of the same type, that second action's output was sometimes written, depending on timing constraints).<br>With version 24.1, this is detected and an error message is sent to the Transcript (it cannot be reported as an error in the activity log, because the first action which is responsible has already finished, whereas the second action (which is innocent) cannot be blamed for that).


=== Calling other Expecco Actions ===
=== Calling other Expecco Actions ===
Zeile 2.603: Zeile 2.889:


=== Reading expecco Environment Variables ===
=== Reading expecco Environment Variables ===
Expecco variables can be fetched via the "<code>environmentAt</code>" function.
Expecco variables can be fetched via the "<code>environmentAt</code>" function. Notice, that this takes a second callback function as argument, which is called when the value is (later) received. The execution of the action proceeds with this continuation, and "<code>environmentAt</code>" does not return.

<br>For example:
<br>For example:
function execute() {
function execute() {
Zeile 2.630: Zeile 2.917:


===== Programmatic Start =====
===== Programmatic Start =====

<code><pre>
nodeBridge := Workflow::ExecutionBridgedNodeJSCodeDescription
nodeBridge := Expecco
newBridgeConnectionForHost:hostName port:portNr
newNodeJSBridgeConnectionForHost:hostName port:portNr
startBridge:true in:aDirectoryOrNil.
in:aDirectoryOrNil.
</pre></code>


===== Using an Action from the Standard Library =====
===== Using an Action from the Standard Library =====
Zeile 2.732: Zeile 3.018:
== Bridged Python Elementary Blocks ==
== Bridged Python Elementary Blocks ==


Code written as a Python elementary block is not executed directly by expecco. Instead, the code is forwarded to a python interpreter. This may be a local python process, whose sole purpose is to provide additional utility functions or which provides an interface to the actual system under test (SUT), or it may be on a remote system.
You have to download and install <code>python</code> or <code>python3</code> or <code>jython</code> separately - the interpreter is not part of the expecco installation procedure. See [https://www.python.org/downloads https://www.python.org/downloads] for python,
[http://jython.org/downloads.html http://jython.org/downloads.html] for jython.


==== Python Versions ====
Similar to Node, Python code execution is supported in expecco via 2 different mechanisms:
Python is available in 2 dialects (2.x vs. 3.x) and different implementations (cPython, Jython, IronPython):
* Python Bridged Action Blocks - these look at the outside like regular action blocks with typed input and output pins, which are available inside the action's code via "<code>.value()</code>" APIs. Objects can be interchanged between expecco and the python process either by value or by reference. The bridge-partner in which the code is to be executed is started once and will remain an active process until terminated. Any state (data) created inside the bridge remains alive while the partner is running and the connection is alive.
* use '''jython''', if you have to interface/embed java classes or jars;
* Python-Script Action Blocks - these look like shell-script blocks, in that the standard input and output is used to interact with the script. For every individual script action, the script interpreter is started anew. No state is alive between and across actions (the script-interpreter is terminated after each action), and no objects can be exchanged (except by means of reading encoded objects via stdin/stdout/stderr).
* use '''ironPython''', if you have to interface with .NET assemblies;
* use '''python3''' to interface to modern frameworks such as Tesseract (optical character recognition), computer vision, Tensorflow (machine learning) etc.
* use '''python2''' for some older frameworks, which have not yet been ported to the newer python3 (notice, that support for python2 is going to cease soon)
Of course, if you need to interface to a Python module which is specifically written for a version, that specific version is obligatory.


You have to download and install Python, Python3, Jython and/or IronPython separately; these interpreters are not part of the expecco installation procedure.
The following describes the first kind of blocks (bridged). Script blocks are described elsewhere, in the [[ElementaryBlock_Element/en#Python-Script_Blocks | Python-Script Blocks]] chapter.
<br>See ([[Installing_additional_Frameworks/en | "Installing Additional Frameworks"]]) and download Python from [https://www.python.org/downloads https://www.python.org/downloads],
[https://jython.org/downloads.html http://jython.org/downloads.html] for Jython, and [ https://ironpython.net/download/] for IronPython.


Additional frameworks may be needed as prerequisite (i.e. Java for Jython, Mono for IronPython on non-Windows machines).
Code written as a Python elementary block is not executed directly by expecco. Instead, the code is forwarded to a python interpreter. This may be a local python process, whose sole purpose is to provide additional utility functions or which provides an interface to the actual system under test (SUT), or it may be on a remote system.

==== Bridged vs. Scripted Actions====
Similar to Node, Python code execution is supported via 2 different mechanisms:
* '''Bridged Python''' action blocks - these look at the outside like regular action blocks with typed input and output pins, which are available inside the action's code via "<code>.value()</code>" APIs. Objects can be interchanged between expecco and the python process either by value or by reference. The bridge-partner in which the code is to be executed is started once and will remain an active process until terminated. Any state (data) created inside the bridge remains alive while the partner is running and the connection is alive.
* '''Python-Script''' action blocks - these look like shell-script blocks, in that the standard input and output is used to interact with the script. For every individual script action, the script interpreter is started anew. No state is alive between and across actions (the script-interpreter is terminated after each action), and no objects can be exchanged (except for very simple objects by means of reading encoded objects via stdin/stdout/stderr).

The following describes the first kind of blocks (bridged). Script blocks are described elsewhere, in the [[ElementaryBlock_Element/en#Python-Script_Blocks | Python-Script Blocks]] chapter.


=== Debugging Python Actions ===
=== Debugging Python Actions ===
Debug support for Python actions is still being developped and more features are added with newer versions. However, there are probably better IDEs available and it might be a good idea to develop and debug Python code in your preferred IDE, and only add interface code to already debugged modules as expecco actions (this is certainly a matter of your personal preferences; in the past, interfaces to many modules have been implemented using only expecco's debug facilities).
Debug support for Python actions is being developped and planned to be deployed with the 19.2 release


In order to support debugging of python actions, you have to install the "''ptvsd''" package with:
In order to support debugging of Python actions, you have to install either the "''debugpy''" or the "''ptvsd''" package (*) with:
pip install ptvsd
pip install debugpy
or:
or:
pip3 install ptvsd
pip3 install debugpy
or:
python3 -m pip install debugpy

*) ptvsd is no longer maintained and has been replaced by the debugpy package. Ptvsd might be obsolete by the time of reading this.

For more information on the ptvsd package, see [https://pypi.org/project/ptvsd https://pypi.org/project/ptvsd] and [https://github.com/Microsoft/ptvsd https://github.com/Microsoft/ptvsd].
For more information on the ptvsd package, see [https://pypi.org/project/ptvsd https://pypi.org/project/ptvsd] and [https://github.com/Microsoft/ptvsd https://github.com/Microsoft/ptvsd].
<br>For debugpy, refer to [https://pypi.org/project/debugpy https://pypi.org/project/debugpy].
Notice that the debug features are currently not supported by python2 or jython.
Notice that the debug features are currently not supported by python2 or jython.
Zeile 2.757: Zeile 3.062:


==== Limited Python Object Conversion ====
==== Limited Python Object Conversion ====
Some limited form of object conversion is automatically performed when passing expecco's Smalltalk objects to Python, and back when returning values from Python. This conversion especially affects values passed to/from pins of a Python action.
Some limited form of object conversion is automatically performed when passing expecco objects to Python, and back when returning values from Python. This conversion especially affects values passed to/from pins of a Python action.


The following table summarizes the conversion process:
The following table summarizes the conversion process:
Zeile 2.789: Zeile 3.094:
| Array of any above
| Array of any above
| Array of any above
| Array of any above
|-
| ByteArray
| Array
| Array
|-
|-
| -
| -
Zeile 2.813: Zeile 3.122:
And only a subset of Smalltalk objects (those which can be represented as JSON string) can be passed to/from Python as described above.
And only a subset of Smalltalk objects (those which can be represented as JSON string) can be passed to/from Python as described above.


In general: if more complex objects need to be interchanged, this must be either done by converting them to an array (of objects), possibly an array of arrays.
In general: if more complex objects need to be interchanged, this must be either done by converting them to an array (of objects), an array of arrays or a dictionary.
Or, alternatively to some ASCII representation (XML or JSON, for a more lightweight approach) and convert this back and forth.
Or, alternatively to some ASCII representation (XML or JSON, for a more lightweight approach) and convert this back and forth.


It is also possible to pass Python-object-references from the Python interpreter to expecco, and pass it back to the Python interpreter later (usually in another action).
It is also possible to pass an object-reference from the Python interpreter to expecco, and pass it back to the Python interpreter later (usually in another action).
This is used to get connection, protocol or device handles from Python, and pass them to other (transmission or control) actions later.
This is used to get connection, protocol or device handles from Python, and pass them to other (transmission or control) actions later.
<!--
<!--
Zeile 2.833: Zeile 3.142:
=== Bridged Python Code API ===
=== Bridged Python Code API ===


WARNING: Bridged Python Elementary Actions are currently being developed - their behavior might not work as described (yet)
WARNING: Bridged Python Elementary Actions are still being developed - their behavior might be subject to changes (aka the protocol will be extended).
Python code may consist of function- and/or class definitions. Among the functions, there should be one named "<CODE>execute()</CODE>"; typically, that is the last defined function, which calls into other python code. If no "execute"-function is defined, expecco tries to call "<code>main()</CODE>" as fallback. This special behavior was added to make it easier to reuse existing script code.

Bridged Python code can use activity functions similar to the above Groovy or Node actions.
Bridged Python code can use activity functions similar to the above Groovy or Node actions.
The API is intended to provide an interface similar to the JavaScript and Smalltalk elementary code API. Of course, technically for every API function below, the code executing in the remote Python interpreter has to make a remote procedure call back to expecco. On the Python side, this is done very similar to the above described remote message mechanism, when messages are sent from Smalltalk to a bridge.
The API is intended to provide an interface similar to the JavaScript and Smalltalk elementary code API. Of course, technically for every API function below, the code executing in the remote Python interpreter has to make a remote procedure call back to expecco. On the Python side, this is done very similar to the above described remote message mechanism, when messages are sent from Smalltalk to a bridge.
Zeile 2.845: Zeile 3.156:
*<code> Stdout </code> <br>supports a few functions to display messages on expecco's stdout stream (see below)
*<code> Stdout </code> <br>supports a few functions to display messages on expecco's stdout stream (see below)
*<code> Stderr </code> <br>supports a few functions to display messages on expecco's stderr stream (see below)
*<code> Stderr </code> <br>supports a few functions to display messages on expecco's stderr stream (see below)
*<code> Logger </code> <br>supports <code>info()/warn()/error()/fatal()</code>; these are forwarded to the Smalltalk Logger object (see Logger)
*<code> Logger </code> <br>supports <code>info(), warn(), error() and fatal()</code>; these are forwarded to the Smalltalk Logger object (see Logger)
*<code> Dialog </code> <br>supports <code>confirm(), warn(), information()</code>; these are forwarded to the Smalltalk Dialog object (see Dialog)
<!--
<!--
*<code>__bridge__</code> <br>the bridge object which handles the communication with expecco. The instance slot named "<code>asynchronous</code>" is of special interest if the called function uses asynchronous callbacks (see below)
*<code>__bridge__</code> <br>the bridge object which handles the communication with expecco. The instance slot named "<code>asynchronous</code>" is of special interest if the called function uses asynchronous callbacks (see below)
Zeile 2.852: Zeile 3.164:
==== Reporting (Python) ====
==== Reporting (Python) ====


*'''error''' (''infoString'') <br>Report a defect (in the test). Stops execution.
*'''error''' (''infoString'') <br>Report a defect (in the test). Finishes the current activity with an ''error'' verdict.


*'''fail''' (''infoString'') <br>Report a failure (in the SUT). Stops execution.
*'''fail''' (''infoString'') <br>Report a failure (in the SUT). Finishes the current activity.


*'''inconclusive''' (''infoString'') <br>Report an inconclusive test. Stops execution.
*'''inconclusive''' (''infoString'') <br>Report an inconclusive test. Finishes the current activity.


*'''activitySuccess''' (''infoString'')<br>Finishes the current activity with success (same as "success()").
*'''activitySuccess''' (''infoString'')<br>Finishes the current activity with success (same as "success()").
Zeile 2.874: Zeile 3.186:
*'''alert''' (''messageString'')<br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "''Execution''" - "''Log -Settings''" dialog (by default it is disabled).
*'''alert''' (''messageString'')<br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "''Execution''" - "''Log -Settings''" dialog (by default it is disabled).


*'''warn''' (''messageString'')<br>Same as ''alert()'' (for Smalltalk compatibility).
*'''warn''' (''messageString'')<br>Same as ''alert()'' (for Smalltalk protocol compatibility).


==== Reflection, Information, Queries and Accessing (Python) ====
==== Reflection, Information, Queries and Accessing (Python) ====
Zeile 2.891: Zeile 3.203:


==== Interaction with Expecco (Python) ====
==== Interaction with Expecco (Python) ====
*'''call'''(&lt;actionName&gt;, &lt;arg1&gt;, &lt;arg2&gt;, ... &lt;argN&gt;, function(rslt...) {...});<br>Executes any other expecco action (inside expecco - not inside the node interpreter), and finally calls the callBack function, when the action has finished.<br>Notice that this is an asynchronous callback; you should not perform any further actions after the call, but instead continue inside the callback (and signal final completion via "<code>success() / error()</code>" call from there).<br>The callback may expect multiple rslt-arguments, to get the output values of more than one pin. However, it is currently not possible to get the values of pins which are written multiple times (i.e. an error will be reported, if that is the case)<br>See example below.
*'''call'''(&lt;actionName&gt;, &lt;arg1&gt;, &lt;arg2&gt;, ... &lt;argN&gt;);<br>Executes any other expecco action (inside expecco - not inside the Python interpreter), and returns the generated output pin values as a tuple<br>It is currently not possible to get the values of pins which are written multiple times (i.e. an error will be reported, if that is the case)<br>See example below.

==== Environment Access (Python) ====
==== Environment Access (Python) ====
*'''environmentAt'''(&lt;varName&gt;);<br>Fetches and returns a value from the expecco environment which is in scope of the current activity.<br>You can only read simple objects (numbers and strings) from python actions.
*'''environmentAt'''(&lt;varName&gt;);<br>Fetches and returns a value from the expecco environment which is in scope of the current activity.<br>You can only read simple objects (numbers, booleans and strings) from python actions.


*'''environmentAtPut'''(&lt;varName&gt;, &lt;newValue&gt;);<br>Writes a value into the expecco environment which is in scope of the current activity.<br>You can only write simple objects (numbers and strings) from node actions.
*'''environmentAtPut'''(&lt;varName&gt;, &lt;newValue&gt;);<br>Writes a value into the expecco environment which is in scope of the current activity.<br>You can only write simple objects (numbers, booleans and strings) from python actions.
<!--
<!--
*'''eval''' (''smalltalkCodeString'') <br>Evaluate a piece of Smalltalk code inside expecco.
*'''eval''' (''smalltalkCodeString'') <br>Evaluate a piece of Smalltalk code inside expecco.
Zeile 2.901: Zeile 3.214:
*'''evalJS''' (''javascriptCodeString'') <br>Evaluate a piece of JavaScript code inside expecco.
*'''evalJS''' (''javascriptCodeString'') <br>Evaluate a piece of JavaScript code inside expecco.
-->
-->

==== Event Sending (Python) ====
==== Event Sending (Python) ====
* '''pushEvent''' (''payloadData'') <br> pushes an event onto the global event handler's event queue. The payload is packed into an Event object with eventType "#default. Raises an error, if no global event handler process is running.
* '''pushEvent''' (''payloadData'') <br> pushes an event onto the global event handler's event queue. The payload is packed into an Event object with eventType "<code>#default</code>. Raises an error, if no global event handler process is running.


* '''pushEventType_data''' (''eventTypeSymbolOrNil'', ''payloadData'') <br> pushes an event of type (or #default) onto the global event handler's event queue. Raises an error, if no global event handler process is running.
* '''pushEventType_data''' (''eventTypeSymbolOrNil'', ''payloadData'') <br> pushes an event of type (or <code>#default</code>) onto the global event handler's event queue. Raises an error, if no global event handler process is running.
&nbsp;


==== Special Functions (Python) ====
==== Special Functions (Python) ====
*'''makeRef''' (&lt;anyNodeObject&gt;) <br>Generates a reference to a NodeJS object, which can be passed to expecco as an output pin value. Such reference objects can be passed back to NodeJS later and are dereferenced there back to the original object. This is used to pass NodeJS handles (i.e. server/client handles) to expecco and later back to NodeJS. Read below about reference passing vs. value passing.<br>Notice that the nodeObject will be registered (i.e. remembered) inside the node interpreter, in order to be found later, when the reference is passed as input by another node action. When the reference is no longer needed, it should be deregistered by calling the "<code>releaseRef</code> function described below.<br>Also notice, that expecco will do this automatically, when the reference object is finalised.<br>See example below.
*'''makeRef''' (&lt;anyPythonObject&gt;) <br>Generates a reference to a Python object, which can be passed to expecco as an output pin value. Such reference objects can be passed back to Python later and are dereferenced there back to the original object. This is used to pass Python handles (i.e. server/client handles) to expecco and later back to Python. Read below about reference passing vs. value passing.<br>Notice that the pythonObject will be registered (i.e. remembered) inside the python interpreter, in order to be found later, when the reference is passed as input by another node action. When the reference is no longer needed, it should be deregistered by calling the "<code>releaseRef</code> function described below.<br>Also notice, that expecco will do this automatically, when the reference object is finalised.<br>See example below.


*'''releaseRef''' (&lt;nodeObjectReference&gt;) <br>removes the reference from the registery inside the node interpreter.<br>See example below.
*'''releaseRef''' (&lt;nodeObjectReference&gt;) <br>removes the reference from the registery inside the Python interpreter.<br>See example below.


*'''wait''' () <br>Tells expecco to wait until one of the "<code>success/error/fail/inconclusive</code>" functions is called.<br>Use this if a pin value has to be written later by a callback function.<br>See example below.
*'''wait''' () <br>Tells expecco to wait until one of the <code>success()</code>, <code>error()</code>, <code>fail()</code>, or <code>inconclusive()</code> functions is called.<br>Use this if a pin value has to be written later by a callback function.<br>See explanation of [[#Asynchronous_and_Callback_Functions|async callbacks]] and the example in the NodeJS API description.
<!--
<!--
*'''mayStillDoExpeccoCalls''' (''boolean'') <br>Make sure that the functions described here will still be callable (by Java callback functions from other threads) even after the step has finished execution. In other words, do NOT release the observer object which is responsible for the transfer of information between the two systems. Be aware that this object remains and will never be released, until the bridge connection is closed. I.e. there is a potential for a memory leak inside the Java VM here.
*'''mayStillDoExpeccoCalls''' (''boolean'') <br>Make sure that the functions described here will still be callable (by Java callback functions from other threads) even after the step has finished execution. In other words, do NOT release the observer object which is responsible for the transfer of information between the two systems. Be aware that this object remains and will never be released, until the bridge connection is closed. I.e. there is a potential for a memory leak inside the Java VM here.
-->
-->
*'''bridge_inMainThread''' (task)<br>OS X only:<br>Task must be a lambda expression which is executed by the main thread (as opposed to the bridge connection thread).<br>This is required on eg. OS X to run code which interacts with OS X windowing framework.<br>On non-OSX systems, this executes the task directly (and also in the main thread).<br>Typical use: <code>bridge_inMainThread(lambda: someWindow.show() )</code>


==== Pin Functions (Python) ====
==== Pin Functions (Python) ====
Currently, Node.js blocks do not support a variable number of input or output pins.
Currently, python blocks do not support a variable number of input or output pins.


Attention:
Attention:
<br>Javascript uses many more reserved keywords for syntax than Smalltalk. These keywords cannot be used as pin names, and you will get a syntax error ("<foo> token not expected") if you try. Be careful to not name your pins as any of: "<code>in</code>", "<code>return</code>", "<code>class</code>", "<code>private</code>", "<code>public</code>", etc. As a proven best practice, add a "''Pin''" suffix to your pin names (i.e. name it "''inPin''", instead of "''in''").
<br>Python uses many more reserved keywords for syntax than Smalltalk. These keywords cannot be used as pin names, and you will get a syntax error if you try. Be careful to not name your pins as any of: "<code>import</code>", "<code>return</code>", "<code>def</code>", "<code>if</code>" etc. As a proven best practice, add a "''Pin''" suffix to your pin names (i.e. name it "''inPin''", instead of "''in''").


===== Input Pins (Python) =====
===== Input Pins (Python) =====


*'''hasValue''' () <br>Returns true if the pin has received a value
*''pinName''.'''hasValue''' () <br>Returns true if the pin has received a value


*'''isConnected''' () <br>Returns true if the pin is connected
*''pinName''.'''isConnected''' () <br>Returns true if the pin is connected


*'''value''' () <br>Returns the value at the pin (the datum). Raises an error if the pin did not receive any value.
*''pinName''.'''value''' () <br>Returns the value at the pin (the datum). Raises an error if the pin did not receive any value.


*'''refValue''' () <br>Returns the object referenced by the value at the pin. The pin must have received a reference. Raises an error if the pin did not receive any value. Use this to pass back the original reference object to expecco. The input pin must have received a reference to a Node object as generated previously by "<code>makeRef</code>". Read below about reference passing vs. value passing.
*''pinName''.'''refValue''' () <br>Returns the object referenced by the value at the pin. The pin must have received a reference. Raises an error if the pin did not receive any value. Use this to pass back the original reference object to expecco. The input pin must have received a reference to a Python object as generated previously by "<code>makeRef</code>". Read below about reference passing vs. value passing.
*'''valueIfPresent''' () <br>Returns the pin-datum if it has one, None otherwise. Similar to "<code>value()</code>", but avoids the exception.
*''pinName''.'''valueIfPresent''' () <br>Returns the pin-datum if it has one, None otherwise. Similar to <code>value()</code>, but avoids the exception.
*'''valueIfAbsent''' (''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>value()</code>", but avoids the exception.
*''pinName''.'''valueIfAbsent''' (''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to <code>value()</code>, but avoids the exception. Use this, if "None" is a possible value at the pin, and the code needs to distinguish between getting a "None" at the pin and not having a value at all at the pin.
<!--
<!--
*'''valueIfAbsent''' (''alternativeValue'') <br>Returns the value of a pin or the alternativeValue if the pin did not receive any value.
*''pinName''.'''valueIfAbsent''' (''alternativeValue'') <br>Returns the value of a pin or the alternativeValue if the pin did not receive any value.
-->
-->


===== Output Pins (Python) =====
===== Output Pins (Python) =====


<!--*'''isBuffered''' () <br>True if the pin is buffered
<!--*''pinName''.'''isBuffered''' () <br>True if the pin is buffered
-->
-->
<!-- *'''isConnected'''() <br>Returns true if the pin is connected
<!-- *''pinName''.'''isConnected'''() <br>Returns true if the pin is connected
-->
-->
*'''value''' (''data'') <br>Writes the value.
*''pinName''.'''value''' (''data'') <br>Writes the value.


==== Transcript, Stderr and Stdout (Python) ====
==== Transcript, Stderr and Stdout (Python) ====


The expecco "<code>Transcript</code>", "<code>Stderr</code>" and "<code>Stdout</code>" are also accessible from Node code. However, only a limited subset of messages is supported:
The expecco "<code>Transcript</code>", "<code>Stderr</code>" and "<code>Stdout</code>" are also accessible from Python code. However, only a limited subset of messages is supported:


*'''cr''' ()<br>Adds a linebreak (i.e. followup text will be shown on the next line)
*'''cr''' ()<br>Adds a linebreak (i.e. followup text will be shown on the next line)
Zeile 2.954: Zeile 3.268:
*'''show''' (''arg'')<br>Adds a textual representation of the argument, which can be a string, number or any other object.
*'''show''' (''arg'')<br>Adds a textual representation of the argument, which can be a string, number or any other object.


*'''show''' (''fmt', ''arg''...)<br>Like "<code>show()</code>, but "%i" sequences in the format argument are expanded by the corresponding arg strings.
*'''show''' (''fmt'', ''arg''...)<br>Like "<code>show()</code>, but "%i" sequences in the format argument are expanded by the corresponding arg strings.


*'''showCR''' (''arg'')<br>A combination of show(), followed by a linebreak.
*'''showCR''' (''arg'')<br>A combination of show(), followed by a linebreak.


*'''showCR''' (''fmt', ''arg''...)<br>Like "<code>showCR()</code>, but "%i" sequences in the format argument are expanded by the corresponding arg strings.
*'''showCR''' (''fmt'', ''arg''...)<br>Like "<code>showCR()</code>, but "%i" sequences in the format argument are expanded by the corresponding arg strings.


In addition, stdout (console.log) and stderr (console.error) are also forwarded to the expecco Transcript window, depending on the settings in expecco ("''Extras''" &#8594; "''Settings''" &#8594; "''Execution''" &#8594; "''Tracing''" &#8594; "''Show Stdout and Stderr on Transcript''").
In addition, stdout (console.log) and stderr (console.error) are also forwarded to the expecco Transcript window, depending on the settings in expecco ("''Extras''" &#8594; "''Settings''" &#8594; "''Execution''" &#8594; "''Tracing''" &#8594; "''Show Stdout and Stderr on Transcript''").
&nbsp;
&nbsp;

==== Dialog (Python) ====
Some of expecco's Dialog messages are forwarded from Python:
*'''information''' (''msg'') (new in 23.2)<br>Opens an information dialog. I.e. "<code>Dialog.information("Hello")</code> will show "Hello" in a popup dialog window.

*'''confirm''' (''msg'')<br>Opens a Yes/No confirmation dialog (and returns True/False)

*'''request''' (''msg'')<br>Opens a dialog asking for a string (and returns the entered string or None on cancel)

*'''requestPassword''' (''msg'')<br>Opens a dialog asking for a password string (and returns the entered string or None on cancel)

*'''requestFilename''' (''msg'') (new in 23.2)<br>Opens a dialog asking for a file name string (and returns the entered file name or None on cancel)

=== Global and Static Variables ===
Python actions are defined within the scope of another function, to prevent accidentically overwriting/redefining any global.
Thus, you have to access globals via "globals().get("name")".

==== Static Variables ====
Starting with v24.1, static variables (which are visible within one activity and which are preserved across calls) can be defined by
prepending the execute function's code with:
# start of static definitions
... any variables which should be static ...
# end of static definitions

Notice that these comment lines must be written exactly as above.
<br>As an example, the following uses a static variable to increment a counter whenever the action is Ivoked:
# start of static definitions
count=0
# end of static definitions
def execute():
nonlocal count
count = count + 1
print(f"count={count}")

Notice that the above example does not work in pre 24.1 versions.
Also notice, that the static definitions are re-executed whenever the action code is changed or recompiled (i.e. that the count variable is reset to 1).

==== Global Variables ====

Example of a global variable that can also be used in other modules.

Caution: Please make sure that the name of the global variable is unique in your test suite!

def execute():
global count
try:
count
except NameError:
# Throws a NameError if not yet initialised
count = 0
count = count + 1
print(f"count={count}")

=== Python Asynchronous and Callback Functions ===
If output pins are to be written by a callback which is invoked AFTER the python function returned, the expecco activity will already be finished, and the pin will NOT be written.
Instead, a warning is generated in the log.

You MUST tell expecco that the execute function has not yet finished, by calling "wait()" and signal the real activityEnd in the callback via one of "success()", "error()"or "fail()".

For a detailed description of this mechanism, read the
[[#Asynchronous and Callback Functions|corresponding section]] in the Node API.


=== Reference passing vs. value passing of Python objects to expecco ===
=== Reference passing vs. value passing of Python objects to expecco ===
Zeile 2.970: Zeile 3.347:
* pass by reference<br>The object is remembered inside Python, and a reference is passed to expecco. Whenever this reference object is later sent to Python again (via an input pin), the original Python object is retrieved and passed to the Python code. This mechanism preserves the object's identity on the Python side and must be used for object handles (such as protocol handles).
* pass by reference<br>The object is remembered inside Python, and a reference is passed to expecco. Whenever this reference object is later sent to Python again (via an input pin), the original Python object is retrieved and passed to the Python code. This mechanism preserves the object's identity on the Python side and must be used for object handles (such as protocol handles).


The default mechanism is "''pass by value''". To pass a reference, use "<code>makeRef(obj)</code>" and pass the generated reference to an output pin.
The default mechanism is "''pass by value''".
<br>To pass a reference, use "<code>makeRef(obj)</code>" and pass the generated reference to an output pin.
==== Example ====

When objects are passed from expecco to python, this is transparent. Python will automatically resolve passed in references. Thus you can send a "pointer" to an object to an output pin via "<code>makeRef(obj)</code>" and pass it to another Python action, where the value will refer to the original object.
==== Example (Python) ====
The following code snippet sends an object's reference to an output pin:
The following code snippet sends an object's reference to an output pin:


Zeile 2.995: Zeile 3.376:
outputPin.value( inputPin.refValue() )
outputPin.value( inputPin.refValue() )


Finally, you want to release the reference inside the node interpreter, to prevent memory leaks.
Finally, you want to release the reference inside the Python interpreter, to prevent memory leaks.
Usually, this should be called, when a handle becomes invalid (e.g. when a connection handle is closed).
Usually, this should be called, when a handle becomes invalid (e.g. when a connection handle is closed).


Zeile 3.010: Zeile 3.391:
releaseRef(referredObject)
releaseRef(referredObject)


<!-- Commented: text was copy-pasted from Node; may need adoption to Python
<!--
=== Asynchronous and Callback Functions ===
=== Asynchronous and Callback Functions ===
A common pattern in node is "''continuation passing style''" control flow. That means, that many functions expect function as callback argument, which is called later whenever the requested operation has finished. This is used especially with I/O and protocol related operations (such as socket connect, http requests, client connect setup etc.).
A common pattern in node is "''continuation passing style''" control flow. That means, that many functions expect function as callback argument, which is called later whenever the requested operation has finished. This is used especially with I/O and protocol related operations (such as socket connect, http requests, client connect setup etc.).
Zeile 3.034: Zeile 3.415:
-->
-->


=== Calling other Expecco Actions (python) ===
=== Calling other Expecco Actions (Python) ===
Not yet implemented
Not yet implemented


Zeile 3.041: Zeile 3.422:
Arguments are passed to the called action's input pin (top to bottom).
Arguments are passed to the called action's input pin (top to bottom).


For now, there is a limitation, in that only a single value (per pin) can be passed to the callback (i.e. you cannot get the values of pins which are written multiple times).
For now, there is a limitation, in that only a single value (per pin) can be returned (i.e. you cannot get the values of pins which are written multiple times).
<br>Example:
<br>Example:


Zeile 3.048: Zeile 3.429:
result = call("myAction", 100, 200)
result = call("myAction", 100, 200)


=== Python2 vs. Python3 ===
=== Reading expecco Environment Variables (python) ===
Although python2 is going to be obsoleted soon, there are still many packages and frameworks around, which require that version.
Expecco variables can be fetched via the "environmentAt" function. Notice, that this is an asynchronous call and thus expected a callback function.

If all of your actions use the same python version, specify it in the "Python" field of the interpreter settings dialog.
Otherwise, change individual action's python version via the "Language" comboList, above the code editor (change to "BridgedPython2" or "BridgedPython3" to make it explicit).

Pathes to the explicit versions and to the unspecific version are all defined in the pathon settings.

=== Reading expecco Environment Variables (Python) ===
Expecco variables can be fetched via the "<code>environmentAt</code>" function.
<br>For example:
<br>For example:
def execute():
def execute():
Zeile 3.056: Zeile 3.445:


=== Python Packages ===
=== Python Packages ===
You should install python packages using "pip" or "pip3", depending on the python version, for which a package is to be installed.
For more information, please consult the [http://pip.pypa.io pip documentation].

On Windows, "pipwin" seems to perform best, if additional C/C++ libraries need to be installed or compiled (eg. "pipwin install pyaudio").

When installing, make sure that the packages are installed in the correct target directory, as per Python environment; i.e. if you have multiple versions of the interpreter or virtual environments, make sure that the Python as started by expecco (and defined in the settings) uses and sees the correct module path.


===Example: Installing Additional Python Packages ===
===Installing Additional Python Packages ===
Use the Python package installer "<code>pip</code>" to install packages.
Use the Python package installer "<code>pip</code>" (or "<code>pip3</code>") to install packages.
(The "'Extras''" &#8594; "''Bridges''" &#8594; "''Python Bridge''" menu also contains an entry to install pip packages).
The "''Plugins''" &#8594; "''Bridges''" &#8594; "''Python Bridge''" menu also contains an entry to install pip packages.
If you use virtual environments, make sure that your packages are installed in the correct location.
<br>Find packages in [https://pypi.org/ https://pypi.org/] or [https://docs.python.org/3/py-modindex.html https://docs.python.org/3/py-modindex.html].


Find packages in [https://pypi.org/ https://pypi.org/] or [https://docs.python.org/3/py-modindex.html https://docs.python.org/3/py-modindex.html].
<!--
<!--
For example, assume you need the current city weather in a suite, navigate to "https://www.npmjs.com", search for "weather", find the "<code>city-weather</code>" package and click on it. You will arrive on a page giving installation instructions and sample code at the end.
For example, assume you need the current city weather in a suite, navigate to "https://www.npmjs.com", search for "weather", find the "<code>city-weather</code>" package and click on it. You will arrive on a page giving installation instructions and sample code at the end.
Zeile 3.071: Zeile 3.467:
-->
-->


=== Example: Using a Python Package in Expecco ===
=== Accessing Assemblies in IronPython ===
Because IronPython executes inside a CLR environment, it has access to any assembly (written in any language).
In this example, we will access the OpenStreetmap API to fetch information about a particular street
For this, IronPython provides a special language extension, which is described in detail in [[https://ironpython.net/documentation/dotnet/dotnet.html the IronPython documentation]].
("Straße der Nationen") in a given city ("Chemnitz").

The example was taken from [https://pypi.org/project/overpy https://pypi.org/project/overpy] which is documented in [https://python-overpy.readthedocs.io/en/latest/index.html https://python-overpy.readthedocs.io/en/latest/index.html].
Example:
*** to be added **

=== Example 1: Using the "overpy" Python Package (open street map access) in Expecco ===
In this example, we will access the OpenStreetMap API to fetch information about a particular street
("Straße der Nationen") in a given city ("Chemnitz").
The example was taken from [https://pypi.org/project/overpy https://pypi.org/project/overpy] which is documented in [https://python-overpy.readthedocs.io/en/latest/index.html https://python-overpy.readthedocs.io/en/latest/index.html].<br>It requires python3 (you will get a UnicodeEncodeError if you try it in python2).


First, install the package with:
First, install the package with:
pip install overpy
pip3 install overpy


For a first test, we will take the original code from the example.
For a first test, we will take the original code from the example.
Zeile 3.104: Zeile 3.508:
Open the expecco console (aka "Transcript" via the "''Extras''" &#8594; "''Tools''" &#8594; "''Transcript''" menu item) and run the test.
Open the expecco console (aka "Transcript" via the "''Extras''" &#8594; "''Tools''" &#8594; "''Transcript''" menu item) and run the test.
A list of street nodes should be displayed on the Transcript.
A list of street nodes should be displayed on the Transcript.

=== Example 2: Accessing other Python Packages or Individual Script Files ===
The following works in 19.2 and later.<br>
In the following example, functions contained in a regular Python script file are called.
We assume, that the file is named "MyLib.py", and located in some directory ''DIR'':
<br>
DIR/MyLib.py:
def fun1(a,b):
return a+b;
def fun2(a,b):
return a-b;

We also assume, that you have created a new python action block, containing the code:

import MyLib
def execute():
Transcript.showCR("fun1:" + str(MyLib.fun1(10,20))
+ " fun2:" + str(MyLib.fun2(10,20)))

If you execute this action, you will probably get a "Module Not Found" error from python,
(unless DIR is already in your PYTHONPATH environment variable).

To fix the problem, open the "''Extras''" &#8594; "''Settings''" &#8594; "''External Script Interpreters''" &#8594; "''Python''" dialog,
and add DIR to the "Module Path" value. Notice that on Unix/Linux, you have to separate directories with colons ":", whereas under Windows, you'll have to separate them with semicolons ";".

You will have to shut down the already running Python interpreter and restart it, for the new PYTHONPATH setting to become valid.<br>For this, use the "''Plugins''" &#8594; "''Bridges''" &#8594; "''Python Bridge''" &#8594; "''Shutdown Interpreters''" menu item (or shut down all external interpreters via the "''Extras''" &#8594; "''Debug''" menu).

=== Example 3: Accessing Python Script Files in Attachments ===
The following works in 19.2 and later.<br>
You can add Python script files (i.e. like the "MyLib.py" file in the above example) as attachment to to the suite.<br>For this you have to add "$(Attachments)" to the Python module path setting in "''Extras''" &#8594; "''Settings''" &#8594; "''External Script Interpreters''" &#8594; "''Python''", and import it in the Python code as in the previous example.

=== Example 4: Using the Python Appium Bindings ===
In the following, we'll use a Python Appium interface for automation of a mobile application
(in this case, not using the mobile plugin, and not using the GUI Browser).
This may be a useful scenario, if you already have python test actions, which you want to integrate into
expecco.

First install the required python package(s) via the command line or via expecco's "''Plugin''" &#8594; "''Bridges''" &#8594; "''Python''" menu:
pip3 install Appium-Python-Client
or:
python -m pip install Appium-Python-Client
then make a dummy BridgePython block to check if the package is found:
import appium
def execute():
Transcript.showCR ("Hello world; Appium was found");
Now take either existing code, or copy-paste samples from the [https://pypi.org/project/Appium-Python-Client Appium-Python-Client website]; here is a typical code fragment (of course, you have to adjust the values as required). Obviously, it is also a good idea to add input pins to the python action, so the capabilities and connection parameters can be passed as arguments.
from appium import webdriver
desired_caps = dict(
platformName='Android',
platformVersion='10',
automationName='uiautomator2',
deviceName='Android Emulator',
)
def execute():
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
el = driver.find_element_by_accessibility_id('item')
el.click()

=== Example 5: Interfacing to Tensorflow ===

* Create a bridged Python action containing the following code:
import numpy as np
import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_datasets as tfds
def execute():
print("Version: ", tf.__version__)
print("Eager mode: ", tf.executing_eagerly())
print("Hub Version: ", hub.__version__)
print("GPU is", "available" if tf.config.experimental.list_physical_devices("GPU") else "NOT AVAILABLE")

* run it
* if you get a "module not found" error message, either click on the link (named "here"), embedded in the error message, or alternatively select the "''Plugins''" &#8594; "''Bridges''" &#8594; "''Python''" &#8594; "''Install pip Package''" menu item. Of course, you can also install packaged from the command line in a shell/cmd window.
* install missing packages (care for the correct virtual environment if you use them)
* repeat running it, until you get an output on the Transcript (or stderr) similar to:
1: Version: 2.4.1
1: Eager mode: True
1: Hub Version: 0.11.0
1: GPU is NOT AVAILABLE

* You may have to shutdown the bridge after installation of new packages (although it usually works without, unless existing and already loaded packages are upgraded during the installation).

=== Example 6: Interfacing to the "astropy" Package ===
The "astropy" package contains a number of very useful functions for astronomy applications. Among others, you'll find time functions, geo-location mapping, physical constants, image processing and much more inside.<br>(see https://docs.astropy.org/en/stable/index.html)

===== Installation =====

First install the required python package(s) via the command line or via expecco's "''Plugin''" &#8594; "''Bridges''" &#8594; "''Python''" menu:
pip3 install numpy
pip3 install astropy (astropy-Python-Client)
or:
python -m pip install numpy
python -m pip install astropy (astropy-Python-Client)

===== Smoke Test if Package is Installed =====

then make a dummy BridgePython block to check if the package is found:
import astropy
def execute():
Transcript.showCR ("Hello world; astropy was found");

Notice: because bumpy and astropy are relatively big packages, the first initial execution may take a few seconds because the Python interpreter has to load these packages (but this only happens when the very first Python action is executed).

===== Interface to an astropy Function =====

The following example demonstrates how to call functions within astropy;
this will convert an expecco timestamp (a DateTime object) to a julianDate (as Float).
(Hint: JulianDates are commonly used in Astronomy and Space Sciences).

Please read https://docs.astropy.org/en/stable/time/index.html for details on time functions.

The code includes a few debug prints (to the Transcript). In a production suite, these should probably be commented (or replaced by calls to a logger, which can be switched on/off dynamically).

[[Datei:AstroPyExample1.png|500px]]

The action can then be used in a diagram as:

[[Datei:AstroPyExample2.png|500px]]

After a run, we get the Julian date as a float:

[[Datei:AstroPyExample3.png|500px]]

Be aware that expecco requires objects to be JSON serializable to be passed from/to the Python interpreter. Sadly, this is not true for all atrophy objects. If required either pass objects by reference or encode/decode them as strings (or extract subfields into an array and pass the array or pass subfields via individual input/output pins).

It may also be possible to define additional JSON encoders/decoders for individual types.

=== Example 7: Interfacing to a .NET Assembly via IronPython ===

IronPython is a python interpreter running within the .NET CLR framework. The ability of IronPython to import .NET assemblies, makes it easy to also interface those to expecco.
Please refer to the IronPython documentation on how to import .NET assemblies.

&nbsp;--- example to be added ---


== Bridged C Elementary Blocks ==
== Bridged C Elementary Blocks ==


The following works in 19.2 and above.<br>
This is currently being developed and will be available in or after the 19.2 version.
You have to make sure that a C-compiler toolchain is available and can be called directly or indirectly.
You have to make sure that a C-compiler toolchain is available and can be called directly or indirectly (see [[Installing_additional_Frameworks/en | "Installing additional Frameworks"]]).


Warning: bridged C-code is highly dependent on the C-compiler toolchain of the target machine. It should only be used by experts and only for very special situations, when all alternatives have been tried and failed. Bridged C code can be used to implement time critical functions or to interface to C/C++ libraries, when the simple DLL-call interface is too complicated to use (for example, if complicated data structures have to be exchanged, C-callbacks are needed, or C++ interfaces are to be implemented).
Bridged C-code is highly dependent on the C-compiler toolchain of the target machine and may require preparations for machine dependencies with #ifdefs (word length, OS-APIs etc.). It is typically used by experts and for special situations, when simpler alternatives are not available.


Bridged C-code can be used to implement time critical functions or to interface to C/C++ libraries, when the simple DLL-call interface is too complicated to use (for example, if complicated data structures have to be exchanged, if C-callbacks are needed, or if C++ interfaces are to be implemented).
Other applications are high-speed data acquisition and protocol implementations, in which a C-part is responsible for the time critical task, and expecco can query/control that task remotely (i.e. fetch gathered data or state information).


In addition, bridged C-Code actions run in a separate process, isolated from expecco. Thus expecco is not affected by errors in the C-code and cannot be disturbed (eg. by invalid memory references), which is not guaranteed for DLL called functions (which are executed within the expecco process).<br>On the downside, there is some overhead involved in calling a bridged action, since input/output parameters and the call itself are transmitted via an interprocess communication mechanism (i.e. socket/network message). Thus the call roundtrip times are in the millisecond range, as opposed to microseconds for DLL calls.
The C-bridge code is available as shared or linkable library in two configurations, with or without the dynamic C code injection facility.


Other applications are high-speed data acquisition and protocol implementations, in which a C-part is responsible for the time critical task, and expecco can query/control that task remotely (i.e. fetch captured data or state information).
Setups without code injection are useful to augment (staticly compiled) embedded systems with a debug interface, through which expecco can later access specific (and explicitly published) interfaces. In such a setup, no dynamic C-code can be injected into the running application. Instead, the set of callable interfaces must be declared and implemented in advance (in the embedded system).


=== The C Bridge ===
The following chapter describes the dynamic C-code injection facility. For static embedded systems, please refer to the [[Embedded Systems C Bridge API | "Embedded Systems C Bridge API"]].
Be reminded that bridged C-Code actions are executed inside a separate OS-process, which runs the c-bridge code.
Whenever a bridged C-action is to be executed, the pin-data is transferred via an interprocess communication mechanism (IPC), and the code executed inside the bridge process. Pin data and event requests are transmitted back to expecco via the same IPC mechanism.

The C-bridge itself is available as executable or as shared or linkable library in two configurations, with or without the dynamic C code injection facility.

Setups without code injection are useful to augment (statically compiled) embedded systems with a debug interface, through which expecco can later access specific (and explicitly published) interfaces. In such a setup, no dynamic C-code can be injected into the running application. Instead, the set of callable interfaces must be declared and implemented in advance (in the embedded system).

The following chapter describes the dynamic C-code injection facility as seen by elementary activity code in expecco.<br>For static embedded systems, please refer to the [[Embedded Systems C Bridge API | "Embedded Systems C Bridge API"]].


=== Bridged C Code API ===
=== Bridged C Code API ===


Note: This is a preliminary API documentation. Bridged C elementary actions are still being developed and the API may change slightly until officially released.
Note: This is a preliminary API documentation. Bridged C elementary actions are still being developed and the API may be extended or improved in the future (However, it is very very likely to be backward compatible).
The API is intended to look similar to the other language's elementary code API.
The API is intended to look similar to the other languages' elementary code API.
However, due to the nature and syntax of the C-language, certain differences are apparent.
However, due to the nature and syntax of the C-language, certain differences are apparent.<br>
The biggest are due to C being a static typed language.
The biggest differences are due to C being a statically typed language: the simple "<code>pin.value()</code>" interface for pins (as used in other languages) cannot be offered here; instead, a datatype-specific API is provided in C.


==== Variables seen by the Executed C Function ====
==== Variables seen by the Executed C Function ====
Zeile 3.132: Zeile 3.685:
The following variables are in the scope of the executed function:
The following variables are in the scope of the executed function:


*<code>Transcript</code> <br>supports a few functions to display messages in the expecco Transcript window (see below)
*<code>Transcript</code> <br>supports a few functions to display messages in the expecco Transcript window (used as argument; see below)
*<code> Stdout </code> <br>supports a few functions to display messages on expecco's stdout stream (see below)
*<code> Stdout </code> <br>supports a few functions to display messages on expecco's stdout stream (used as argument; see below)
*<code> Stderr </code> <br>supports a few functions to display messages on expecco's stderr stream (see below)
*<code> Stderr </code> <br>supports a few functions to display messages on expecco's stderr stream (used as argument; see below)
<!--
<!--
*<code>__bridge__</code> <br>the bridge object which handles the communication with expecco. The instance slot named "<code>asynchronous</code>" is of special interest if the called function uses asynchronous callbacks (see below)
*<code>__bridge__</code> <br>the bridge object which handles the communication with expecco. The instance slot named "<code>asynchronous</code>" is of special interest if the called function uses asynchronous callbacks (see below)
Zeile 3.141: Zeile 3.694:
==== Reporting (C) ====
==== Reporting (C) ====


*''void'' '''error''' (''fmt'' , ...) <br>Report a defect (in the test). Stops execution. The arguments are printf-style.
*void '''error''' (char* ''fmt'' , ...) <br>Report a defect (in the test). Stops execution. The arguments are printf-style.


*''void'' '''fail''' (''fmt'', ...) <br>Report a failure (in the SUT). Stops execution. The arguments are printf-style.
*void '''fail''' (char* ''fmt'', ...) <br>Report a failure (in the SUT). Stops execution. The arguments are printf-style.


*''void'' '''inconclusive''' (''fmt'' , ...) <br>Report an inconclusive test. Stops execution. The arguments are printf-style.
*void '''inconclusive''' (char* ''fmt'' , ...) <br>Report an inconclusive test. Stops execution. The arguments are printf-style.


*''void'' '''activitySuccess''' (''fmt'' , ...)<br>Finishes the current activity with success (same as "success()"). The arguments are printf-style.
*void '''activitySuccess''' (char* ''fmt'' , ...)<br>Finishes the current activity with success (same as "success()"). The arguments are printf-style.


==== Logging (C) ====
==== Logging (C) ====


*''void'' '''logFail''' (''fmt'' , ...) <br>Adds a fail message to the activity log, but continues execution. The arguments are printf-style.
*void '''logFail''' (char* ''fmt'' , ...) <br>Adds a fail message to the activity log, but continues execution. The arguments are printf-style.<br>Notice: although the action continues to be executed, it will be marked as failed at the end.


*''void'' '''logError''' (''fmt'' , ...) <br>Adds a error message to the activity log, but continues execution. The arguments are printf-style.
*void '''logError''' (char* ''fmt'' , ...) <br>Adds a error message to the activity log, but continues execution. The arguments are printf-style.<br>Notice: although the action continues to be executed, it will be marked as erronous at the end.


*''void'' '''logWarning''' (''fmt'' , ...) <br>Adds a warning to the activity log, but continues execution. The arguments are printf-style.
*void '''logWarning''' (char* ''fmt'' , ...) <br>Adds a warning to the activity log, but continues execution. The arguments are printf-style.


*''void'' '''logInfo''' (''fmt'' , ...) <br>Adds an info message to the activity log, and continues execution. The arguments are printf-style.
*void '''logInfo''' (char* ''fmt'' , ...) <br>Adds an info message to the activity log, and continues execution. The arguments are printf-style.


*''void'' '''alert''' (''fmt'' , ...)<br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "''Execution''" - "''Log -Settings''" dialog (by default it is disabled).
*void '''alert''' (char* ''fmt'' , ...)<br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "''Execution''" &#8594; "''Log -Settings''" dialog (by default it is disabled).


*void '''warn''' (char* ''fmt'' , ...)<br>Same as alert(). (For Smalltalk protocol compatibility)
==== Reflection, Information, Queries and Accessing (C) ====


==== Event Sending (C) ====
*void '''pushEventType_data'''(char* ''event'', char* ''dataOrNULL'')<br>adds an event to expecco's event queue. The payload in ''dataOrNULL'' must be a 0-terminated string.

*void '''pushEventType_dataBytes'''(char* ''event'', unsigned char* ''dataOrNULL'', int ''numBytes'')<br>adds an event to expecco's event queue. The payload in ''dataOrNULL'' points to a byte-buffer of given length.

*void '''pushEventType_dataJSON'''(char* ''event'', jsonObject ''dataOrNULL'')<br>adds an event to expecco's event queue. The payload in ''dataOrNULL'' is a [[Embedded_Systems_C_Bridge_API#JSON_Library|json object]].
<!--
<!--
==== Reflection, Information, Queries and Accessing (C) ====

*'''environmentAt''' (''anEnvironmentVarName'') <br>The value of an environment variable
*'''environmentAt''' (''anEnvironmentVarName'') <br>The value of an environment variable


Zeile 3.179: Zeile 3.740:


Attention:
Attention:
<br>C uses many more reserved keywords for syntax than Smalltalk. These keywords cannot be used as pin names, and you will get a syntax error if you try. Be careful to not name your pins as any of: "<code>return</code>", "<code>char</code>", "<code>int</code>", etc. As a proven best practice, add a "''Pin''" suffix to your pin names (i.e. name it "''inPin''", instead of "''in''").
<br>C uses many more reserved keywords for syntax than Smalltalk. These keywords cannot be used as pin names, and you will get a syntax error if you try.<br>Be careful to not name your pins as any of: "<code>return</code>", "<code>char</code>", "<code>int</code>", etc.

As a proven best practice, add a "''Pin''" suffix to your pin names (e.g. name it "''inPin''", instead of "''in''").


===== Input Pins (C) =====
===== Input Pins (C) =====


====== Queries ======
*''bool_t'' '''hasValue''' (''inPin'') <br>Returns true if the pin has received a value, where ''true'' is represented as 1, false as 0.
*bool_t '''hasValue''' (''inPin'') <br>Returns true if the pin has received a value, where ''true'' is represented as 1, false as 0.


*''bool_t'' '''isConnected''' (''inPin'') <br>Returns true if the pin is connected.
*bool_t '''isConnected''' (''inPin'') <br>Returns true if the pin is connected.


====== Pin Value ======
*char* '''stringValue''' (''inPin'') <br>Returns the string value at the pin as a c-char*. Raises an error if the pin did not receive a value, or if the value was not a string. The returned pointer becomes invalid after the action's execution; it should therefore be strcpy'd if it is needed later (i.e. do not keep a reference to it).


*''char*'' '''stringValue''' (''inPin'') <br>Returns the string value at the pin as a c-char*. Raises an error if the pin did not receive a value, or if the value was not a string. The returned pointer becomes invalid after the action's execution; it should therefore be strcpy'd if it is needed later (i.e. do not keep a reference to it).
*long '''longValue''' (''inPin'') <br>Returns the integer value at the pin as a c-long. Raises an error if the pin did not receive a value, or if the value was not an integer.


*''long'' '''longValue''' (''inPin'') <br>Returns the integer value at the pin as a c-long. Raises an error if the pin did not receive a value, or if the value was not an integer.
*longlong '''longLongValue''' (''inPin'') (vsn24.1, 64bit)<br>Returns the integer value at the pin as a c-longLong. Raises an error if the pin did not receive a value, or if the value was not an integer.<br>This API is only avalable in 64bit versions of the C-bridge (i.e. not in the cBridge32.exe for Windows) and only with expecco vsn 24.1 and later.


*''double'' '''doubleValue''' (''inPin'') <br>Returns the string value at the pin as a c-double Raises an error if the pin did not receive a value, or if the value was not a number.
*double '''doubleValue''' (''inPin'') <br>Returns the double value at the pin as a c-double Raises an error if the pin did not receive a value, or if the value was not a number.


*''bool_t'' '''booleanValue''' (''inPin'') <br>Returns the string value at the pin as a c-int. Raises an error if the pin did not receive a value, or if the value was not a boolean.
*bool_t '''booleanValue''' (''inPin'') <br>Returns the boolean value at the pin as a c-int. Raises an error if the pin did not receive a value, or if the value was not a boolean.


*''jsonObject'' '''jsonValue''' (''inPin'') <br>Returns the value at the pin as a jsonObject. Raises an error if the pin did not receive a value. The returned pointer becomes invalid after the action's execution; field values should therefore extracted and copied if needed later (i.e. do not keep a reference to it or its components).
*uchar* '''bytesValue''' (''inPin'') <br>Returns the byteArray value at the pin as a c-unsigned char*. Raises an error if the pin did not receive a value, or if the value was not a byteArray or C-struct. This can also be used to receive other bulk data, such as an int32 or float array. For example, by converting a collection of numbers first to a byteArray, and then casting the bytesValue to another pointer type in the bridge code.


*void* '''pointerValue''' (''inPin'') <br>Returns the pointer value at the pin as a void*. Raises an error if the pin did not receive a value, or if the value was not a pointer. This means, that it must be a pointer as previously sent to expecco with putPointer. Please read the description of "putPointer()" below.


*''char*'' '''stringValueIfAbsent''' (''inPin'', ''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>stringValue()</code>", but avoids the exception. The returned pointer becomes invalid after the action's execution.
*jsonObject '''jsonValue''' (''inPin'') <br>Returns the value at the pin as a [[Embedded_Systems_C_Bridge_API#JSON_Library|jsonObject]]. Raises an error if the pin did not receive a value. The returned pointer becomes invalid after the action's execution; field values should therefore be extracted and copied if needed later (i.e. do not keep a reference to it or its components).


*''long'' '''longValueIfAbsent''' (''inPin'', ''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>longValue()</code>", but avoids the exception.


*''double'' '''doubleValueIfAbsent''' (''inPin'', ''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>doubleValue()</code>", but avoids the exception.
*int '''arraySize''' (''inPin'') <br>Returns the number of array elements, if there is a byteArray value at the pin. Raises an error if the pin did not receive a value, or if the value was not a byteArray or C-struct.


*''bool_t'' '''booleanValueIfAbsent''' (''inPin'', ''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>booleanValue()</code>", but avoids the exception.


*''jsonObject'' '''jsonValueIfAbsent''' (''inPin'', ''repl'') <br>Returns the pin-datum as a jsonObject if it has one, ''repl'' otherwise. Similar to "<code>jsonValue()</code>", but avoids the exception. The returned pointer becomes invalid after the action's execution.
*char* '''stringValueIfAbsent''' (''inPin'', char* ''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>stringValue()</code>", but avoids the exception. The returned pointer becomes invalid after the action's execution.


*long '''longValueIfAbsent''' (''inPin'', long ''repl'') (vsn24.1, 64bit)<br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>longValue()</code>", but avoids the exception.


*''char*'' '''stringValueIfPresent''' (''inPin'') <br>Returns the pin's datum if it has one, an empty string otherwise. Similar to "<code>stringValue()</code>", but avoids the exception. The returned pointer becomes invalid after the action's execution.
*longlong '''longLongValueIfAbsent''' (''inPin'', longlong ''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>longLongValue()</code>", but avoids the exception.<br>This API is only avalable in 64bit versions of the C-bridge (i.e. not in the cBridge32.exe for Windows) and only with expecco vsn 24.1 and later.


*''long'' '''longValueIfPresent''' (''inPin'') <br>Returns the pin's datum if it has one, 0 (zero) otherwise. Similar to "<code>longValue()</code>", but avoids the exception.
*double '''doubleValueIfAbsent''' (''inPin'', double ''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>doubleValue()</code>", but avoids the exception.


*''double'' '''doubleValueIfPresent''' (''inPin'') <br>Returns the pin's datum if it has one, 0.0 (zero) otherwise. Similar to "<code>doubleValue()</code>", but avoids the exception.
*bool_t '''booleanValueIfAbsent''' (''inPin'', bool_t ''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>booleanValue()</code>", but avoids the exception.


*''bool_t'' '''booleanValueIfPresent''' (''inPin'') <br>Returns the pin's datum if it has one, 0 (false) otherwise. Similar to "<code>booleanValue()</code>", but avoids the exception.
*uchar* '''bytesValueIfAbsent''' (''inPin'', uchar* ''repl'') <br>Returns the pin's datum if it has one, ''repl'' otherwise. Similar to "<code>bytesValue()</code>", but avoids the exception.


*''jsonObject'' '''jsonValueIfPresent''' (''inPin'') <br>Returns the pin-datum as a jsonObject if it has one, NULL otherwise. Similar to "<code>jsonValue()</code>", but avoids the exception. The returned pointer becomes invalid after the action's execution.
*jsonObject '''jsonValueIfAbsent''' (''inPin'', jsonObject ''repl'') <br>Returns the pin-datum as a [[Embedded_Systems_C_Bridge_API#JSON_Library|jsonObject]] if it has one, ''repl'' otherwise. Similar to "<code>jsonValue()</code>", but avoids the exception. The returned pointer becomes invalid after the action's execution.


*char* '''stringValueIfPresent''' (''inPin'') <br>Returns the pin's datum if it has one, an empty string otherwise. Similar to "<code>stringValue()</code>", but avoids the exception. The returned pointer becomes invalid after the action's execution.

*long '''longValueIfPresent''' (''inPin'') <br>Returns the pin's datum if it has one, 0 (zero) otherwise. Similar to "<code>longValue()</code>", but avoids the exception.

*longlong '''longLongValueIfPresent''' (''inPin'') (vsn24.1, 64bit)<br>Returns the pin's datum if it has one, 0 (zero) otherwise. Similar to "<code>longLongValue()</code>", but avoids the exception.<br>This API is only avalable in 64bit versions of the C-bridge (i.e. not in the cBridge32.exe for Windows) and only with expecco vsn 24.1 and later.

*double '''doubleValueIfPresent''' (''inPin'') <br>Returns the pin's datum if it has one, 0.0 (zero) otherwise. Similar to "<code>doubleValue()</code>", but avoids the exception.

*bool_t '''booleanValueIfPresent''' (''inPin'') <br>Returns the pin's datum if it has one, 0 (false) otherwise. Similar to "<code>booleanValue()</code>", but avoids the exception.

*uchar* '''bytesValueIfPresent''' (''inPin'') <br>Returns the pin's datum if it has one, NULL otherwise. Similar to "<code>bytesValue()</code>", but avoids the exception.

*jsonObject '''jsonValueIfPresent''' (''inPin'') <br>Returns the pin-datum as a [[Embedded_Systems_C_Bridge_API#JSON_Library|jsonObject]] if it has one, NULL otherwise. Similar to "<code>jsonValue()</code>", but avoids the exception. The returned pointer becomes invalid after the action's execution.

Variable number of input/output pins are not supported. You should pass an array or structure if required.


===== Output Pins (C) =====
===== Output Pins (C) =====


* void '''putString''' (''outPin'', char* ''data'') <br>Writes a string to the output pin.
* void '''putString''' (''outPin'', char* ''data'') <br>Writes a string to the output pin. The string must be zero-terminated.


* void '''putLong''' (''outPin'', long ''data'') <br>Writes a long to the output pin.
* void '''putStringN''' (''outPin'', char* ''data'', int ''len'') <br>Writes len bytes of a string to the output pin.


* void '''putDouble''' (''outPin'', double ''data'') <br>Writes a double to the output pin.
* void '''putLong''' (''outPin'', long ''data'') <br>Writes a long to the output pin.<br>Be aware that the size of a long depends on the cbridge host's CPU architecture, the OS and the C-compiler: on Unix machines, it is typically an int64, whereas on Windows it is usually an int32.

* void '''putULong''' (''outPin'', unsigned long ''data'') <br>Writes an unsigned long to the output pin.<br>Be aware that the size of an unsigned long depends on the cbridge host's CPU architecture, the OS and the C-compiler: on Unix machines, it is typically an uint64, whereas on Windows it is usually an uint32.

* void '''putLongLong''' (''outPin'', long long ''data'') <br>Writes a long long to the output pin. Typically, a long long is an int64 on all architectures.

* void '''putULongLong''' (''outPin'', unsigned long long ''data'') <br>Writes an unsigned long long to the output pin. Typically, an unsigned long long is an uint64 on all architectures (depends on cbridge host's CPU, OS and compiler). This API is not available in the 32 bit cBridge version.

* void '''putDouble''' (''outPin'', double ''data'') <br>Writes a double (float64) to the output pin.


* void '''putBoolean''' (''outPin'', bool_t ''data'') <br>Writes a boolean to the output pin. Anything but 0 (zero) is interpreted as true.
* void '''putBoolean''' (''outPin'', bool_t ''data'') <br>Writes a boolean to the output pin. Anything but 0 (zero) is interpreted as true.


* void '''putJsonObject''' (''outPin'', jsonObject ''data'') <br>Writes a jsonObject to the output pin.
* void '''putJsonObject''' (''outPin'', jsonObject ''data'') <br>Writes a [[Embedded_Systems_C_Bridge_API#JSON_Library|jsonObject]] to the output pin. Refer to [[Embedded_Systems_C_Bridge_API#JSON_Library | the JSON library documentation]] on how to create a [[Embedded_Systems_C_Bridge_API#JSON_Library|jsonObject]].


* void '''putBytes''' (''outPin'', unsigned char* ''bytes'', int ''numBytes'') <br>Writes a byte array to the output pin.
* void '''putBytesN''' (''outPin'', unsigned char* ''bytes'', int ''nBytes'') <br>Writes nBytes from a byte array to the output pin.

* void '''putPointer''' (''outPin'', void* ''dataPtr'') <br>Writes a reference for dataPtr to the output pin. This value can be passed to another cBridge action (executed on the same bridge), where it can be read from the input with <code>pointerValue()</code> (this is similar to the makeRef mechanism of other bridged languages).

The following API will be available with the 21.2 release:

* void '''putDoublesN''' (''outPin'', double* ''doubles'', int ''count'') <br>Writes count double values as a vector to the output pin. expecco will receive these values as a collection of double precision floating point values.

* void '''putFloatsN''' (''outPin'', float* ''floats'', int ''count'') <br>Writes count float values as a vector to the output pin. expecco will receive these values as a collection of single precision floating point values.

* void '''putIntsN''' (''outPin'', int* ''ints'', int ''count'') <br>Writes count int values as a vector to the output pin. expecco will receive these values as a collection of integer values.

* void '''putLongsN''' (''outPin'', long* ''longs'', int ''count'') <br>Writes count long values as a vector to the output pin. expecco will receive these values as a collection of integer values.<br>Be reminded that the sizeof longs may be different between Windows and non-Windows machines.

These vectors will appear at the output pin as a collection; the type of collection is given by the pin's datatype. For most space efficient representation, we recommend the use of the bulk data types (FloatArray, DoubleArray, etc.)

The following API will be available with the 21.2.1 update release:

* void '''putShortsN''' (''outPin'', short* ''longs'', int ''count'') <br>Writes count (signed) short values as a vector to the output pin. expecco will receive these values as a collection of integer values.

* void '''putUIntsN''' (''outPin'', unsigned int* ''ints'', int ''count'') <br>Writes count unsigned int values as a vector to the output pin. expecco will receive these values as a collection of integer values.

* void '''putULongsN''' (''outPin'', unsigned long* ''longs'', int ''count'') <br>Writes count unsigned long values as a vector to the output pin. expecco will receive these values as a collection of integer values.<br>Be reminded that the sizeof longs may be different between Windows and non-Windows machines.

* void '''putUShortsN''' (''outPin'', unsigned short* ''longs'', int ''count'') <br>Writes count unsigned short values as a vector to the output pin. expecco will receive these values as a collection of integer values.


==== Transcript, Stderr and Stdout (C) ====
==== Transcript, Stderr and Stdout (C) ====


The expecco "<code>Transcript</code>", "<code>Stderr</code>" and "<code>Stdout</code>" are also accessible from C code. However, only a very limited subset of operations is supported:
The expecco "<code>Transcript</code>", "<code>Stderr</code>" and "<code>Stdout</code>" are also accessible from C code (as pseudo variables).
However, only a very limited subset of operations is supported (in the following, ''stream'' stands for one of the above wellknown expecco streams and is given as argument to the function):


*''void'' '''cr''' (''stream'')<br>Adds a linebreak (i.e. followup text will be shown on the next line)
*void '''cr''' (''stream'')<br>Adds a linebreak (i.e. followup text will be shown on the next line)


*''void'' '''show''' (''stream'', ''arg'', ...)<br>Adds a textual representation of the argument. The argument is printf-style.
*void '''show''' (''stream'', char* ''fmt'', ...)<br>Adds a textual representation of the argument. The argument is printf-style.


*''void'' '''showCR''' (''stream'', ''arg'', ...)<br>A combination of show(), followed by a linebreak.
*void '''showCR''' (''stream'', char* ''fmt'', ...)<br>A combination of show(), followed by a linebreak.


In addition, stdout and stderr are also forwarded to the expecco Transcript window, depending on the settings in expecco ("''Extras''" &#8594; "''Settings''" &#8594; "''Execution''" &#8594; "''Tracing''" &#8594; "''Show Stdout and Stderr on Transcript''").
If the bridge was started by expecco (as opposed to being executed on a remote host), stdout and stderr are also forwarded to the expecco Transcript window, depending on the settings in expecco ("''Extras''" &#8594; "''Settings''" &#8594; "''Execution''" &#8594; "''Tracing''" &#8594; "''Show Stdout and Stderr on Transcript''").

<br>Thus, you can use "<code>fprintf(stdout, ...)</code>" or "<code>fprintf(stderr, ...)</code>".
Thus, you can use "<code>fprintf(stdout, ...)</code>" or "<code>fprintf(stderr, ...)</code>" or "<code>showCR(Transcript, ...)</code>".
&nbsp;
&nbsp;

=== Referring to Functions in Shared Libraries ===

If the cBridge code refers to any functions which are located in additional shared libraries (DLLs), the bridge needs to know where to find those.

The location of any shared library must be specified in the C-bridge settings dialog, unless the library is found in the CBridge executable's folder or at a standard place (eg. "/usr/lib", "/usr/local/lib", etc. on Unix and "C:\Windows" and others on Windows).

Navigate to "''Extras''" &#8594; "''Settings''" &#8594; "''Execution''" &#8594; "''External Script Interpreters''" &#8594; "''CBridge''" and add the path to the "DLL Path" field.

As an alternative, edit the CC-Script and add appropriate command line arguments to the linker command near the end.

You'll also have to edit the script, if additional compiler or linkage arguments are required, or if you have to determine the location of additional frameworks dynamically.

=== Referring to Types defined in expecco ===

User defined struct, union and enum "CType (C-Defined)" dataypes (which are declared in expecco and present in the suite's element tree) are visible inside the activity code, and can be included as:
#include "expecco/Types/XXX.h"
where "XXX" is the name of the expecco type (in the tree).
If the type declares an unnamed struct, the type will be known as a typedef in the C-code.
==== Typedef Name vs. struct/union Name ====
If the type is named "myStruct" and defined as:
struct {
...
}
it should be referred to in the C-code as:
myStruct s, *sP;
In this case, the name of the typedef is the name of the type.

In contrast, if it was defined as:
struct foo {
...
}
you can also write:
struct foo s;
struct foo *sP;
In other words, there will be a typedef for "myStruct" (the name of the type) and a struct by the name as in the type's definition.

Notice, that these files do actually not exist; instead, the activity's C-code is scanned and dynamically expanded to include those definitions. See example below.
You should shutdown any already running bridge, when a type is changed, to enforce a recompilation of the C code.

=== Including Attachments as Header Files ===

Attached files can be included as:
#include "expecco/Attachments/XXX.h"
where "XXX.h" is the name of the expecco attachment file.
<br>Be reminded that the name of the attachment itself (the tree-item name) is not required to be the same as the name of the file, although usually, they are.

=== C++ Interfaces ===
C++ functions should be called indirectly via a C function wrapper (i.e. <code>extern "C"</code>).
<br>Object references can be passed from the bridge to expecco and back via the "<code>putPointer</code>" and "<code>pointerValue</code>" functions.

=== Running Bridged C-Code on a Remote Computer ===

The remote computer must have the cBridge executable running.
The executable is found in the "<code>packages/bridgeFramework/cBridge/cLibrary</code>" folder under your expecco installation folder and should be copied to the remote machine. Windows users will find both a 64bit version ("cBridge.exe") and a 32bit one ("cBrige32.exe"). If your C-code needs or refers to any existing dll, make sure that the correct bridge is configured in the settings (a 32bit bridge will not be able to load a 64bit dll and vice versa).

On the expecco side, you have two options:
# specify that the bridge is already running in the settings; then all cBridge actions which have not been explicitly given a bridge to execute in will be executed there
# explicitly connect to a running bridge via the "''Connect to Running Bridge''" action block, and passing the resulting bridge handle to the C-action.
The handle can be passed either explicitly via the step's cBridge input pin, or implicitly by declaring a variable named "CBRIDGE" and storing the handle there. The cBridge input pin is generated via the step's ''Special Pins'' menu.

Bridges can be started either by one of the bridge start actions in the standard library or by a batch-, shell- or powershell script. Type "<code>cBridge --help</code>" for command line options.

You can have multiple bridges running, even in heterogenous networks (i.e. call for a remote C-action on a Windows machine, while running expecco on a Linux machine).
We provide bridge executables for additional architectures (eg. Raspberry-PI) upon request and for a small additional fee.

=== Bulk Data ===

If a huge number of data elements (known as "''Bulk Data''") are to be transferred, transmission times may get much longer than the actual execution time of the action (keep in mind that the communication times take most of the time if the executed computation is simple).

There are multiple possible ways to deal with this situation:

* Passing a Pointer to expecco
:if you can leave the data inside the bridge (eg. as malloc'd data vector), '''and''' expecco does not need to access the elements of the vector, you can leave the data inside the cBridge and only pass a handle to expecco. This handle may later be sent to another C-action to process the data at non-critical times (eg. at the end of the test).
:This mechanism is the fastest because only a small handle (typically 32/64 bits) need to be transmitted to expecco. However, it degrades to become very very slow if you do access individual elements of the vector from expecco, as this will result in one IPC roundtrip per accessed element.
* Passing Bulk Data as a Vector
:If each vector element is sent individually to an output pin (i.e. in a loop with putFloat()/putDouble()), one IPC roundtrip will be performed per element.
:It is much faster to send the whole vector data sent as one array via putDoublesN(), putFloats() or putIntsN(). This still does introduce some additional cost due to JSON-encoding (on the cBridge side) and JSON-decoding on the expecco side, but the effective transmission time is dramatically reduced.
* Passing Bulk Data as a ByteArray
:Further speedup is possible by not sending the data as a number vector, but instead as a byte vector, actually sending the raw ended bytes from the bridge to expecco. For this, use a putBytesN() call and case your bulk data to a "char *".
:However (big warning): expecco will receive a byte-vector and you will have to add code to extract the relevant elements from this. If the cBridge CPU and the expecco-CPU use a different byte order or a different floating-point-number representations, you will have to deal with that in that extraction code. Be aware, that it may even depend on the compiler: Microsoft VisualC compilers for x86_64 interpret "long" as a 32bit integer, whereas most other compilers/systems treat them as 64bit integers.<br>
:This is probably the fastest method to transmit bytes, but comes with those possible pitfalls. We therefore recommend to try the "Bulk Data as Vector" method first. In most situations that is either within the acceptable timing constraints, or too far off, such that the ByteArray transmission will also not be fast enough, and you should use the pointer passing method anyway.

If the cBridge runs on the same machine as expecco, shared memory will be used to exchange bulk data. This may reduce the transmission times drastically, depending on the amount of data (there are still other messages exchanged via the socket communication).


=== Example1: Writing and Calling C Code in Expecco ===
=== Example1: Writing and Calling C Code in Expecco ===


In this example, numbers and a string will be passed to a C action block.<br>The action has been defined with 3 input pins:
In this example, numbers and a string will be passed to a C action block.<br>The action has been defined with 3 input pins:
* <code>in1</code> (with type Float)
* <code>in1</code> (type Float)
* <code>in2</code> (with type Integer)
* <code>in2</code> (type Integer)
* <code>in3</code> (with type String)
* <code>in3</code> (type String)
and the single output "<code>out1</code>", with type String.
and the single output "<code>out1</code>", with type String.


The code will generate a string by concatenating the printf strings of its arguments.
The code will generate a string by concatenating the printf strings of its arguments at the output
and also print a number of messages via different output channels.


Create a new bridged C action, and enter the code:
Create a new bridged C action,<br>
[[Datei:CBridge Demo Action1.png]]
<br>and enter the code:
static void
static void
Zeile 3.268: Zeile 3.972:
snprintf(buffer, sizeof(buffer), "%s / %ld / %g", sVal, lVal, dVal);
snprintf(buffer, sizeof(buffer), "%s / %ld / %g", sVal, lVal, dVal);
<span style="color:#007F00">// write to output pin</span>
<span style="color:#007F00">// write to output pin</span>
'''putString'''(out1, buffer);
'''putString'''(out1, buffer);
<span style="color:#007F00">// regular print (will be shown inside expecco's transcript, if io-trace is enabled</span>
<span style="color:#007F00">// a regular print</span>
fprintf(stderr, "hello from C-Code!\n");
<span style="color:#007F00">// (will be shown inside expecco's Transcript, if io-trace is enabled)</span>
fprintf(stderr, "hello on stderr from C-Code!\n");
<span style="color:#007F00">// another regular print</span>
<span style="color:#007F00">// (will be shown inside expecco's Transcript, if io-trace is enabled)</span>
fprintf(stdout, "hello on stdout from C-Code!\n");
<span style="color:#007F00">// explicit output to expecco's Transcript</span>
<span style="color:#007F00">// explicit output to expecco's stdout</span>
'''show'''(Stdout, "hello on expecco's stdout from C-Code!\n");
<span style="color:#007F00">// explicit output to expecco's stderr</span>
'''show'''(Stderr, "hello on expecco's stderr from C-Code!\n");
<span style="color:#007F00">// explicit output to expecco's Transcript</span>
<span style="color:#007F00">// explicit output to expecco's Transcript</span>
'''show'''(Transcript, "another hello from C-Code!\n");
'''show'''(Transcript, "another hello from C-Code!\n");
<span style="color:#007F00">// possibly report failure to expecco</span>
<span style="color:#007F00">// possibly report failure to expecco</span>
if (strlen(buffer) < 20) {
if (strlen(sVal) < 20) {
'''fail'''("short string (%d)", strlen(buffer));
'''fail'''("short string \"%s\" (length=%d)", sVal, strlen(sVal))
}
}
}
}


=== Example2: Passing Complex Objects to and from C-Code ===
=== Example2: Passing Complex Objects to and from C-Code (by value) ===


Complex struct objects can be passed by value to the C-code by defining a CStruct type
--- to be added ---
i.e. create a new type named eg. "myStruct" in the tree, define it as C-Type and enter a definition similar to:

/* C: */
struct {
int i1;
float f1;
double d1;
char c[20];
}

then, add an input pin with that datatype to the C-action,
and add the following line to the action's C-code, to include the type's definition:

#include "expecco/Types/myStruct.h"

(the name of the include must match the type's typename in the tree)

From within your C-code, access the fields with:

struct myStruct* p;
p = (struct myStruct*)'''bytesValue'''(inPin);
fprintf(stderr, "got i1:%d\n", p->i1);
fprintf(stderr, "got f1:%f\n", p->f1);
fprintf(stderr, "got d1:%F\n", p->d1);
fprintf(stderr, "got c:%s\n", p->c);

and write such a struct to an output pin with:

'''putBytesN'''(outPin, p, sizeof(struct myStruct));

To create such a struct argument, use an instance creation action block (and possibly additional field setter actions).
Those can be automatically generated by selecting the type in the tree,
and invoking the popup menu function "''Refactoring''" &#8594; "''Generate...''".

=== Example3: Passing Handles/Pointers to expecco and Back to Another C Action ===

A common situation is when two C actions are given, where one generates a handle (i.e. pointer),
which must be passed to the other action. For example, the first may be a "<code>createConnection()</code>" function,
and the second needs that connection handle as argument (typically a kind of "<code>send()</code>", "<code>receive()</code>" or "<code>close()</code>" function.

For this scenario, the cBridge provides a mechanism similar to the "<code>makeRef</code>" facility of other bridges.
The first action function will send the handle to an output pin via "<code>putPointer()</code>";
the pin's datatype should be "''Any''", "''Handle''" or a struct type as described above:

void execute() {
...
'''putPointer'''( outPin, (void*) handle );
}

and the other action functions can get this handle from an input pin:

void execute() {
handle = '''pointerValue'''( inPin );
...
}

Warning:<br>expecco does not interfere with or manage allocated memory. If data is malloc'd in the first action and passed via "<code>putValue()</code>" to another action, the data must be freed by calling "<code>free()</code>" eventually. Otherwise, you'll have a memory leak (in the cBridge).

Here is a concrete example:<br>
[[Datei:CBridge in out example.png]]
<br>with code:
void
execute() {
char* inVal = '''pointerValue'''(in1);
char* outVal;
fprintf(stderr, "inVal is \"%s\"\n", inVal);
outVal = malloc(strlen(inVal) + 10);
strcpy(outVal, inVal);
strcat(outVal, "XXX");
fprintf(stderr, "outVal is \"%s\"\n", outVal);
// write to output pin
'''putPointer'''(out1, outVal);
}

And a test setup:<br>
[[Datei:CBridge in out test setup.png]]

== Bridged Smalltalk Elementary Blocks ==

The following works in 20.1 and above.<br>
Similar to the above described bridged Node and bridged Python actions, these actions are coded in the Smalltalk language and executed by a separate Smalltalk engine (separate process). Both local and remote execution are possible.

By the time of writing, bridged Smalltalk actions can be executed in an ST/X Smalltalk engine, support for VA-Smalltalk and VW-Smalltalk is being developed and will be available in one of the next expecco releases.


=== Bridged Smalltalk Code API ===

Note: This is a preliminary API documentation.
Bridged Smalltalk elementary actions are still being developed and the API may change slightly until officially released.

Bridged Smalltalk actions will execute in another Smalltalk system; by the time of writing, this may be another Smalltalk/X or a VisualWorks Smalltalk system. With enough customer interest, other dialects (VisualAge and Squeak) may be supported in the future.
The API looks similar to the API of regular Smalltalk actions, which execute inside expecco itself.
However, for some objects, only a subset of the protocol is available.

==== Variables (Bridged Smalltalk) ====

The following variables are in the scope of the executed code:

*<code>Transcript</code> <br>a proxy which supports a few functions to display messages in the expecco Transcript window (see below)
*<code> Stdout </code> <br>a proxy supports a few functions to display messages on expecco's stdout stream (see below)
*<code> Stderr </code> <br>a proxy supports a few functions to display messages on expecco's stderr stream (see below)
<!--
*<code>bridge</code> <br>the bridge object which handles the communication with expecco.
-->
&nbsp;

==== Reporting (Bridged Smalltalk) ====

*'''error''':''infoString'' [ '''with''':''arg1'' ... '''with''':''arg4'' ] <br>Report a defect (in the test). Up to 4 optional with:-args are sliced into the string, if it contains "%i" placeholders. Stops execution.

*'''fail''':''infoString'' [ '''with''':''arg1'' ... '''with''':''arg4'' ] <br>Report a failure (in the SUT). Stops execution.

*'''inconclusive''':''infoString'' [ '''with''':''arg1'' ... '''with''':''arg4'' ] <br>Report an inconclusive test. Stops execution.

*'''activitySuccess''':''infoString'' [ '''with''':''arg1'' ... '''with''':''arg4'' ] <br>Finishes the current activity with success (same as "success()").

*'''pass''':''infoString'' [ '''with''':''arg1'' ... '''with''':''arg4'' ] <br>Finishes the current testCase with success.
&nbsp;

==== Logging (Bridged Smalltalk) ====

*'''logFail''':''messageString'' <br>Adds a fail message to the activity log.

*'''logError''':''messageString'' <br>Adds a error message to the activity log.

*'''logWarning''':''messageString'' <br>Adds a warning to the activity log.

*'''logInfo''':''messageString'' <br>Adds an info message to the activity log.

*'''alert''':''messageString''<br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "''Execution''" &#8594; "''Log Settings''" dialog (by default it is disabled).

*'''warn''':''messageString''<br>Same as ''alert:'' (for JavaScript compatibility).
&nbsp;

==== Environment Access (Bridged Smalltalk) ====
*'''environmentAt'''(&lt;varName&gt;);<br>Fetches and returns a value from the expecco environment which is in scope of the current activity.<br>You can only read simple objects (numbers, booleans and strings) from remote smalltalk actions.

*'''environmentAtPut'''(&lt;varName&gt;, &lt;newValue&gt;);<br>Writes a value into the expecco environment which is in scope of the current activity.<br>The variable must be writable.<br>You can only write simple objects (numbers, booleans and strings) from remote smalltalk actions.
<!--
*'''eval''' (''smalltalkCodeString'') <br>Evaluate a piece of Smalltalk code inside expecco.

*'''evalJS''' (''javascriptCodeString'') <br>Evaluate a piece of JavaScript code inside expecco.
-->
&nbsp;

==== Input Pins (Bridged Smalltalk) ====

*''pin'' '''hasValue'''<br>Returns true if the pin has received a value

*''pin'' '''value'''<br>Returns the value of the pin. Raises an error if the pin did not receive any value.

*''pin'' '''valueIfAbsent''':''alternativeValue'' <br>Returns the value of a pin or the value from alternativeValue if the pin did not receive any value.

*''pin'' '''valueIfPresent''' <br>Returns the value of a pin or nil if the pin did not receive any value.

*''pin'' '''isConnected''' <br>Returns true if the pin has a connection
&nbsp;

==== Output Pins (Bridged Smalltalk) ====

*''pin'' '''value''':''someValue''<br>Writes the value to the pin. Only simple object can be transferred by value (nil, booleans, integers, floats, strings).<br>Anything else should be passed by reference (see makeRef below).
&nbsp;

==== Transcript, Stderr and Stdout (Bridged Smalltalk) ====

The expecco "<code>Transcript</code>", "<code>Stderr</code>" and "<code>Stdout</code>" are also accessible from remote Smalltalk images.
However, only a limited subset of messages is supported:

*'''cr''' ()<br>Adds a linebreak (i.e. followup text will be shown on the next line)

*'''show''':''arg''<br>Adds a textual representation of the argument, which can be a string, number or any other object.

*'''showCR''':''arg''<br>A combination of show(), followed by a linebreak.

*'''nextPutAll''':''string''<br>String writing

*'''nextPutLine''':''string''<br>String printing with cr

In addition, stdout and stderr are also forwarded to the expecco Transcript window, depending on the settings in expecco ("''Extras''" &#8594; "''Settings''" &#8594; "''Execution''" &#8594; "''Tracing''" &#8594; "''Show Stdout and Stderr on Transcript''").
&nbsp;

==== Passing Objects by Reference (Bridged Smalltalk) ====
By default, objects written to output pins via "'''value:'''" will be marshalled to JSON, transferred to expecco and decoded there.
Effectively, a copy of the object is created, which looses its identity when sent back later to the remote smalltalk in another action.
This would make it impossible to get handles from a remote action, which is to be sent to another action on the same remote machine.
The "makeRef" method solves this.
*''self'' '''makeRef''':''object''<br>creates a reference object, which can be written to a pin.

*''self'' '''makeRef''':''object'' '''name''':''aString''<br>ditto, but gives it a user friendly name (eg. for the expecco log)

I.e. to write a reference to a pin, use:
''somePin'' '''value''':(self '''makeRef''':''someObject'')

=== Additional Information on Bridged VisualWorks Smalltalk Actions ===

Inside bridged VisualWorks actions, the above described "Transcript" refers to the expecco Transcript; not the VisualWorks Transcript (the remote code is compiled inside an environment, where the Transcript variable has been redefined).

To send output to the VisualWorks Transcript, use "Smalltalk.Core.Transcript", as in:
...
Smalltalk.Core.Transcript show:'Hello on VW Transcript'; cr
...
Transcript show:'Hello on expecco Transcript'; cr
...
Be aware, that the VW Transcript does not understand the "showCR:" message; you need "show:" followed by "cr".

== Bridged Ruby Elementary Blocks ==

Note: This is a preliminary API documentation.
Bridged Ruby elementary actions are still being developed and the API may change slightly until officially released.

Bridged Ruby actions will execute in a Ruby interpreter.
The API looks similar to the API of regular actions, which execute inside expecco itself or another language interpreter.
However, due to Ruby language specifics, some differences are noticable.

==== Variables (Bridged Ruby) ====

The following variables are in the scope of the executed code<br>(notice the "$" prefix; Ruby requires global variables to be prefixed by a "$" character):

*<code>$Transcript</code> <br>a proxy which supports a few functions to display messages in the expecco Transcript window (see below)
*<code> $Stdout </code> <br>a proxy which supports a few functions to display messages on expecco's stdout stream (see below)
*<code> $Stderr </code> <br>a proxy which supports a few functions to display messages on expecco's stderr stream (see below)
<!-- *<code> $Logger</code> <br>a proxy which supports a few functions to generate log messages using expecco's Logger stream (see below) -->
*<code> $Dialog</code> <br>a proxy which supports a few functions to display confirmation dialogs (see below)
&nbsp;

==== Reporting (Bridged Ruby) ====

*'''error'''(''infoString'' [ ,''arg1'' ... ,''arg4'' ]) <br>Report a defect (in the test). Up to 4 optional args are sliced into the string, if it contains "%i" placeholders. Stops execution.

*'''fail'''(''infoString'' [ ,''arg1'' ... ,''arg4'' ]) <br>Report a failure (in the SUT). Stops execution.

*'''inconclusive'''(''infoString'' [ ,''arg1'' ... ,''arg4'' ]) <br>Report an inconclusive test. Stops execution.

*'''activitySuccess'''(''infoString'' [ ,''arg1'' ... ,''arg4'' ]) <br>Finishes the current activity with success (same as "success()").

*'''pass'''(''infoString'' [ ,''arg1'' ... ,''arg4'' ]) <br>Finishes the current testCase with success.
&nbsp;

==== $Transcript, $Stderr and $Stdout (Bridged Ruby) ====

The expecco "<code>Transcript</code>", "<code>Stderr</code>" and "<code>Stdout</code>" are also accessible from Ruby actions (with a $-prefix).
Notice, that $Stderr refers to expecco's stderr and $Stdout refers to expecco's stdout. Both may be (and usually are) different from the Ruby interpreter's STDERR/STDOUT.

*'''cr''' ()<br>Adds a linebreak (i.e. followup text will be shown on the next line)

*'''show'''(''arg'')<br>Adds a textual representation of the argument, which can be a string, number or any other object.

*'''showCR'''(''arg'')<br>A combination of show(), followed by a linebreak.

*'''nextPutAll'''(''string'')<br>String writing

*'''nextPutLine'''(''string'')<br>String printing with cr

*'''puts'''(''string'')<br>String printing with cr

In addition, stdout and stderr are also forwarded to the expecco Transcript window, depending on the settings in expecco ("''Extras''" &#8594; "''Settings''" &#8594; "''Execution''" &#8594; "''Tracing''" &#8594; "''Show Stdout and Stderr on Transcript''").

&nbsp;

==== $Dialog (Bridged Ruby) ====


*'''confirm'''(''msg'')<br>Opens a simple yes/no dialog; returns a boolean.

*'''request'''(''msg'')<br>Opens a simple string-input dialog; returns a string or nil if cancel was pressed.

*'''requestPassword'''(''msg'')<br>same, but the entered string is not shown on the screen

*'''requestFilename'''(''msg'')<br>asks for a filename
&nbsp;

==== Logging (Bridged Ruby) ====

*'''logFail'''(''messageString'') <br>Adds a fail message to the activity log.

*'''logError'''(''messageString'') <br>Adds a error message to the activity log.

*'''logWarning'''(''messageString'') <br>Adds a warning to the activity log.

*'''logInfo'''(''messageString'') <br>Adds an info message to the activity log.

*'''alert'''(''messageString'')<br>Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "''Execution''" &#8594; "''Log Settings''" dialog (by default it is disabled).

*'''warn'''(''messageString'')<br>Same as ''alert:'' (for JavaScript compatibility).
&nbsp;
==== Environment Access (Bridged Ruby) ====
*'''environmentAt'''(&lt;varName&gt;);<br>Fetches and returns a value from the expecco environment which is in scope of the current activity.<br>You can only read simple objects (numbers, booleans and strings) from ruby actions.

*'''environmentAtPut'''(&lt;varName&gt;, &lt;newValue&gt;);<br>Writes a value into the expecco environment which is in scope of the current activity.<br>The variable must be writable.<br>You can only write simple objects (numbers, booleans and strings) from ruby actions.
<!--
*'''eval''' (''smalltalkCodeString'') <br>Evaluate a piece of Smalltalk code inside expecco.

*'''evalJS''' (''javascriptCodeString'') <br>Evaluate a piece of JavaScript code inside expecco.
-->
&nbsp;

==== Input Pins (Bridged Ruby) ====

*''pin''.'''hasValue'''()<br>Returns true if the pin has received a value

*''pin''.'''value'''()<br>Returns the value of the pin. Raises an error if the pin did not receive any value.

*''pin''.'''valueIfAbsent'''(''alternativeValue'') <br>Returns the value of a pin or the value from alternativeValue if the pin did not receive any value.

*''pin''.'''valueIfPresent'''() <br>Returns the value of a pin or nil if the pin did not receive any value.

*''pin''.'''isConnected'''() <br>Returns true if the pin has a connection
&nbsp;

==== Output Pins (Bridged Ruby) ====

*''pin''.'''value'''(''someValue'')<br>Writes the value to the pin. Only simple object can be transferred by value (nil, booleans, integers, floats, strings).<br>Anything else should be passed by reference (see <code>makeRef</code> below).
&nbsp;

==== Passing Objects by Reference (Bridged Ruby) ====
By default, objects written to output pins via "'''value:'''" will be marshalled to JSON, transferred to expecco and decoded there.
Effectively, a copy of the object is created, which looses its identity when sent back later to the remote ruby in another action.
This would make it impossible to get a handle from a remote action, which is to be sent to another action on the same remote machine.
The "<code>makeRef</code>" method solves this, by creating a "pointer" or "handle" to the object inside the remote machine.
*'''makeRef'''(''object'')<br>creates a reference object, which can be written to a pin.

*'''makeRef'''(''object'', '''name''')<br>ditto, but gives it a user friendly name (eg. for the expecco log)
:I.e. to write a reference to a pin, use:
::: ''somePin''.'''value'''('''makeRef'''(''someObject''))

== Bridged Dart Elementary Blocks ==
coming with one of the next expecco versions


== DotNET Elementary Blocks ==
== DotNET Elementary Blocks ==


Editing support for C# will be provided in a later expecco version. Currently, .NET objects must be instantiated and methods be called via the transparent message forwarning mechanism provided by Smalltalk. This means, that you use Smalltalk code to send (Smalltalk-) messages to proxy objects, which forward the call information to the corresponding bridge object.
--- to be documented ---

As an alternative, you can use bridged IronPython actions and refer to the assembly from there.


== VisualBasic Elementary Blocks ==
== VisualBasic Elementary Blocks ==
Zeile 3.304: Zeile 4.362:


These run on a remote expecco system, and can be used to generate load (stress) or perform additional measurements.
These run on a remote expecco system, and can be used to generate load (stress) or perform additional measurements.
Remote expecco actions will be available in or after the 19.2 expecco version.
Remote expecco actions will be available in or after the 24.2 expecco version.


--- to be documented ---
--- to be documented ---
Zeile 3.322: Zeile 4.380:


Notice, that in Smalltalk all collection indices are 1-based. I.e. the index of the first element is 1, and the last index is the collection's size. This is different from C, JavaScript and Java.
Notice, that in Smalltalk all collection indices are 1-based. I.e. the index of the first element is 1, and the last index is the collection's size. This is different from C, JavaScript and Java.
Thus Smalltalk loops over collection elements should run from 1 to the collection's size, not from zero to the size minus 1. You will get an index exception, if you try. Also notice, that index-based enumeration is actually seldom needed, due to the powerful "''do:''", "''collect:''", "''select:''" etc. family of enumeration methods.
Thus Smalltalk loops over collection elements should run from 1 to the collection's size, not from zero to the size minus 1. You will get an index exception, if you try.

These do all the index computations for you, and are often heavily tuned for best performance.
Also notice, that index-based enumerations (i.e. loops from 1 to the size) are actually seldom needed, due to the powerful "<code>do:</code>", "<code>collect:</code>", "<code>select:</code>" etc. family of enumeration methods.
These do all the index computations for you, and are often heavily tuned for better performance.


Also notice, that in Smalltalk, String is a subclass of Collection, and most of the functions described below are actually not implemented in the String class, but inherited from a superclass. Take this into consideration, when searching for String utility functions in the class browser (i.e. look there first or click on the [[Datei:SeeInheritedInBrowser.png]]-"see inherited methods" button in the class browser).
Also notice, that in Smalltalk, String is a subclass of Collection, and most of the functions described below are actually not implemented in the String class, but inherited from a superclass. Take this into consideration, when searching for String utility functions in the class browser (i.e. look there first or click on the [[Datei:SeeInheritedInBrowser.png]]-"see inherited methods" button in the class browser).
On the other hand, the fact that most of those functions are implemented in the Collection superclass also means, that they work on many other collections (Arrays, ByteArray, OrderedCollection).
On the other hand, the fact that most of those functions are implemented in the Collection superclass also means, that they work on many other collections (Arrays, ByteArray, OrderedCollection, Set, Dictionary etc.).


If you are in doubt, open a workspace window, enter a piece of code and evaluate it the (using the "''printIt''" menu function).
If you are in doubt, open a [[Tools_Notepad/en|workspace]] window, enter a piece of code and evaluate it the (using the "''printIt''" menu function).
Or open the method finder to find an operation given the desired outcome.
Or open the [[How_to_Program/en#Find_Functions_by_Example|method finder]] to find an operation given the desired outcome.




Zeile 3.358: Zeile 4.418:
To search for elements of a string (which are characters), use:
To search for elements of a string (which are characters), use:


''aString'' indexOf: ''aCharacter''
''aString'' indexOf: ''aCharacter''
''aString'' indexOf: ''aCharacter'' startingAt: ''startIndex''
''aString'' indexOf: ''aCharacter'' startingAt: ''startIndex''


Zeile 3.365: Zeile 4.424:


''aString'' lastIndexOf: ''aCharacter''
''aString'' lastIndexOf: ''aCharacter''
''aString'' lastIndexOf: ''aCharacter'' startingAt: ''startIndex''
''aString'' lastIndexOf: ''aCharacter'' startingAt: ''startIndex''


Zeile 3.378: Zeile 4.436:


''aString'' indexOfAny: ''aBunchOfCharacters''
''aString'' indexOfAny: ''aBunchOfCharacters''
''aString'' indexOfAny: ''aBunchOfCharacters'' startingAt: ''startIndex''
''aString'' indexOfAny: ''aBunchOfCharacters'' startingAt: ''startIndex''


Zeile 3.389: Zeile 4.446:
Both receiver and argument may actually be any kind of Collection;
Both receiver and argument may actually be any kind of Collection;
thus:
thus:

''aString'' indexOfAny: #( $a $A $e $E )
''aString'' indexOfAny: #( $a $A $e $E )


Zeile 3.405: Zeile 4.461:


''aString'' findFirst:[:ch | ch isDigit or:[ ch == $. or:[ ch == $$]]]
''aString'' findFirst:[:ch | ch isDigit or:[ ch == $. or:[ ch == $$]]]
''aString'' indexOfAny:'0123456789$.'
''aString'' indexOfAny:'0123456789$.'


Zeile 3.416: Zeile 4.471:


''aString'' indexOfSeparatorStartingAt: ''startIndex''
''aString'' indexOfSeparatorStartingAt: ''startIndex''
''aString'' indexOfNonSeparatorStartingAt: ''startIndex''
''aString'' indexOfNonSeparatorStartingAt: ''startIndex''
''aString'' indexOfControlCharacterStartingAt: ''startIndex''
''aString'' indexOfControlCharacterStartingAt: ''startIndex''


Zeile 3.436: Zeile 4.489:
''aString'' indexOfSubCollection: ''anotherString''
''aString'' indexOfSubCollection: ''anotherString''
''aString'' indexOfSubCollection: ''anotherString'' startingAt: ''startIndex''
''aString'' indexOfSubCollection: ''anotherString'' startingAt: ''startIndex''


There are also corresponding backward search variants "<code>lastIndexOfSubCollection:</code>".
There are also corresponding backward search variants "<code>lastIndexOfSubCollection:</code>".
Zeile 3.454: Zeile 4.507:
==== If you don't need the Index ====
==== If you don't need the Index ====


If you only need to check if a character or substring is present, but not in the exact position,
If you only need to check if a character or substring is present, but not its position,
you can use "<code>includes:</code>", "<code>includesSubCollection:</code>", "<code>includesAny:</code>" or "<code>includesAll:</code>" in a similar spirit.
you can use "<code>includes:</code>", "<code>includesString:</code>", "<code>includesString:caseSensitive:</code>", "<code>includesSubCollection:</code>", "<code>includesAny:</code>" or "<code>includesAll:</code>" in a similar spirit.
These may be slightly faster and make the code more readable. They return a boolean.
These may be slightly faster and make the code more readable. They return a boolean.


Zeile 3.507: Zeile 4.560:


Notice, that we used "<code>js_split:</code>" in the above examples, which is the method used by JavaScript's "<code>split()</code>" function. This is because in Smalltalk, the "<code>split:</code>" method has a different semantics: the receiver is a "''splitter''", and the argument is splitted by it.
Notice, that we used "<code>js_split:</code>" in the above examples, which is the method used by JavaScript's "<code>split()</code>" function. This is because in Smalltalk, the "<code>split:</code>" method has a different semantics: the receiver is a "''splitter''", and the argument is splitted by it.
<br>In Smalltalk, the splitter can be both a simple string or a regular expression. For example:
<br>In Smalltalk, the splitter can be both a simple string or a [[Regex Pattern Info| regular expression]]. For example:


'[a-z]*' asRegex split:'123abc456def789'
'[a-z]*' asRegex split:'123abc456def789'
Zeile 3.539: Zeile 4.592:
because many temporary string objects are created. Its time complexity is O(n<sup>2</sup>), where n is the number of strings concatenated.
because many temporary string objects are created. Its time complexity is O(n<sup>2</sup>), where n is the number of strings concatenated.


If you have to construct a string from many individually, either use one of the join functions below (with O(n) complexity),
If you have to construct a string from many substrings, either use one of the join functions below (with O(n) complexity),
or a writeStream with O(n log n) complexity.
or a writeStream with O(n log n) complexity.
Both handle the reallocations much more efficiently (actually: avoiding most of them).
Both handle the reallocations much more efficiently (actually: avoiding most of them).
Zeile 3.589: Zeile 4.642:
''formatString'' bindWithArguments: ''argumentCollection'' (any number of arguments)
''formatString'' bindWithArguments: ''argumentCollection'' (any number of arguments)
using "{" .. "}" as array constructor (notice the periods as expression separators), this is usually written as:
using the "<code>{ .. }</code>" array constructor (notice the periods as expression separators), this is usually written as:


''formatString'' bindWithArguments: { ''argument1'' . ''argument2'' . ... . ''argumentN'' }
''formatString'' bindWithArguments: { ''argument1'' . ''argument2'' . ... . ''argumentN'' }
Zeile 3.601: Zeile 4.654:


The last of the above methods is a powerful tool, in that it does not only handle argumentCollections indexed by numbers (i.e. Array of arguments), but also Dictionaries, which are indexed by name.
The last of the above methods is a powerful tool, in that it does not only handle argumentCollections indexed by numbers (i.e. Array of arguments), but also Dictionaries, which are indexed by name.

For example, if you have a Dictionary collection of a person record (in expecco: a compound datatype instance), with element keys "firstName", "lastName" and "city",
For example, if you have a Dictionary collection of a person record (in expecco: a compound datatype instance), with element keys "firstName", "lastName" and "city",
you can generate a nice printed string with:
you can generate a nice printed string with:
Zeile 3.614: Zeile 4.668:


gives you the string: 'Müller, Fritz lives in Stuttgart'.
gives you the string: 'Müller, Fritz lives in Stuttgart'.
In combination with a expecco compound datatype instance, this would be:
<br>In combination with a expecco compound datatype instance, this would be:


|person|
|person|
Zeile 3.655: Zeile 4.709:


Notice, that printf translates the standard C-character escapes "\n", ”\t" etc.
Notice, that printf translates the standard C-character escapes "\n", ”\t" etc.

==== Formatting Strings with Embedded Expressions (Method C) ====
A string constant prefixed with "e" is a so called expression-string. This may contain Smalltalk expressions enclosed in "{" .. "}", which are sliced into the string (actually, the expression's printString is sliced in). In addition, C-style escape sequences like "\n" are recognized in e-strings.

Transcript show: e'today is {Date today} and the time is {Time now}\nAnd the dayName is {Date today dayName}'.


==== Regex and Glob Matching ====
==== Regex and Glob Matching ====
Zeile 3.660: Zeile 4.719:
Both match a string against a match pattern, but the syntax and functionality is different.
Both match a string against a match pattern, but the syntax and functionality is different.
* Glob is much easier to use but less powerful. Glob is the match algorithm used in the Unix and MS-DOS filesystem (e.g. when saying "<code>ls *.foo</code>" or "<code>dir *.foo</code>" on the command line).
* Glob is much easier to use but less powerful. Glob is the match algorithm used in the Unix and MS-DOS filesystem (e.g. when saying "<code>ls *.foo</code>" or "<code>dir *.foo</code>" on the command line).
* Regex is the algorithm used in tools like "grep" and many text processing systems and languages.
* [[Regex Pattern Info| Regex]] is the algorithm used in tools like "grep" and many text processing systems and languages.


Notice that regex patterns are different and not just a superset of glob patterns. Especially the meanings of "*" and "." are different.
Notice that regex patterns are different and not just a superset of glob patterns. Especially the meanings of "*" and "." are different.
<br>
<br>
Please consult Wikipedia for more information on Regex [https://en.wikipedia.org/wiki/Regular_expression] and GLOB [https://en.wikipedia.org/wiki/Glob_%28programming%29].
Please consult Wikipedia for more information on Regex [https://en.wikipedia.org/wiki/Regular_expression] and GLOB [https://en.wikipedia.org/wiki/Glob_%28programming%29].
A chapter on regex is also found in this [[Regex Pattern Info| wiki]].


For a Glob match, use:
For a Glob match, use:
Zeile 3.735: Zeile 4.795:
gives you the records from the big collection as individual objects. If any modification is made to any of the slices, that modification is actually made in the underlying data collection (i.e. C-Programmers may think of this as a "''pointer into the data object''").
gives you the records from the big collection as individual objects. If any modification is made to any of the slices, that modification is actually made in the underlying data collection (i.e. C-Programmers may think of this as a "''pointer into the data object''").


Slices are especially useful with bulk data processing, to avoid the creation of individual record copies. They are a bit dangerous, as the programmer has to be always aware of the side effects.
Slices are especially useful with bulk data processing, to avoid the creation of individual record copies.

However, they are a bit dangerous, as the programmer has to be always aware of possible side effects. If you are uncertain, do not pass a slice to another (unknown) function; instead use the corresponding copy methods.


=== Stream Handling ===
=== Stream Handling ===
Zeile 3.741: Zeile 4.803:
Streams can be both internal streams (streaming out-of or into a collection) or external streams.
Streams can be both internal streams (streaming out-of or into a collection) or external streams.
External streams are always byte- or character-oriented and operate on files, sockets, pipes or i/o devices.
External streams are always byte- or character-oriented and operate on files, sockets, pipes or i/o devices.
An internal stream elements' type depends on the underlying collection.
An internal stream's element type depends on the underlying collection.


The following presents only a very small excerpt of the full stream protocol.
The following presents only a very small excerpt of the full stream protocol.
Zeile 3.750: Zeile 4.812:
streams for reading:
streams for reading:
rs := ReadStream on:''aCollection''.
rs := ReadStream on:''aCollection''.

rs := ''aCollection'' readStream
rs := ''aCollection'' readStream


streams for writing:
streams for writing:
ws := WriteStream on:(String new:''initialSize'')
ws := WriteStream on:(String new:''initialSize'')

ws := &#39;&#39; writeStream
ws := &#39;&#39; writeStream

ws := WriteStream on:(Array new)
ws := WriteStream on:(Array new)


Zeile 3.766: Zeile 4.828:


''s'' atEnd
''s'' atEnd
returns true iff the read stream "s" is positioned at the end.
returns true iff the read stream "s" is positioned at the end.

''s'' size
''s'' size

returns the size of the read stream's buffer, or the number of elements written into a write stream.
returns the size of the read stream's buffer, or the number of elements written into a write stream.


Zeile 3.876: Zeile 4.935:


''aString'' asFilename readStream
''aString'' asFilename readStream

''aString'' asFilename writeStream
''aString'' asFilename writeStream


Zeile 3.891: Zeile 4.950:


Thus, in general, code to read a number looks like:
Thus, in general, code to read a number looks like:

<class> readFrom:''aString''
<class> readFrom:''aString''

or:
or:

<class> readFrom:(ReadStream on:''aString'')
<class> readFrom:(ReadStream on:''aString'')


Zeile 3.920: Zeile 4.976:
Integer readFrom:'1234bla'
Integer readFrom:'1234bla'
will raise an error, but:
will raise an error, but:
myStream := ReadStream on:'1234bla'.
Integer readFrom: myStream.


returns the integer 1234 and leave the stream positioned on the 'bla'. Thus further elements can be read from the stream later.
Integer readFrom:(ReadStream on:'1234bla')

returns the integer 1234 and leave the stream positioned on the 'bla'.


Notice that "<code>Integer readFrom:</code>" will read an integer, but not a float.
Notice that "<code>Integer readFrom:</code>" will read an integer, but not a float.
Zeile 3.957: Zeile 5.013:


will return the number as usual if OK, but zero if not.
will return the number as usual if OK, but zero if not.
Of course, arbitrary code can be provided inside this handler - especially it may show an error dialog and ask the user for a replacement, or return from the executed elementary block:
<br>Of course, arbitrary code can be provided inside this handler - especially it may show an error dialog and ask the user for a replacement, or return from the executed elementary block:


val := Integer
val := Integer
Zeile 4.035: Zeile 5.091:


myString := '1234 456.7 0.5 2345.3456 3234234234234234234234234234234234234'.
myString := '1234 456.7 0.5 2345.3456 3234234234234234234234234234234234234'.
values := '%d %f %f %f %d' scanf:myString.
values := '%d %f %f %f %d' scanf: myString.


generates a collection of numbers in "values":
generates a collection of numbers in "values":
Zeile 4.106: Zeile 5.162:
For example, to fetch numbers after certain keywords in a string, you could use:
For example, to fetch numbers after certain keywords in a string, you could use:


"/ assuming that the string is of the form:
<span style="color:#007F00">"/ assuming that the string is of the form:</span>
"/ <..arbitrary-text...>key1<number1><...arbitrary text...>key2<number><...arbitrary text...>
<span style="color:#007F00">"/ <..arbitrary-text...>key1<number1><...arbitrary text...>key2<number><...arbitrary text...></span>
"/ where arbitrary text may even contain key1 and key2, but not followed by a number,
<span style="color:#007F00">"/ where arbitrary text may even contain key1 and key2, but not followed by a number,</span>
"/ we first use a regex to match, then convert the matched regex parts:
<span style="color:#007F00">"/ we first use a regex to match, then convert the matched regex parts:</span>
myString := 'bla bla key1 bla key11234more bla key2 bla key29876bla bla'.
myString := 'bla bla key1 bla key11234more bla key2 bla key29876bla bla'.
parts := myString allRegexMatches:'((key[12]))([0-9]+)'.
parts := myString allRegexMatches:'((key[12]))([0-9]+)'.
<span style="color:#007F00">"/ now, parts contains #( 'key11234' 'key29876'</span>
numbers := parts collect:[:eachPart | Number readFrom: (eachPart copyFrom:5) ].
numbers := parts collect:[:eachPart | Number readFrom: (eachPart copyFrom:5) ].
<span style="color:#007F00">"/ now, numbers contains #( 1234 9876 )</span>


notice that parts delivers the matched strings i.e. the collection ('key11234' 'key29876'),
notice that ''parts'' delivers the matched strings i.e. the collection ('key11234' 'key29876'),
so that we have to skip over the 'keyX' prefix before extracting the numbers.
so that we have to skip over the 'keyX' prefix before extracting the numbers.


Zeile 4.134: Zeile 5.192:


''aString'' asFilename - to get a Filename-instance for a given String instance
''aString'' asFilename - to get a Filename-instance for a given String instance

''aFilename'' readStream - to get a readStream on the contents of a file
''aFilename'' readStream - to get a readStream on the contents of a file

''aFilename'' writeStream - to get a writeStream writing to a file
''aFilename'' writeStream - to get a writeStream writing to a file

''aFilename'' appendingWriteStream - to get a writeStream appending to the end of a file
''aFilename'' appendingWriteStream - to get a writeStream appending to the end of a file


Zeile 4.144: Zeile 5.202:


''aFilename'' exists - true if the file exists
''aFilename'' exists - true if the file exists

''aFilename'' isReadable - true if it is readable
''aFilename'' isReadable - true if it is readable

''aFilename'' isWritable
''aFilename'' isWritable

''aFilename'' isExecutable - for folders, this means: "can be changed into"
''aFilename'' isExecutable - for folders, this means: "can be changed into"

''aFilename'' isExecutableProgram
''aFilename'' isExecutableProgram

''aFilename'' fileSize
''aFilename'' fileSize

''aFilename'' isDirectory
''aFilename'' isDirectory

''aFilename'' isNonEmptyDirectory
''aFilename'' isNonEmptyDirectory

''aFilename'' isRegularFile
''aFilename'' isRegularFile

''aFilename'' isSymbolicLink
''aFilename'' isSymbolicLink

''aFilename'' info - gets all info; follows any symbolic link. Includes ownerID, groupID, access and modification times etc.
''aFilename'' info - gets all info; follows any symbolic link. Includes ownerID, groupID, access and modification times etc.

''aFilename'' linkInfo - gets all info of a symbolic link itself. Includes ownerID, groupID, access and modification times etc.
''aFilename'' linkInfo - gets all info of a symbolic link itself. Includes ownerID, groupID, access and modification times etc.

''aFilename'' accessTime
''aFilename'' accessTime

''aFilename'' modificationTime
''aFilename'' modificationTime

''aFilename'' creationTime (same as modificationTime on Unix systems)
''aFilename'' creationTime (same as modificationTime on Unix systems)

''aFilename'' fileType
''aFilename'' fileType

''aFilename'' mimeType
''aFilename'' mimeType


Zeile 4.180: Zeile 5.238:


''aFilename'' pathName
''aFilename'' pathName

''aFilename'' directory - the containing directory, as a Filename instance
''aFilename'' directory - the containing directory, as a Filename instance

''aFilename'' directoryName - ditto, as string
''aFilename'' directoryName - ditto, as string

''aFilename'' baseName
''aFilename'' baseName

''aFilename'' suffix
''aFilename'' suffix

''aFilename'' hasSuffix:''aString''
''aFilename'' hasSuffix:''aString''

''aFilename'' isAbsolute
''aFilename'' isAbsolute

''aFilename'' isRelative
''aFilename'' isRelative

''aFilename'' / ''subPath'' - yes, "/" is an operator to construct the name of a file inside a folder
''aFilename'' / ''subPath'' - yes, "/" is an operator to construct the name of a file inside a folder
alternative:
alternative:
Zeile 4.202: Zeile 5.260:


''aFilename'' makeDirectory
''aFilename'' makeDirectory

''aFilename'' recursiveMakeDirectory - ensures that all needed intermediate folders along the path are also created.
''aFilename'' recursiveMakeDirectory - ensures that all folders along the path are also created.

''aFilename'' removeDirectory - raises an error, if the folder is not empty
''aFilename'' removeDirectory - raises an error, if the folder is not empty

''aFilename'' recursiveRemoveDirectory - removes everything below also
''aFilename'' recursiveRemoveDirectory - removes everything below also

''aFilename'' moveTo:''newName''
''aFilename'' moveTo:''newName''

''aFilename'' copyTo:''newName''
''aFilename'' copyTo:''newName''

''aFilename'' recursiveCopyTo:''destinationName''
''aFilename'' recursiveCopyTo:''destinationName''



==== Directory Contents ====
==== Directory Contents ====


''aFilename'' directoryContents
''aFilename'' directoryContents

''aFilename'' directoryContentsDo:[:eachFile | ...]
''aFilename'' directoryContentsDo:[:eachFile | ...]

''aFilename'' filesMatching:aGLOBPatternString
''aFilename'' filesMatching:aGLOBPatternString


Zeile 4.229: Zeile 5.286:
retrieves the contents as a collection of line strings.
retrieves the contents as a collection of line strings.
Be careful - this should not be used for huge files.
Be careful - this should not be used for huge files.

''aFilename'' contentsAsString
''aFilename'' contentsAsString
retrieves the contents as one (possibly big) string.
retrieves the contents as one (possibly big) string.
Be careful - this should not be used for huge files.
Be careful - this should not be used for huge files.

''aFilename'' binaryContentsOfEntireFile
''aFilename'' binaryContentsOfEntireFile
retrieves the contents as one (possibly big) byte array.
retrieves the contents as one (possibly big) byte array.
Be careful - this should not be used for huge files.
Be careful - this should not be used for huge files.

''aFilename'' readingLinesDo:[:eachLineString | ... ]
''aFilename'' readingLinesDo:[:eachLineString | ... ]
better use this for large files. Ensures that the file is closed.
better use this for large files. Ensures that the file is closed.

''aFilename'' readingFileDo:[:stream | ... ]
''aFilename'' readingFileDo:[:stream | ... ]
or that to read inside the code block. Ensures that the file is closed.
or that to read inside the code block. Ensures that the file is closed.
Zeile 4.252: Zeile 5.309:


The following code examples contain versions for Smalltalk, JavaScript, Groovy and Node.js.
The following code examples contain versions for Smalltalk, JavaScript, Groovy and Node.js.
Notice that Groovy and Node.js actions are invoked via a remote procedure call (RPC) mechanism, which means that they are much slower due to the communication, encoding, decoding and general round trip delays. Therefore, Groovy and Node.js blocks (bridged actions in general) should only be used to access data inside the system under test or to call functions which are not available in the base system (such as additional protocols or hardware access, for which only a JAR or node- or python package is available). Or, functions for which the call overhead is small compared to the runtime (i.e. for heavy computing tasks, it may very well make sense).
Notice that Groovy and Node.js actions are invoked via a remote procedure call ([[Glossary#RPC|RPC]]) mechanism, which means that they are much slower due to the communication, encoding, decoding and general round trip delays. Therefore, Groovy and Node.js blocks (bridged actions in general) should only be used to access data inside the system under test or to call functions which are not available in the base system (such as additional protocols or hardware access, for which only a JAR or node- or python package is available). Or, functions for which the call overhead is small compared to the runtime (i.e. for heavy computing tasks, it may very well make sense).


Thus, most of the examples below are for demonstration purposes, not for real world scenarios (i.e. it will certainly slow down your test runs, if you do math via Groovy or even Node.js, as shown below).
Thus, most of the examples below are for demonstration purposes, not for real world scenarios (i.e. it will certainly slow down your test runs, if you do math via Groovy or even Node.js, as shown below).
Zeile 4.259: Zeile 5.316:
in Groovy, because there will be no signalling of overflow/underflow situations. Especially with integer arithmetic, which may be outside the 32bit/64bit range. Also, Java has no exact fraction representations, which means that you may loose precision in floating point operations.
in Groovy, because there will be no signalling of overflow/underflow situations. Especially with integer arithmetic, which may be outside the 32bit/64bit range. Also, Java has no exact fraction representations, which means that you may loose precision in floating point operations.


Things are even worse in Node.js, which represents all numbers as double precision floats, giving roughly 53 bits of precision. Thus, in Node.js the two integers 12345678901234567890 and 12345678901234567891 will be reported as being equal!
Things are even worse in Node.js, which represents all numbers as double precision floats, giving roughly 53 bits of precision. Thus, in Node.js the two integers 12345678901234567890 and 12345678901234567891 will be reported as being equal! (this may change in a future Node version, though).


[[Datei:point_right.png|20px]] Do NOT use Groovy or Node.js for numeric computations, if large integer values are involved.
[[Datei:point_right.png|20px]] Do NOT use Groovy or Node.js for numeric computations, if large integer values are involved (or use a Bignum package).


=== Reading/Writing Pin Values ===
=== Reading/Writing Pin Values ===
Zeile 4.315: Zeile 5.372:
}
}


Note: be aware, that the calling overhead of bridged actions is in the order of milliseconds,
Note: be aware, that due to the Remote Procedure Call ([[Glossary#RPC|RPC]]), the calling overhead of bridged actions is in the order of milliseconds,
whereas actions executed inside expecco (Smalltalk and JavaScript) are executed typically within a few hundred nanoseconds.
whereas actions executed inside expecco (Smalltalk and JavaScript) are executed typically within a few hundred nanoseconds.

Thus, bridged actions are typically used to invoke longer running, complex operations or to trigger services, which run autonomous.
Thus, bridged actions are typically used to invoke longer running, complex operations or to trigger services, which run autonomous, or to talk to interfaces/devices. They are normally not be used for trivial operations like string manipulation or arithmetic.


=== Reading/Writing Environment Variables ===
=== Reading/Writing Environment Variables ===
Zeile 4.449: Zeile 5.507:


===== Groovy=====
===== Groovy=====
(runs inside a JVM/the SUT, when executed).<br>As mentioned above, this kind of operation should definitely be executed in Smalltalk/JavaScript inside expecco, and not in the system under test, due to the RPC overheads.
(runs inside a JVM/the SUT, when executed).<br>As mentioned above, this kind of operation should definitely be executed in Smalltalk/JavaScript inside expecco, and not in the system under test, due to the [[Glossary|RPC]] overheads.
def execute {
def execute {
Object inValue;
Object inValue;
Zeile 4.503: Zeile 5.561:
=== Calling other Actions ===
=== Calling other Actions ===


Script code can call other action blocks via the "call" method/function.
Script code can call other action blocks via the "<code>call</code>" method/function.
Notice, that the call is "''by name''" or "''by ID''", and the called function is specified by a string argument.
Notice, that the call is "''by name''" or "''by ID''", and the called function is specified by a string argument.
The following examples assume that action blocks named "''Action_A''" and "''Action_B''" are present, and that "Action_B" expects two numeric arguments and returns a value through its single output pin.
The following examples assume that action blocks named "''Action_A''" and "''Action_B''" are present, and that "Action_B" expects two numeric arguments and returns a value through its single output pin.
Zeile 4.631: Zeile 5.689:
]
]


Take a look at the "Dialog" class (using "Extras" - "Tools" - "Class Browser"),
Take a look at the "Dialog" class (using "''Extras''" - "''Tools''" - "''Class Browser''"),
where you'll find many common standard dialogs, for confirmations, simple questions, file dialogs etc.
where you'll find many common standard dialogs, for confirmations, simple questions, file dialogs etc.


Zeile 4.651: Zeile 5.709:
The Smalltalk/X class library has been around for more than 30 years now,
The Smalltalk/X class library has been around for more than 30 years now,
and of course grew over time.
and of course grew over time.
We have always tried hard to keep the API backward compatible, in not removing existing methods, even when new methods appeared which provided a similar or superset functionality.
We have always tried hard to keep the API backward compatible, in not removing existing methods, even when new methods appeared, which provided a similar or superset functionality.


Thus, code written 30 years ago can still depend on the interface and it is still working unchanged.
Thus, code written 30 years ago can still depend on the interface and it is still working unchanged.
Zeile 4.662: Zeile 5.720:
If you use internal expecco functionality (which you may find when browsing in the class browser),
If you use internal expecco functionality (which you may find when browsing in the class browser),
we cannot guarantee backward compatibility in future versions.
we cannot guarantee backward compatibility in future versions.
If you feel a need for using such an interface, please consult eXept to either get help on a cleaner solution, and/or to get your used interface be marked as "stable EXPECCO_API".
If you feel a need for using such an interface, please consult/inform eXept to either get help on a cleaner solution, and/or inform us that the API is used and to get your used interface be marked as "stable EXPECCO_API".


== Appendix ==
== Appendix ==
Zeile 4.670: Zeile 5.728:
See also: [http://rosettacode.org/wiki/Category:Smalltalk Smalltalk Short Description ], [http://live.exept.de/doc/online/english/getstart/tut_2.html Smalltalk Basics]
See also: [http://rosettacode.org/wiki/Category:Smalltalk Smalltalk Short Description ], [http://live.exept.de/doc/online/english/getstart/tut_2.html Smalltalk Basics]


<span style="color:#007F00">;; notice: an elementary Smalltalk action's "execute" method is a unary method (no argument).</span>
<span style="color:#007F00">;; Notice: an elementary Smalltalk action's "execute" method is a unary method (no argument).</span>
<span style="color:#007F00">;; there can be only one single such method in an elementary action (private functions should be defined as Smalltalk blocks)</span>
<span style="color:#007F00">;; there can be only one single such method in an elementary action (private functions should be defined as Smalltalk blocks)</span>
<span style="color:#007F00">;;</span>
<span style="color:#007F00">;; This is standard Smalltalk language, with Smalltalk/X language extensions</span>
<span style="color:#007F00">;;</span>
method ::= selectorSpec body
method ::= selectorSpec body
Zeile 4.712: Zeile 5.772:
primary ::= identifier
primary ::= identifier
| literalConstant
| literalConstant
| expandedStringExpression
| braceArray
| braceArray
| "(" expression ")"
| "(" expression ")"
Zeile 4.745: Zeile 5.806:
byteArrayConstant ::= "#[" integerConstant(0..255)* "]"
byteArrayConstant ::= "#[" integerConstant(0..255)* "]"
stringConstant ::= " ' " character* " ' "
stringConstant ::= ' characters... '
| cString
characterConstant ::= "$" character
characterConstant ::= "$" character
braceArray ::= "{" [ expression | expression ( "." expression )+ ] "}"
block ::= "[" [ blockArgList ] body "]”
block ::= "[" [ blockArgList ] body "]”
Zeile 4.783: Zeile 5.843:
TOKEN
TOKEN
braceArray ::= "{" [ expression | expression ( "." expression )+ ] "}" (an array with computed elements)
cString ::= c'string-characters with C-escapes'
literalObject ::= "#{" ( fieldName: constant )+ "}" (a constant object with getters for fields)
immediateObject ::= "{" ( fieldName: expression )+ "}" (an immediate object with getters and setters)
cString ::= c'string with C-escapes'
eString ::= e'string with C-escapes and {embedded expressions}'
iString ::= i'string with C-escapes and {embedded expressions} which is also internationalized'
typedArrayLiteral ::= #u8(...) "/ unsigned byte array constant
typedArrayLiteral ::= #u8(...) "/ unsigned byte array constant
Zeile 4.810: Zeile 5.880:
-- incomplete w.r.t. expression syntax -- to be added
-- incomplete w.r.t. expression syntax -- to be added


<span style="color:#007F00">;; notice: an elementary JavaScript action's "execute" function must be a unary function (no argument).</span>
<span style="color:#007F00">;; Notice: an elementary JavaScript action's "execute" function</span>
<span style="color:#007F00">;; however, it is possible to define additional (private) helper functions.</span>
<span style="color:#007F00">;; must be a unary function (no argument).</span>
<span style="color:#007F00">;; </span>
<span style="color:#007F00">;; However, it is possible to define additional (private) helper functions.</span>
<span style="color:#007F00">;; These will only be visible within that single elementary action's code.</span>
<span style="color:#007F00">;; These will only be visible within that single elementary action's code.</span>
<span style="color:#007F00">;; </span>
<span style="color:#007F00">;; Also notice the syntax extensions for character constants and the "return from" statement.</span>
<span style="color:#007F00">;; </span>
method ::= functionSpec functionBody
method ::= functionSpec functionBody
Zeile 4.939: Zeile 6.013:
----
----
Back to [[ Online_Documentation#Code_API_Overview Online Documentation ]]
Back to [[ Online_Documentation#Code_API_Overview Online Documentation ]]

[[Category: Advanced/en]]<br>

Aktuelle Version vom 18. März 2024, 14:44 Uhr

Inhaltsverzeichnis

How to Program[Bearbeiten]

Before you start programming, please read the "How to Program" document, which describes how program code is handled in expecco. Unless you are familiar with the dynamics of a Smalltalk development environment, some of it may be unknown to you, and you will have more fun and be more productive, if you know the power of the tools. For the best development experience, take a look at the debugger, workspace (notepad) and data inspectors.

The rest of this document describes the syntax and semantics of the elementary action languages. For tool usage, please read the HowTo document.

expecco API[Bearbeiten]

The expecco API provides functions and access to the underlying class library for the use in elementary blocks written in Smalltalk and JavaScript (i.e. for code which is executed inside expecco itself). This API is not available for elementary blocks written in other languages which are executed by external script engines (Shell, Batch, Python, Node.js etc.) or inside a different Java or CLR Virtual Machine (Groovy, VBScript, IronPython). However, a subset of the functions are supported as RPC (remote procedure calls) in bridged elementary actions.

For a short introduction to the Smalltalk programming language, please read the Smalltalk tutorial in the Smalltalk/X online manual. For a full book on learning Smalltalk, read "The joy of Smalltalk, An introduction to Smalltalk" by Ivan Tomek.

The API for Groovy elementary blocks is different and described below.
The API for bridged Node.js actions is described here.
The API for bridged Python and Jython actions is described here.
The API for bridged Ruby actions is described here.
The API for bridged C actions is described here.

JavaScript and Smalltalk Elementary Blocks[Bearbeiten]

The expecco JavaScript and the Smalltalk API consist of the same functions - both call into the underlying Smalltalk/X system, for which extensive documentation is available as Online Documentation and as Class Reference.

The Smalltalk/X language and class library are ANSI compatible; therefore the official ANSI documents, Smalltalk literature and tutorials are also valuable sources of information (for example, the online tutorials you may find in youtube).

If you wonder "why Smalltalk?", you should know that Smalltalk ranked nr. 2 in a 2017 survey of "most loved languages" - admittedly, this ranking may be subjective (in that Smalltalk programmers might be more loyal), but it should at least raise your eyebrows.
It is not among the most used languages - but that decision is usually made by people who do not even know Smalltalk or who think that language does not matter.
If you think it is outdated, be reminded that Smalltalk has the most consistent object model and reflection facilities of all OO languages, and that modern clones like Ruby and Python only provide a subset of Smalltalk's facilities. If you don't know it, don't judge it.
The underlying virtual machine (VM) is feature rich and provides many mechanisms which are not possible in most 'modern' languages. For example, in most (if not all) of the current languages it is not possible to interrupt a thread which blocks in a system call (eg. in a read). In none of them, are exceptions proceedable. In none of them can you interrupt other threads in any situation (again: especially not if it is inside a blocking read).
The set of integrated tools is massive in Smalltalk: full access to classes, threads, semaphores, any object's internals are all provided by graphical tools which can be opened by double clicks or via menus.

Expecco's JavaScript is implemented by compiling JavaScript syntax to the underlying Smalltalk bytecode. It is not fully compatible with a "real" JavaScript: it is class based, and the underlying object model and class library are actually the Smalltalk/X object model and class library. Thus, expecco's JavaScript can be seen as "a Smalltalk with JavaScript syntax".

Please be not confused by two JavaScript-based elementary actions: the builtin JavaScript described here is executed within expecco itself whereas Node-actions are executed in a separate Node.js virtual machine. These Node actions are "real JavaScript" and are described in a separate chapter below.

Smalltalk / JavaScript Short Syntax Overview[Bearbeiten]

Expecco's builtin JavaScript and Smalltalk only differ in their syntax - semantically they are very similar:

JavaScript Smalltalk
this self the current activity (inside an elementary action)
this.functionName() self functionName a call without arguments
this.functionName(arg) self functionName:arg a call with one argument
this.namePart1_part2(arg1,arg2) self namePart1:arg1 part2:arg2 two arguments
Notice that in Smalltalk, the arguments are sliced into the name parts, and that the concatenation of the parts is the actual name (incl. the colons).
Thus, in "x at:5 put:10" the name of the called method is "at:put:" and it gets two arguments: 5 and 10. Whereas "(x at:5) put:10" would first send an "at:" message, then send a"put:" message to the object returned from "at:".
In JavaScript, ":" is not a value character in a function name, and a translation mechanism is applied which replaces ":" by underline, and drops the final colon.
functionName(...) self functionName... implicit this receiver in JS
explicit in Smalltalk
accessor self accessor slot access - implicit this receiver in JS
explicit in Smalltalk
stat1 ; stat2 ; stat1 . stat2 statement terminator / separator
in ST: to separate statements
in JS: to terminate a statement.
In Smalltalk, the last period "." inside a method or block can be and often is omitted.
if (cond) {
    ifStats
} else {
    elseStats
}
cond ifTrue:[
    ifStats
] ifFalse:[
    elseStats
]
conditional execution.
Notice the square brackets in Smalltalk
while (cond) {
    stats
}
[ cond ] whileTrue:[
    stats
]
while-loop.
Notice the brackets around the condition in Smalltalk
for (i=start; i<=end; i++) {
    stats using i
}
start to:end do:[:i |
    stats using i
]
counting-loop.
Seldom used in Smalltalk
foreach (el in collection) {
    stats using el
}
collection do:[:el |
    stats using el
]
loop over collection elements.
Very often used in Smalltalk
function () { stats } [ stats ] an anonymous (inner) function (called "block" in ST).
JS: return value via explicit return
ST: return value is last expression's value
function (a1, a2,...) { stats } [:a1 :a2 ...| stats ] in ST: blocks are references to anonymous function
var v1, v2, ... ; | v1 v2 ... | local variables inside a function (or block). Semantically, these are "let" variables (i.e. only seen in the current scope)
return expr; ^ expr return a value.
In ST: a return from within a block returns the enclosing top-level method
In JS: a return returns from the inner function
'...' String constant without c-escapes
"..." or '...' c'...' String constant with c-escapes (\n, \t, etc.)
e'..{ expr1 } .. { expr2 } ...' String with sliced in expressions (exprs will be converted to string)
/* ... */ " ... " comment
// ... "/ ... end of line comment
"<<TOKEN
...
TOKEN
token comment

Smalltalk Operator Precedence[Bearbeiten]

Smalltalk beginners may be irritated by the missing binary operator precedence rules: in Smalltalk, all operators have the same precedence and are evaluated left to right.
Thus:

a + b * 5 

in JavaScript means:

a + (b * 5) 

whereas in Smalltalk, it is evaluated as:

(a + b) * 5

(i.e. left to right).

Therefore, as a guideline (and actually a good convention), all Smalltalk binary expressions with more than one operator should be parenthesized to make the intention clear. It should be done even if the left-to-right order matches the mathematical precedences.

Smalltalk Operators[Bearbeiten]

Another uncommon feature of the Smalltalk language is that there are almost no keywords in the language (except "self", "super", "true", "false", "nil" and "thisContext"). Every other word is either the name of a variable or the name of a message to be sent to an object (aka a "virtual function call" or "method invocation").

Every sequence of special characters (except ":", ".", "(", ")", "[", "]", ":=" and ";") is interpreted as operators. Thus, the sequences "->", "=", "=>", "<-" and especially the comma (",") are operators which are treated like "+", "-" etc.

Finally, message sends ("function calls") with arguments are always written with keyword arguments. I.e. the arguments are prefixed by a keyword (which is an identifier with a colon). Smalltalk does not allow positional arguments in a message send.

Calling Functions and Invoking Methods (aka "Sending Messages")[Bearbeiten]

All functions are actually implemented in Smalltalk and follow the standard Smalltalk naming conventions. The same function names are used for JavaScript. As seen above, this scheme works well for functions without or with a single argument, but requires a name translation for functions with more than one argument. This translation is done by the JavaScript compiler by replacing every colon (:) of the Smalltalk name by an underline (_) character, except for the last colon. Thus for example, the Smalltalk name "at:put:" will be named "at_put" in JavaScript.

In JavaScript, a function-name alone (i.e. without explicit receiver) is translated into a self-send; thus "this.foo()" and "foo()" are equivalent.


For example, the JavaScript call:

this.environmentAt("foo")

is written in Smalltalk as:

self environmentAt:'foo'

and, to demonstrate the multi-argument translation rule, the Smalltalk code:

self environmentAt:'foo' put:1234

is written in JavaScript as:

this.environmentAt_put("foo", 1234)

Syntax Summary[Bearbeiten]

For a formal specification of the JavaScript and Smalltalk languages, see the appendixes below. The following gives a rough overview over the most common syntactic constructs.

JavaScript Smalltalk Notes
this self the current activity
null nil a null reference (UndefinedObject)
; . statement terminator/separator
In JavaScript it is a terminator, meaning that every statement (except brace-blocks) must be terminated by a semicolon.
In Smalltalk, it is a separator, meaning that the last statement in a block or method does not need one.
"..." '...' a String constant (JavaScript allows single quotes too);
no C-escapes in Smalltalk
c'...' a Smalltalk String constant with c-escapes
e'..{ expr } ..' a Smalltalk String with embedded expressions (sliced-in)
#'...' a Symbol constant
(not available in JavaScript; use "xxx".asSymbol() in JS)
#... also a Symbol constant
(not available in JavaScript; use "xxx".asSymbol() in JS)
[ el1 , el2 , ... ] #( el1 el2 ... ) an Array constant
(elements must be constant literals)
Notice the separating comma in JS, but space-separated elements in Smalltalk.
In Smalltalk, the array is created at compile time (i.e. runtime cost is zero); the array is immutable,
whereas in JS it is created mutable at runtime
[ expr1 , expr2 , ... ] { expr1 . expr2 . ... } a computed Array
(elements are expressions)
notice the expression terminators in ST (periods),
and that "," (comma) is an operator in ST.
The array is created at runtime; the array is mutable
#[ el1 el2 ... ] an immutable ByteArray constant
(not available in JavaScript)
1234 1234 an Integer constant
(arbitrary precision)
0x1234 0x1234 or 16r1234 a hexadecimal Integer constant (radix is 16)
(arbitrary precision)
0b10001010 0b10001010 or 2r10001010 a binary Integer constant (radix is 2)
(arbitrary precision)
3r10001010 a ternary Integer constant (radix is 3);
not available in JavaScript
<N>rxxxx an N-ary Integer constant (radix is N);
not available in JavaScript
( num / den ) a Fraction constant (not available in JavaScript);
both numerator and denominator must be integers
1.234
1e5
1.234
1e5
Floating Point constants
(double precision)
v = expression v := expression assignment to a variable
expr1 == expr2 expr1 = expr2 compare for equal value
expr1 === expr2 expr1 == expr2 compare for identity
be careful: 1.0 (the float) is NOT identical to 1 (the integer)
However, they are equal in value.
rcvr.f () rcvr f function call
rcvr.f (arg) rcvr f: arg with 1 arg
rcvr.a_b (arg1, arg2) rcvr a: arg1 b: arg2 with args
notice the different names of the function
"a_b" vs."a:b:"
return; ^ self without return value
ST: from enclosing method;
JS: from current function
return expr; ^ expr with return value
ST: from enclosing method;
JS: from current function
return from execute; ^ self return from execute activity from within an inner function
! expr expr not
e1 && e2 (e1 and:[ e2 ]) non evaluating and
(e2 is not evaluated if e1 is false)
e1 || e2 (e1 or:[ e2 ]) non evaluating or
(e2 is not evaluated if e1 is true)
pin <- value only for output pins: writes value to the pin (same as "pin value(val)")

Syntactic Sugar[Bearbeiten]

Some syntactic constructs of the JavaScript language are implemented as library functions in Smalltalk. Among others, most noteworthy are conditional execution (if-the-else) and loops.

Non-Smalltalkers should be aware that in Smalltalk all control structures are implemented as library functions (i.e. method invocations) to corresponding receiver objects. They get a block (=closure or anonymous function) as argument, which is then possibly evaluated. The Smalltalk block syntax "[...]" makes this extremely convenient and easy to use.

It is the responsibility of the Smalltalk compiler and runtime system, to care for optimized execution of such code (typically by inlining the code). However, it is still possible to put a block into a variable, to pass it as argument, or to return it as value from a method or block. Thus, in Smalltalk, variables or computed blocks may also be used as arguments to messages like "ifTrue:", "whileTrue:", "do:" etc.


The JavaScript syntax for control structures is mapped to corresponding Smalltalk library functions as follows:

JavaScript Smalltalk Notes
if (expr) {
...
}
(expr) ifTrue:[
...
]
Notice the square brackets
if (! expr) {
...
}
(expr) ifFalse:[
...
]
if (expr) {
...
} else {
...
}
(expr) ifTrue:[
...
] ifFalse:[
...
]
if (!expr) {
..
} else {
...
}
(expr) ifFalse:[
...
] ifTrue:[
...
]
while (expr) {
..
}
[expr] whileTrue:[
...
]
Notice the square brackets around the condition
for (expr1;expr2;expr3) {
..
}
expr1. [expr2] whileTrue:[
... .
expr3
]
for (var i=start; i<=stop; i++) {
..
}
start to:stop do:[:i |
...
]
for (var i=start; i<=stop; i+=incr) {
..
}
start to:stop by:incr do:[:i |
...
]
try {
..
} finally {
...
}
[
...
] ensure:[
...
]
try {
..
} catch(e) {
...
}
[
...
] on:Error do:[:e |
...
]
try {
..
} catch(e) {
...
} finally {
...
}
[
...
] on:Error do:[:e |
...
] ensure:[
...
]
var++ var := var + 1
var-- var := var - 1
arr[ idx ] arr at:idx
arr[ idx ]
Array and Dictionary indexed fetch
Notice that real JavaScript uses 0-based indexing, whereas Smalltalk and expecco JavaScript is 1-based.
Smalltalk/X also allows the bracket notation for indexed access.
arr[ idx ] = expr arr at:idx put:expr
arr[ idx ] := expr
Array and Dictionary indexed store
Smalltalk/X also allows the bracket notation for indexed access.

Operators[Bearbeiten]

JavaScript Smalltalk Notes
% \\
%
modulu operator
<< leftShift: left-shift
negative shift count is right-shift
>> rightShift: right-shift
negative shift count is left-shift
bitShift: left or right shift;
negative shift count is right-shift
positive is left shift
(not available in JavaScript)

More details are in the Smalltalk Syntax Cheat Sheet.

Array Indexing[Bearbeiten]

A big potential pitfall is the different indexing base used in Smalltalk vs. (standard) JavaScript: in Smalltalk, array indices start at 1 and end at the array's size. In (standard) JavaScript, indices start at 0 and end at the array's size minus 1.

This also affects some character- and substring search functions, which return 0 (zero) in Smalltalk, whereas some of the standard JavaScript functions return -1 if nothing is found.

The following functions expect or return 0-based indices if used in JavaScript:

  • collection.indexOf(element)
    attention: there exists a corresponding Smalltalk method, which returns a 1-based index
  • collection.lastIndexOf(element)
    attention: there exists a corresponding Smalltalk method, which returns a 1-based index
  • collection.indexOf(element, startIndex)
    the corresponding Smalltalk method "indexOf:element startingAt:startIndex" uses 1-based indices
  • collection.lastIndexOf(element, startIndex)
    ditto


We admit that this is a little confusing at times, but there is no easy solution to this: and we decided to neither change the Smalltalk nor the JavaScript scripting languages inside expecco.

To make your intentions explicit, you can use variants of the "indexOf"/"lastIndexOf" functions which make these implicit differences more explicit: "indexOf0()" / "lastIndexOf0()" and "indexOf1()" / "lastIndexOf1()" are available for both languages.

Shame on us:
the above array indexing behavior was changed for indexOf: between 2.7 and 2.8 (due to a missing translation, the 2.7 versions used 1-based indices in both languages). This leads to a problem, if you run any old 2.7 suite in newer expecco versions without reimporting the StandardLibrary or if you used those functions in your own elementary code. To avoid a need to touch those old suites, we have added a backward-bug-compatibility mode, which is described in a separate note of the release notes.

Inner Functions[Bearbeiten]

Inner Functions in JavaScript[Bearbeiten]

It is possible to create functions within the elementary block's code. This example shows how to do so:

execute() {
   ...
   function f(arg1, arg2) {
     return(arg1 + arg2);
   }
   ...
}

An inner function "f" is created with two arguments ("arg1" and "arg2"), which returns the sum of both. The function is called like this:

  f(3,5)

Inner functions can be used for example to filter specific elements from collections, as in:

  function isEven(n) { return n.even(); }

  var numbers = [1,2,3,4,5,6,7];
  var evenNumbers = numbers.select( isEven );

Inner functions are first class objects: they can be stored in variables, collections, passed as argument or returned from functions. They can even be passed to other blocks via input- and output pins or via environment variables. Functions can be anonymous; the above example could also be written as:

execute() {
   var f;
   ...
   f = function (arg1, arg2) {
     return(arg1 + arg2);
   }
   ...
}

(notice the missing function name after the "function" keyword).


Notice, that there is a difference in the behavior of the return statement between JavaScript inner functions and Smalltalk blocks: a JavaScript "return" statement inside an inner function returns from that inner function only, whereas a return in Smalltalk always returns from the outer-most method.

To return from the outermost function in JavaScript, use the "return from <fnName>" statement form.

In expecco, where the outermost function is always named "execute", write:

   execute() {
      ...
      function myFunction() {
          ...
          return from execute;
      }
      ...
   }

(notice that the execute function is not supposed to return a value)

Lambda Expressions in JavaScript[Bearbeiten]

JavaScript functions can also be written in a slightly shorter lambda notation as:

   (arg1, ... argN) => { statement; ... return expression; }

or (with a single argument) as:

   arg => { statement; ... return expression; }

or (without arguments) as:

   () => { statement; ... return expression; }

and results in a function object comparable to:

   function (arg1, ... argN) { statement; ... return expression; }

The function's body may also be a single expression (without braces), as:

   arg => expression

For example:

   aCollection.map( el => el ** 2 )

returns a collection of squared numbers, the same as the corresponding Smalltalk code:

   aCollection map:[:el | el ** 2 ]

This is plain "syntactic sugar" - it does not give any new semantic functionality, but makes the code shorter and possibly easier to read.

Inner Functions in Smalltalk (Blocks)[Bearbeiten]

In Smalltalk, inner functions are called "block", and are defined as:

  f := [:arg1 :arg2 | arg1 + arg2 ].

This block can be called by sending at a "value:value:" message (i.e. one "value:" for each argument) (1):

  f value:3 value:5

The returned value is the value of the last expression inside the block. Thus, blocks can contain multiple expressions separated by a period:

  f := [:arg1 :arg2 | 
            Transcript showCR: e'my first arg is: {arg1}'.
            Transcript showCR: e'my second arg is: {arg2}'.
            arg1 + arg2 "/ the block's return value
       ].

This is in contrast to a JavaScript inner function, where an explicit "return" statement is needed. The return value of JavaScript inner function without an explicit return will be void. Therefore, the above inner function in JavaScript has to have a return:

  f = function(arg1, arg2) { 
            Transcript.showCR('my first arg is: '+arg1);
            Transcript showCR('my second arg is: '+arg2);
            return (arg1 + arg2); // the function's return value
      };

Blocks without argument are defined as:

  f := [ Transcript showCR:'blabla: Euler was great' ].

and invoked with a simple "value" message.

In both JavaScript and Smalltalk code, it is an error to invoke an inner function/block with a wrong number of arguments (2).

Smalltalk blocks and JavaScript inner functions can be used interchangeable - i.e. it is possible to pass a JS inner function to a collection method such as "collect:" or "select:". It is also possible, to pass either via input/output pins to other activities (which is not considered good style, as it could make the program quite hard to understand, if not used with caution).

Notice again, that the behavior of the Smalltalk return ("^") is different from a JavaScript return ("return-Statement") inside inner functions. The JavaScript return returns a value from the inner function, whereas the Smalltalk return forces a return from the containing method (the block's "execute" method). The Smalltalk return always behaves like the "return from execute" JavaScript special form.

1) Smalltalk/X allows for a non-standard block evaluation message using the "normal" parentheses notation. I.e. you can call a block with its arguments in parentheses as in "f(1, 2, 3). This is syntactic sugar for the standard f value:1 value:2 value:3.

2) in Smalltalk, you can define "varargs blocks" with the "asVarArgBlock" message. The block will then receive its arguments as a vector, and fetch them using collection protocol (i.e. "size", "at:" or enumeration messages like "do:"). For example,

b := [:args | Transcript showCR: e'my arguments are {args}' ] asVarArgBlock

can be called with any number of arguments.

Example uses for Inner Functions[Bearbeiten]

In Smalltalk, blocks are very often used when enumerating collections.
For example, code corresponding to a C# 3.0 collection "select (where(x => ...))" is written in Smalltalk as:

   |names namesStartingWithA|

   names := #( 'Alice' 'Ann' 'Bob' 'Mallory' ).
   namesStartingWithA := names select:[:n | n startsWith:'A'].

or in JavaSript as:

   var names, namesStartingWithA;

   names = [ "Alice" , "Ann" , "Bob" , "Mallory" ];
   namesStartingWithA = names.select( function(n) { n.startsWith("A"); } );

or alternative using lambda expressions as:

   var names, namesStartingWithA;

   names = [ "Alice" , "Ann" , "Bob" , "Mallory" ];
   namesStartingWithA = names.select( n => n.startsWith("A") );

To find the first element in a collection, for which some condition is true, use:

   |names firstNameContainingAnO|

   names := #( 'Alice' 'Ann' 'Bob' 'Mallory' ).
   firstNameContainingAnO:= names detect:[:n | n includesString:'o'].

or in JavaSript as:

   var names, firstNameContainingAnO;

   names = [ "Alice" , "Ann" , "Bob" , "Mallory" ];
   firstNameContainingAnO = names.detect( function(n) { n.includesString("o"); } );

For more information on the Smalltalk syntax, see [Smalltalk Basics] in the [Smalltalk Online Tutorial].

Builtin Data Types[Bearbeiten]

The builtin types below and user defined primary types are mapped to corresponding classes of the underlying Smalltalk runtime system. An introductory overview and links to the detailed documentation of individual classes is found in the Smalltalk Class Documentation of the Smalltalk/X online documentation. The following gives a rough summary of the main concepts. A good entry point for further learning is the Smalltalk/X Basic Classes Overview Document, which provides links to the most heavily used classes and more detailed information.

Numeric Types[Bearbeiten]

The underlying numeric type implementation supports multiple number representations. These can be used transparently in mixed-operations, and values are automatically converted as required. For example, the division of two integers returns another integer iff the division does not produce a remainder. Otherwise, a fraction object is returned. Although very very rarely required in practice, values can be converted explicitly, via one of the "asXXX" messages:

  • asFloat64() - to convert to a double precision IEEE 64bit float
  • asFloat32() - to convert to a single precision IEEE 32bit float
  • asFloat80() - to convert to an extended precision IEEE 80bit float
  • asInteger()
  • asFraction()
  • asScaledDecimal(scale)

the older API is still supported, but less self explanatory:

  • asFloat() - to convert to a double precision IEEE 64bit float
  • asShortFloat() - to convert to a single precision IEEE 32bit float
  • asLongFloat() - to convert to an extended precision IEEE 80bit float

Thus, if you want to avoid fractions (why would you?), process the result of an integer division using one of the asFloat(), truncated(), floor(), ceiling() or rounded() functions. But be aware that fractions provide an exact result, whereas float arithmetic is inherently imprecise, rounds on the the last bit and that such rounding errors accumulate.

Integer[Bearbeiten]

This represents arbitrary precision integral numbers. Conversion and memory allocation is completely automatic.
Thus, without danger of overflow, you can write:

x = 1234567890 * 12345678901234567890 / 10

to get:

1524157875171467887501905210

or:

x = 100.factorial();

to get the huge number:

933262154439441526816992388562667004907159682643816214685929638952175999932299156089
41463976156518286253697920827223758251185210916864000000000000000000000000

Small integer values (smaller than 32 or 64bit, depending in the underlying hardware's native word length) are stored more efficiently than large integers - however, the conversion and representation as used by the system is transparent and automatic. The same is true for mixed mode arithmetic between small and large integers.

Occasionally it is required to perform modulo arithmetic in 32 or 64bit; for example to reproduce or check values as generated by C or Java programs. For this, use methods from the "special modulo" category, such as "add_32()", "add_32u()", "mul_32()" etc. Use a class browser to find those methods.

Integer constants (of arbitrary size) can be given in hexadecimal (Smalltalk: 16rxxx, JavaScript 0xXXXX), octal (Smalltalk: 8rxxx, JavaScript 0XXXX), and binary (Smalltalk: 2rxxx, JavaScript 0bXXXX). In fact, Smalltalk supports any base from 2 to 36 written as <b>rxxx, where <b> is the base. Thus 3r100 is the ternary representation of the integer 9. For ease of use, Smalltalk also supports 0x, 0o and 0b radix prefixes (hex, octal and binary).

Overview: More Info
Reference: Class Documentation

Float[Bearbeiten]

Float represents double precision IEEE floating point numbers (64bit). Although seldom needed, conversion to single precision (32bit) and high precision (80bit/128bit, depending on the underlying hardware) is possible via the asShortFloat() and asLongFloat() conversion functions. Notice that the actual precision of longFloat numbers depends on the CPU: x86 and x86_64 provide 80bits, Sparc and a few other CPUs provide 128 bits.
For more precision, two additional classes called "QDouble" and "LargeFloat" are provided: QDouble provides roughly 200 bits (70 digits) of precision, and LargeFloat offers an arbitrary (configurable) precision. Both are described in the Numeric Limits and Number API Functions documents.


Number API Functions
Overview: More Info in the Smalltalk/X online documentation
Reference: Class Documentation
Reference: ShortFloat, LongFloat, QDouble Class Documentation

Float Rounding Errors[Bearbeiten]

Be reminded that floating point arithmetic is inherently inaccurate: there will almost always be rounding errors on the last bit. The reason is inherent in that most rational numbers simply cannot be represented as the sum of powers of two. For example, 0.1 and 0.3 are two of them: no sum of powers of two (1/2, 1/4, 1/8, ...) is able to represent these numbers exactly. Thus, there will definitely be an error in the last bit of the stored float value.

Typically, print functions (which convert floating point values to a string representation) will "cheat" and round on the last bit. However, if you operate with such numbers (i.e. add, multiply, etc.) these rounding errors will accumulate and may finally add up to more than one bit of error. When this happens, the print function will no longer round up, and you will get the typical ".999999" result strings.

As an example, try adding 0.3 a thousand times, as in:

|t|
t := 0.3.
1000 timesRepeat:[t := t + 0.3].
t print

may give "300.300000000006" as a result.

As a consequence, NEVER ever compare floating point values for equality. In the above case, the comparison with "300.3" would return false!

Floating point numbers should always be compared by giving a range (i.e. between x-delta and x+delta) or by giving a number of valid digits or bits, by which to compare. expecco provides a number of action blocks for this kind of "almost-equal" comparisons.

Of course, there are also similar functions in the Float class, in case you need this in elementary Smalltalk or JavaScript code.

And also: NEVER ever compute monetary values using floating point - or your legal advisor will blame you for missing Cents in the invoice. Monetary values should never be processed with floats (expecco provides a ScaledDecimal number type for that, as described below).

Problems with numbers are discussed in detail in "Numeric Limits".

Fraction[Bearbeiten]

Fractions are arbitrary-precision rational numbers. You will get them when dividing two integers and the result is not integral. Fractions represent an exact result, in contrast to floats, which are only approximations.

For example:

1 / 3

returns a fractional result with a numerator of 1 and a denominator of 3 (as opposed to the Smalltalk // operator, which truncates the result of the division to an integer).

Fractions have the advantage of avoiding rounding errors. Thus, when the above fraction is divided by 3, you will get (1/9), which we can multiply by 9 to get the exact 1 (an integer) without any rounding error. Be reminded that this is typically not the case with Float or Double precision numbers. Due to rounding errors on the last bit, you will often get 0.9999999 as a result there (even though printf may "cheat" and round it to "1.0", as long as the error is in the last bit only; however, when doing multiple operations in a row, these errors accumulate into the next bit, and rounding will no longer generate a "nice looking" output).

Fraction results are automatically reduced - therefore, when adding (1/3) + (2/6), you will get (2/3).
And if you add another (1/3) to it, you will get an exact 1.

Be aware that fractional arithmetic is done by software, whereas floating point arithmetic is done by CPU instructions. Therefore, floating point arithmetic is usually faster.

Overview: More Info
Reference: Class Documentation

ScaledDecimal[Bearbeiten]

ScaledDecimal (also called "FixedPoint") numbers are decimals, with a configurable number of post-decimal digits. They are typically used when dealing with money. Like fractions, they avoid rounding errors. When printed, they round in their last digit, however, the exact information is always kept for further processing. ScaledDecimal numbers are perfect to represent money and other fractional data, which must be represented with a fixed number of post-decimal digits. Internally, fixed point numbers are represented as fractions with a power-of-10 denominator.
ScaledDecimal numbers are also great to print tabular data; for example: someNumber as ScaledDecimal:3 will print the number with and rounded to 3 post-decimal digits.


Overview: More Info
Reference: Class Documentation

Complex[Bearbeiten]

Complex numbers with real and imaginary part are sometimes used in technical/physical applications. They can be read as "a * b i", for example: "5 + 3i" represents the complex number with 5 as real and 3 as imaginary part.

Complex numbers can result from math functions such as sqrt and log (by default, these raise an error, which can be ignored for a complex result)
Reference: Class Documentation

Number[Bearbeiten]

The Number type is an abstract union type which includes all of the above types. Thus, a variable or pin declared as being of "Number" type can take any of the above values. Due to the polymorphic implementation of the underlying numeric class library, most operations can be passed any of the above values (even mixing arguments is possible). For example, the "[Add]" elementary block, which is implemented by simply calling the Smalltalk "+" operation, is able to deal with any combination of argument types.
Overview: More Info
Reference: Class Documentation

String Types[Bearbeiten]

Strings can come in three flavours (subclasses of CharacterArray), depending on how many bits are required to encode the character's code point. In general, Unicode is used internally as the encoding. However, converters are available to translate into other encodings, such as JIS or ISO 8859-x.

Bulb.png Notice that ASCII (0..127) and ISO 8859-1 (0..255) are proper subsets of the Unicode set. Do not confuse Unicode with UTF-8: Unicode is the numeric value associated to a character and used everywhere within expecco. UTF-8 is an encoding format for data interchange of Unicode characters. UTF-8 is NOT used inside expecco, only for data exchange with the external world (see "Encoding and Decoding" in the String API doc).

In most situations, you do not need to care for the actual number of bits required to store the characters, and expecco/Smalltalk takes care of the actual representation in memory. However, when dealing with the external world (files, sockets, devices), it may be required to perform explicit conversions to/from wider formats or different encodings (UTF-8, UTF-16, etc.)

String[Bearbeiten]

This is used to represent character strings, where each individual character has a one-byte encoding. I.e. the values are 0..255. Individual characters of a string can be accessed by an integral index starting with 1 in both Smalltalk and JavaScript. The String class provides many useful functions for substring searching, matching, concatenation/replacing etc. Also, because String inherits all functions from the Collection superclass hierarchy, all enumeration, mapping, filtering functions are also applicable to strings.
See also: Most Useful String Functions and Most Useful Collection Functions
Overview: More Info
Reference: Class Documentation

Unicode16String[Bearbeiten]

This is used to represent character strings, where at least one character needs a two-byte encoding. I.e. any character's code point is in the range 0x0100 .. 0xFFFF. In fact, there is even a Unicode32String class (see below), which supports the full four-byte Unicode character set. But in practice, such strings are very rarely ever encountered or needed.

Unicode32String[Bearbeiten]

This is used to represent character strings, where at least one character needs more than a two-byte encoding. I.e. any character's code point is above 0xFFFF. Such strings are very rarely ever encountered or needed.

Character[Bearbeiten]

Individual characters (as extracted from a string or possibly read from a file) are represented as instances of the Character class. Queries are available to ask the character for its type (isLetter, isDigit etc.) or its Unicode encoding (codePoint). There is virtually no limit and every character from the full Unicode set (e.g. even above 0xFFFF) can be represented by instances of the Character class.
Overview: More Info
Reference: Class Documentation

Collection Types[Bearbeiten]

Smalltalk and therefore also expecco provides a very complete set of collection types which are tuned for different access patterns or memory consumption. An introductory overview on the collection classes is found in the
Most Useful API: Most Useful Collection Functions
Overview: Collections Overview of the Smalltalk/X online documentation.
Reference: Class Documentation

The following paragraphs list the most used collection classes. Notice, that Smalltalk provides many more container classes, so please take a look at the online documentation or take a life tour with the browser.

Array[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1 in both Smalltalk and JavaScript. Can store any type of object, and storage requirements are 1 machine word per element (i.e. arrays store pointers to the elements). As the size is fixed (when created), adding and removing of elements is not possible. Use one of the other collections (OrderedCollection, Set or Dictionary) if the number of elements needs to change later or is not known in advance.
When accessing elements by index, access time complexity is constant O(1).
A search for an element in an Array (using includes:, indexOf:, findFirst: or similar methods) has O(N) time complexity (because the search is linear through all elements of the array).
Overview: More Info
Reference: Class Documentation

ByteArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
ByteArrays can only store very small byte-valued unsigned integers (0..255), and allocate 1 byte per element. Access time and complexity are like with ordinary arrays, but the required memory is much less. ByteArrays are used to represent the contents of binary data, binary files, bitmap images, protocol elements (data packets) or low level memory dumps.
UInt8Array is an alias for ByteArray.
Overview: More Info
Reference: Class Documentation

SignedByteArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
These store very small 8bit signed integer values (-128..127), requiring 1 byte of storage per element.
Int8Array is an alias for SignedByteArray.
Reference: Class Documentation

BitArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
BitArrays can only store tiny bit-valued unsigned integers (0..1), but only need one byte of storage for every 8 elements.
Reference: Class Documentation

BooleanArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
BooleanArrays can only store booleans (false..true), but only need 1 byte for every 8 stored boolean elements.
Reference: Class Documentation

FloatArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
FloatArrays store short IEEE floats (32bit) and allocate 4 bytes per element. Access times and complexity are like those of regular arrays, but they require much less memory. FloatArrays are often used for data interchange with C language functions or in data acquisition scenarios (series of measurement values).
Float32Array is an alias for FloatArray, and preferable, as it makes the precision explicit, und thus the code more readable.
Overview: More Info
Reference: Class Documentation

DoubleArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
DoubleArrays store IEEE doubles (64bit) and allocate 8 bytes per element. These are used in similar situations as FloatArrays.
Float64Array is an alias for DoubleArray, and preferable, as it makes the precision explicit, und thus the code more readable.
Overview: More Info
Reference: Class Documentation

HalfFloatArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
HalfFloatArray can only store IEEE half floats (16bit) and allocate 2 bytes per element.
Bulb.png Notice that half floats have a very low precision and are seldom used.
Exceptions are game programs and 3D graphics accelerators which preserve memory by using this very compact format for depth information, texture attributes and some audio formats.
They are also used with some AI programs in neural networks.
Float16Array is an alias for HalfFloatArray , and preferable, as it makes the precision explicit, und thus the code more readable.

IntegerArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
These store small 32bit unsigned integer values (0..0xFFFFFFFF) and require 4 bytes of storage per element.
UInt32Array is an alias for IntegerArray, and preferable, as it makes the precision explicit, und thus the code more readable.
Reference: Class Documentation

SignedIntegerArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
They store small 32bit signed integer values (-0x800000000..0x7FFFFFFF), requiring 4 bytes of storage per element.
Int32Array is an alias for SignedIntegerArray, and preferable, as it makes the precision explicit, und thus the code more readable.
Reference: Class Documentation

WordArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
They store small 16bit unsigned integer values (0..0xFFFF), allocating 2 bytes per element.
UInt16Array is an alias for WordArray, and preferable, as it makes the precision explicit, und thus the code more readable.
Reference: Class Documentation

SignedWordArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
They store small 16bit signed integer values (-0x8000..0x7FFF), allocating 2 bytes per element.
Int16Array is an alias for SignedWordArray, and preferable, as it makes the precision explicit, und thus the code more readable.
Reference: Class Documentation

LongIntegerArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
They store small 64bit unsigned integer values (0..0xFFFFFFFFFFFFFF), allocating 8 bytes per element.
UInt64Array is an alias.
Reference: Class Documentation

SignedLongIntegerArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
These store small 64bit signed integer values (-0x80000000000000000..0x7FFFFFFFFFFFFFFF), allocating 8 bytes per element.
Int64Array is an alias.
Reference: Class Documentation

ComplexFloatArray and ComplexDoubleArray[Bearbeiten]

Ordered, fixed size, indexed by an integral index starting with 1.
These store complex numbers as pairs of IEEE floats (32bit) or doubles (64bit) and allocate 8/16 bytes per element. These are used in similar situations as FloatArrays and DoubleArrays.
Reference: [1] [2]

OrderedCollection[Bearbeiten]

Ordered, variable size, indexed by an integral index starting with 1.
OrderedCollections can store any type of object and are specifically tuned for adding/removing elements at either end.
Access time to elements by index has a time complexity of O(1).
Insertion and remove at either end approach O(1), but is O(n) for inner elements, because the elements are moved inside a linear container. When searching for an element in an OrderedCollection (using includes:, indexOf: or similar methods), time complexity is O(n) (because a linear search is done).

Java users will recognize the similarity to "ArrayLists".

Note: For very big or lightly sparse collections, you can use a SegmentedOrderedCollection. This uses a hierarchy of buckets and does not need storage for big unused regions inside the collection. However, due to the additional computations, the breakeven in performance compared to regular OrderedCollections is usually at around 100K to 1M elements.

Note: For very very big collections (millions of elements) which are very lightly filled, use a SparseArray. This only keeps the filled slots, and is great, if you have only a few valid elements inside a virtually huge index space.

Overview: More Info
Reference: Class Documentation

SortedCollection[Bearbeiten]

Sorts itself, variable size, indexed by an integral index starting with 1.
SortedCollections can store any type of object for which a order relationship is either implicit (i.e. the elements responds to the "<" (less) message) or for which an explicit comparison block can is given (i.e. you provide a sort-block, which takes two elements and returns a boolean depending on the ordering).

By default, sorting is by ascending order based on the "<"-message. However, the sort order can be arbitrarily changed by setting the sortBlock, a two-argument JS-function or a two-argument Smalltalk block which should return true, if its first argument is to be "considered" smaller than the order
(i.e. the default sort block is "[:a :b | a < b]" or in Javascript: "(a,b) => a < b).

Access time complexity by numeric index is O(1).
When searching for an element in a SortedCollection (using includes:, indexOf: or similar methods); time complexity is O(log n), because a binary search can be done.
Insertion and remove is O(1) for the first and last element, O(log n + n) for inner elements, because the insert index is first searched, then elements are moved inside a linear container.

When a big sorted collection is created, it is faster to first create it as a non-sorted collection (OrderedCollection) and then converting the whole collection via asSortedCollection, instead of adding individual elements one-by-one.
Overview: More Info
Reference: Class Documentation

Set[Bearbeiten]

Unordered, variable size, keeps a single reference only, per equal element.
Sets can store any type of object. When searching a Set for an element (eg. using the includes: method), time complexity approaches O(1), because the search can be done based on a hashing algorithm in asymptotic constant time (however, the time to compute the hash of an element may affect the effective execution time; it should not be too slow).
Overview: More Info
Reference: Class Documentation
In addition to the unordered Set, useful classes are also OrderedSet and SortedSet.

Bag[Bearbeiten]

Unordered, variable size, keeps a count, per equal element. Adding, removing and insertion check all run in O(1). Can store any type of object.
Bags are similar to Sets, but keep the number of occurrences of each element. Thus, bags are perfect to count the number of elements in a collection, for example words and word counts in a document.
Reference: Class Documentation

Dictionary[Bearbeiten]

Unordered, variable size, implements arbitrary key-value mappings.
Dictionaries can store any type of object and use any kind of object as key. When accessing or searching an element in a Dictionary (using at:, includesKey: and similar methods), the time complexity asymptotically approaches O(1), because the key search is based on a hashing algorithm. Notice, that the same value may be stored multiple times in a dictionary (i.e. stored under multiple keys). Asking for the presence of a particular value in the dictionary (i.e. the reverse query without key) still has O(N) time complexity.

If you need fast access both via the key and the value, either use an additional reverse mapping dictionary, or store an additional value->key mapping in the same dictionary (if the key and value spaces are disjoint).

Java users will know a subset of the dictionary functionality as "Hashtable".

Overview: More Info
Reference: Class Documentation

OrderedDictionary[Bearbeiten]

Ordered, variable size, implements arbitrary key-value mappings. Can store any type of object and use any kind of object as key. Being similar to dictionaries, these remember the order by which elements were added. It provides both access by hash-key and access by index (which is the order in which elements have been added). Creation of an OrderedDictionary is somewhat slower than for Dictionary or OrderedCollection, but access by either numeric or hashkey is fast with O(1) time complexity.
Reference: Class Documentation

OrderedSet[Bearbeiten]

This is very similar to a Set, but remembers the order in which elements were added, and provides index read access methods. When enumerated, the elements are generated in this order.

Queue[Bearbeiten]

Ordered, variable size. Can store any type of object. Queues are especially tuned for adding elements at one end, and removing them at the other. The internal representation uses a buffer in round-robin fashion. Both adding and removal are done on O(1) time.
Overview: More Info
Reference: Class Documentation

If multiple processes are accessing the same queue concurrently, use a SharedQueue, which is implicitly synchronized.

SharedCollection[Bearbeiten]

Any collection can be wrapped into a SharedCollection to add synchronization locks around its access functions. These should be used if multiple processes (threads) are going to access (i.e: update) the collection simultaneously.
Reference: Class Documentation

BTree, AATree, BinaryTree[Bearbeiten]

Combining the functionality of Dictionary and SortedCollection, these keep their elements in a sorted order, indexed by a key, both of which can be arbitrary objects. In contrast to dictionaries, which use hashing, these use a tree-like representation and guarantee both asymptotic O(log N) worst case and average runtime. (Dictionaries have a much better typical time complexity of O(C), but also a worst case of O(N), if the hash keys are badly distributed). These trees are self balancing to avoid the linear worst case access time of non balanced trees. [Class Documentation]

There is also a non-balancing BinaryTree, which is slightly faster (by not balancing), but its worst case access times may degenerate to O(N), if elements are added non-randomly.
Reference: Class Documentation

Interval and GeometricSeries[Bearbeiten]

This represent ranges of numbers as in "(1 to:5)" or "(1 to:10 by:2)" or "(7 to:4 by:-1)", which represent collections of numbers [1,2,3,4,5], [1,3,5,7,9] and [7,6,5,4] respectively.

GeometricSeries are similar, but have a constant factor between elements, instead of a constant difference. For example, the "(GeometricSeries from:1 to:64 byFactor:2)" represents the collection of [1,2,4,8,16,32,64]".

Intervals and GeometricSeries can be used wherever a readonly collection can be used.
Reference: Interval Class Documentation and GeometricSeries Class Documentation

Stream Types[Bearbeiten]

Streams are accessors for either internal collections or external files, sockets and other character oriented I/O devices. They are created by either "opening" a file or device or by creation "on" a numerical indexable collection object (String, Array, OrderedCollection etc.). Once created, readstreams deliver the next element via a "next()" operation, writestreams append elements via the "nextPut(e)" operation. In addition to the above elementary functions, bulk read/write and linewise read/write and various other functionality is provided.

An introductory overview on the stream classes is found in the [Streams Overview] of the [Smalltalk/X online documentation].

ExternalStream[Bearbeiten]

A common superclass providing bytewise or linewise access to any stream of the underlying operating system. Concrete subclasses are Socket, Filestream, Console Streams, PTYStream etc.
Overview: More Info
Reference: Class Documentation

FileStream[Bearbeiten]

Provide bytewise or linewise access to the underlying file system.
Overview: More Info
Reference: Class Documentation

CharacterWriteStream[Bearbeiten]

A special stream usable for mixed single- and multibyte characters (i.e. Unicode). This stream starts to collect into a single byte string buffer, but switches automatically to a multibyte character string as required.
Reference: Class Documentation

Socket[Bearbeiten]

Provides low level bytewise or linewise access to a communication socket. Typically, but not limited to TCP/IP connections.
Overview: More Info

Reference: Class Documentation

There are a number of useful companion classes which deal with host addresses:
Reference: SocketAddress, IPSocketAddress and IPv6SocketAddress.

PipeStream[Bearbeiten]

These are used to read/write from/to another program's standard input or standard output. For details, consult a Unix programmer's manual.
Reference: Class Documentation

Other useful Types[Bearbeiten]

Date[Bearbeiten]

Represents a calendar date. Instances can be created with eg. "Date today", by giving day/month/year or a calendar day. Instances offer a large number of queries, such as weekday, dayInWeek, leapYear etc.
Overview: More Info
Reference: Class Documentation

Time[Bearbeiten]

Represents a time-of-day in second resolution. Instances are created with "Time now" or by giving hour/minute/seconds.
Overview: More Info
Reference: Class Documentation

Timestamp (Date and Time)[Bearbeiten]

Represents a timestamp. With the 18.1 release, the resolution has been improved to sub-nanosecond resolution, although internal timestamps as generated by the operating system will usually not generate such fine grained values (some systems will only generate, 1 millisecond resolution stamps).

Update: now the resolution supported by Timestamps is 1 picosecond, and microsecond times are also supported on Windows systems.

For backward compatibility, the default resolution is 1 millisecond for internally generated stamps, and whatever resolution is provided when stamps are read from an external source. When printed, the default print format includes 3 millisecond digits, but a separate print generator can produce higher resolution strings (in 18.1).

The Timestamp class has been enhanced to support dates before 1.1.1970 and after 2036, which are typical restrictions of systems which represent times as "seconds since the epoch" in a 32bit integer. Expecco timestamps handle values before and after those dates, although no calendar adjustments are done for timestamps before the julian/gregorian switch (i.e. this affects weekdays, leap years, etc. before 1582, which are very unlikely to be encountered in technical environments ;-) ).
Overview: More Info
Reference: Class Documentation

TimeDuration[Bearbeiten]

Represents an amount of time, to represent time intervals in picosecond resolution (before rel18.1: millisecond resolution).

When reading from a string or stream, a unit-specifier character is allowed to specify milliseconds (ms), seconds (s), minutes (m), hours (h) or days (d). For example, "1h 20s" specifies 1 hour and 20 seconds.

As a programmer, you'd write (in Smalltalk): "1 hours + 20 seconds"

Overview: More Info
Reference: Class Documentation

Delay[Bearbeiten]

Delay is a utility providing timed delay functions. Most useful are (written in Smalltalk):

  Delay waitForSeconds: n.   (arg is a number)
and: 
  Delay waitFor: n seconds.  (arg is a time duration)
and: 
  Delay waitUntil: timestamp. (arg is a timestamp)

or (written in JavaScript):

  "Delay.waitForSeconds(n)";
  "Delay.waitFor(n.seconds())";
and 
  "Delay.waitUntil(timestamp)";

Reference: Class Documentation

Filename[Bearbeiten]

Represents names of files in the filesystem. Provides many useful query functions for file size, access- and modification time, parent directory, children, path queries etc.
Most Useful API: Filename API Functions
Reference: Class Documentation

Encoders / Decoders[Bearbeiten]

The CharacterEncoder class provides a number of functions to encode/decode strings into various other encodings; for example, to read an ISO 8859-7 encoded string, use:

   decodedString := 
       (CharacterEncoder encoderFor:#'ISO8859-7') 
           decodeFrom:encodedString.

and vice versa:

   encodedString := 
       (CharacterEncoder encoderFor:#'ISO8859-7')
           encodeFrom:originalString.

By the time of writing, the following encodings are supported (many are aliases of others):

adobe-fontspecific ansi_x3.4-1968 arabic ascii ascii7 asmo-708 big5 cns11643 cp-037 cp-37 cp-437 cp10000 cp1250 cp1251 cp1252 cp1253 cp1254 cp1255 cp1256 cp1257 cp367 cp437 cp878 cyrillic ebcdic ebcdic-037 ecma-114 ecma-118 gb2312 gb2312.1980 gb2312.1980-0 greek hangul hebrew ibm-367 ibm-437 ibm-819 ibm-cp367 ibm-cp437 ibm-cp819 iso-10646-1 iso-8859-1 iso-8859-10 iso-8859-11 iso-8859-13 iso-8859-14 iso-8859-15 iso-8859-16 iso-8859-2 iso-8859-3 iso-8859-4 iso-8859-5 iso-8859-6 iso-8859-7 iso-8859-8 iso-8859-9 iso-ir-100 iso-ir-101 iso-ir-109 iso-ir-110 iso-ir-126 iso-ir-127 iso-ir-138 iso-ir-144 iso-ir-148 iso-ir-157 iso-ir-203 iso-ir-6 iso10646-1 iso10646_1 iso646-us iso8859 iso8859-1 iso8859-10 iso8859-11 iso8859-13 iso8859-14 iso8859-15 iso8859-16 iso8859-2 iso8859-3 iso8859-4 iso8859-5 iso8859-6 iso8859-7 iso8859-8 iso8859-9 iso8859_1 iso8859_10 iso8859_11 iso8859_13 iso8859_14 iso8859_15 iso8859_16 iso8859_2 iso8859_3 iso8859_4 iso8859_5 iso8859_6 iso8859_7 iso8859_8 iso8859_9 java javaText jis0201 jis0208 jis0212 jisx0201.1976-0 jisx0208 jisx0208.1983-0 jisx0208.1990-0 johab koi7 koi8-r koi8-u ksc5601 latin-1 latin-10 latin-2 latin-3 latin-4 latin-5 latin-6 latin-7 latin-8 latin-9 latin-celtic latin1 latin10 latin2 latin3 latin4 latin5 latin6 latin7 latin8 latin9 mac-arabic mac-centraleurope mac-centraleuropean mac-croatian mac-cyrillic mac-dingbats mac-farsi mac-greek mac-hebrew mac-iceland mac-japanese mac-korean mac-roman mac-romanian mac-symbol mac-thai mac-turkish macarabic maccentraleurope maccentraleuropean maccroatian maccyrillic macdingbat macdingbats macfarsi macgreek machebrew maciceland macintosh macjapanese mackorean macroman macromanian macsymbol macthai macturkish microsoft-ansi microsoft-arabic microsoft-baltic microsoft-cp1250 microsoft-cp1251 microsoft-cp1252 microsoft-cp1253 microsoft-cp1254 microsoft-cp1255 microsoft-cp1256 microsoft-cp1257 microsoft-cp437 microsoft-cyrillic microsoft-easteuropean microsoft-greek microsoft-hebrew microsoft-turkish ms-ansi ms-arabic ms-baltic ms-cp1250 ms-cp1251 ms-cp1252 ms-cp1253 ms-cp1254 ms-cp1255 ms-cp1256 ms-cp1257 ms-cp367 ms-cp437 ms-cp819 ms-cyrillic ms-default ms-easteuropean ms-ee ms-greek ms-hebrew ms-oem ms-turkish next nextstep sgml thai us-ascii utf-16b utf-16be utf-16e utf-16le utf-8 utf-8-mac utf16b utf16be utf16l utf16le utf8 utf8-XML utf8-mac windows-1250 windows-1251 windows-1252 windows-1253 windows-1254 windows-1255 windows-1256 windows-1257 windows-latin1

For base64 encoding, refer to the Base64Coder class, or use the string messages: base64Encoded and base64Decoded.

For utf8 encoding/decoding, use the string messages: utf8Encoded and utf8Decoded.

CTypes[Bearbeiten]

These types describe C-data structures. They are needed when data is exchanged with DLL functions or is to be read/written in binary from/to a file or a communication channel.

CTypes are usually parsed from a string using the C Parser, which is invoked by expecco when a type definition starts with a comment like "/* C: */". But of course, the C-Parser can also be invoked explicitly in a workspace or programmatically via an elementary block. The following example would create a C-type from a structure definition string (in JavaScript code):

   t = CParser.parseDeclaration("
       struct s {
           unsigned long DPR_Address;
           unsigned long DPR_Length;
           unsigned long PortAddress;
           struct p {
               unsigned char ProtocolID;
               union u {
                   uint16 dpInOutAreaSize;
                   uint16 dpInAreaSize;
               } Parameter;
           } Protocol[4];
       };
   ");

the type in variable "t" can then be instantiated by sending it a "new" message:

   cDatum = t.new();

or with:

   cDatum = t.malloc();

The difference is that "new" allocates the bytes in the regular garbage collected expecco memory (which may move objects), whereas "malloc" allocates in pinned (not moving) memory on the C heap. Thus references to such malloc'd memory can be passed to DLL code which wants to keep a reference to it, whereas C-data allocated with new can only be used temporarily by C code and will vanish sooner or later after the call returns.

You can inspect the created instance via the inspector (evaluate: "cDatum.inspect()").

Individual fields of the c-datum are accessed via the following functions:

  • cDatum.memberAt(fieldName)
    retrieves a field. If the field is a composite datum (array or structure), a new datum is extracted (i.e. a structure copy is made).
  • cDatum.at(index0Based)
    retrieves an indexed array element as a copy.
  • cDatum.memberAtPut(fieldName, value)
    stores a field. If the field is a composite datum (array or structure), the whole value is copied.
  • cDatum.atPut(index0Based)
    stores an indexed array element.
  • CDatum.refMemberAt(fieldName)
    creates a pointer to a field. This allows for subfields to be modified.
  • cDatum.refAt(index0Based)
    creates a pointer to an array element.

notice that in JavaScript, indexed access can be written more convenient as "cDatum[index0Based]" and that member field access can be written as "cDatum.fieldName".

For the full protocol of CDatum, please refer to the class code in the Class Documentation.
Additional information is also found in the Datatype Element/en#CTypes documentation.

Additional Standard Objects for JavaScript[Bearbeiten]

In order for a "well known" environment to be provided to those who know JavaScript, but do not know Smalltalk, mimicri classes (Math) and mimicri protocol has been added to the underlying Smalltalk system. These functions simply call corresponding Smalltalk functions and only exist to provide a JavaScript compatible API which is described below.

Notice that this represents only a small fraction (less than 1/100th) of the real functionality of the base system. In order to find out more about all existing classes, you should open a class browser and navigate through the system yourself (eg. Number and subclasses).
There is also a more detailed introduction to the basic classes found in the Smalltalk/X Base Classes Documentation.

Math[Bearbeiten]

Constants[Bearbeiten]
  • E
    Euler's constant, the base of the natural logarithm (approximately 2.718).
    Same as "Float e" in Smalltalk (or "Math E").
  • LN10
    Natural logarithm of 10 (approximately 2.302).
    In Smalltalk, you can use "Float ln10" (or "Math LN10").
  • LN2
    Natural logarithm of 2 (approximately 0.693).
    Same as "Float ln2" in Smalltalk.
  • LOG10E
    Base 10 logarithm of E (approximately 0.434)
  • LOG2E
    Base 2 logarithm of E (approximately 1.442)
  • PI
    Pi (approximately 3.14159).
    Same as "Float pi" in Smalltalk.
  • SQRT1_2
    Square root of 1/2 (approximately 0.707)
  • SQRT2
    Square root of 2 (approximately 1.414).
    Same as "Float sqrt2" in Smalltalk.
Min & Max[Bearbeiten]
  • max (number1 , number2,...)
    Returns the largest of up to 6 arguments
  • min (number1, number2,...)
    Returns the smallest of up to 6 arguments
Miscellaneous[Bearbeiten]
  • abs (aNumber)
    Returns the absolute value of aNumber
  • binco (n, k)
    Returns the binomial coefficient C(n,k) (n over k, choose k from n)
  • ceil (aNumber)
    Returns the smallest integer greater than or equal to aNumber
  • exp (aNumber)
    Returns eaNumber, where aNumber is the argument, and e is Euler's constant, the base of the natural logarithm
  • fac (aNumber)
    Returns the factorial of aNumber
  • floor (aNumber)
    Returns the greatest integer less than or equal to aNumber
  • gcd (a, b)
    Returns the greatest common divisor of a and b
  • log10 (aNumber)
    Returns the log base 10 of aNumber
  • log (aNumber)
    Returns the log base E of aNumber
  • pow (base, exp)
    Returns base to the exponent power, that is, baseexp
  • random
    Returns a pseudo random number between 0 and 1
  • round (aNumber)
    Returns the value of aNumber rounded to the nearest integer
  • sqrt (aNumber)
    Returns the square root of aNumber
  • cbrt (aNumber)
    Returns the cubic root of aNumber
Trigonometric[Bearbeiten]
  • sin (aNumber)
    Returns the sine of aNumber (given in radians)
  • cos (aNumber)
    Returns the cosine of aNumber (given in radians)
  • tan (aNumber)
    Returns the tangent of aNumber (given in radians)
  • cot (aNumber)
    Returns the cotangent of aNumber (given in radians)
  • csc (aNumber)
    Returns the cosecant of aNumber (given in radians)
  • sec (aNumber)
    Returns the secant of aNumber (given in radians)
  • arcSin (aNumber)
    Returns the arcsine (in radians) of aNumber
  • arcCos (aNumber)
    Returns the arccosine (in radians) of aNumber
  • arcTan (aNumber)
    Returns the arctangent (in radians) of aNumber
  • arcTan2 (x, y)
    Returns the arctangent of the quotient of its arguments (in radians)
  • sinh / cosh / tanh / arSinh / arCosh etc.
    Hyperbolic versions of above

Number[Bearbeiten]

Properties[Bearbeiten]
  • MAX_VALUE
    The largest representable number. The returned number is 0x7FFFFFFFFFFFFFFF, to mimicri a 64 bit limit on numbers for JS programs ported from other systems. In fact, expecco does not impose any such limit in integer values.
  • MIN_VALUE
    The smallest representable number. The returned number is -0x8000000000000000, to mimicri a 64 bit limit on numbers for JS programs ported from other systems. In fact, expecco does not impose any such limit in integer values.
  • NEGATIVE_INFINITY
    Returns the special 'negative infinity' value
  • NaN
    Returns the special 'not a number' value
  • POSITIVE_INFINITY
    Returns the special 'positive infinity' value
Methods[Bearbeiten]
  • aNumber.asFloat ()
    Returns a floating-point version of the receiver object.
    For example: "1.asFloat()" yields the floating point number: "1.0".
  • aNumber.asInteger ()
    Returns an integer version of the receiver object (truncating).
    For example: "1.0.asInteger()" yields the integer number: "1". Alternatives are ceiling(), floor() and rounded().
  • aNumber.ceiling ()
    For non-integral values, ceiling returns the smallest integer which is larger than the receiver. For integers, the original value is returned.
    For example: "1.4.ceiling()" yields the integer number: "2", whereas "1.ceiling()" returns "1". See also: floor() and rounded().
  • aNumber.floor ()
    For non-integral values, floor returns the largest integer which is smaller than the receiver. For integers, the original value is returned.
    For example: "1.4.floor()" yields the integer number: "1", whereas "1.floor()" returns "1". See also: ceiling() and rounded().
  • aNumber.rounded ()
    For non-integral values, rounded returns the nearest integer from rounding. For integers, the original value is returned.
    For example: "1.4.rounded()" yields the integer number: "1", "1.6.rounded()" yields the integer number: "2" and whereas "1.rounded()" returns "1". See also: ceiling() and floor().
  • aNumber.roundTo (r)
    Rounds towards the nearest multiple of r.
    For example, "1.543.roundedTo(0.01)" returns "1.54" and "1.567.roundedTo(0.01)" returns "1.57". See also: ceiling(), floor() and rounded().
  • aNumber.toExponential (nDigits)
    Returns a string representing the number in exponential notation, where nDigits is the number of digits to appear after the decimal point
  • aNumber.toExponential (nDigits, nDigitsAfter)
    Returns a string representing the number in exponential notation
  • aNumber.toFixed (nDigits)
    Returns a string representing the number in fixed notation, where nDigits is the number of digits to appear after the decimal point

Random[Bearbeiten]

A built in random generator, which generates linear congruential pseudo random numbers itself. On Unix/Linux operating systems, an additional class called "RandomGenerator" uses the operating system's random generator (/dev/rand). In addition, alternative generators are available as "RandomParkMiller" and "RandomTT800". For most applications, the default generator named "Random" should provide a reasonable random sequence (this will be the "/dev/rand"-based generator on Unix/Linux, and a Cryptographic Hash-based generator on other systems).

  • Random.next ()
    Returns a random number between 0 and 1 (float)
  • Random.next (n)
    Returns n random numbers between 0 and 1 (float) as an array
  • Random.nextBoolean ()
    Randomly returns either true or false
  • Random.nextBetween_and_ (min, max)
    Returns a random float between min and max
  • Random.nextIntegerBetween_and_ (min, max)
    Returns a random integer between min and max (use 1 and 6, to simulate a dice)

Time / Date[Bearbeiten]

Time and Date instances provide a rich protocol for all kinds of queries. The list below is only a short extract of the most used methods:

  • Time now
    Returns the current time
  • Date today
    Returns the current date
  • aDate.getDate()
    Returns the day of the month (1..31)
  • aDate.getDay()
    Returns the day of the week (0..6); Sunday is 0
  • aDate.getMonth()
    Returns the day of the month (1..12)
  • aDate.getFullYear()
    Returns the year
  • aTime.getHours()
    Returns the hours (0..24)
  • aTime.getMinutes()
    Returns the minutes (0..60)

String[Bearbeiten]

Be reminded that the String class inherits from CharacterArray, ByteArray, SequentialCollection, Collection and Object. Thus all of those methods are also available (and useful) with strings. Use the browser or take a look at the online documentation for the full protocol (which is much larger than what is listed below).

  • aString.charAt0 (index)
    Returns the n'th character as a Character instance object, using a 0-based indexing scheme
  • aString.charAt1 (index)
    Returns the n'th character as a Character instance object, using a 1-based indexing scheme
  • aString.charCodeAt0 (index)
    Returns the code of the n'th character, using a 0-based indexing scheme
  • aString.charCodeAt1 (index)
    Returns the code of the n'th character, using a 1-based indexing scheme
  • aString.indexOf0 (aCharacter)
    Returns the index of aCharacter, using a 0-based indexing scheme; -1 if not found
  • aString.indexOf1 (aCharacter)
    Returns the index of aCharacter, using a 1-based indexing scheme; 0 if not found
  • aString.lastIndexOf0 (aCharacter)
    Returns the last index of aCharacter, using a 0-based indexing scheme; -1 if not found
  • aString.lastIndexOf1 (aCharacter)
    Returns the last index of aCharacter, using a 1-based indexing scheme; 0 if not found
  • aString.quote()
    Wraps the receiver into double quotes (")
  • aString.split (separator)
    Splits the string into a collection of substrings using the separator. Warning: the JavaScript split(xxx) function and the Smalltalk split: method have different semantics (and argument order)
  • aString.substr0 (index, count)
    Extracts a substring starting at the index with the given length, using a 0-based indexing scheme
  • aString.substr1 (index, count)
    Extracts a substring starting at the index with the given length, using a 1-based indexing scheme
  • aString.substring0 (index1)
    Extracts a substring starting at the index, using a 0-based indexing scheme
  • aString.substring0 (index1, index2)
    Extracts a substring between the given indices, using a 0-based indexing scheme
  • aString.substring1 (index1)
    Extracts a substring starting at the index, using a 1-based indexing scheme
  • aString.substring1 (index1, index2)
    Extracts a substring between the given indices, using a 1-based indexing scheme
  • aString.toLowerCase
    Returns a copy of the receiver with all chars in lower case
  • aString.toUpperCase
    Returns a copy of the receiver with all chars in upper case
  • aString.trim
    Returns a copy of the receiver with all leading and trailing white space removed
  • aString.trimLeft
    Returns a copy of the receiver with all leading white space removed
  • aString.trimRight
    Returns a copy of the receiver with all trailing white space removed

 

Collection (Array)[Bearbeiten]

Notice that Array, OrderedCollection, ByteArray, String and others are also inheriting the Collection protocol. Thus the below listed messages can also be applied to instances of those.

  • aCollection.concat (aCollection)
    Returns a new collection consisting of the concatenation of the receiver and the argument
  • aCollection.every (filterFunction)
    Returns true, if "filterFunction" returns true for all elements
  • aCollection.filter (filterFunction)
    Selects elements for which "filterFunction" returns true
  • aCollection.forEach (function)
    Applies "function" for each element
  • aCollection.join (seperator)
    Joins the strings of the receiver into a new single string using the separator
  • aCollection.map (function)
    Returns a new collection collecting the results of applying "function" to each element in sequence
  • aCollection.pop
    Removes and returns the last element of the collection. Modifies the receiver collection as a side effect.
  • aCollection.push (value)
    Adds an element to the end of the collection. Modifies the receiver collection as a side effect.
  • aCollection.reduce0 (filterFunction)
    Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 0-based indices to the filter.
  • aCollection.reduce0 (filterFunction, initialValue)
    Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 0-based indices to the filter.
  • aCollection.reduce1 (filterFunction)
    Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 1-based indices to the filter.
  • aCollection.reduce1 (filterFunction, initialValue)
    Applies "function" against two values, reducing from left to right. Function must be declared as: f(previousValue, currentValue, index, arr). Pass 1-based indices to the filter.
  • aCollection.shift
    Removes and returns the first element of the collection. Same as removeFirst. Notice that this modifies the receiver collection as a side effect.
  • aCollection.slice0 (index1, index2)
    Extracts a subcollection, using a 0-based indexing scheme
  • aCollection.slice1 (index1, index2)
    Extracts a subcollection, using a 1-based indexing scheme
  • aCollection.some (filterFunction)
    Returns true, if "filterFunction" returns true for any element
  • aCollection.unshift (arg)
    Adds an element to the beginning of the collection. Same as addFirst. Notice that this modifies the receiver collection as a side effect.

 

Transcript[Bearbeiten]

The global variable "Transcript" refers to either the Transcript information window (if it is open) or the standard error stream. As such, it implements most of the stream interface functions. Most noteworthy are:

  • Transcript.cr ()
    Adds a linebreak (i.e. followup text will be shown on the next line)
  • Transcript.show (arg)
    Adds a textual representation of the argument, which can be a string, number or any other object.
  • Transcript.show (fmt, arg,...)
    Like show, but "%i" (i=1..8) sequences in the format argument are expanded by corresponding argument strings. Up to 8 arguments are allowed.
  • Transcript.showCR (arg)
    A combination of show(), followed by a linebreak.
  • Transcript.showCR (fmt, arg,...)
    Like showCR, but "%i" (i=1..8) sequences in the format argument are expanded by corresponding argument strings. Up to 8 arguments are allowed.

and from the inherited write stream interface:

  • Transcript.nextPut (aChar)
    Sends a single character
  • Transcript.nextPutAll (aString)
    Sends a string
  • Transcript.nextPutLine (aString)
    Sends a string followed by a linebreak

 

Logger[Bearbeiten]

The global variable "Logger" refers to an object which will handle system log messages. This is the underlying Smalltalk framework's logging mechanism, not to be confused with expecco's ActivityLog.

The Logger will filter messages according to their severity, which is one of {debug, info, warn, error, fatal}. Severities are ordered as debug < info < warn < error < fatal. For filtering, a severityThreshold can be set, and only messages with a higher severity will be reported.

By default, the Logger sends its output to the stderr stream, which is usually redirected into a (log-)file when expecco runs unattended as a service or controlled by command line arguments. This behavior can be changed by assigning a different logger to the global "Logger". Under Unix systems (OSX and Linux), the defaul logger can also be replaced by one which writes to the syslog facility.
The Logger supports the following protocol:

  • Logger.debug (message)
    logs a debug message
  • Logger.info (message)
    logs an info message
  • Logger.warn (message)
    logs an warning message
  • Logger.error (message)
    logs an error message
  • Logger.fatal (message)
    logs a fatal message

 

Expecco Objects[Bearbeiten]

Many objects internal to expecco's execution machinery are reachable via elementary code (i.e. from within an elementary-coded action's code). A lot of useful and required information can be acquired by consulting these objects. The anchor to all those objects is the "current activity" object.

Current Activity[Bearbeiten]

Within elementary code the activity instance which is executing this piece of code can be accessed via the variable "this" (in Smalltalk: "self"). For every executed action, a new activity object is created. It is usually alive during the execution only (i.e. it is destroyed and its memory reused automatically, after the block's action has finished).

The current activity object supports the following functions:

Reporting[Bearbeiten]
  • error ()
    Report a defect (in the test). Stops execution.
  • error (infoString)
    Report a defect (in the test) with infoString to be shown in the activity log. Stops execution.
  • fail ( [infoString] )
    Report a failure (in the SUT) with optional infoString to be shown in the activity log. Stops execution.
  • inconclusive ( [infoString] )
    Report an inconclusive test. Stops execution.
  • success ( [infoString] ) OBSOLETE - see activitySuccess below
    Finishes the current activity with success.
  • activitySuccess ( [infoString] )
    Finishes the current activity with success. Actually not needed, because simply returning from an action's execute method is semantically equivalent (however, this allows for early successful finish). Due to the more descriptive name, use this function instead of "success()".
  • testPass ( [infoString] )
    Finishes the current test case with success. Notice: this aborts the current activity and all of the callers up to the test case level. This behavior is different from activitySuccess's behavior.
  • pass ( [infoString] )
    Same as testPass (backward compatibility).

 

Logging[Bearbeiten]
  • logData (data)
    Adds data to the activity log.
  • logDataFile (fileName)
    Adds a file attachment to the activity log.
  • logError (messageString)
    Adds a error message to the activity log.
  • logError (messageString, detail)
    Adds a error message to the activity log.
  • logWarning (messageString)
    Adds a warning to the activity log.
  • logWarning (messageString, detail)
    Adds a warning to the activity log.
  • logInfo (messageString)
    Adds an info message to the activity log.
  • logInfo (messageString, detail)
    Adds an info message to the activity log.
  • logImageInfo (messageString, image)
    Adds an info message with an image (screendump) to the activity log. There is an option in the report generator to include those in the generated pdf-report.
  • alert (messageString)
    Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "Execution-Log-Settings" dialog (by default, it is disabled).
  • warn (messageString)
    Same as alert() (for Smalltalk protocol compatibility).

Notice the similarity and difference between "error()" and "logError()": both actually create a log-data entry, but "error()" stops the execution, whereas "logError()" proceeds (maybe, the naming is a bit confusing here...)

 

Execution[Bearbeiten]
  • call (calledActionName)
    Calls another action without any input values. The called action is specified by the "calledActionName" parameter.
  • call (calledActionName, arg...)
    Calls another action with argi input value(s). The called action is specified by the "calledActionName" parameter. Up to 8 arguments are supported.
  • call_with (calledActionName, inPinValues)
    Calls another action passing input values as a collection of values. The called action is specified by the "calledActionName" parameter.
  • call_into (calledActionName, funcReceivingValues)
    Not passing any input values (similar to call), for actions with multiple output values or which write multiple values.
  • call_with_into (calledActionName, inPinValues, funcReceivingValues)
    Similar to the above, for actions with multiple output values or which write multiple values.

The called action is specified by the "calledActionName" parameter which must be one of:

  • a UUID (the called action-block's version- or function-ID),
  • a UUID-string (the called action-block's version- or function-ID),
  • a name-string (the called action-block's name),
  • a direct reference to the action-block object.

Although giving a simple name-string is propably the easiest way to specify the action, it may lead to an error, if multiple actions are found with the same name (which never happens, if a UUID is used). If a UUID is used, this can be either the action's functional- or version-ID. If the version-ID is given, that particular action is called. If a functional-ID is given, the first action which is found to have a matching functional-ID is called. The version-ID is specific, but intolerant to reimports or changes in the called action. Therefore, you should propably use the functional-ID, if you do not want to use the action's name.
In most "normal" situations, the action's name is unique and good enough.

There is an automatic mechanism, to deal with renaming, in that constant action names are found and replaced. Also, such action references are found via the "References" tree menu item and you'll get a warning if such an action is renamed or removed.

The "inPinValues" argument can be either a vector (array) or a dictionary (mapping pin-name to value) of values to be passed to the input pins of the called action. An input value of "Void" ("void" in JavaScript) will be treated like an unconnected pin.

The first variants (without the "into:" argument) return the value of the first output pin (or nil, if there are no output pins). If the output is written multiple times, an error is reported.

The other variant expects a "funcReceivingValues" argument, which must be a JS-function or Smalltalk-Block. This function/block is called with the output pin values as arguments. For each pin, one collection containing all values written to the corresponding pin is passed to it as argument (a collection of values is passed, because the pins could be written multiple times by the action).

For example from a Smalltalk coded action, the "Arith [MinMax]" action (which has 2 outputs) can be called with:

self
    call:"Arith [ MinMax ]" 
    with:{ addend1 . addend2 } 
    into:[:minOutValues :maxOutValues | 
       "/ ... do something with the min/max values ...
       min := minOutValues first.
       max := maxOutputValues first.
    ]

with JavaScript code, the above would be written as:

this.call_with_into("Arith [ MinMax ]",
                          [ addend1 , addend2 ], 
                          function (minOutValues , maxOutValues) { 
                             // ... do something with the min/max values ...
                             min = minOutValues.first();
                             max = maxOutputValues.first();
                          })

or, using a lambda function, as:

this.call_with_into("Arith [ MinMax ]",
                          [ addend1 , addend2 ], 
                          (minOutValues , maxOutValues) => { 
                             // ... do something with the min/max values ...
                             min = minOutValues.first();
                             max = maxOutputValues.first();
                          })

In most cases, this callback function would simply store the output values into local variables, as in:

execute
   |.. minResult maxResult ..|
   ...
   self 
       call:'Arith [ MinMax ]' with:#(10 20)
       into:[:mins :maxes |
           minResult := mins first.
           maxResult := maxes first.
       ].
   ...
   do something with minResult and maxResult
   ...

Exceptions in the called action can be handled in the calling action:

    ...
   [
       result := self call:'Arith [ Quotient ]' with:{ dividend . 0}.
   ] on:ZeroDivide do:[:ex |
       Transcript showCR:'a division by zero occurred'.
   ].
   ...

Notice, that a test-fail verdict is reported as TestFail, which is a subclass of Exception, whereas a test-error verdict is reported as TestError, which is a subclass of Error. Thus, to handle both failure and error in one handler, you may write:

   ...
   [
       result := self call: ...
   ] on:(Error, Exception) do:[:ex |
       Transcript showCR:'either error or fail'.
   ].
   ...

or in JS:

   ...
   try {
       result = call( ... )
   } catch(Error + Exception) {
       Transcript.showCR("either error or fail");
   }
   ...
Reflection, Information, Queries and Accessing[Bearbeiten]
  • activityCreator ()
    The triggering activity (i.e. the caller). Except for special tricks, this is only useful to generate nice print messages (such as "activity foo, called from bar".
  • activityProcess ()
    The thread executing the activity
  • blockDescription ()
    The definition of the activity (i.e. the tree definition item of the action's step)
  • inputValueForPin (pin)
    The value of a certain pin
  • inputValueFor (pinName)
    The value of a certain pin by name. Reports an error, if there is no such pin, the pin is unconnected or has no value.
  • inputValueFor_ifAbsent (pinName, ifAbsentValue)
    The value of a certain pin by name. Returns "ifAbsentValue" if there is no such pin, the pin is unconnected or has no value.
  • inventory ()
    The inventory of the activity
  • nameOfStep ()
    The name of the corresponding step of the activity
  • resources ()
    All resources which have been acquired for the activity (as specified in the resource-definition of the action). Retrieves a collection of Resource objects, as described below.
  • resourcesForId (skillId)
    Resources which have been acquired for one particular skill-requirement. The argument skillId corresponds to the name as specified in the first column of the resource definition of the action). Notice, that a collection of Resource objects is returned - even if only one resource has been acquired.
  • step ()
    The corresponding step of the activity
  • stepLocalStorageAt ("<varName>")
    Retrieves a step's local storage value.
  • stepLocalStorageAt_put ("<varName>", value)
    Defines a step's local storage value.
Step local storage can be seen as a kind of static variable, which is only visible inside the code of a single execute function, but with the live time of the step. This means that the value is still present, when the step is executed the next time. However, it will vanish, whenever the diagram which contains the step is edited. Also, its value is not copied, when the step is copied. It is also not made persistent. The value will be reset to nil with every edit operation on the diagram, or via a menu function in the project's "More" menu, or the step's menu.
Step local storage can be used to count the number of invocations, or to implement static state which is to be preserved between step executions.
This is an experimental feature, only to be used by advanced users.

 

Environment Access[Bearbeiten]

Starting at every compound activity, a nested stack of outer environments is present, ending in a top project environment. The following activity methods provide access to some of them. This can be used to automatically setup environment variables from a configuration or when autodetecting a system-under-test's configuration and leaving the values in a defined outer environment. Depending on the intended lifetime of those values, one of the following environments should be chosen as target of such operations:

  • environment ()
    The currently valid environment variables of the compound step which contains this action, or (if none) of the test plan in which this action executes (see "Environment Access" below)
  • environmentAt (anEnvironmentVarName)
    The value of an environment variable (in the environment as described above)
  • environmentAt_put (anEnvironmentVarName, value)
    Changing the value of an environment variable (in the environment as describe above)


  • executorEnvironment ()
    an environment which is only present during a single testplan execution
  • projectEnvironment ()
    an environment which is attached to the current test suite (but not persistent when the suite is saved/loaded from an ets file)
  • browserEnvironment ()
    an environment which is present while the expecco application is running (i.e. the current session). (it is not made persistent)

None of the following are made persistent directly (i.e. the values will not saved with the ".ets"file). However, every test suite, test plan and compound block provides a description of initial values and how to compute them in their environment description, which is edited using the Environment Editor or which can be modified with actions from the reflection library. Also, such descriptions can be saved and loaded separately from the test suite via the "Load/Save Parameter Set" menu functions of the "File" menu or via the "--parameters" command line argument.

 

Event Sending[Bearbeiten]

Events can be pushed onto an event queue from either actions found in the standard library or via API calls to the current activity (also from bridged elementary actions). The global event handler should be already running (and especially, a handler should be registered) before the first event is sent. Otherwise, the event might be ignored or an error is raised. A sample suite is found in "demos/d62_Event_Queue_Demos".

  • pushEvent (eventOrPayloadData)
    pushes an event onto the global event handler's event queue. The argument may be either an instance of Event, or the payload (which is packed into an Event instance with type #default). Raises an error, if no global event handler process is running.
  • pushEventType_data (eventTypeSymbolOrNil, payloadData)
    pushes an event of type (or #default) onto the global event handler's event queue. Raises an error, if no global event handler process is running.

 

Executor Functions[Bearbeiten]

The executor (as returned from the current activity via the "executor()" function described above) is responsible to execute steps in a network and elementary code in a controlled fashion. In addition to sequencing and assigning activities to a particular execution thread, it also cares for the activityLog and error handling. Usually, you do not need to interact with an executor directly.

  • executorsWorkingDirectory ()
    Temporary working directory. This is removed after the execution (i.e. useful only for very temporary files, which are generated in an action and read in another)
  • testplan ()
    The currently executed testplan (nil if a block test blocks are executed)
  • project ()
    Project (testsuite or library) in which the test is executed

 

Resource Functions[Bearbeiten]

Resources can be assigned to an action as a "required resource", and the execution will be synchronized with the allocation and reservation of a particular resource as acquired from an inventory. Once allocated, the resources are available to the action via resources() or resourcesForId() functions:

  • resources ()
    retrieves the set of resources allocated for the action. The collection is keyed by the name as given in the "Resources" tab, the associated value is a resource instance providing the concrete values of the resource's skills.
  • resourceForId (idString)
    retrieves the resource allocated for the idString as given in the "Resources" tab, the returned value is a resource instance providing the concrete values of the resource's skills.

Resource instances respond to the following protocol:

  • name ()
    The name of the resource
  • skillNamed (nameOrUUID)
    Fetch a concrete skill-value of that resource. The argument can be either a skills name, or the functional-UUID of the skill element (in the project tree).
  • skillAttributeNamed (name)
    Fetch a concrete skill-attribute-value of the resource

 

Test Suite (Project) Functions[Bearbeiten]

Every activity, step or action has a reference to its containing project (or library, if imported). The direct enclosing project/library is returned by the "project()" function, the top level project (i.e. the loaded suite) via the "topProject()" call. Projects, Test Suites and Libraries are all the same kind of object (i.e. any project can be used as a test suite or imported into another as imported library).

  • aProject.documentation ()
    The documentation or nil
  • aProject.functionalId ()
    A unique ID which defines this testSuite independent of its name (remains constant after change)
  • aProject.projectsWorkingDirectory ()
    Project directory - i.a. all attachments. This directory is created when a project file is loaded and is deleted when expecco is closed. Do not use it for permanently needed files.
  • aProject.modelLanguageStringFor (aString)
    translates a string according to the model-language translation table
  • aProject.versionId ()
    A unique ID which defines this testSuite in exactly this version. (changed with any edit operation)
  • aProject.elementWithName (aString)
    Retrieve an element by name
  • aProject.elementWithId (aUUID)
    Retrieve an element by its version id
  • aProject.elementWithFunctionalId (aUUID)
    Retrieve an element by its functional id
  • aProject.typeNamed (aTypeName)
    Retrieve a datatype. To instantiate a type, write
    "(self project typeNamed:'Foo') new".

 

Testplan Functions[Bearbeiten]

  • aTestPlan.documentation ()
    The documentation or nil
  • aTestPlan.name ()
    The name of the Testplan
  • aTestPlan.tags ()
    A collection of tags of the testplan
  • aTestPlan.functionalId ()
    A unique ID which defines this testplan independent of its name (remains constant after change)
  • aTestPlan.versionId ()
    A unique ID which defines this testplan in exactly this version. (changed with any edit operation)

 

Step Functions[Bearbeiten]

  • aStep.name ()
    The name of the Step
  • aStep.tags ()
    A collection of tags of the Step
  • aStep.inputPins()
    A collection with all input pins
  • aStep.inputPinForPinName(inputPinName)
    The corresponding input pin. Raises an error, if no such input pin exists.
  • aStep.inputPinForPinName_ifAbsent(inputPinName, ifAbsentValue)
    The corresponding input pin. Returns ifAbsentValue, if no such input pin exists.
  • aStep.outputPins()
    A collection with all output pins
  • aStep.outputPinForPinName(outputPinName)
    The corresponding output pin. Raises an error, if no such output pin exists.
  • aStep.outputPinForPinName_ifAbsent(outputPinName, ifAbsentValue)
    The corresponding output pin. Returns ifAbsentValue, if no such output pin exists.

 

Pin Functions[Bearbeiten]

The input and output pins can be referenced in elementary code directly by their name. The name refers to the pin - NOT the passed value (this allows for the code to check, if a value is present). For indexed pins (i.e. in steps with a variable number of pins), the collection of pins is referred to by the name, and pins are accessed via an index (1..). See more about variable pins in the section below.

Common to all Pins:[Bearbeiten]
  • pinName.datatype ()
    Returns the data type of the pin (Reflection-API)
Input Pins:[Bearbeiten]
  • pinName.hasValue ()
    Returns true if the pin has received a value (useful to determine if there is a value if the trigger condition is "AndConnected" or "Or")
  • pinName.hasEnvironmentFreezeValue ()
    Returns true if the pin has a freezeValue from an environment
  • pinName.hasFreezeValue ()
    Returns true if the pin has a freezeValue
  • pinName.isConnected ()
    Returns true if the pin is connected (but not frozen)
  • pinName.value ()
    Returns the value of the pin. This is either the value received via a connection, or a constant freeze value, or an environment variable freeze value. Raises an error if the pin did not receive any value.
  • pinName.valueIfPresent ()
    Returns the value of a pin or nil if the pin did not receive any value. Similar to "value()", but avoids the exception.
  • pinName.valueIfAbsent (alternativeValue)
    Returns the value of a pin or the alternativeValue, if the pin did not receive any value. Similar to "value()", but avoids the exception.
  • mbxPinName.waitForValue () - Mailbox pins only
    The pin has to be a mailbox pin. The execution of this action will be suspended until the pin receives a value.
  • mbxPinName.waitForValueWithTimeout (seconds) - Mailbox pins only
    Like above, but the execution will only wait for the specified time limit, given in seconds. The parameter is typically an integer, but can also be a fraction (e.g. 1/3) or a floating-point number (e.g. 0.3).
  • mbxPinName.consumeValue () - Mailbox pins only
    Consumes the current value which is present at the mailbox input pin. If there is another pending input value, hasValue() will continue to return true, and value() will return the next pending value.
Output Pins:[Bearbeiten]
  • outPinName.isBuffered ()
    True if the pin is buffered (only generates a value if the action completes successful)
  • outPinName.isConnected ()
    Returns true if the pin is connected. This can be used to not perform expensive computations in an elementary action, iff no-one is interested in that value.
  • outPinName.value (data )
    Writes the value. If the output pin is unbuffered and connected to another step's input pin, the other step's action may be triggered (if the other step's trigger condition is fulfilled). If the output pin is buffered, the datum will be passed to the connected input, when the action has finished with success.
Variable Input Pins[Bearbeiten]

Elementary steps can have a variable number of input pins, if the pin's schema has the "variable pin" attribute set. To change the number of pins of a step in the diagram editor, pull the pin-handle of a placed step, to add or remove pins. In elementary code, the following API call entries (to the variable pin) are provided to deal with this situation:

  • pinName
    now refers to a collection of pins, representing the set of pins actually present at the step. In contrast to regular pins, where pinName refers to a single pin.
  • varPinName.numberOfVariablePins ()
    returns the number of actual pins present in the step for the (variable) schema pin named "pinName"
  • varPinName.size ()
    same as the above "numberOfVariablePins"
  • varPinName.inputPinAt (idx)
    returns the idx'th actual pin of the schema pin named "pinName". Notice this retrieves the pin for reflection queries, NOT the pin value. To get value or query if it has a value, call valueAt:() / hasValueAt(). Pin indexing starts with 1.
  • varPinName.at (idx)
    same as inputPinAt(idx)
  • varPinName.hasValueAt (idx)
    true if the idx'th variable pin is connected and has received a value. Same as hasValue() for variable input pins. Indices start with 1.
  • varPinName.values ()
    Retrieves the values passed to the pins as an array. The array contains nil entries for disconnected pins and those without a value
  • varPinName.valuesPresent ()
    Retrieves the values as present; skips unconnected pins or those without a value.
  • varPinName.valueAt (idx)
    to get the idx'th pin's value; raises an error if no value was given or pin is not connected. Same as value() for variable input pins.
  • varPinName.valueIfPresentAt (idx)
    to get the idx'th pin's value, or nil if no value was given or pin is not connected
  • varMbxPinName.waitForValueAt (index) -Variable mailbox pins only
    Same as waitForValue() for variable input mailbox pins.
  • varMbxPinName.waitForValueAt_withTimeout (index, seconds) - Variable mailbox pins only
    Same as waitForValue() for variable input mailbox pins.
  • varMbxPinName.consumeValueAt (index) - Variable mailbox pins only
    Same as consumeValue() for variable input mailbox pins.
Variable Output Pins[Bearbeiten]

Starting with expecco v2.1, steps can also have a variable number of output pins, if the schema has the "variable output pin" attribute set. To change the number of pins, pull the pin-handle of a placed step, to add or remove pins. In elementary code, the following API call entries (to the variable pin) are provided to deal with this situation:

  • varPinName.numberOfVariablePins ()
    returns the number of actual pins present in the step for the (variable) schema pin named "pinName".
  • varPinName.outputPinAt_value (idx, value)
    write a value to the idx'th output pin. Index starts with 1.
  • varPinName.outputPinAt (idx)
    returns the idx'th actual pin of the schema pin named "pinName". Notice that this retrieves the pin; to write an output value to it, call value(v) on the returned pin.
  • varPinName.at (idx)
    same as outputPinAt(idx)
  • varPinName.outputPins ()
    returns a collection of all actual pins present in the step for the (variable) schema pin named "pinName".

 

Datatype Functions[Bearbeiten]

A datatype can be retrieved by asking a pin for its datatype using the datatype () function. This is sometimes useful in elementary code, in order to instantiate a new value (given the pin) or to ask for details of the type.

  • aType.name ()
    The name of the Datatype
  • aType.isAnyType ()
    true if the datatype is the builtin "Any" type
  • aType.isArrayType ()
    true if the datatype is an "Array" type (with any element type)
  • aType.isCompoundType ()
    true if the datatype is a compound (structure) type
  • aType.isEnumType ()
    true if the datatype is an enumeration type
  • aType.isPrimaryType ()
    true if the datatype is any builtin primary type
  • aType.isRangeType ()
    true if the datatype is a subrange of another type
  • aType.isTupleType ()
    true if the datatype is a tuple type
  • aType.isUnionType ()
    true if the datatype is a union type
  • aType.isStringPrimaryType ()
    true if the datatype is the builtin "String" primary type

Enum Type Functions[Bearbeiten]

  • anEnumType.values ()
    returns an array of enum-value names (symbolic)
  • anEnumType.integerValueOf (elementName)
    returns the associated integer value.

Compound Type Functions[Bearbeiten]

  • aCompoundType.namedFieldDescriptions ()
    returns an array of descriptions of the named fields (if any)
  • aCompoundType.indexedFieldDescriptins ()
    returns a description of indexed fields (if any)

Each field description can be asked for the "fieldName()", "fieldType()" and "defaultValue()".

Primary Type Functions[Bearbeiten]

  • aPrimaryType.typeClass ()
    returns the underlying Smalltalk class, of which values are instances of.

Range Type Functions[Bearbeiten]

  • aRangeType.baseType ()
    returns the underlying base type. Typically this will be the Integer primary type.
  • aRangeType.minValue ()
    returns the minimum value of the range
  • aRangeType.maxValue ()
    returns the maximum value of the range

 

Functions for Any Tree- or Diagram Element[Bearbeiten]

These include: BlockDescriptions, Types, TestPlans, TestCases, Steps, Connections, Pins, Annotations etc.

  • object.versionId
    A globally unique identifier (GUID or UUID) which is changed with every modification of the item
  • object.functionalId
    A globally unique identifier (GUID or UUID) which is assigned once-and-for-all to the item
  • object.id
    alias for versionId().
  • object.tags
    A collection of tags.
  • object.isAnnotation
    true, if it is an annotation (in a diagram)
  • object.isStep
    true, if it is a step (in a diagram)
  • object.isConnection
    true, if it is a connection (in a diagram)
  • object.isBlockDescription
    true, if it is a block description (in the tree)
  • object.isAttachment
    true, if it is any attachment (in the tree)
  • object.isFileAttachment
    true, if it is a file attachment (in the tree)
  • object.taggedValueAt (aKey)
    To retrieve a tagged value. These are usually preserved when objects are transported to/from other systems.
  • object.taggedValueAt_put (aKey, anObject)
    to change a tagged value. Only a limited set of datatypes are supported: anObject must be a String or a Number.
  • object.propertyAt (aKey)
    to retrieve an expecco property. These are expecco-internal properties. These are usually not recognized by other systems.
  • object.propertyAt_put (aKey, anObject)
    to set an expecco property. These are usually not recognized by other systems. Only a limited set of datatypes are supported: anObject must be a String or a Number.

 

Groovy Elementary Blocks[Bearbeiten]

Groovy is an open source interpreter and compiler for a Java like scripting language which runs on top of the Java Virtual machine. For details, visit "groovy-lang.org" and especially the Groovy Documentation Portal.

Code written as a Groovy elementary block is not executed directly by expecco. Instead, the code is forwarded to a Groovy shell which runs inside a Java Virtual Machine (JVM). This may be a local JVM, whose sole purpose is to provide additional utility functions or which provides an interface to the actual system under test (SUT), or it may be the JVM which executes the tested application and is running physically on the SUT.

By using Groovy blocks, expecco's basic black-box test functionality is extended to include powerful gray- and white-box tests. You can access the internals of objects inside the SUT, call its functions and even define new classes and functions and call them. Groovy is especially useful, if you have to install callback- or hooks inside the SUT, which interact back to expecco (e.g. to collect SUT-internal events and allow expecco to read those from the SUT). However, you should keep in mind that your tests may become affected by changes in the SUT and needs to be modified if interfaces change (which is a general property of white box tests, not a limitation of expecco).

The machinery to interact with a JVM (and therefore to execute Groovy code blocks) requires the Java Bridge plugin, which is now (rel18.1) part of the basic expecco package. Groovy itself is available for free as a jar class library, which is loaded with or into the target application. The Java Bridge plugin already includes a prepackaged Groovy interpreter which can be loaded transparently into the SUT application whenever a Groovy block is executed. So except for the setting of the JVM path, no further setup or installation is required.

Why use Groovy Elementary Blocks?[Bearbeiten]

The Java Bridge and especially Groovy code is very useful when testing GUI frameworks, communication protocols or machine control soft- and hardware which is written in Java. Especially, if you have to call functions internal to the SUT in order to trigger actions or if you need to be informed about state changes via listeners or callbacks, Groovy is the perfect tool for the task. Note that calls into the JVM are relatively straight forward, using the method forwarding mechanism, even from Smalltalk code. However, callbacks and listeners, which usually have to be implemented as instances of special derived classes of a an abstract framework class, are hard to realize without Groovy.

Without Groovy, you would have to add such classes to the software inside the SUT - e.g. by adding them to the tested software's jar packages, or by adding extra jar packages. In addition, you'd have to provide a communication mechanism (socket, pipe or file interchange), by which your test action is able to retrieve this information.

What is the Difference between a regular (expecco-) JavaScript Block and a Groovy Block?[Bearbeiten]

In one sentence: expecco JavaScript actions are executed inside expecco, Groovy actions inside the SUT (System Under Test) or any other external JVM (Java Virtual Machine).

More detailed:

Expecco's JavaScript actions are actually alternative syntactic forms of Smalltalk actions. When executed, they run inside the expecco executable and have access to any class or object inside expecco itself, or those which are passed in via input pins or environment variables.

On the other hand, Groovy actions are "injected" (eg. downloaded) into a Java VM and execute in that object space. They cannot directly access expecco classes or objects, but instead can access all public classes and objects within the JVM in which they run, which can be the SUT (System Under Test). By means of proxy objects (which are forwarding operation-calls), expecco can deal with remote objects almost transparently: these can be returned from a Groovy action to expecco, and later be used again as arguments to other Groovy actions; i.e. they can be passed around via output- and input pins, stored in variables and inspected.

What is the Difference between a Groovy Block and a Bridged Node Block?[Bearbeiten]

Groovy blocks execute inside a JVM (Java Virtual Machine) and can access any Java code (eg. classes inside a java application or inside a dynamically loaded jar-library).

In contrast, Node actions are executed inside a Node (aka "Node.js") interpreter (which is not a JVM).

There are also slight differences in the syntax: Groovy is more Java-like, whereas Node blocks use pure JavaScript.

Groovy Compilation Mechanism and Performance Issues[Bearbeiten]

When a Groovy action is executed for the very first time, the block's code is transmitted via the bridge to the Groovy shell on the target JVM as a string. There, the code is parsed and an anonymous jar is created, containing Java byte code. Thus, there is some initial overhead involved in both sending the code and more so in compiling the code in the JVM. However, for every followup call, the JVM will directly call the generated code and run at full Java speed (i.e. the execution speed is almost as if native Java code was executed), although there is still some overhead due to the interprocess communication (typically a few milliseconds for every call)

Using a Single Local JVM Connection / Executing Groovy Actions on the Local Machine[Bearbeiten]

By default, a Groovy action is executed on a JVM which runs on the local machine; i.e. the machine on which expecco itself is running. With the execution of the very first Groovy action, a JVM is started as defined in the expecco settings dialog, then a bridge connection is established to that JVM, and the Groovy interpreter (called "GroovyShell") is loaded and instantiated on the JVM. Then a reference to that GroovyShell object is remembered and used for all followup Groovy actions which are to execute on that local JVM. This default behavior is convenient if you want to use Java support libraries for reporting, statistics or other computations, or if your SUT is running inside a local JVM, or if you want to call interface functions (protocols) which actually talk to your SUT.

Using a Particular JVM Connection / Executing Groovy on a Possibly Remote Machine[Bearbeiten]

If you either need to customize the JVM startup and/or your test scenario has to talk to one or multiple remote systems (or multiple java programs within one scenario), you have to setup multiple java connections, and pass the connection to use to your Groovy actions. This can be done either via an input pin, or via an expecco environment variable. The algorithm to provide this is as follows:

  • if the action has a pin named "groovy", and it is connected, that pin's value is used as GroovyShell handle
  • otherwise, if the environment contains a variable named "GROOVY", that variable's value is used
  • otherwise, a new GroovyShell is instantiated and used, on the JVM which is given by:
    • if the action has a pin named "java", and it is connected, that pin's value is used as Java Bridge handle
    • otherwise, if the environment contains a variable named "JAVA", that variable's value is used
    • finally, otherwise a local JVM connection is used (a shared singleton connection).

This scheme makes the most common case easy, where only one single local JVM is required, but still allows the flexibility to handle multiple JVMs and even multiple different GroovyShell instances within each JVM.

To use input pins, you have to create corresponding pins manually, and the pin names MUST be "java" / "groovy". You cannot use these two reserved names as regular input pin names.

Choosing another JVM Version[Bearbeiten]

By default, the Groovy interpreter runs in the default Java virtual machine. That is the JWM which would be executed when entering "java" on the command line. To verify the version, open a command shell (or cmd.exe window) and enter

java -version

To specify another JVM, go to "Extras" → "Settings" → "Plugins" → "Java Bridge", and enter the path to a JRE (Java Runtime Environment) folder into the "Java Installation Path" field. You can also enter a JDK (Java Development Kit) folder name into that field.
See also Settings_JavaBridgeSettings.

Additional JAR Class Libraries[Bearbeiten]

By default, the Groovy code runs inside a JVM which has its classPath initialized from the preferences. These class path preferences can be changed in the "Settings" → "Project Management" → "Groovy Class Path" settings dialog. Additional jars can be added to the common classPath (before a JVM is started) by the "[Add JAR to Groovy Class Path]" action, or dynamically to a concrete already running JVM instance via the "[Add JAR to JVM]" action.

Notice that you have to restart existing groovy connections to make such changes effective (via the "Extras" → "Debugging" → "Shut Down Bridge Connections" menu function).

Groovy Code Snippets / Examples[Bearbeiten]

Calling Existing Code in the System Under Test (SUT) via a Groovy Action[Bearbeiten]

A Groovy block's code looks very similar to a regular JavaScript block's code (with some differences in the Groovy language). However, it is executed inside a JVM, which may be inside the SUT. It can therefore instantiate Java objects and call static and member functions inside the target system, if it is programmed in Java.

For example, too call a static function named "foo" in a class named "MyTestClass", use the following Groovy block:

def execute() {
   MyTestClass.foo("hello world");
}

if the class is located in another package (or especially: in one of your own), use explicit package prefixes:

def execute() {
   javax.swing.JFrame frame = new javax.swing.JFrame();
   javax.swing.JButton button = new javax.swing.JButton( "Press Me" );

   frame.add(button);
   frame.setSize(300,100);
   frame.setVisible(true);
}

or an import declaration:

import javax.swing.*;

// end of import definitions

def execute() {
   JFrame frame = new JFrame();
   JButton button = new JButton( "Press Me" );

   frame.add(button);
   frame.setSize(300,100);
   frame.setVisible(true);
}

Notice that you may not remove the comment line starting with "// end of....".
This separator pattern has been hardcoded into the Groovy machinery to separate any class and import definitions from the execution method.

Defining a New Class in the System Under Test using Groovy[Bearbeiten]

Drag this image into expecco

You can define your own classes in a Groovy block:

For demonstration, let's start with a simple running example:

class TestSleep {
  static void main(String[] args) {          
     println 'Step 1'
     sleep(1000)
     println 'Step 2'
     sleep(1000)
     println 'Step 3'
  }
}

// end of definitions -- do not remove this line unless imports above is empty

def execute() {
   TestSleep.main(null);
}

Instances of new classes may be required especially to install listeners and callbacks when testing UI or server applications. A typical scenario is when you want expecco to be informed about mouse or keyboard events (MouseListener).

Usually, instances of such classes are created in one Groovy action, and passed to expecco via an output pin:

class MyClass extends Object {
   Object someState;

   def set_someState(Object newValue) {
       someState = newValue;
   }
   def get_someState() {
       return someState;
   }
   def String toString() {
       return "a very\nlong\nmultiline\na\nb\nstring\n";
   }
}

// end of local definitions

def execute() {
   outputPin.value( new MyClass() );
}

Then, additional Groovy actions take that as an input value and call the classes methods:

def execute() {
   Object myInst = inputPin.value();

   myInst.setSomeState(123);
}

or:

def execute() {
   Object myInst = inputPin.value();

   output.value( myInst.getSomeState() );
}

Defining a Listener Class in the System Under Test using Groovy[Bearbeiten]

A common pattern in Java communication frameworks is to define an abstract classes or interfaces which has to be subclassed by users of the framework. This pattern is common for callbacks or event listeners. The programmer has to define a listener class, create an instance of it, and register it towards the communication framework. To interact with such a framework in expecco, you will need a listener class, which responds to callbacks from the framework and either remembers them for later queries from a testcase's activity, or by directly calling back into expecco. The later is much harder to deal with, as those callbacks may come at any time, even when the test has already finished or the test is being debugged (remember that the JVM may run independent from expecco). So synchronization issues may be hard to solve. It is much easier and recommended to implement the first approach: the listener collects incoming events, until expecco is ready to process them.

You will need an elementary Groovy block to instantiate and register the listener towards the framework similar to the conceptual code below:

// a Groovy Elementary Block

import <interface or abstract class from which to inherit>;

class MyListener extends AbstractListener {
   ArrayList eventQueue = new ArrayList();
   Object eventNotifier = new Object();

   /**
     * Called from a framework when an event occurs.
     */
   public void onExampleEvent(Object event) {
       synchronized (eventQueue) {
           eventQueue.add(event);
       }
       synchronized (eventNotifier) {
           eventNotifier.notifyAll();
       }
   }

   /**
     * Called from expecco to fetch next event.
     *
     * Of there is no event in the queue, blocks
     * until an event arrives (by means of #onExampleEvent())
     */
   public Object getNextEvent() {
       while (true) {
           synchronized (eventQueue) {
               if (! eventQueue.isEmpty()) {
                   return eventQueue.remove(0);
               }
           }
           synchronized (eventNotifier) {
               try {
                   eventNotifier.wait();
               } catch (InterruptedException ie) {
                   // Pass, try again
               }
           }
       }
   }
}

// end of local definitions

// called from expecco, to instantiate a listener
def execute() {
   var theListener = new MyListener();

   framework.registerListener ( theListener );
   outputPin.value( theListener ); // return it to expecco
}

Make sure that the above elementary code is called from your activity diagram in the initialization/setup phase, and that the returned listener reference is either remembered in an environment variable of your suite, or passed down to the actual test case via input/output pins.

Define an additional elementary block (here in JavaScript, but could also be Smalltalk or Groovy), which takes the listener as input pin and fetches the next event via getNextEvent():

// a JavaScript Elementary Block

execute() {
   var theListener;
   var event;
 
   theListener = inPin.value();
   event = theListener.getNextEvent();
   outpin.value ( event );
}

The above code blocks the test suite until an event arrives. It may be useful to add another method to query the number of received events, or to flush any previously received and already enqueued events.

In your test case, perform any stimulation required to make the framework generate an event, so that the listener's framework entry is called. Then, invoke the #getNextEvent() action to suspend execution until an incoming event arrives (you will probably set a time limit on that step's execution). Once this action finishes, the event is available in expecco as a reference to the real event object (remember: the event object is an object inside the remote Java machine, so what you have in expecco is a handle to that remote object).

All getters, setters and other methods of the event's implementation can be invoked from elementary code (any of the programming languages can do that, due to the message forwarding of the bridge).

For example, assuming that the event provides getEventID() and getMessageString() getters, which can be called from a Smalltalk action with:

id := theEvent getEventID. msg := theEvent getMessageString.

and from a JavaScript action or from another Groovy action witht:

id = theEvent.getEventID(); msg = theEvent.getMessageString();.

Also, the expecco object inspector has been enhanced to recognize those proxy bridge objects, and presents the internal state, inheritance and class protocol in a multitab fashion. This makes looking into those remote objects almost as comfortable as looking into local ones.

Accessing JMX MBeans[Bearbeiten]

import java.lang.management.*

// end of definitions

def execute() {
   def os = ManagementFactory.operatingSytemMXBean;
   def mem = ManagementFactory.memoryMXBean;

   Transcript.showCR("OS architecture: " + os.arch.toString() );
   Transcript.showCR("MEM heapUsage  : " + mem.heapMemoryUsage.toString() );
   Transcript.showCR("POOLS:");
   ManagementFactory.memoryPoolMXBeans.each { eachPool ->
       Transcript.showCR("  pool name     : " + eachPool.name.toString() );
       Transcript.showCR("  pool peakUsage: " + eachPool.peakUsage.toString() );
   }
}

Please refer to the corresponding Java documentation for details.

Executing Jython, JRuby or other Script Code inside the JVM[Bearbeiten]

Bulb.png the following example is somewhat outdated due to the new builtin support for jython (in 19.2).

The following example executes Jython (Python) code inside the JVM (please make sure that the jython jar is in your Groovy class path):

import org.python.util.PythonInterpreter; 
import org.python.core.*; 

// end of definitions -- do not remove this line unless imports above is empty

def main()
   throws PyException
{ 
   Properties p = new Properties();
   // p.setProperty("sys.path", "/Users/cg/work/exept/bridgeFramework/javaBridge/javaBin/jython2.7.0");

   p.setProperty("python.path", "<path to jython2.7.0/Lib>");
   p.setProperty("python.home", "<jython2.7.0>");

   Object props = System.getProperties();
   // String[] args = new String[0];
   String[] args = { "" };
   PythonInterpreter.initialize(props, p, args);

   PythonInterpreter interp =
       new PythonInterpreter();

   System.out.println("Hello, brave new world");
   interp.exec("import sys");
   interp.exec("print sys");

   interp.set("a", new PyInteger(42));
   interp.exec("print a");
   interp.exec("x = 2+2");
   PyObject x = interp.get("x");

   System.out.println("x: "+x);
   System.out.println("Goodbye, cruel world");
}

def execute() {
   main();
}

Of course, you can also write the "interp" instance to an output pin, and remember it somewhere or pass it to other actions. Then, other actions can directly invoke the "exec(...)" function, without instantiating a new interpreter.

Groovy Datatype Limitations[Bearbeiten]

Integer types[Bearbeiten]

As Groovy dynamically compiles to plain Java bytecode and executes on top of a regular JVM, the integer range is limited to 32bit (64bit for "long int" types). No automatic conversion between small and large integers is performed. If required, you will have to use a BigNum package and/or cast int to long int. There is currently no automatic conversion or transparent support to pass large numbers from expecco to Groovy and vice versa. Integers passed from Java to expecco will be represented as instances of the Integer type there.

Limited Object Conversion to/from Groovy[Bearbeiten]

Some limited form of object conversion is automatically performed when passing expecco's Smalltalk objects to Groovy, and back when returning values from Groovy. This conversion especially affects values passed to/from pins of a Groovy action.

The following table summarizes the conversion process:

from Smalltalk to Java and from Java to Smalltalk
String String String
Integer (up to 32/64 bit) int, long int, boxed Integer Integer
Float, Double float, double; boxed or unboxed Float
(Notice that Smalltalk Floats have double precision)
Fraction float, double; boxed or unboxed (danger alert: precision may be lost) Float
(Notice that Smalltalk Floats have double precision)
Boolean Boolean Boolean
Array of any above ArrayList of any above Array of any above
other Smalltalk Object(!) - -

(!) does not work, yet: at the time of writing, Smalltalk objects other than the basic types listed above cannot be passed to Java. However, the reverse direction is possible, and Smalltalk includes a mechanisms to handle (and even inspect) remote Java objects.

No Smalltalk Classes, No Smalltalk Objects in Groovy[Bearbeiten]

Of course, no Smalltalk class can be used directly in Groovy code. And Smalltalk objects can only be passed as opaque handles to/from Groovy as described above. However, all Java classes are at your hands now! Some limited interaction with expecco and underlying Smalltalk objects is possible via the "eval()" and "perform()" API described below.

However, in general: if more complex objects need to be interchanged, this must be either done by converting them to an array (of objects), possibly an array of arrays, which is passed to a Groovy "interface function" first, which instantiates the required Java object(s). Or, alternatively to some ASCII representation (XML or JSON, for a more lightweight approach) and convert this back and forth.

Handling of Java (and Groovy) Objects in expecco/Smalltalk[Bearbeiten]

Java Object References (Handles)[Bearbeiten]

Groovy elementary function can return references to Java objects via output pins. Because the actual object is located inside a JVM (i.e. outside of expecco), these references are treated like opaque handles by the underlying Smalltalk runtime system. However, a mechanism is provided to allow for almost transparent access to the referenced object's private slots, its classes' static slots, its class hierarchy and method information. The object inspector is able to present that information in a convenient way, similar to how Smalltalk objects are presented. Also methods of the Java object can be called and values returned to expecco.

Message Forwarding[Bearbeiten]

Smalltalk and JavaScript code can send regular messages to Java objects inside a remote JVM. This is possible via the Smalltalk "doesNotUnderstand:" mechanism, which intercepts any call, sends a message to the real object (via the Java Bridge), awaits an answer and returns the result, which is usually a reference to another Java object.

All of this is transparent to the programmer of the elementary code, except for the fact that Java method naming is different from Smalltalk. And of course a performance penalty, due to the interprocess communication, marshalling overhead and dynamic lookup via reflection on the Java side.

Method Name Conversions[Bearbeiten]

When a message is sent from Smalltalk code to a Java object, a message translation mechanism similar to the Smalltalk <-> JavaScript mechanism is used:

foo                  -> foo()
foo:arg              -> foo(arg)
foo:a1 _:a2 ... _:aN -> foo(a1, a2, ... aN) where the "_" are arbitrary strings.

thus, to call a Java object's "installHandler(arg1, arg2, arg3)" function, you should write in Smalltalk:

javaObject installHandler:arg1 _:arg2 _:arg3

and in JavaScript:

javaObject.installhandler(arg1, arg2, arg3)

It is obvious, that this kind of code is better written in JavaScript, being the same as a native Java call would look like.

Conversion of Dynamic Smalltalk Objects to Static Java Types[Bearbeiten]

Inside expecco (which is running under a Smalltalk VM), all object references are dynamically typed. This means, that in Smalltalk, every object holds a reference to its class and in principle, any object can be passed as argument to a called function. In contrast, Java requires the type of a function argument to be declared (either as a class or interface), and the argument type(s) are part of a method's signature.

Therefore, when calling a Java function from Smalltalk, the correct function must be found by reflection at execution time.

For example, if the Java object provides two methods as in:

public void foo(Integer arg) { ... }
public void foo(Float arg) { ... }

and it is called from Smalltalk with:

aJavaObjectHandle foo: 1

then the "Integer"-argument version of foo must be called.

Whereas if called as:

aJavaObjectHandle foo: 1.0

then the "Float"-argument version is to be called.

For this, the Java Bridge side uses reflection to scan the available methods for a best fit, and transforms the passed Smalltalk argument to the best matching type before calling the actual function.

There are some rare situations, where this automatic lookup fails or finds a wrong method. For example, if same-named functions exist with both a "float" (unboxed) and a "Float" (boxed) argument, and you need to make sure which is called. In such cases, you can either use a Groovy block as mediator, which calls the desired function with an explicitly casted argument, and call the Groovy block from Smalltalk (or JavaScript) or use an explicit cast operation ("Bridge2::Cast object:o as:typeName").

Current IDE Limitations[Bearbeiten]

The Smalltalk IDE has currently only limited support to extract and present Java's static type information. Currently, there is no class- and function name completion in the editor, making elementary code development for Java/Groovy less pleasant. Therefore, for complicated setups, it is a good idea to open a specialized Java development environment (e.g. eclipse) in parallel, and test the setup code there before copy-pasting it into a Groovy block.

Eclipse is also useful to find out class- and method names during development.

Groovy Code API[Bearbeiten]

Groovy code supports a subset of the above activity functions, which is intended provide an interface similar to the JavaScript and Smalltalk elementary code API. Of course, technically for every API function below, the elementary code executing in the remote Java VM has to make a remote procedure call back to expecco. On the JVM side, this is done very similar to the above described remote message mechanism, when messages are sent from Smalltalk to Java/Groovy. However, due to the static type system, it is not possible to call previously unknown interfaces. Therefore, only a fixed set of API entry points is supported.

Attention / Warning (Groovy)[Bearbeiten]

Unless explicitly prevented by the "mayStillDoExpeccoCalls()" function (described below), all called expecco functions listed below will ONLY work as expected WHILE the Groovy action is still ACTIVE (i.e. while inside the Groovy "execute()" function).

The reason is that expecco ensures that handlers, which are responsible for transferring information from the Groovy code back to expecco, will always be released, when the Groovy step has finished its execution. This is mostly required to prevent memory leaks in the Java VM, because the observer instance object is registered inside the JVM and would remain there forever, if not unregistered. This observer object is a proxy for Java code, which implements all the functions below and forwards them (via an IPC mechanism) to expecco. One new such object is required for every Groovy action execution and if not released, they sooner or later consume huge amounts of JVM memory. Therefore by default, the observer is released after the Groovy action execution.

In most Groovy blocks this is not a problem, except for those which install callback methods, AND those callbacks get called AFTER the Groovy block has finished (for example by another Java thread), AND the callback code calls one of those expecco interface functions. This includes the Transcript output functions, notifications and pin value writers.

Unless the observer is still around, callbacks are ignored and behave like a no-operation, if called after the step has finished. Of course, writing to a pin from within a callback AFTER the execute() function has finished does not work in any case (with or without a kept observer). The behavior in this case is undefined; currently, it is a no-operation, but it may be changed to raise an error in future versions.

The Current Activity (Groovy)[Bearbeiten]

The current activity instance is accessed as "this" inside the Groovy action code (like in an elementary JavaScript block). For every executed action, a new activity object is instantiated. It is usually alive during the execution only (i.e. it is destroyed and its memory reused automatically, after the block's action has finished).

In Groovy (as in JavaScript or Java), if no receiver is given for a function call, "this" is the implicit receiver. Thus the statements "this.logInfo("hello")" and "logInfo("hello")" are equivalent.

Objects are passed by Reference[Bearbeiten]

Except for Strings and Numbers, objects are passed from Groovy to expecco by reference. These references can later be sent back to other Groovy actions transparently. Notice, that these references will keep the Groovy object (which is actually a Java object) alive until the reference object is garbage collected in expecco. Be aware that such references may have a very long life time, if kept in an activity log. Therefore, output pins which receive such references should either be marked as non-logging, or you should set the "Log Strings of Bridge Objects" flag in the |"Settings" - "Execution" - "Logging" dialog.

Reporting (Groovy)[Bearbeiten]

  • error ()
    Report a defect (in the test). Stops execution.
  • error (infoString)
    Report a defect (in the test). Stops execution. The infoString argument will be shown in the activity log.
  • fail ([infoString])
    Report a failure (in the SUT) with optional infoString. Stops execution.
  • inconclusive ([infoString])
    Report an inconclusive test (with optional infoString). Stops execution.
  • activitySuccess ([infoString])
    Finishes the current activity with success.
  • pass ([infoString])
    Finishes the current testCase with success. The optional infoString argument will be shown in the activity log.
  • testPass ([infoString])
    Same as pass(); for compatibility with bridged languages where "pass" is a reserved keyword (i.e. python).

 

Logging (Groovy)[Bearbeiten]

  • logError (messageString)
    Adds a error message to the activity log.
  • logError (messageString, detail)
    Adds a error message to the activity log.
  • logWarning (messageString)
    Adds a warning to the activity log.
  • logWarning (messageString, detail)
    Adds a warning to the activity log.
  • logInfo (messageString)
    Adds an info message to the activity log.
  • logInfo (messageString, detail)
    Adds an info message to the activity log.
  • alert (messageString)
    Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "Execution-Log-Settings" dialog (by default it is disabled).
  • warn (messageString)
    Same as alert() (for Smalltalk protocol compatibility).

 

Reflection, Information, Queries and Accessing (Groovy)[Bearbeiten]

  • environmentAt (anEnvironmentVarName)
    The value of an environment variable
  • environmentAt_put (anEnvironmentVarName, value)
    Changing the value of an environment variable.
  • nameOfActiveTestPlan ()
    The name of the currently executing text plan
  • nameOfActiveTestPlanItem ()
    The name of the currently executing text case
  • nameOfStep ()
    The name of the corresponding step of the activity

 

Interaction with Expecco and Debugging (Groovy)[Bearbeiten]

  • eval (smalltalkCodeString)
    Evaluate a piece of Smalltalk code inside expecco.
  • evalJS (javascriptCodeString)
    Evaluate a piece of JavaScript code inside expecco.
  • inspect (javaObject)
    Opens the expecco-inspector showing details of the argument object.
  • halt() or halt(message)
    stops (breakpoint) and opens the expecco-debugger; however, this debugger cannot show the internals of the suspended Groovy code, but will only present the expecco calling chain up to the bridge call.

 

Special Functions (Groovy)[Bearbeiten]

  • mayStillDoExpeccoCalls (boolean)
    Ensures that the API-functions described here will still be callable (by Java callback functions from other threads) even after the step has finished execution. In other words, it prevents releasing the observer object which is responsible for the transfer of information between the two systems. Be aware that this object remains and will never be released, until the bridge connection is closed. I.e. there is a potential for a memory leak inside the Java VM here.
Even with mayStillDoExpeccoCalls(true), only functions which are not depending on the step's activity may be called after the activity has finished. This includes event, logging and transcript/stderr functions, but no pin access or verdict reporting functions.

 

Pin Functions (Groovy)[Bearbeiten]

Currently, Groovy blocks do not support a variable number of input or output pins.

Attention:
Groovy uses many more reserved keywords for syntax than JavaScript or Smalltalk. These keywords cannot be used as pin names, and you will get a syntax error ("<foo> token not expected") if you try. Be careful to not name your pins as any of: "in", "return", "class", "private", "public", etc. As a proven best practice, add a "Pin" suffix to your pin names (i.e. name it "inPin", instead of "in").

Input Pins (Groovy)[Bearbeiten]
  • hasValue ()
    Returns true if the pin has received a value
  • value ()
    Returns the value of the pin. Raises an error if the pin did not receive any value.
  • valueIfAbsent (alternativeValue)
    Returns the value of a pin or the alternativeValue if the pin did not receive any value.

 

Output Pins (Groovy)[Bearbeiten]
  • isBuffered ()
    True if the pin is buffered
  • value (data)
    Writes the value.

 

Transcript, Stderr and Stdout (Groovy)[Bearbeiten]

The expecco "Transcript", "Stderr" and "Stdout" are also accessible from Groovy code. However, only a limited subset of messages is supported:

  • cr ()
    Adds a linebreak (i.e. followup text will be shown on the next line)
  • show (arg)
    Adds a textual representation of the argument, which can be a string, number or any other object.
  • showCR (arg)
    A combination of show(), followed by a linebreak.
  • writeLine (string)
    For compatibility with Java's PrintStream protocol.
  • print (string)
    For compatibility with Java's PrintStream protocol (expecco 2.8).
  • println (string)
    For compatibility with Java's PrintStream protocol (expecco 2.8).
  • println ()
    For compatibility with Java's PrintStream protocol (expecco 2.8).

In addition, stdout and stderr are also forwarded to the expecco Transcript window, depending on the settings in expecco ("Extras" → "Settings" → "Execution" → "Tracing" → "Show Stdout and Stderr on Transcript").  

Node.js (Bridged) Elementary Blocks[Bearbeiten]

Node.js is an open source interpreter for JavaScript. For details on the language and libraries, visit "NodeJS Tutorial" .
You have to download and install "node" separately - the interpreter is not part of the expecco installation procedure (see "Installing additional Frameworks") and https://nodejs.org/en/download ).

Node.js code execution in expecco is supported via 2 different mechanisms:

  • Node.js Bridged Action Blocks - these look at the outside like regular action blocks with typed input and output pins, which are available inside the action's code via ".value()" APIs. The bridge-partner in which the code is to be executed is started once and will remain an active process until terminated. Any state (data) created inside the bridge remains alive while the partner is running and the connection is alive.
  • Node.js-Script Action Blocks - these look like shell-script blocks, in that the standard input and output is used to interact with the script. For every individual script action, the script interpreter is started anew. No state is alive between and across actions (the script-interpreter is terminated after each action)

The following describes the first kind of blocks (bridged). Node.js-Script blocks are described elsewhere, in the Node.js-Script Blocks chapter.

Code written as a Node.js elementary block is not executed directly by expecco. Instead, the code is forwarded to a node interpreter. This may be a local node process, whose sole purpose is to provide additional utility functions or which provides an interface to the actual system under test (SUT), or it may be on a remote system.

NodeJS Datatype Limitations[Bearbeiten]

Because expecco objects and Node objects live in different processes, no direct object access is possible between the two partners. When objects are passed via an input pin from expecco to Node, or via an output pin from Node to expecco, the object is either converted to JSON on the sender side and reconstructed on the receiver side, or it is passed by reference, in that the sender transmits a "handle" (which is some unique number) to the partner.

Since the class system / object model is different, not all objects are exactly representable on the other side, and some information may be lost. If required, additional information must be explicitly passed (eg. by generating a string representation manually).

Limited NodeJS Object Conversion[Bearbeiten]

Some limited form of object conversion is automatically performed when passing expecco's Smalltalk objects to NodeJS, and back when returning values from NodeJS. This conversion especially affects values passed to/from pins of a NodeJS action.

The following table summarizes the conversion process:

from Smalltalk to NodeJS and from NodeJS to Smalltalk
String String String
Float
(node ONLY supports IEEE double numbers)
Float Float
Float, Double Float Float
(Smalltalk Floats have double precision)
Fraction Float Float
(Smalltalk Floats have double precision)
Boolean Boolean Boolean
Array of any above Array of any above Array of any above
- any any NodeJS object as Reference
via "makeRef(obj)"
fetch in Node via "inpin.refValue()"
Filename String (i.e. pathname) String
arbitrary Smalltalk Object (!) Object with slots Dictionary with slots

(-) does not work. In general, only objects which can be represented by JSON can be transmitted between expecco and the node action.

No Smalltalk Classes, No Smalltalk Objects in NodeJS[Bearbeiten]

Of course, no Smalltalk class can be used directly in NodeJS code. And only a subset of Smalltalk objects (those which can be represented as JSON string) can be passed to/from NodeJS as described above. However, all node.js code (packages/modules) are at your hands now!

In general: if more complex objects need to be interchanged, this must be either done by converting them to an array (of objects), possibly an array of arrays. Or, alternatively to some ASCII representation (XML or JSON, for a more lightweight approach) and convert this back and forth.

It is also possible to pass Node-object-references from the Node interpreter to expecco, and pass it back to the Node interpreter later (usually in another action). This is used to get connection, protocol or device handles from Node, and pass them to other (transmission or control) actions later.

Limited Error Reporting[Bearbeiten]

Notice, that arithmetic errors are usually not reported by Node. Dividing by zero or taking the logarithm of a negative number will deliver a Nan ("Not a Number") instead of raising an error. Thus you have to explicitly check the result from such computations in your code (this is different in expecco's builtin JavaScript actions, where an error is reported).

Debugging NodeJS Actions[Bearbeiten]

Some debugging facilities are provided to support development of Node code. If your code contains an endless loop, you can interrupt the Node interpreter (via the "Interrupt Execution" button at the top right) and get a debugger window, showing the call stack and local variables.

However, there are situations, when the Node interpreter gets completely locked up and ceases to react. In this situation, you will have to shut down the Node interpreter via the "Plugins" → "Bridges" → "Node JS" → "Close Connections" menu function. Of course, this also implies, that any state inside the Node interpreter is lost, and you'll have to rerun your test setup from the start.

Debugger Functions[Bearbeiten]

Currently, you cannot look "into" objects, and you cannot modify the local variables. The debugger looks very similar to the Smalltalk/JavaScript debugger and supports continue, line stepping, stepping into called functions, stepping out of called functions, aborting an activity and terminating the whole node-interpreter.

Limitations of the Debugger[Bearbeiten]

One problem which is encountered with the current node version is that the breakpoint line numbers are sometimes off-by-one; this means, that the line reported by the node-debugger is wrong and therefore also shown wrong in the debugger. This is a problem of the particular node version, and may or may not appear in your concrete installation (i.e. node interpreter version).

Sorry, but we cannot currently provide a workaround for this problem, as we have not yet figured out, what constellation of source-code/statement/situation leads to this problem. (this problem was also reported by other node users in the internet forums).

NodeJS Code API[Bearbeiten]

NodeJS code supports a subset of the above activity functions, which is intended provide an interface similar to the JavaScript and Smalltalk elementary code API. Of course, technically for every API function below, the code executing in the remote Node interpreter has to make a remote procedure call back to expecco. On the Node side, this is done very similar to the above described remote message mechanism, when messages are sent from expecco to a bridge. Also notice, that due to the single threaded nature of Node programs, all functions which ask for a value from expecco (eg. "environmentAt") will take a callback argument, which is called when the return value arrives.

Be remonded that the objects described below are proxy objects inside node, which will forward messages back to expecco when functions listed below are called on them. Due to the roundtrip times, these will be relatively slow (in the order of milliseconds).

Variables seen by the Executed Node Function[Bearbeiten]

The following variables are in the scope of the executed function:

  • Transcript
    supports a few functions to display messages in the expecco Transcript window (see below)
  • Stdout
    supports a few functions to display messages on expecco's stdout stream (see below)
  • Stderr
    supports a few functions to display messages on expecco's stderr stream (see below)
  • Logger
    supports info()/warn()/error()/fatal(); these are forwarded to the Smalltalk Logger object (see Logger)
  • __bridge__
    the bridge object which handles the communication with expecco. The instance slot named "asynchronous" is of special interest if the called function uses asynchronous callbacks (see below)

Reporting (NodeJS)[Bearbeiten]

  • error (infoString,...)
    Report a defect (in the test). Stops execution.
    If the infoString argument contains "%i" placeholders ("%1","%2",...), these will be expanded by the remaining arguments.
    (eg. logFail("foo:%1 bar:%2", 123, 234.0) will generate the failure string "foo:123 bar:234.0").
  • fail (infoString,...)
    Report a failure (in the SUT). Stops execution.
  • inconclusive (infoString,...)
    Report an inconclusive test. Stops execution.
  • activitySuccess (infoString,...)
    Finishes the current activity with success (same as "success()").
  • success (infoString,...)
    Finishes the current activity with success (same as "activitySuccess()"). Please use "activitySuccess" ("success" is not supported in all bridges due to name conflictswith other libraries. For readability, it is better to use the same name in all bridges: "activitySuccess", which is supported on all bridges)
  • pass (infoString,...)
    Finishes the current testCase with success.
  • testPass (infoString,...)
    Same as pass(); for compatibility with bridged languages where "pass" is a reserved keyword (i.e. python).

Logging (NodeJS)[Bearbeiten]

  • logFail (messageString...)
    Adds a fail message to the activity log.
    Execution continues, but the test will be marked as failed.
    If the messageString argument contains "%i" placeholders ("%1","%2",...), these will be expanded by the remaining arguments.
    (eg. logFail("foo:%1 bar:%2", 123, 234.0) will generate the failure string "foo:123 bar:234.0").
  • logError (messageString...)
    Adds a error message to the activity log.
    Execution continues, but the test will be marked as erroneous.
  • logWarning (messageString...)
    Adds a warning to the activity log.
    Execution continues.
  • logInfo (messageString...)
    Adds an info message to the activity log.
    Execution continues
  • alert (messageString)
    Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in "Extras" → "Settings" → "Execution" → "Log -Settings" (by default it is enabled).
    Notice that the code in the bridge is suspended until the box is confirmed.
  • warn (messageString...)
    Same as alert() (for Smalltalk protocol compatibility).

Reflection, Information, Queries and Accessing (NodeJS)[Bearbeiten]

  • nameOfAction ()
    The name of the activity

Interaction with Expecco (NodeJS)[Bearbeiten]

  • call(<actionName>, <arg1>, <arg2>, ... <argN>, function(rslt...) {...});
    Executes any other expecco action (inside expecco - not inside the node interpreter), and finally calls the callBack function, when the action has finished.
    Notice that this is an asynchronous callback; you should not perform any further actions after the call, but instead continue inside the callback (and signal final completion via "activitySuccess() / error()" call from there).
    The callback may expect multiple rslt-arguments, to get the output values of more than one pin. However, it is currently not possible to get the values of pins which are written multiple times (i.e. an error will be reported, if that is the case)
    See example below.

Event Sending (NodeJS)[Bearbeiten]

  • pushEvent (payloadData)
    pushes an event onto the global event handler's event queue. The payload is packed into an Event object with eventType "#default. Raises an error, if no global event handler process is running.
  • pushEventType_data (eventTypeSymbolOrNil, payloadData)
    pushes an event of type (or #default) onto the global event handler's event queue. Raises an error, if no global event handler process is running.

Environment Access (NodeJS)[Bearbeiten]

  • environmentAt(varName, function(err, rslt) {...});
    Fetches a value from the expecco environment which is in scope of the current activity, and finally calls the callBack function, when the value has been retrieved (passing the retrieved value as argument).
    Notice that this is an asynchronous callback; you should not perform any further actions after the call, but instead continue inside the callback (and signal final completion via "activitySuccess() / error()" call from there).
    You can only read simple objects (numbers, booleans and strings) from node actions.
  • environmentAtPut(varName, newValue, function(err) {...});
    Writes a value into the expecco environment which is in scope of the current activity, and finally calls the callBack function, when the value has been stored.
    Notice that this is an asynchronous callback; you should not perform any further actions after the call, but instead continue inside the callback (and signal final completion via "activitySuccess() / error()" call from there).
    The varName argument must be a string and newValue a simple object (number, boolean or string) from node actions.

Special Functions (NodeJS)[Bearbeiten]

  • makeRef (<anyNodeObject> [, <optionalName> ])
    Generates a reference to a NodeJS object, which can be passed to expecco as an output pin value. Such reference objects can be passed back to NodeJS later and are dereferenced there back to the original object. This is used to pass NodeJS handles (i.e. server/client handles) to expecco and later back to NodeJS. Read below about reference passing vs. value passing.
    Notice that the nodeObject will be registered (i.e. remembered) inside the node interpreter, in order to be found later, when the reference is passed as input by another node action. When the reference is no longer needed, it should be deregistered by calling the "releaseRef function described below.
    Also notice, that expecco will do this automatically, when the reference object is finalized.
    See example below.
  • releaseRef (<nodeObjectReference>)
    removes the reference from the registery inside the node interpreter.
    See example below.
  • wait ()
    Tells expecco to wait until one of the "activitySuccess/error/fail/inconclusive" functions is called.
    Use this if the action's execution is not finished when the execute() function returns, but instead will explicitly notify the finish from within a callback. This is also to be used if a pin value has to be written later by a callback function.
    See example and description of callbacks/async functions below.
  • waitForPin (pinName)
    Tells expecco to wait until the given output pin receives a value. This is needed for async/await (promise resolved) pin values.
    See example and description of callbacks/async functions below.

Pin Functions (NodeJS)[Bearbeiten]

Attention:
Javascript uses many more reserved keywords for syntax than Smalltalk. These keywords cannot be used as pin names, and you will get a syntax error ("<foo> token not expected") if you try. Be careful to not name your pins as any of: "in", "return", "class", "private", "public", etc.
As a proven best practice, add a "Pin" suffix to your pin names (i.e. name it "inPin", instead of "in").

Input Pins (NodeJS)[Bearbeiten]

Currently, NodeJS blocks do not support a variable number of input or output pins.

  • pinName.hasValue ()
    Returns true if the pin has received a value
  • pinName.isConnected ()
    Returns true if the pin is connected
  • pinName.value ()
    Returns the value at the pin (the datum). Raises an error if the pin did not receive any value.
  • pinName.refValue ()
    Returns the object referenced by the value at the pin. The pin must have received a reference. Raises an error if the pin did not receive any value. Use this to pass back the original reference object to expecco. The input pin must have received a reference to a Node object as generated previously by "makeRef". Read below about reference passing vs. value passing.
  • pinName.valueIfPresent ()
    Returns the pin's datum if it has one, null otherwise. Similar to "value()", but avoids the exception.
  • pinName.valueIfAbsent (repl)
    Returns the pin's datum if it has one, repl otherwise. Similar to "value()", but avoids the exception.
Output Pins (NodeJS)[Bearbeiten]

Currently, NodeJS blocks do not support a variable number of input or output pins.

  • pinName.isConnected()
    Returns true if the pin is connected (this can be used to prevent writing output values and thus speed up the execution, especially if big arrays of data values are generated)
  • pinName.value (data)
    Writes the value.

Transcript, Stderr and Stdout (NodeJS)[Bearbeiten]

The expecco "Transcript", "Stderr" and "Stdout" are also accessible from Node code. However, only a limited subset of messages is supported:

  • cr ()
    Adds a linebreak (i.e. followup text will be shown on the next line)
  • show (arg)
    Adds a textual representation of the argument, which can be a string, number or any other object.
  • show (fmt, arg...)
    Like "show(), but "%i" sequences in the format argument are expanded by the corresponding arg strings.
  • showCR (arg)
    A combination of show(), followed by a linebreak.
  • showCR (fmt', arg...)
    Like "showCR(), but "%i" sequences in the format argument are expanded by the corresponding arg strings.

In addition, stdout (console.log) and stderr (console.error) are also forwarded to the expecco Transcript window, depending on the settings in expecco ("Extras" → "Settings" → "Execution" → "Tracing" → "Show Stdout and Stderr on Transcript").  

Reference passing vs. value passing of Node objects to expecco[Bearbeiten]

When values are passed via an output pin from Node to expecco, two mechanisms are possible:

  • pass by value
    The object's JSON string is generated and passed to expecco. There, the JSON encoding is decoded and a corresponding expecco object is constructed. Conceptional, the expecco object is a copy of the original object.
  • pass by reference
    The object is remembered inside Node, and a reference is passed to expecco. Whenever this reference object is later sent to Node again (via an input pin), the original Node object is retrieved and passed to the JavaScript code. This mechanism preserves the object's identity on the Node side and must be used for object handles (such as protocol handles).

The default mechanism is "pass by value". To pass a reference, use "makeRef(obj)" and pass the generated reference to an output pin.

Example (NodeJS)[Bearbeiten]

The following code snippet sends an object's reference to an output pin:

function execute() {
    ... generate a handle object ...
   outputPin.value( makeRef (someHandle , "aNodeHandle" ) );
}

the returned handle can later be sent via an input pin to another Node action, and used transparently there:

function execute() {
    var handle = inputPin.value();
    ... do something with handle ...
}

occasionally, it is required that a reference which was received via an input pin must later be sent to an output pin again, preserving the original reference. This is to prevent additional memory allocations which would result from calling "makeRef" again (you would get multiple references to the same object). For this, use "inputPin.refValue()" and send this to an output pin without a "makeRef":

function execute() {
    var handle = inputPin.value();

    ... do something with handle ...
    // send the original reference to the output
    outputPin.value( inputPin.refValue() );
}

Finally, you want to release the reference inside the node interpreter, to prevent memory leaks. Usually, this should be called, when a handle becomes invalid (e.g. when a connection handle is closed).

For this, call the "releaseRef" function (on the node side), passing the reference instead of the referenced object.
For example, a close-connection action block might look like:

function execute() {
    var reference = inputPin.refValue();
    var referredObject = inputPin.value();

    ... do something with referredObject ...
    closeConnection( referredObject );
    // release to prevent memory leaks
    releaseRef(referredObject);
}

Asynchronous and Callback Functions[Bearbeiten]

A common pattern in node is "continuation passing style" control flow. That means, that many functions expect a function as callback argument, which is called later whenever the requested operation has finished. This is used especially with I/O and protocol related operations (such as socket connect, http requests, client connect setup etc.). A similar situation arises with async functions, which somehow get a promise as value, and the promise is resolved via an await.

If your "execute" function calls any of those or is an async function which awaits on a promise, it has to tell expecco that the node-action is effectively still active when the execute function returns and that expecco should wait for an explicit finished-notification. This is done by either calling either "wait()" or "waitForPin()" somewhere within the execute function.

Then expecco will continue to wait for the action to be finished until the node code calls one of the "fail()", "error()", "success()", "pass()", "inconclusive()" functions (listed above) or - in case of a previous waitForPin(), until that pin receives a value.

Example 1:
the following code fragment represents a typical callback situation: the "myProtocolConnect()" call gets a callback, which will be called later (asynchronously), when a connection is established. However, "myProtocolConnect()" will return immediately. Without the "wait()" at the end, expecco would assume that the action has finished and would proceed with other actions (possibly without any output being written to the output pin (actually, it would later detect a value being written by an already finished action, and write a warning message to the console and the log).

function execute() {
    ... call a function which does a callback ...
   myProtocolConnect( ... , function(err) {
       ... callback possibly called much later ...
       // tell expecco that we're done
       if (err) {
           error("some error happened: "+err.toString());
       } else {
           outputPin.value(someConnectionHandle); 
           success();
       }
   });
   // tell expecco to continue waiting
   wait();
}

Notice that callbacks can be either written as functions (with a statement body):

function(err) { statement; ... statement; }

or as a lambda expression, where the body is an expression:

(err) => expression

Example 2:
the following code fragment is typical for async/await functions. Similar to the above, but covered by syntactic sugar which makes this less obvious, the execute function will return early, but execute code asynchronously due to the awaited promise:

async function execute() {
   let appiumMacDriver = require("appium-mac-driver");  

   // due to the async operations,
   // this execute function will effectively finish when
   // the await receives the value,
   // and it is written to the pin.
   // tell expecco that the function is finished when that pin
   // receives a value
   waitForPin(sessionOut);

   let driver = new appiumMacDriver.MacDriver();
   let session = driver.createSession(defaultCaps);
   let s = await session;
   sessionOut.value(s);
}

Example 3:
The following example demonstrates what happens if the "wait()" call is missing. The code below installs a callback which writes to an output pin 1 second AFTER the finished action:

function execute() {
    output.value("Value1");
    setTimeout(function(err) {
        output.value("Value2");
    }, 1000);
}

the above code will lead to an error.

The correct version is:

function execute() {
    output.value("Value1");
    setTimeout(function(err) {
        output.value("Value2");
        success();
    }, 1000);
    wait();
}

Up to version 23.2, this situation was not always detected and could have lead to an output value being written to a same-named output pin of a followup action (iff another bridged action was triggered which was of the same type, that second action's output was sometimes written, depending on timing constraints).
With version 24.1, this is detected and an error message is sent to the Transcript (it cannot be reported as an error in the activity log, because the first action which is responsible has already finished, whereas the second action (which is innocent) cannot be blamed for that).

Calling other Expecco Actions[Bearbeiten]

Any expecco action (i.e. elementary or compound) can be called from Node code. However, as node uses a callback mechanism, the code looks a bit different from other languages, in that an additional callback argument is to be passed. You can pass either the name or the UUID of the activity which is to be called. Arguments are passed to the called action's input pin (top to bottom).

For now, there is a limitation, in that only a single value (per pin) can be passed to the callback (i.e. you cannot get the values of pins which are written multiple times).
Example:

 function execute() {
    ... call an expecco activity ...
   call("myAction", 100, 200 , 
       function(result) {
           ... callback gets the first value from myAction's first output pin ...
           success();
       });
   // not reached
}

Notice, that in order to get a sequence of calls, the code needs nested functions, as in:

function execute() {
   
   call("myAction1", 1000, 2000, 
       function(rslt1) {
           Transcript.showCR("after first call: "+rslt1);
           call("myAction2", 10000, 20000, 
               function(err, rslt2) {
                   Transcript.showCR("after second call: "+rslt2);
                   success();
               });
           // not reached
       });
   // not reached
}

If the called action has multiple output pins, provide a callback function which expects more than one rslt argument, as in:

function execute() {
   
   call("actionWithTwoOutputPins", 1000, 2000, 
       function(valueAtPin1, valueAtPin2) {
           Transcript.showCR("received two values: "+valueAtPin1+" and: "+valueAtPin2);
           success();
       });
   // not reached
}

Reading expecco Environment Variables[Bearbeiten]

Expecco variables can be fetched via the "environmentAt" function. Notice, that this takes a second callback function as argument, which is called when the value is (later) received. The execution of the action proceeds with this continuation, and "environmentAt" does not return.


For example:

function execute() {
   environmentAt("variable1", function(err, varValue) {
           Transcript.showCR("the variable value is: "+varValue);
           success();
   });
   // not reached

}

Executing Node Actions in Multiple Node Interpreters[Bearbeiten]

In order to test the interaction between multiple node programs, or to generate load for performance tests, it may be required to start multiple node interpreters, and run action code in them.

This chapter describes how to start a node interpreter and how to interact with them.

Starting another Node Interpreter on the Local Machine[Bearbeiten]

Every Node bridge uses the given port for bridge communication plus the next port for debugging. Every bridge running on the same host must use different ports. The default bridge uses port 8777 (and 8778 for debugging). Thus, additional bridges should be given ports incremented in steps of 2, and useful ports are 8779, 8781, 8783 etc. for additional node bridges.

Programmatic Start[Bearbeiten]
nodeBridge := Expecco 
                   newNodeJSBridgeConnectionForHost:hostName port:portNr 
                   in:aDirectoryOrNil.
Using an Action from the Standard Library[Bearbeiten]

use the "Start new Local Node Bridge" action from the library.

Executing an Action inside another Node Interpreter[Bearbeiten]

Either add a "nodejs" input pin to the action (passing the other node-bridge's handle) or set the "NODEJS" environment variable before the action. An example is found in the "d62_Event_Queue_Demos.ets" suite:
Event Queue Demo.png

Node Packages[Bearbeiten]

Example: Installing Additional Node Packages[Bearbeiten]

Use the Node package manager "npm" to install packages.
The "Extras" → "Bridges" → "Node Bridge" menu also contains an entry to install npm packages.
Find packages in https://www.npmjs.com.

For example, assume you need the current city weather in a suite, navigate to "https://www.npmjs.com", search for "weather", find the "city-weather" package and click on it. You will arrive on a page giving installation instructions and sample code at the end. Scroll down to the example code and keep that page open (we can use the code later).

Open a cmd/shell window and execute on the command line:

npm install city-weather

Examples: Using a Node Package in Expecco[Bearbeiten]

Example1: Access to the City Weather Service[Bearbeiten]

For a first test, we will take the original code from the example. Create a new node action, and enter the code:

var weather = require("city-weather");

function execute() {
    weather.getActualTemp('Rome', function(temp){
        console.log("Actual temperature: " + temp);
    });
}

(you can copy-paste the sample code from the webpage)

Open the expecco console (aka "Transcript" via the "Extras" → "Tools" → "Transcript" menu item) and run the test. A temperature should be displayed on the Transcript.

Now add an input pin named "city", and change the code to:

...
weather.getActualTemp(city.value(), function(temp){
...

and try the code in the "Test/Demo" page with a few different cities at the input pin.

Then, add an output pin named "temperature", and change the code again to:

var weather = require("city-weather");

function execute() {
    weather.getActualTemp(city.value(), function(temp){
        console.log("Actual temperature: " + temp);
        temperature.value( temp );
    });
}

if you now execute this action, you will notice, that the output pin does NOT get the value. The reason is, that the pin is written by a node-callback function, which is called at a time after the action's "execute" function has already finished. Thus, we have to tell expecco, that it should wait until the callback is actually called. For this, you should add a call to the "wait()" function.

This tells expecco, that the execute-function is not complete, and will wait for one of the "success/fail/error" functions to be called. So we also have to add a call to "success()" inside the callback (otherwise, expecco would wait forever).

Thus, we change the code to:

var weather = require("city-weather");

function execute() {
    weather.getActualTemp(city.value(), function(temp){
        console.log("Actual temperature: " + temp);
        temperature.value( temp );
        success();
    });
   wait();
}

As a final step, you should remove or comment the call to "console.log()" and add some error reporting, in case a city was not found:

var weather = require("city-weather");

function execute() {
   weather.getActualTemp(city.value(), function(temp){
       // console.log("Actual temperature: " + temp);        
       if (temp == "City was not found") {
           error(temp+": "+city.value());
       }
       temperature.value( temp );
       success();
   });
   wait();
}

Example2: Access OpenStreetMap Services[Bearbeiten]

This is described in a separate document.

Bridged Python Elementary Blocks[Bearbeiten]

Code written as a Python elementary block is not executed directly by expecco. Instead, the code is forwarded to a python interpreter. This may be a local python process, whose sole purpose is to provide additional utility functions or which provides an interface to the actual system under test (SUT), or it may be on a remote system.

Python Versions[Bearbeiten]

Python is available in 2 dialects (2.x vs. 3.x) and different implementations (cPython, Jython, IronPython):

  • use jython, if you have to interface/embed java classes or jars;
  • use ironPython, if you have to interface with .NET assemblies;
  • use python3 to interface to modern frameworks such as Tesseract (optical character recognition), computer vision, Tensorflow (machine learning) etc.
  • use python2 for some older frameworks, which have not yet been ported to the newer python3 (notice, that support for python2 is going to cease soon)

Of course, if you need to interface to a Python module which is specifically written for a version, that specific version is obligatory.

You have to download and install Python, Python3, Jython and/or IronPython separately; these interpreters are not part of the expecco installation procedure.
See ( "Installing Additional Frameworks") and download Python from https://www.python.org/downloads, http://jython.org/downloads.html for Jython, and [ https://ironpython.net/download/] for IronPython.

Additional frameworks may be needed as prerequisite (i.e. Java for Jython, Mono for IronPython on non-Windows machines).

Bridged vs. Scripted Actions[Bearbeiten]

Similar to Node, Python code execution is supported via 2 different mechanisms:

  • Bridged Python action blocks - these look at the outside like regular action blocks with typed input and output pins, which are available inside the action's code via ".value()" APIs. Objects can be interchanged between expecco and the python process either by value or by reference. The bridge-partner in which the code is to be executed is started once and will remain an active process until terminated. Any state (data) created inside the bridge remains alive while the partner is running and the connection is alive.
  • Python-Script action blocks - these look like shell-script blocks, in that the standard input and output is used to interact with the script. For every individual script action, the script interpreter is started anew. No state is alive between and across actions (the script-interpreter is terminated after each action), and no objects can be exchanged (except for very simple objects by means of reading encoded objects via stdin/stdout/stderr).

The following describes the first kind of blocks (bridged). Script blocks are described elsewhere, in the Python-Script Blocks chapter.

Debugging Python Actions[Bearbeiten]

Debug support for Python actions is still being developped and more features are added with newer versions. However, there are probably better IDEs available and it might be a good idea to develop and debug Python code in your preferred IDE, and only add interface code to already debugged modules as expecco actions (this is certainly a matter of your personal preferences; in the past, interfaces to many modules have been implemented using only expecco's debug facilities).

In order to support debugging of Python actions, you have to install either the "debugpy" or the "ptvsd" package (*) with:

pip install debugpy

or:

pip3 install debugpy

or:

python3 -m pip install debugpy
  • ) ptvsd is no longer maintained and has been replaced by the debugpy package. Ptvsd might be obsolete by the time of reading this.

For more information on the ptvsd package, see https://pypi.org/project/ptvsd and https://github.com/Microsoft/ptvsd.
For debugpy, refer to https://pypi.org/project/debugpy.

Notice that the debug features are currently not supported by python2 or jython.

Python Datatype Limitations[Bearbeiten]

Limited Python Object Conversion[Bearbeiten]

Some limited form of object conversion is automatically performed when passing expecco objects to Python, and back when returning values from Python. This conversion especially affects values passed to/from pins of a Python action.

The following table summarizes the conversion process:

from Smalltalk to Python and from Python to Smalltalk
String String String
Float
Float Float
Float, Double Float Float
(Smalltalk Floats have double precision)
Fraction Float Float
(Smalltalk Floats have double precision)
Boolean Boolean Boolean
Array of any above Array of any above Array of any above
ByteArray Array Array
- any any Python object as Reference
via "makeRef(obj)"
Python object reference
as previously generated
by "makeRef(obj)"
fetch the reference in Python via "inpin.refValue()"
or the value via "inpin.value()"
fetch the reference in Python via "inpin.refValue()"
or the value via "inpin.value()"
send value or reference with "makeRef(obj)"
Filename String (i.e. pathname) String
arbitrary Smalltalk Object (!) Object with slots Dictionary with slots

(-) does not work. In general, only objects which can be represented by JSON can be transmitted between expecco and the python action.

No Smalltalk Classes, No Smalltalk Objects in Python[Bearbeiten]

Of course, no Smalltalk class can be used directly in Python code. And only a subset of Smalltalk objects (those which can be represented as JSON string) can be passed to/from Python as described above.

In general: if more complex objects need to be interchanged, this must be either done by converting them to an array (of objects), an array of arrays or a dictionary. Or, alternatively to some ASCII representation (XML or JSON, for a more lightweight approach) and convert this back and forth.

It is also possible to pass an object-reference from the Python interpreter to expecco, and pass it back to the Python interpreter later (usually in another action). This is used to get connection, protocol or device handles from Python, and pass them to other (transmission or control) actions later.

Limited Syntax Highlighting, Code Completion and Debugging Support[Bearbeiten]

Currently, all of those are very limited: the syntax highlighter only detects keywords and some constants as such; code completion does not work and no interactive debugger is provided currently.

Bridged Python Code API[Bearbeiten]

WARNING: Bridged Python Elementary Actions are still being developed - their behavior might be subject to changes (aka the protocol will be extended).

Python code may consist of function- and/or class definitions. Among the functions, there should be one named "execute()"; typically, that is the last defined function, which calls into other python code. If no "execute"-function is defined, expecco tries to call "main()" as fallback. This special behavior was added to make it easier to reuse existing script code.

Bridged Python code can use activity functions similar to the above Groovy or Node actions. The API is intended to provide an interface similar to the JavaScript and Smalltalk elementary code API. Of course, technically for every API function below, the code executing in the remote Python interpreter has to make a remote procedure call back to expecco. On the Python side, this is done very similar to the above described remote message mechanism, when messages are sent from Smalltalk to a bridge.

Variables seen by the Executed Python Function[Bearbeiten]

The following variables are in the scope of the executed function:

  • Transcript
    supports a few functions to display messages in the expecco Transcript window (see below)
  • Stdout
    supports a few functions to display messages on expecco's stdout stream (see below)
  • Stderr
    supports a few functions to display messages on expecco's stderr stream (see below)
  • Logger
    supports info(), warn(), error() and fatal(); these are forwarded to the Smalltalk Logger object (see Logger)
  • Dialog
    supports confirm(), warn(), information(); these are forwarded to the Smalltalk Dialog object (see Dialog)

Reporting (Python)[Bearbeiten]

  • error (infoString)
    Report a defect (in the test). Finishes the current activity with an error verdict.
  • fail (infoString)
    Report a failure (in the SUT). Finishes the current activity.
  • inconclusive (infoString)
    Report an inconclusive test. Finishes the current activity.
  • activitySuccess (infoString)
    Finishes the current activity with success (same as "success()").
  • testPass (infoString)
    Finishes the current testCase with success. This has the same effect as "pass()" in other actions - its name had to be changed because "pass" is a reserved keyword in Python.

Logging (Python)[Bearbeiten]

  • logFail (messageString)
    Adds a fail message to the activity log.
  • logError (messageString)
    Adds a error message to the activity log.
  • logWarning (messageString)
    Adds a warning to the activity log.
  • logInfo (messageString)
    Adds an info message to the activity log.
  • alert (messageString)
    Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "Execution" - "Log -Settings" dialog (by default it is disabled).
  • warn (messageString)
    Same as alert() (for Smalltalk protocol compatibility).

Reflection, Information, Queries and Accessing (Python)[Bearbeiten]

Interaction with Expecco (Python)[Bearbeiten]

  • call(<actionName>, <arg1>, <arg2>, ... <argN>);
    Executes any other expecco action (inside expecco - not inside the Python interpreter), and returns the generated output pin values as a tuple
    It is currently not possible to get the values of pins which are written multiple times (i.e. an error will be reported, if that is the case)
    See example below.

Environment Access (Python)[Bearbeiten]

  • environmentAt(<varName>);
    Fetches and returns a value from the expecco environment which is in scope of the current activity.
    You can only read simple objects (numbers, booleans and strings) from python actions.
  • environmentAtPut(<varName>, <newValue>);
    Writes a value into the expecco environment which is in scope of the current activity.
    You can only write simple objects (numbers, booleans and strings) from python actions.

Event Sending (Python)[Bearbeiten]

  • pushEvent (payloadData)
    pushes an event onto the global event handler's event queue. The payload is packed into an Event object with eventType "#default. Raises an error, if no global event handler process is running.
  • pushEventType_data (eventTypeSymbolOrNil, payloadData)
    pushes an event of type (or #default) onto the global event handler's event queue. Raises an error, if no global event handler process is running.

Special Functions (Python)[Bearbeiten]

  • makeRef (<anyPythonObject>)
    Generates a reference to a Python object, which can be passed to expecco as an output pin value. Such reference objects can be passed back to Python later and are dereferenced there back to the original object. This is used to pass Python handles (i.e. server/client handles) to expecco and later back to Python. Read below about reference passing vs. value passing.
    Notice that the pythonObject will be registered (i.e. remembered) inside the python interpreter, in order to be found later, when the reference is passed as input by another node action. When the reference is no longer needed, it should be deregistered by calling the "releaseRef function described below.
    Also notice, that expecco will do this automatically, when the reference object is finalised.
    See example below.
  • releaseRef (<nodeObjectReference>)
    removes the reference from the registery inside the Python interpreter.
    See example below.
  • wait ()
    Tells expecco to wait until one of the success(), error(), fail(), or inconclusive() functions is called.
    Use this if a pin value has to be written later by a callback function.
    See explanation of async callbacks and the example in the NodeJS API description.
  • bridge_inMainThread (task)
    OS X only:
    Task must be a lambda expression which is executed by the main thread (as opposed to the bridge connection thread).
    This is required on eg. OS X to run code which interacts with OS X windowing framework.
    On non-OSX systems, this executes the task directly (and also in the main thread).
    Typical use: bridge_inMainThread(lambda: someWindow.show() )

Pin Functions (Python)[Bearbeiten]

Currently, python blocks do not support a variable number of input or output pins.

Attention:
Python uses many more reserved keywords for syntax than Smalltalk. These keywords cannot be used as pin names, and you will get a syntax error if you try. Be careful to not name your pins as any of: "import", "return", "def", "if" etc. As a proven best practice, add a "Pin" suffix to your pin names (i.e. name it "inPin", instead of "in").

Input Pins (Python)[Bearbeiten]
  • pinName.hasValue ()
    Returns true if the pin has received a value
  • pinName.isConnected ()
    Returns true if the pin is connected
  • pinName.value ()
    Returns the value at the pin (the datum). Raises an error if the pin did not receive any value.
  • pinName.refValue ()
    Returns the object referenced by the value at the pin. The pin must have received a reference. Raises an error if the pin did not receive any value. Use this to pass back the original reference object to expecco. The input pin must have received a reference to a Python object as generated previously by "makeRef". Read below about reference passing vs. value passing.
  • pinName.valueIfPresent ()
    Returns the pin-datum if it has one, None otherwise. Similar to value(), but avoids the exception.
  • pinName.valueIfAbsent (repl)
    Returns the pin's datum if it has one, repl otherwise. Similar to value(), but avoids the exception. Use this, if "None" is a possible value at the pin, and the code needs to distinguish between getting a "None" at the pin and not having a value at all at the pin.
Output Pins (Python)[Bearbeiten]
  • pinName.value (data)
    Writes the value.

Transcript, Stderr and Stdout (Python)[Bearbeiten]

The expecco "Transcript", "Stderr" and "Stdout" are also accessible from Python code. However, only a limited subset of messages is supported:

  • cr ()
    Adds a linebreak (i.e. followup text will be shown on the next line)
  • show (arg)
    Adds a textual representation of the argument, which can be a string, number or any other object.
  • show (fmt, arg...)
    Like "show(), but "%i" sequences in the format argument are expanded by the corresponding arg strings.
  • showCR (arg)
    A combination of show(), followed by a linebreak.
  • showCR (fmt, arg...)
    Like "showCR(), but "%i" sequences in the format argument are expanded by the corresponding arg strings.

In addition, stdout (console.log) and stderr (console.error) are also forwarded to the expecco Transcript window, depending on the settings in expecco ("Extras" → "Settings" → "Execution" → "Tracing" → "Show Stdout and Stderr on Transcript").  

Dialog (Python)[Bearbeiten]

Some of expecco's Dialog messages are forwarded from Python:

  • information (msg) (new in 23.2)
    Opens an information dialog. I.e. "Dialog.information("Hello") will show "Hello" in a popup dialog window.
  • confirm (msg)
    Opens a Yes/No confirmation dialog (and returns True/False)
  • request (msg)
    Opens a dialog asking for a string (and returns the entered string or None on cancel)
  • requestPassword (msg)
    Opens a dialog asking for a password string (and returns the entered string or None on cancel)
  • requestFilename (msg) (new in 23.2)
    Opens a dialog asking for a file name string (and returns the entered file name or None on cancel)

Global and Static Variables[Bearbeiten]

Python actions are defined within the scope of another function, to prevent accidentically overwriting/redefining any global. Thus, you have to access globals via "globals().get("name")".

Static Variables[Bearbeiten]

Starting with v24.1, static variables (which are visible within one activity and which are preserved across calls) can be defined by prepending the execute function's code with:

# start of static definitions
... any variables which should be static ...
# end of static definitions

Notice that these comment lines must be written exactly as above.
As an example, the following uses a static variable to increment a counter whenever the action is Ivoked:

# start of static definitions
count=0
# end of static definitions

def execute():
    nonlocal count

    count = count + 1
    print(f"count={count}")

Notice that the above example does not work in pre 24.1 versions. Also notice, that the static definitions are re-executed whenever the action code is changed or recompiled (i.e. that the count variable is reset to 1).

Global Variables[Bearbeiten]

Example of a global variable that can also be used in other modules.

Caution: Please make sure that the name of the global variable is unique in your test suite!

def execute():
    global count
    try:
        count
    except NameError:
        # Throws a NameError if not yet initialised
        count = 0
    count = count + 1
    print(f"count={count}")

Python Asynchronous and Callback Functions[Bearbeiten]

If output pins are to be written by a callback which is invoked AFTER the python function returned, the expecco activity will already be finished, and the pin will NOT be written. Instead, a warning is generated in the log.

You MUST tell expecco that the execute function has not yet finished, by calling "wait()" and signal the real activityEnd in the callback via one of "success()", "error()"or "fail()".

For a detailed description of this mechanism, read the corresponding section in the Node API.

Reference passing vs. value passing of Python objects to expecco[Bearbeiten]

When values are passed via an output pin from Python to expecco, two mechanisms are possible:

  • pass by value
    The object's JSON string is generated and passed to expecco. There, the JSON encoding is decoded and a corresponding expecco object is constructed. Conceptional, the expecco object is a copy of the original object.
  • pass by reference
    The object is remembered inside Python, and a reference is passed to expecco. Whenever this reference object is later sent to Python again (via an input pin), the original Python object is retrieved and passed to the Python code. This mechanism preserves the object's identity on the Python side and must be used for object handles (such as protocol handles).

The default mechanism is "pass by value".
To pass a reference, use "makeRef(obj)" and pass the generated reference to an output pin.

When objects are passed from expecco to python, this is transparent. Python will automatically resolve passed in references. Thus you can send a "pointer" to an object to an output pin via "makeRef(obj)" and pass it to another Python action, where the value will refer to the original object.

Example (Python)[Bearbeiten]

The following code snippet sends an object's reference to an output pin:

def execute():
    ... generate a handle object ...
   outputPin.value( makeRef (someHandle ) )

the returned handle can later be sent via an input pin to another Node action, and use transparently there:

def execute():
    handle = inputPin.value()
    ... do something with handle ...
}

occasionally, it is required that a reference which was received via an input pin must later be sent to an output pin again, preserving the original reference. This is to prevent additional memory allocations which would result from calling "makeRef" again (you would get multiple references to the same object). For this, use "inputPin.refValue()" and send this to an output pin without a "makeRef":

def execute():
    handle = inputPin.value();

    ... do something with handle ...
    # send the original reference to the output
    outputPin.value( inputPin.refValue() )

Finally, you want to release the reference inside the Python interpreter, to prevent memory leaks. Usually, this should be called, when a handle becomes invalid (e.g. when a connection handle is closed).

For this, call the "releaseRef" function (on the node side), passing the reference instead of the referenced object.
For example, a close-connection action block might look like:

def execute():
    reference = inputPin.refValue()
    referredObject = inputPin.value()

    ... do something with referredObject ...
    closeConnection( referredObject )
    # release to prevent memory leaks
    releaseRef(referredObject)


Calling other Expecco Actions (Python)[Bearbeiten]

Not yet implemented

Any expecco action (i.e. elementary or compound) can be called from Python code. You can pass either the name or the UUID of the activity which is to be called. Arguments are passed to the called action's input pin (top to bottom).

For now, there is a limitation, in that only a single value (per pin) can be returned (i.e. you cannot get the values of pins which are written multiple times).
Example:

 def execute():
    ... call an expecco activity ...
   result = call("myAction", 100, 200)

Python2 vs. Python3[Bearbeiten]

Although python2 is going to be obsoleted soon, there are still many packages and frameworks around, which require that version.

If all of your actions use the same python version, specify it in the "Python" field of the interpreter settings dialog. Otherwise, change individual action's python version via the "Language" comboList, above the code editor (change to "BridgedPython2" or "BridgedPython3" to make it explicit).

Pathes to the explicit versions and to the unspecific version are all defined in the pathon settings.

Reading expecco Environment Variables (Python)[Bearbeiten]

Expecco variables can be fetched via the "environmentAt" function.
For example:

def execute():
   varValue = environmentAt("variable1")
   ...

Python Packages[Bearbeiten]

You should install python packages using "pip" or "pip3", depending on the python version, for which a package is to be installed. For more information, please consult the pip documentation.

On Windows, "pipwin" seems to perform best, if additional C/C++ libraries need to be installed or compiled (eg. "pipwin install pyaudio").

When installing, make sure that the packages are installed in the correct target directory, as per Python environment; i.e. if you have multiple versions of the interpreter or virtual environments, make sure that the Python as started by expecco (and defined in the settings) uses and sees the correct module path.

Installing Additional Python Packages[Bearbeiten]

Use the Python package installer "pip" (or "pip3") to install packages. The "Plugins" → "Bridges" → "Python Bridge" menu also contains an entry to install pip packages. If you use virtual environments, make sure that your packages are installed in the correct location.

Find packages in https://pypi.org/ or https://docs.python.org/3/py-modindex.html.

Accessing Assemblies in IronPython[Bearbeiten]

Because IronPython executes inside a CLR environment, it has access to any assembly (written in any language). For this, IronPython provides a special language extension, which is described in detail in [the IronPython documentation].

Example:

*** to be added **

Example 1: Using the "overpy" Python Package (open street map access) in Expecco[Bearbeiten]

In this example, we will access the OpenStreetMap API to fetch information about a particular street ("Straße der Nationen") in a given city ("Chemnitz").

The example was taken from https://pypi.org/project/overpy which is documented in https://python-overpy.readthedocs.io/en/latest/index.html.
It requires python3 (you will get a UnicodeEncodeError if you try it in python2).

First, install the package with:

pip3 install overpy

For a first test, we will take the original code from the example. Create a new Python action, and enter the code:

import overpy

api = overpy.Overpass()

def execute(): 
   # fetch all ways and nodes
   result = api.query("""
       way(50.746,7.154, 50.748,7.157) ["highway"];
       (._;>;);
       out body;
       """)

   for way in result.ways:
       print("Name:%s" % way.tags.get("name", "n/a"))
       print("  Highway:%s" % way.tags.get("highway", "n/a"))
       print("  Nodes:")
       for node in way.nodes:
           print("    Lat:%f, Lon:%f" % (node.lat, node.lon))

(you can copy-paste the sample code from the webpage)

Open the expecco console (aka "Transcript" via the "Extras" → "Tools" → "Transcript" menu item) and run the test. A list of street nodes should be displayed on the Transcript.

Example 2: Accessing other Python Packages or Individual Script Files[Bearbeiten]

The following works in 19.2 and later.
In the following example, functions contained in a regular Python script file are called. We assume, that the file is named "MyLib.py", and located in some directory DIR:
DIR/MyLib.py:

def fun1(a,b):
   return a+b;

def fun2(a,b):
   return a-b;

We also assume, that you have created a new python action block, containing the code:

import MyLib

def execute():
   Transcript.showCR("fun1:" + str(MyLib.fun1(10,20))
                   + " fun2:" + str(MyLib.fun2(10,20)))

If you execute this action, you will probably get a "Module Not Found" error from python, (unless DIR is already in your PYTHONPATH environment variable).

To fix the problem, open the "Extras" → "Settings" → "External Script Interpreters" → "Python" dialog, and add DIR to the "Module Path" value. Notice that on Unix/Linux, you have to separate directories with colons ":", whereas under Windows, you'll have to separate them with semicolons ";".

You will have to shut down the already running Python interpreter and restart it, for the new PYTHONPATH setting to become valid.
For this, use the "Plugins" → "Bridges" → "Python Bridge" → "Shutdown Interpreters" menu item (or shut down all external interpreters via the "Extras" → "Debug" menu).

Example 3: Accessing Python Script Files in Attachments[Bearbeiten]

The following works in 19.2 and later.
You can add Python script files (i.e. like the "MyLib.py" file in the above example) as attachment to to the suite.
For this you have to add "$(Attachments)" to the Python module path setting in "Extras" → "Settings" → "External Script Interpreters" → "Python", and import it in the Python code as in the previous example.

Example 4: Using the Python Appium Bindings[Bearbeiten]

In the following, we'll use a Python Appium interface for automation of a mobile application (in this case, not using the mobile plugin, and not using the GUI Browser). This may be a useful scenario, if you already have python test actions, which you want to integrate into expecco.

First install the required python package(s) via the command line or via expecco's "Plugin" → "Bridges" → "Python" menu:

pip3 install Appium-Python-Client

or:

python -m pip install Appium-Python-Client

then make a dummy BridgePython block to check if the package is found:

import appium

def execute():
    Transcript.showCR ("Hello world; Appium was found");

Now take either existing code, or copy-paste samples from the Appium-Python-Client website; here is a typical code fragment (of course, you have to adjust the values as required). Obviously, it is also a good idea to add input pins to the python action, so the capabilities and connection parameters can be passed as arguments.

from appium import webdriver

desired_caps = dict(
    platformName='Android',
    platformVersion='10',
    automationName='uiautomator2',
    deviceName='Android Emulator',
)

def execute():
    driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
    el = driver.find_element_by_accessibility_id('item')
    el.click()

Example 5: Interfacing to Tensorflow[Bearbeiten]

  • Create a bridged Python action containing the following code:
import numpy as np 
import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_datasets as tfds

def execute():
    print("Version: ", tf.__version__)
    print("Eager mode: ", tf.executing_eagerly())
    print("Hub Version: ", hub.__version__)
    print("GPU is", "available" if tf.config.experimental.list_physical_devices("GPU") else "NOT AVAILABLE")
  • run it
  • if you get a "module not found" error message, either click on the link (named "here"), embedded in the error message, or alternatively select the "Plugins" → "Bridges" → "Python" → "Install pip Package" menu item. Of course, you can also install packaged from the command line in a shell/cmd window.
  • install missing packages (care for the correct virtual environment if you use them)
  • repeat running it, until you get an output on the Transcript (or stderr) similar to:
1: Version:  2.4.1
1: Eager mode:  True
1: Hub Version:  0.11.0
1: GPU is NOT AVAILABLE
  • You may have to shutdown the bridge after installation of new packages (although it usually works without, unless existing and already loaded packages are upgraded during the installation).

Example 6: Interfacing to the "astropy" Package[Bearbeiten]

The "astropy" package contains a number of very useful functions for astronomy applications. Among others, you'll find time functions, geo-location mapping, physical constants, image processing and much more inside.
(see https://docs.astropy.org/en/stable/index.html)

Installation[Bearbeiten]

First install the required python package(s) via the command line or via expecco's "Plugin" → "Bridges" → "Python" menu:

pip3 install numpy
pip3 install astropy (astropy-Python-Client)

or:

python -m pip install numpy
python -m pip install astropy (astropy-Python-Client)
Smoke Test if Package is Installed[Bearbeiten]

then make a dummy BridgePython block to check if the package is found:

import astropy

def execute():
    Transcript.showCR ("Hello world; astropy was found");

Notice: because bumpy and astropy are relatively big packages, the first initial execution may take a few seconds because the Python interpreter has to load these packages (but this only happens when the very first Python action is executed).

Interface to an astropy Function[Bearbeiten]

The following example demonstrates how to call functions within astropy; this will convert an expecco timestamp (a DateTime object) to a julianDate (as Float). (Hint: JulianDates are commonly used in Astronomy and Space Sciences).

Please read https://docs.astropy.org/en/stable/time/index.html for details on time functions.

The code includes a few debug prints (to the Transcript). In a production suite, these should probably be commented (or replaced by calls to a logger, which can be switched on/off dynamically).

AstroPyExample1.png

The action can then be used in a diagram as:

AstroPyExample2.png

After a run, we get the Julian date as a float:

AstroPyExample3.png

Be aware that expecco requires objects to be JSON serializable to be passed from/to the Python interpreter. Sadly, this is not true for all atrophy objects. If required either pass objects by reference or encode/decode them as strings (or extract subfields into an array and pass the array or pass subfields via individual input/output pins).

It may also be possible to define additional JSON encoders/decoders for individual types.

Example 7: Interfacing to a .NET Assembly via IronPython[Bearbeiten]

IronPython is a python interpreter running within the .NET CLR framework. The ability of IronPython to import .NET assemblies, makes it easy to also interface those to expecco.

Please refer to the IronPython documentation on how to import .NET assemblies.

 --- example to be added ---

Bridged C Elementary Blocks[Bearbeiten]

The following works in 19.2 and above.
You have to make sure that a C-compiler toolchain is available and can be called directly or indirectly (see "Installing additional Frameworks").

Bridged C-code is highly dependent on the C-compiler toolchain of the target machine and may require preparations for machine dependencies with #ifdefs (word length, OS-APIs etc.). It is typically used by experts and for special situations, when simpler alternatives are not available.

Bridged C-code can be used to implement time critical functions or to interface to C/C++ libraries, when the simple DLL-call interface is too complicated to use (for example, if complicated data structures have to be exchanged, if C-callbacks are needed, or if C++ interfaces are to be implemented).

In addition, bridged C-Code actions run in a separate process, isolated from expecco. Thus expecco is not affected by errors in the C-code and cannot be disturbed (eg. by invalid memory references), which is not guaranteed for DLL called functions (which are executed within the expecco process).
On the downside, there is some overhead involved in calling a bridged action, since input/output parameters and the call itself are transmitted via an interprocess communication mechanism (i.e. socket/network message). Thus the call roundtrip times are in the millisecond range, as opposed to microseconds for DLL calls.

Other applications are high-speed data acquisition and protocol implementations, in which a C-part is responsible for the time critical task, and expecco can query/control that task remotely (i.e. fetch captured data or state information).

The C Bridge[Bearbeiten]

Be reminded that bridged C-Code actions are executed inside a separate OS-process, which runs the c-bridge code. Whenever a bridged C-action is to be executed, the pin-data is transferred via an interprocess communication mechanism (IPC), and the code executed inside the bridge process. Pin data and event requests are transmitted back to expecco via the same IPC mechanism.

The C-bridge itself is available as executable or as shared or linkable library in two configurations, with or without the dynamic C code injection facility.

Setups without code injection are useful to augment (statically compiled) embedded systems with a debug interface, through which expecco can later access specific (and explicitly published) interfaces. In such a setup, no dynamic C-code can be injected into the running application. Instead, the set of callable interfaces must be declared and implemented in advance (in the embedded system).

The following chapter describes the dynamic C-code injection facility as seen by elementary activity code in expecco.
For static embedded systems, please refer to the "Embedded Systems C Bridge API".

Bridged C Code API[Bearbeiten]

Note: This is a preliminary API documentation. Bridged C elementary actions are still being developed and the API may be extended or improved in the future (However, it is very very likely to be backward compatible).

The API is intended to look similar to the other languages' elementary code API. However, due to the nature and syntax of the C-language, certain differences are apparent.
The biggest differences are due to C being a statically typed language: the simple "pin.value()" interface for pins (as used in other languages) cannot be offered here; instead, a datatype-specific API is provided in C.

Variables seen by the Executed C Function[Bearbeiten]

The following variables are in the scope of the executed function:

  • Transcript
    supports a few functions to display messages in the expecco Transcript window (used as argument; see below)
  • Stdout
    supports a few functions to display messages on expecco's stdout stream (used as argument; see below)
  • Stderr
    supports a few functions to display messages on expecco's stderr stream (used as argument; see below)

Reporting (C)[Bearbeiten]

  • void error (char* fmt , ...)
    Report a defect (in the test). Stops execution. The arguments are printf-style.
  • void fail (char* fmt, ...)
    Report a failure (in the SUT). Stops execution. The arguments are printf-style.
  • void inconclusive (char* fmt , ...)
    Report an inconclusive test. Stops execution. The arguments are printf-style.
  • void activitySuccess (char* fmt , ...)
    Finishes the current activity with success (same as "success()"). The arguments are printf-style.

Logging (C)[Bearbeiten]

  • void logFail (char* fmt , ...)
    Adds a fail message to the activity log, but continues execution. The arguments are printf-style.
    Notice: although the action continues to be executed, it will be marked as failed at the end.
  • void logError (char* fmt , ...)
    Adds a error message to the activity log, but continues execution. The arguments are printf-style.
    Notice: although the action continues to be executed, it will be marked as erronous at the end.
  • void logWarning (char* fmt , ...)
    Adds a warning to the activity log, but continues execution. The arguments are printf-style.
  • void logInfo (char* fmt , ...)
    Adds an info message to the activity log, and continues execution. The arguments are printf-style.
  • void alert (char* fmt , ...)
    Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "Execution" → "Log -Settings" dialog (by default it is disabled).
  • void warn (char* fmt , ...)
    Same as alert(). (For Smalltalk protocol compatibility)

Event Sending (C)[Bearbeiten]

  • void pushEventType_data(char* event, char* dataOrNULL)
    adds an event to expecco's event queue. The payload in dataOrNULL must be a 0-terminated string.
  • void pushEventType_dataBytes(char* event, unsigned char* dataOrNULL, int numBytes)
    adds an event to expecco's event queue. The payload in dataOrNULL points to a byte-buffer of given length.
  • void pushEventType_dataJSON(char* event, jsonObject dataOrNULL)
    adds an event to expecco's event queue. The payload in dataOrNULL is a json object.

Pin Functions (C)[Bearbeiten]

Currently, C blocks do not support a variable number of input or output pins.

Attention:
C uses many more reserved keywords for syntax than Smalltalk. These keywords cannot be used as pin names, and you will get a syntax error if you try.
Be careful to not name your pins as any of: "return", "char", "int", etc.

As a proven best practice, add a "Pin" suffix to your pin names (e.g. name it "inPin", instead of "in").

Input Pins (C)[Bearbeiten]
Queries[Bearbeiten]
  • bool_t hasValue (inPin)
    Returns true if the pin has received a value, where true is represented as 1, false as 0.
  • bool_t isConnected (inPin)
    Returns true if the pin is connected.
Pin Value[Bearbeiten]
  • char* stringValue (inPin)
    Returns the string value at the pin as a c-char*. Raises an error if the pin did not receive a value, or if the value was not a string. The returned pointer becomes invalid after the action's execution; it should therefore be strcpy'd if it is needed later (i.e. do not keep a reference to it).
  • long longValue (inPin)
    Returns the integer value at the pin as a c-long. Raises an error if the pin did not receive a value, or if the value was not an integer.
  • longlong longLongValue (inPin) (vsn24.1, 64bit)
    Returns the integer value at the pin as a c-longLong. Raises an error if the pin did not receive a value, or if the value was not an integer.
    This API is only avalable in 64bit versions of the C-bridge (i.e. not in the cBridge32.exe for Windows) and only with expecco vsn 24.1 and later.
  • double doubleValue (inPin)
    Returns the double value at the pin as a c-double Raises an error if the pin did not receive a value, or if the value was not a number.
  • bool_t booleanValue (inPin)
    Returns the boolean value at the pin as a c-int. Raises an error if the pin did not receive a value, or if the value was not a boolean.
  • uchar* bytesValue (inPin)
    Returns the byteArray value at the pin as a c-unsigned char*. Raises an error if the pin did not receive a value, or if the value was not a byteArray or C-struct. This can also be used to receive other bulk data, such as an int32 or float array. For example, by converting a collection of numbers first to a byteArray, and then casting the bytesValue to another pointer type in the bridge code.
  • void* pointerValue (inPin)
    Returns the pointer value at the pin as a void*. Raises an error if the pin did not receive a value, or if the value was not a pointer. This means, that it must be a pointer as previously sent to expecco with putPointer. Please read the description of "putPointer()" below.
  • jsonObject jsonValue (inPin)
    Returns the value at the pin as a jsonObject. Raises an error if the pin did not receive a value. The returned pointer becomes invalid after the action's execution; field values should therefore be extracted and copied if needed later (i.e. do not keep a reference to it or its components).


  • int arraySize (inPin)
    Returns the number of array elements, if there is a byteArray value at the pin. Raises an error if the pin did not receive a value, or if the value was not a byteArray or C-struct.


  • char* stringValueIfAbsent (inPin, char* repl)
    Returns the pin's datum if it has one, repl otherwise. Similar to "stringValue()", but avoids the exception. The returned pointer becomes invalid after the action's execution.
  • long longValueIfAbsent (inPin, long repl) (vsn24.1, 64bit)
    Returns the pin's datum if it has one, repl otherwise. Similar to "longValue()", but avoids the exception.
  • longlong longLongValueIfAbsent (inPin, longlong repl)
    Returns the pin's datum if it has one, repl otherwise. Similar to "longLongValue()", but avoids the exception.
    This API is only avalable in 64bit versions of the C-bridge (i.e. not in the cBridge32.exe for Windows) and only with expecco vsn 24.1 and later.
  • double doubleValueIfAbsent (inPin, double repl)
    Returns the pin's datum if it has one, repl otherwise. Similar to "doubleValue()", but avoids the exception.
  • bool_t booleanValueIfAbsent (inPin, bool_t repl)
    Returns the pin's datum if it has one, repl otherwise. Similar to "booleanValue()", but avoids the exception.
  • uchar* bytesValueIfAbsent (inPin, uchar* repl)
    Returns the pin's datum if it has one, repl otherwise. Similar to "bytesValue()", but avoids the exception.
  • jsonObject jsonValueIfAbsent (inPin, jsonObject repl)
    Returns the pin-datum as a jsonObject if it has one, repl otherwise. Similar to "jsonValue()", but avoids the exception. The returned pointer becomes invalid after the action's execution.


  • char* stringValueIfPresent (inPin)
    Returns the pin's datum if it has one, an empty string otherwise. Similar to "stringValue()", but avoids the exception. The returned pointer becomes invalid after the action's execution.
  • long longValueIfPresent (inPin)
    Returns the pin's datum if it has one, 0 (zero) otherwise. Similar to "longValue()", but avoids the exception.
  • longlong longLongValueIfPresent (inPin) (vsn24.1, 64bit)
    Returns the pin's datum if it has one, 0 (zero) otherwise. Similar to "longLongValue()", but avoids the exception.
    This API is only avalable in 64bit versions of the C-bridge (i.e. not in the cBridge32.exe for Windows) and only with expecco vsn 24.1 and later.
  • double doubleValueIfPresent (inPin)
    Returns the pin's datum if it has one, 0.0 (zero) otherwise. Similar to "doubleValue()", but avoids the exception.
  • bool_t booleanValueIfPresent (inPin)
    Returns the pin's datum if it has one, 0 (false) otherwise. Similar to "booleanValue()", but avoids the exception.
  • uchar* bytesValueIfPresent (inPin)
    Returns the pin's datum if it has one, NULL otherwise. Similar to "bytesValue()", but avoids the exception.
  • jsonObject jsonValueIfPresent (inPin)
    Returns the pin-datum as a jsonObject if it has one, NULL otherwise. Similar to "jsonValue()", but avoids the exception. The returned pointer becomes invalid after the action's execution.

Variable number of input/output pins are not supported. You should pass an array or structure if required.

Output Pins (C)[Bearbeiten]
  • void putString (outPin, char* data)
    Writes a string to the output pin. The string must be zero-terminated.
  • void putStringN (outPin, char* data, int len)
    Writes len bytes of a string to the output pin.
  • void putLong (outPin, long data)
    Writes a long to the output pin.
    Be aware that the size of a long depends on the cbridge host's CPU architecture, the OS and the C-compiler: on Unix machines, it is typically an int64, whereas on Windows it is usually an int32.
  • void putULong (outPin, unsigned long data)
    Writes an unsigned long to the output pin.
    Be aware that the size of an unsigned long depends on the cbridge host's CPU architecture, the OS and the C-compiler: on Unix machines, it is typically an uint64, whereas on Windows it is usually an uint32.
  • void putLongLong (outPin, long long data)
    Writes a long long to the output pin. Typically, a long long is an int64 on all architectures.
  • void putULongLong (outPin, unsigned long long data)
    Writes an unsigned long long to the output pin. Typically, an unsigned long long is an uint64 on all architectures (depends on cbridge host's CPU, OS and compiler). This API is not available in the 32 bit cBridge version.
  • void putDouble (outPin, double data)
    Writes a double (float64) to the output pin.
  • void putBoolean (outPin, bool_t data)
    Writes a boolean to the output pin. Anything but 0 (zero) is interpreted as true.
  • void putBytesN (outPin, unsigned char* bytes, int nBytes)
    Writes nBytes from a byte array to the output pin.
  • void putPointer (outPin, void* dataPtr)
    Writes a reference for dataPtr to the output pin. This value can be passed to another cBridge action (executed on the same bridge), where it can be read from the input with pointerValue() (this is similar to the makeRef mechanism of other bridged languages).

The following API will be available with the 21.2 release:

  • void putDoublesN (outPin, double* doubles, int count)
    Writes count double values as a vector to the output pin. expecco will receive these values as a collection of double precision floating point values.
  • void putFloatsN (outPin, float* floats, int count)
    Writes count float values as a vector to the output pin. expecco will receive these values as a collection of single precision floating point values.
  • void putIntsN (outPin, int* ints, int count)
    Writes count int values as a vector to the output pin. expecco will receive these values as a collection of integer values.
  • void putLongsN (outPin, long* longs, int count)
    Writes count long values as a vector to the output pin. expecco will receive these values as a collection of integer values.
    Be reminded that the sizeof longs may be different between Windows and non-Windows machines.

These vectors will appear at the output pin as a collection; the type of collection is given by the pin's datatype. For most space efficient representation, we recommend the use of the bulk data types (FloatArray, DoubleArray, etc.)

The following API will be available with the 21.2.1 update release:

  • void putShortsN (outPin, short* longs, int count)
    Writes count (signed) short values as a vector to the output pin. expecco will receive these values as a collection of integer values.
  • void putUIntsN (outPin, unsigned int* ints, int count)
    Writes count unsigned int values as a vector to the output pin. expecco will receive these values as a collection of integer values.
  • void putULongsN (outPin, unsigned long* longs, int count)
    Writes count unsigned long values as a vector to the output pin. expecco will receive these values as a collection of integer values.
    Be reminded that the sizeof longs may be different between Windows and non-Windows machines.
  • void putUShortsN (outPin, unsigned short* longs, int count)
    Writes count unsigned short values as a vector to the output pin. expecco will receive these values as a collection of integer values.

Transcript, Stderr and Stdout (C)[Bearbeiten]

The expecco "Transcript", "Stderr" and "Stdout" are also accessible from C code (as pseudo variables). However, only a very limited subset of operations is supported (in the following, stream stands for one of the above wellknown expecco streams and is given as argument to the function):

  • void cr (stream)
    Adds a linebreak (i.e. followup text will be shown on the next line)
  • void show (stream, char* fmt, ...)
    Adds a textual representation of the argument. The argument is printf-style.
  • void showCR (stream, char* fmt, ...)
    A combination of show(), followed by a linebreak.

If the bridge was started by expecco (as opposed to being executed on a remote host), stdout and stderr are also forwarded to the expecco Transcript window, depending on the settings in expecco ("Extras" → "Settings" → "Execution" → "Tracing" → "Show Stdout and Stderr on Transcript").

Thus, you can use "fprintf(stdout, ...)" or "fprintf(stderr, ...)" or "showCR(Transcript, ...)".  

Referring to Functions in Shared Libraries[Bearbeiten]

If the cBridge code refers to any functions which are located in additional shared libraries (DLLs), the bridge needs to know where to find those.

The location of any shared library must be specified in the C-bridge settings dialog, unless the library is found in the CBridge executable's folder or at a standard place (eg. "/usr/lib", "/usr/local/lib", etc. on Unix and "C:\Windows" and others on Windows).

Navigate to "Extras" → "Settings" → "Execution" → "External Script Interpreters" → "CBridge" and add the path to the "DLL Path" field.

As an alternative, edit the CC-Script and add appropriate command line arguments to the linker command near the end.

You'll also have to edit the script, if additional compiler or linkage arguments are required, or if you have to determine the location of additional frameworks dynamically.

Referring to Types defined in expecco[Bearbeiten]

User defined struct, union and enum "CType (C-Defined)" dataypes (which are declared in expecco and present in the suite's element tree) are visible inside the activity code, and can be included as:

#include "expecco/Types/XXX.h"

where "XXX" is the name of the expecco type (in the tree). If the type declares an unnamed struct, the type will be known as a typedef in the C-code.

Typedef Name vs. struct/union Name[Bearbeiten]

If the type is named "myStruct" and defined as:

struct {
   ...
}

it should be referred to in the C-code as:

myStruct s, *sP;

In this case, the name of the typedef is the name of the type.

In contrast, if it was defined as:

struct foo {
   ...
}

you can also write:

struct foo s;
struct foo *sP; 

In other words, there will be a typedef for "myStruct" (the name of the type) and a struct by the name as in the type's definition.

Notice, that these files do actually not exist; instead, the activity's C-code is scanned and dynamically expanded to include those definitions. See example below. You should shutdown any already running bridge, when a type is changed, to enforce a recompilation of the C code.

Including Attachments as Header Files[Bearbeiten]

Attached files can be included as:

#include "expecco/Attachments/XXX.h"

where "XXX.h" is the name of the expecco attachment file.
Be reminded that the name of the attachment itself (the tree-item name) is not required to be the same as the name of the file, although usually, they are.

C++ Interfaces[Bearbeiten]

C++ functions should be called indirectly via a C function wrapper (i.e. extern "C").
Object references can be passed from the bridge to expecco and back via the "putPointer" and "pointerValue" functions.

Running Bridged C-Code on a Remote Computer[Bearbeiten]

The remote computer must have the cBridge executable running. The executable is found in the "packages/bridgeFramework/cBridge/cLibrary" folder under your expecco installation folder and should be copied to the remote machine. Windows users will find both a 64bit version ("cBridge.exe") and a 32bit one ("cBrige32.exe"). If your C-code needs or refers to any existing dll, make sure that the correct bridge is configured in the settings (a 32bit bridge will not be able to load a 64bit dll and vice versa).

On the expecco side, you have two options:

  1. specify that the bridge is already running in the settings; then all cBridge actions which have not been explicitly given a bridge to execute in will be executed there
  2. explicitly connect to a running bridge via the "Connect to Running Bridge" action block, and passing the resulting bridge handle to the C-action.

The handle can be passed either explicitly via the step's cBridge input pin, or implicitly by declaring a variable named "CBRIDGE" and storing the handle there. The cBridge input pin is generated via the step's Special Pins menu.

Bridges can be started either by one of the bridge start actions in the standard library or by a batch-, shell- or powershell script. Type "cBridge --help" for command line options.

You can have multiple bridges running, even in heterogenous networks (i.e. call for a remote C-action on a Windows machine, while running expecco on a Linux machine). We provide bridge executables for additional architectures (eg. Raspberry-PI) upon request and for a small additional fee.

Bulk Data[Bearbeiten]

If a huge number of data elements (known as "Bulk Data") are to be transferred, transmission times may get much longer than the actual execution time of the action (keep in mind that the communication times take most of the time if the executed computation is simple).

There are multiple possible ways to deal with this situation:

  • Passing a Pointer to expecco
if you can leave the data inside the bridge (eg. as malloc'd data vector), and expecco does not need to access the elements of the vector, you can leave the data inside the cBridge and only pass a handle to expecco. This handle may later be sent to another C-action to process the data at non-critical times (eg. at the end of the test).
This mechanism is the fastest because only a small handle (typically 32/64 bits) need to be transmitted to expecco. However, it degrades to become very very slow if you do access individual elements of the vector from expecco, as this will result in one IPC roundtrip per accessed element.
  • Passing Bulk Data as a Vector
If each vector element is sent individually to an output pin (i.e. in a loop with putFloat()/putDouble()), one IPC roundtrip will be performed per element.
It is much faster to send the whole vector data sent as one array via putDoublesN(), putFloats() or putIntsN(). This still does introduce some additional cost due to JSON-encoding (on the cBridge side) and JSON-decoding on the expecco side, but the effective transmission time is dramatically reduced.
  • Passing Bulk Data as a ByteArray
Further speedup is possible by not sending the data as a number vector, but instead as a byte vector, actually sending the raw ended bytes from the bridge to expecco. For this, use a putBytesN() call and case your bulk data to a "char *".
However (big warning): expecco will receive a byte-vector and you will have to add code to extract the relevant elements from this. If the cBridge CPU and the expecco-CPU use a different byte order or a different floating-point-number representations, you will have to deal with that in that extraction code. Be aware, that it may even depend on the compiler: Microsoft VisualC compilers for x86_64 interpret "long" as a 32bit integer, whereas most other compilers/systems treat them as 64bit integers.
This is probably the fastest method to transmit bytes, but comes with those possible pitfalls. We therefore recommend to try the "Bulk Data as Vector" method first. In most situations that is either within the acceptable timing constraints, or too far off, such that the ByteArray transmission will also not be fast enough, and you should use the pointer passing method anyway.

If the cBridge runs on the same machine as expecco, shared memory will be used to exchange bulk data. This may reduce the transmission times drastically, depending on the amount of data (there are still other messages exchanged via the socket communication).

Example1: Writing and Calling C Code in Expecco[Bearbeiten]

In this example, numbers and a string will be passed to a C action block.
The action has been defined with 3 input pins:

  • in1 (type Float)
  • in2 (type Integer)
  • in3 (type String)

and the single output "out1", with type String.

The code will generate a string by concatenating the printf strings of its arguments at the output and also print a number of messages via different output channels.

Create a new bridged C action,
CBridge Demo Action1.png
and enter the code:

static void
execute() {
   double dVal = doubleValue(in1); // fetch input pin's double value
   long lVal   = longValue(in2);   // fetch input pin's integer value
   char* sVal  = stringValue(in3); // fetch input pin's string value
   char buffer[512];

   snprintf(buffer, sizeof(buffer), "%s / %ld / %g", sVal, lVal, dVal);

   // write to output pin
   putString(out1, buffer);

   // a regular print
   // (will be shown inside expecco's Transcript, if io-trace is enabled)
   fprintf(stderr, "hello on stderr from C-Code!\n");

   // another regular print
   // (will be shown inside expecco's Transcript, if io-trace is enabled)
   fprintf(stdout, "hello on stdout from C-Code!\n");
   // explicit output to expecco's Transcript

   // explicit output to expecco's stdout
   show(Stdout, "hello on expecco's stdout from C-Code!\n");

   // explicit output to expecco's stderr
   show(Stderr, "hello on expecco's stderr from C-Code!\n");

   // explicit output to expecco's Transcript
   show(Transcript, "another hello from C-Code!\n");

   // possibly report failure to expecco
   if (strlen(sVal) < 20) {
       fail("short string \"%s\" (length=%d)", sVal, strlen(sVal))
   }
}

Example2: Passing Complex Objects to and from C-Code (by value)[Bearbeiten]

Complex struct objects can be passed by value to the C-code by defining a CStruct type i.e. create a new type named eg. "myStruct" in the tree, define it as C-Type and enter a definition similar to:

/* C: */
struct {
   int i1;
   float f1;
   double d1;
   char c[20];
}

then, add an input pin with that datatype to the C-action, and add the following line to the action's C-code, to include the type's definition:

#include "expecco/Types/myStruct.h"

(the name of the include must match the type's typename in the tree)

From within your C-code, access the fields with:

struct myStruct* p;

p = (struct myStruct*)bytesValue(inPin);

fprintf(stderr, "got i1:%d\n", p->i1);
fprintf(stderr, "got f1:%f\n", p->f1);
fprintf(stderr, "got d1:%F\n", p->d1);
fprintf(stderr, "got c:%s\n", p->c);

and write such a struct to an output pin with:

putBytesN(outPin, p, sizeof(struct myStruct));

To create such a struct argument, use an instance creation action block (and possibly additional field setter actions). Those can be automatically generated by selecting the type in the tree, and invoking the popup menu function "Refactoring" → "Generate...".

Example3: Passing Handles/Pointers to expecco and Back to Another C Action[Bearbeiten]

A common situation is when two C actions are given, where one generates a handle (i.e. pointer), which must be passed to the other action. For example, the first may be a "createConnection()" function, and the second needs that connection handle as argument (typically a kind of "send()", "receive()" or "close()" function.

For this scenario, the cBridge provides a mechanism similar to the "makeRef" facility of other bridges. The first action function will send the handle to an output pin via "putPointer()"; the pin's datatype should be "Any", "Handle" or a struct type as described above:

void execute() {
    ...
    putPointer( outPin, (void*) handle );
}

and the other action functions can get this handle from an input pin:

void execute() {
    handle = pointerValue( inPin );
    ...
}

Warning:
expecco does not interfere with or manage allocated memory. If data is malloc'd in the first action and passed via "putValue()" to another action, the data must be freed by calling "free()" eventually. Otherwise, you'll have a memory leak (in the cBridge).

Here is a concrete example:
CBridge in out example.png
with code:

void
execute() {
  char* inVal  = pointerValue(in1); 
  char* outVal;

  fprintf(stderr, "inVal is \"%s\"\n", inVal);

  outVal = malloc(strlen(inVal) + 10);
  strcpy(outVal, inVal);
  strcat(outVal, "XXX");
  
  fprintf(stderr, "outVal is \"%s\"\n", outVal);
  // write to output pin
  putPointer(out1, outVal);
}

And a test setup:
CBridge in out test setup.png

Bridged Smalltalk Elementary Blocks[Bearbeiten]

The following works in 20.1 and above.
Similar to the above described bridged Node and bridged Python actions, these actions are coded in the Smalltalk language and executed by a separate Smalltalk engine (separate process). Both local and remote execution are possible.

By the time of writing, bridged Smalltalk actions can be executed in an ST/X Smalltalk engine, support for VA-Smalltalk and VW-Smalltalk is being developed and will be available in one of the next expecco releases.


Bridged Smalltalk Code API[Bearbeiten]

Note: This is a preliminary API documentation. Bridged Smalltalk elementary actions are still being developed and the API may change slightly until officially released.

Bridged Smalltalk actions will execute in another Smalltalk system; by the time of writing, this may be another Smalltalk/X or a VisualWorks Smalltalk system. With enough customer interest, other dialects (VisualAge and Squeak) may be supported in the future.

The API looks similar to the API of regular Smalltalk actions, which execute inside expecco itself. However, for some objects, only a subset of the protocol is available.

Variables (Bridged Smalltalk)[Bearbeiten]

The following variables are in the scope of the executed code:

  • Transcript
    a proxy which supports a few functions to display messages in the expecco Transcript window (see below)
  • Stdout
    a proxy supports a few functions to display messages on expecco's stdout stream (see below)
  • Stderr
    a proxy supports a few functions to display messages on expecco's stderr stream (see below)

 

Reporting (Bridged Smalltalk)[Bearbeiten]

  • error:infoString [ with:arg1 ... with:arg4 ]
    Report a defect (in the test). Up to 4 optional with:-args are sliced into the string, if it contains "%i" placeholders. Stops execution.
  • fail:infoString [ with:arg1 ... with:arg4 ]
    Report a failure (in the SUT). Stops execution.
  • inconclusive:infoString [ with:arg1 ... with:arg4 ]
    Report an inconclusive test. Stops execution.
  • activitySuccess:infoString [ with:arg1 ... with:arg4 ]
    Finishes the current activity with success (same as "success()").
  • pass:infoString [ with:arg1 ... with:arg4 ]
    Finishes the current testCase with success.

 

Logging (Bridged Smalltalk)[Bearbeiten]

  • logFail:messageString
    Adds a fail message to the activity log.
  • logError:messageString
    Adds a error message to the activity log.
  • logWarning:messageString
    Adds a warning to the activity log.
  • logInfo:messageString
    Adds an info message to the activity log.
  • alert:messageString
    Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "Execution" → "Log Settings" dialog (by default it is disabled).
  • warn:messageString
    Same as alert: (for JavaScript compatibility).

 

Environment Access (Bridged Smalltalk)[Bearbeiten]

  • environmentAt(<varName>);
    Fetches and returns a value from the expecco environment which is in scope of the current activity.
    You can only read simple objects (numbers, booleans and strings) from remote smalltalk actions.
  • environmentAtPut(<varName>, <newValue>);
    Writes a value into the expecco environment which is in scope of the current activity.
    The variable must be writable.
    You can only write simple objects (numbers, booleans and strings) from remote smalltalk actions.

 

Input Pins (Bridged Smalltalk)[Bearbeiten]

  • pin hasValue
    Returns true if the pin has received a value
  • pin value
    Returns the value of the pin. Raises an error if the pin did not receive any value.
  • pin valueIfAbsent:alternativeValue
    Returns the value of a pin or the value from alternativeValue if the pin did not receive any value.
  • pin valueIfPresent
    Returns the value of a pin or nil if the pin did not receive any value.
  • pin isConnected
    Returns true if the pin has a connection

 

Output Pins (Bridged Smalltalk)[Bearbeiten]

  • pin value:someValue
    Writes the value to the pin. Only simple object can be transferred by value (nil, booleans, integers, floats, strings).
    Anything else should be passed by reference (see makeRef below).

 

Transcript, Stderr and Stdout (Bridged Smalltalk)[Bearbeiten]

The expecco "Transcript", "Stderr" and "Stdout" are also accessible from remote Smalltalk images. However, only a limited subset of messages is supported:

  • cr ()
    Adds a linebreak (i.e. followup text will be shown on the next line)
  • show:arg
    Adds a textual representation of the argument, which can be a string, number or any other object.
  • showCR:arg
    A combination of show(), followed by a linebreak.
  • nextPutAll:string
    String writing
  • nextPutLine:string
    String printing with cr

In addition, stdout and stderr are also forwarded to the expecco Transcript window, depending on the settings in expecco ("Extras" → "Settings" → "Execution" → "Tracing" → "Show Stdout and Stderr on Transcript").  

Passing Objects by Reference (Bridged Smalltalk)[Bearbeiten]

By default, objects written to output pins via "value:" will be marshalled to JSON, transferred to expecco and decoded there. Effectively, a copy of the object is created, which looses its identity when sent back later to the remote smalltalk in another action. This would make it impossible to get handles from a remote action, which is to be sent to another action on the same remote machine. The "makeRef" method solves this.

  • self makeRef:object
    creates a reference object, which can be written to a pin.
  • self makeRef:object name:aString
    ditto, but gives it a user friendly name (eg. for the expecco log)

I.e. to write a reference to a pin, use:

somePin value:(self makeRef:someObject)

Additional Information on Bridged VisualWorks Smalltalk Actions[Bearbeiten]

Inside bridged VisualWorks actions, the above described "Transcript" refers to the expecco Transcript; not the VisualWorks Transcript (the remote code is compiled inside an environment, where the Transcript variable has been redefined).

To send output to the VisualWorks Transcript, use "Smalltalk.Core.Transcript", as in:

...
Smalltalk.Core.Transcript show:'Hello on VW Transcript'; cr
...
Transcript show:'Hello on expecco Transcript'; cr
...
Be aware, that the VW Transcript does not understand the "showCR:" message; you need "show:" followed by "cr".

Bridged Ruby Elementary Blocks[Bearbeiten]

Note: This is a preliminary API documentation. Bridged Ruby elementary actions are still being developed and the API may change slightly until officially released.

Bridged Ruby actions will execute in a Ruby interpreter.

The API looks similar to the API of regular actions, which execute inside expecco itself or another language interpreter. However, due to Ruby language specifics, some differences are noticable.

Variables (Bridged Ruby)[Bearbeiten]

The following variables are in the scope of the executed code
(notice the "$" prefix; Ruby requires global variables to be prefixed by a "$" character):

  • $Transcript
    a proxy which supports a few functions to display messages in the expecco Transcript window (see below)
  • $Stdout
    a proxy which supports a few functions to display messages on expecco's stdout stream (see below)
  • $Stderr
    a proxy which supports a few functions to display messages on expecco's stderr stream (see below)
  • $Dialog
    a proxy which supports a few functions to display confirmation dialogs (see below)

 

Reporting (Bridged Ruby)[Bearbeiten]

  • error(infoString [ ,arg1 ... ,arg4 ])
    Report a defect (in the test). Up to 4 optional args are sliced into the string, if it contains "%i" placeholders. Stops execution.
  • fail(infoString [ ,arg1 ... ,arg4 ])
    Report a failure (in the SUT). Stops execution.
  • inconclusive(infoString [ ,arg1 ... ,arg4 ])
    Report an inconclusive test. Stops execution.
  • activitySuccess(infoString [ ,arg1 ... ,arg4 ])
    Finishes the current activity with success (same as "success()").
  • pass(infoString [ ,arg1 ... ,arg4 ])
    Finishes the current testCase with success.

 

$Transcript, $Stderr and $Stdout (Bridged Ruby)[Bearbeiten]

The expecco "Transcript", "Stderr" and "Stdout" are also accessible from Ruby actions (with a $-prefix). Notice, that $Stderr refers to expecco's stderr and $Stdout refers to expecco's stdout. Both may be (and usually are) different from the Ruby interpreter's STDERR/STDOUT.

  • cr ()
    Adds a linebreak (i.e. followup text will be shown on the next line)
  • show(arg)
    Adds a textual representation of the argument, which can be a string, number or any other object.
  • showCR(arg)
    A combination of show(), followed by a linebreak.
  • nextPutAll(string)
    String writing
  • nextPutLine(string)
    String printing with cr
  • puts(string)
    String printing with cr

In addition, stdout and stderr are also forwarded to the expecco Transcript window, depending on the settings in expecco ("Extras" → "Settings" → "Execution" → "Tracing" → "Show Stdout and Stderr on Transcript").

 

$Dialog (Bridged Ruby)[Bearbeiten]

  • confirm(msg)
    Opens a simple yes/no dialog; returns a boolean.
  • request(msg)
    Opens a simple string-input dialog; returns a string or nil if cancel was pressed.
  • requestPassword(msg)
    same, but the entered string is not shown on the screen
  • requestFilename(msg)
    asks for a filename

 

Logging (Bridged Ruby)[Bearbeiten]

  • logFail(messageString)
    Adds a fail message to the activity log.
  • logError(messageString)
    Adds a error message to the activity log.
  • logWarning(messageString)
    Adds a warning to the activity log.
  • logInfo(messageString)
    Adds an info message to the activity log.
  • alert(messageString)
    Adds a warning message to the activity log, and also shows a DialogBox, which has to be confirmed by the operator. The dialog box and confirmation can be disabled by a settings flag in the "Execution" → "Log Settings" dialog (by default it is disabled).
  • warn(messageString)
    Same as alert: (for JavaScript compatibility).

 

Environment Access (Bridged Ruby)[Bearbeiten]

  • environmentAt(<varName>);
    Fetches and returns a value from the expecco environment which is in scope of the current activity.
    You can only read simple objects (numbers, booleans and strings) from ruby actions.
  • environmentAtPut(<varName>, <newValue>);
    Writes a value into the expecco environment which is in scope of the current activity.
    The variable must be writable.
    You can only write simple objects (numbers, booleans and strings) from ruby actions.

 

Input Pins (Bridged Ruby)[Bearbeiten]

  • pin.hasValue()
    Returns true if the pin has received a value
  • pin.value()
    Returns the value of the pin. Raises an error if the pin did not receive any value.
  • pin.valueIfAbsent(alternativeValue)
    Returns the value of a pin or the value from alternativeValue if the pin did not receive any value.
  • pin.valueIfPresent()
    Returns the value of a pin or nil if the pin did not receive any value.
  • pin.isConnected()
    Returns true if the pin has a connection

 

Output Pins (Bridged Ruby)[Bearbeiten]

  • pin.value(someValue)
    Writes the value to the pin. Only simple object can be transferred by value (nil, booleans, integers, floats, strings).
    Anything else should be passed by reference (see makeRef below).

 

Passing Objects by Reference (Bridged Ruby)[Bearbeiten]

By default, objects written to output pins via "value:" will be marshalled to JSON, transferred to expecco and decoded there. Effectively, a copy of the object is created, which looses its identity when sent back later to the remote ruby in another action. This would make it impossible to get a handle from a remote action, which is to be sent to another action on the same remote machine. The "makeRef" method solves this, by creating a "pointer" or "handle" to the object inside the remote machine.

  • makeRef(object)
    creates a reference object, which can be written to a pin.
  • makeRef(object, name)
    ditto, but gives it a user friendly name (eg. for the expecco log)
I.e. to write a reference to a pin, use:
somePin.value(makeRef(someObject))

Bridged Dart Elementary Blocks[Bearbeiten]

coming with one of the next expecco versions

DotNET Elementary Blocks[Bearbeiten]

Editing support for C# will be provided in a later expecco version. Currently, .NET objects must be instantiated and methods be called via the transparent message forwarning mechanism provided by Smalltalk. This means, that you use Smalltalk code to send (Smalltalk-) messages to proxy objects, which forward the call information to the corresponding bridge object.

As an alternative, you can use bridged IronPython actions and refer to the assembly from there.

VisualBasic Elementary Blocks[Bearbeiten]

Support for VisualBasic Script elementary block execution is provided as an extension plugin, and requires a separate plugin license. It is only available for expecco running on the MS Windows operating system.

Code written as a VisualBasic elementary block is not executed directly by expecco. Instead, the code is forwarded to a VisualBasic scripting host which runs as another process either on the local or on a remote host. The scripting host must be a Microsoft Windows host (but expecco itself may run on any type of operating system). By using VisualBasic blocks, expecco's basic black box test functionality can be easily extended by many powerful gray- and white-box tests. Many libraries for device and equipment control, COM/DCOM and other technologies and UI interaction are possible with the VisualBasic plugin.

Please consult the separate VisualBasic plugin documentation for more detail.

Shell, Batch and other Script Elementary Blocks[Bearbeiten]

There is no special API for those, except for pin-value expansion and stdin/stdout/stderr handling. All of this is described in the "Script Action Blocks" documentation

Remote expecco Elementary Blocks[Bearbeiten]

These run on a remote expecco system, and can be used to generate load (stress) or perform additional measurements. Remote expecco actions will be available in or after the 24.2 expecco version.

--- to be documented ---

Tutorial: Common Tasks[Bearbeiten]

This chapter gives code fragments and examples for common tasks.

The underlying Smalltalk class library contains (among others) very powerful and robust collection, stream and number representation systems, of which many functions are useful for elementary block developers. The set of usable functions is much larger than for example in Java or C standard libraries. Thus for a newcomer to Smalltalk, it is highly recommended and fruitful to read the introduction texts and/or use the class browser for a deeper understanding. You can benefit from existing code and save a lot of development time in knowing a little about your class libraries.

Also, it is a good idea to try the sample code in a "workspace" window or within the code editor, by selecting the piece of code to be tried, and performing the "printIt" menu function. Thus you can quickly test&try code fragments without a need for an elementary block and its setup.


String Handling[Bearbeiten]

Notice, that in Smalltalk all collection indices are 1-based. I.e. the index of the first element is 1, and the last index is the collection's size. This is different from C, JavaScript and Java. Thus Smalltalk loops over collection elements should run from 1 to the collection's size, not from zero to the size minus 1. You will get an index exception, if you try.

Also notice, that index-based enumerations (i.e. loops from 1 to the size) are actually seldom needed, due to the powerful "do:", "collect:", "select:" etc. family of enumeration methods. These do all the index computations for you, and are often heavily tuned for better performance.

Also notice, that in Smalltalk, String is a subclass of Collection, and most of the functions described below are actually not implemented in the String class, but inherited from a superclass. Take this into consideration, when searching for String utility functions in the class browser (i.e. look there first or click on the SeeInheritedInBrowser.png-"see inherited methods" button in the class browser). On the other hand, the fact that most of those functions are implemented in the Collection superclass also means, that they work on many other collections (Arrays, ByteArray, OrderedCollection, Set, Dictionary etc.).

If you are in doubt, open a workspace window, enter a piece of code and evaluate it the (using the "printIt" menu function). Or open the method finder to find an operation given the desired outcome.


Copying Parts of a String[Bearbeiten]

To extract a substring, and the index and/or count is known, use one of:

   aString copyFrom: startIndex to: stopIndex
   
   aString copyFrom: startIndex
 
   aString copyTo: stopIndex
 
   aString copyFrom: startIndex count: n
 
   aString copyFirst: count   "/ same as copyTo:
 
   aString copyLast: count    "/ gives the last count characters

All of the above will copy including the character at the start- and stop index. I.e.

   'hello world' copyFrom:2 to:5

will return 'ello'.
Point right.pngThis is different from some C/Java functions, which usually expect the stopIndex to be one-after the last copied element.

Finding Elements and Substrings[Bearbeiten]

To search for elements of a string (which are characters), use:

   aString indexOf: aCharacter 
   aString indexOf: aCharacter startingAt: startIndex

and:

   aString lastIndexOf: aCharacter
   aString lastIndexOf: aCharacter startingAt: startIndex

to search backward from the end.


Point right.pngThe Smalltalk methods return a 1-based index, and 0 (zero) if not found; JavaScript methods return a 0-based index and -1 if not found.


Point right.pngNotice that a character constant is written in Smalltalk as $<x>, where <x> is a printable character or a space. Non-printable characters must be specified by their name ("Character return", "Character lf", "Character tab") or their Unicode-point (decimal: "Character value: 127" or hex: "Character value: 16r1F00").

If there are multiple characters to be searched for, pass a collection of searched characters to one of:

   aString indexOfAny: aBunchOfCharacters
   aString indexOfAny: aBunchOfCharacters startingAt: startIndex

for example,

   'hello world' indexOfAny: 'eiou' startingAt:3

looks for the next vocal at or after position 3.

Both receiver and argument may actually be any kind of Collection; thus:

   aString indexOfAny: #( $a $A $e $E )

works the same.

Finally, there is a very generic search function, where you can pass a test-procedure as argument:

   aString findFirst:[:ch | ...a boolean test on ch ...]

will return the index of the next character for which your boolean check computes a true value. For example, to search for the next digit, use:

   aString findFirst:[:ch | ch isDigit]

to search for a digit OR period OR dollar character, you can use one of:

   aString findFirst:[:ch | ch isDigit or:[ ch == $. or:[ ch == $$]]]
   aString indexOfAny:'0123456789$.'

use whichever you find more readable. (there is also a "findFirst:startingAt:", and corresponding "findLast:" and "findLast:startingAt:" for backward searches)


Because it is a common task to search for separators and other non-printable characters, special methods exist for those:

   aString indexOfSeparatorStartingAt: startIndex
   aString indexOfNonSeparatorStartingAt: startIndex
   aString indexOfControlCharacterStartingAt: startIndex

where separators are the whitespace characters (space, tab, vt, cr and lf), and control characters are all characters below ASCII 32.

All of the index-search methods return a 0 (zero) if the character is not found.

Substrings can be searched with:

   aString indexOfString: anotherString
   aString indexOfString: anotherString startingAt: startIndex
   aString indexOfString: anotherString caseSensitive: false
   aString indexOfString: anotherString startingAt: startIndex caseSensitive: false

   aString indexOfString: anotherString startingAt: startIndex

   aString indexOfSubCollection: anotherString
   aString indexOfSubCollection: anotherString startingAt: startIndex

There are also corresponding backward search variants "lastIndexOfSubCollection:".

Notice the somewhat generic names "indexOfSubCollection:" instead of a more intuitive "indexOfString:". The reason is that these methods are inherited from collection, which does not care if the elements are characters or other objects (indexOfString: is an alias for indexOfSubCollection:).

This means, that you can use the same method to search for a slice in an array:

   #(1 2 3 4 5 6 7 8 9 8 7 6 5 1 2 3 2 1) indexOfSubCollection:#(9 8 7)

using the same code.

To find all such methods in the class browser, search for implementors of "*indexOf*", and look at the matches in Collection, SequenceableCollection, CharacterArray and String.

If you don't need the Index[Bearbeiten]

If you only need to check if a character or substring is present, but not its position, you can use "includes:", "includesString:", "includesString:caseSensitive:", "includesSubCollection:", "includesAny:" or "includesAll:" in a similar spirit. These may be slightly faster and make the code more readable. They return a boolean.

Caseless Searches[Bearbeiten]

For strings, special substring searches are available, which ignore case (upper/lower) differences:

   aString includesString: anotherString caseSensitive: false
   aString indexOfString: anotherString caseSensitive: false

Prefix and Suffix Checks[Bearbeiten]

   aString startsWith: anotherString 
   aString endsWith: anotherString

are obvious. The powerful:

   aString startsWithAnyOf: aCollectionOfStrings

searches for multiple string prefixes. For example,

   'hello' startsWithAnyOf: #( 'ab' 'ce' 'de' )

would look for all of them and return false in this concrete example. Of course, there is a corresponding "endsWithAnyOf:".

Splitting Parts of a String (or Collection)[Bearbeiten]

The simplest are:

   aString asCollectionOfWords
   aString asCollectionOfLines

which split at separators and line-ends respectively and return a collection as they say in their name.

More generic is:

   aString js_split: charOrString

which returns a collection of substrings split at "charOrString" elements. as the name suggests, you can pass either a single character or a substring as argument. Thus:

   'hello world isn't this nice' js_split:(Character space)

will give you the strings ('hello' 'world' 'isnt' 'this' 'nice'). Whereas:

   'some : string : separated : by : space : colon space' js_split:' : '

will return a collection containing: ('some' 'string' 'separated' 'by' 'space' 'colon' 'space').

Notice, that we used "js_split:" in the above examples, which is the method used by JavaScript's "split()" function. This is because in Smalltalk, the "split:" method has a different semantics: the receiver is a "splitter", and the argument is splitted by it.
In Smalltalk, the splitter can be both a simple string or a regular expression. For example:

   '[a-z]*' asRegex split:'123abc456def789'

will generate the collection: #('123' '456' '789').


To split into same-sized pieces, use:

   aString splitForSize: charCount

this may be less of interest for strings, but is handy to split fixed records of binary data (remember: these functions are implemented in a superclass of String, from which ByteArray also inherits).

Joining and Concatenating Strings[Bearbeiten]

The simplest of them is the "," (comma) operator. This takes two collections (and as such also Strings) and creates a new collection containing the concatenation of them. Thus:

   'hello' , 'world'

will create a new string containing 'helloworld', and:

   'hello' , ' ' , 'world'

will give you 'hello world',
and:

   #(1 2 3) , #(4 5 6)

will generate a 6-element array containing the elements 1 to 6.

Notice that concatenation may become somewhat inefficient, if many strings are concatenated, because many temporary string objects are created. Its time complexity is O(n2), where n is the number of strings concatenated.

If you have to construct a string from many substrings, either use one of the join functions below (with O(n) complexity), or a writeStream with O(n log n) complexity. Both handle the reallocations much more efficiently (actually: avoiding most of them).


To join a set of strings, use:

   asCollectionOfStrings asStringWith: separator

where separator may be a character, a separator string or nil. For example:

   #( 'a' 'b' 'c') asStringWith:nil

gives 'abc', and:

   #( 'a' 'b' 'c') asStringWith:' : '

returns: 'a : b : c'

Using a writeStream, write:

   |w result|

   w := WriteStream on:(String new:10).
   w nextPutAll: 'a'.
   w nextPutAll: 'b'.
   ...
   result := w contents

or, using a convenient utility method (which does exactly the same):

   result := String streamContents:[:s |
       s nextPutAll: 'hello'.
       s nextPutAll: 'world'.
       ...
       s nextPutAll: 'Nice world'
   ].

but please read more on streams in the stream chapter below.

Formatting Strings (Method A)[Bearbeiten]

For text messages, you may want to construct a pretty user readable string which contains printed representations of other objects. For this, use:

   formatString bindWith: argument
 
   formatString bindWith: argument1 with: argument2
 
   formatString bindWith: argument1 with: ... with: argumentN (up to 5 arguments)
 
   formatString bindWithArguments: argumentCollection (any number of arguments)

using the "{ .. }" array constructor (notice the periods as expression separators), this is usually written as:

   formatString bindWithArguments: { argument1 . argument2 . ... . argumentN }

The formatString itself may contain "%X" placeholders, which are replaced by corresponding printed representations of the argumentX. The arguments will be converted to strings if they are not, thus arbitrary objects can be given as argument (although, in practice, these are usually numbers).

If many arguments are to be passed in, the placeholder names X may consist of a single digit (1-9) only for the first 9 arguments. For other arguments, you must write "%(X)". I.e. '%(10)' to get the tenth argument sliced at that position (1).

The last of the above methods is a powerful tool, in that it does not only handle argumentCollections indexed by numbers (i.e. Array of arguments), but also Dictionaries, which are indexed by name.

For example, if you have a Dictionary collection of a person record (in expecco: a compound datatype instance), with element keys "firstName", "lastName" and "city", you can generate a nice printed string with:

   |record|

   record := Dictionary new.  "/ example using a dictionary
   record at:'firstName' put: 'Fritz'.
   record at:'lastName' put: 'Müller'.
   record at:'city' put: 'Stuttgart'.

   '%(lastName), %(firstName) lives in %(city)' bindWithArguments: record.

gives you the string: 'Müller, Fritz lives in Stuttgart'.
In combination with a expecco compound datatype instance, this would be:

   |person|

   person := myType new.
   person firstName:'Fritz'.
   person lastName:'Müller'.
   person city:'Stuttgart'.
 
   '%(lastName), %(firstName) lives in %(city)' bindWithArguments: person.

1) Note: this has been relaxed in 19.2, where %10 is allowed. However, all digits up to the first non-digit character will make up the index. If you need a sliced in value followed by a digit, you'd still have to use %(x).

Formatting Strings with Printf (Method B)[Bearbeiten]

There is also a formatting method similar to "printf". It takes a format string and a number of arguments and constructs a string representation. As Smalltalk does not support variable numbers of arguments, these must be passed with different keyword messages:

   formatString printfWith:arg

   formatString printfWith:arg1 with:arg2

   formatString printfWith:arg1 with:arg2 with:arg3
   ... up to 5 arguments

   formatString printf:argVector

For the argVector, use the "{" .. "}" construct.

For example,

   '%05d 0x%-7x %f' printf:{ 123 . 517 . 1.234 }

generates the string:

   '00123 0x205     1.234'

The above provides the functionality of C's "sprintf" - i.e. it generates a string as result. However, streams also understand printf-messages, so you can also print directly to a stream:

   Transcript printf:'%05x\n' with:12345

or:

   Transcript printf:'%05x %d %f %o\n' withAll:{ 123. 234*5. 1.234. 254 }

Notice, that printf translates the standard C-character escapes "\n", ”\t" etc.

Formatting Strings with Embedded Expressions (Method C)[Bearbeiten]

A string constant prefixed with "e" is a so called expression-string. This may contain Smalltalk expressions enclosed in "{" .. "}", which are sliced into the string (actually, the expression's printString is sliced in). In addition, C-style escape sequences like "\n" are recognized in e-strings.

   Transcript show: e'today is {Date today} and the time is {Time now}\nAnd the dayName is {Date today dayName}'.

Regex and Glob Matching[Bearbeiten]

Both match a string against a match pattern, but the syntax and functionality is different.

  • Glob is much easier to use but less powerful. Glob is the match algorithm used in the Unix and MS-DOS filesystem (e.g. when saying "ls *.foo" or "dir *.foo" on the command line).
  • Regex is the algorithm used in tools like "grep" and many text processing systems and languages.

Notice that regex patterns are different and not just a superset of glob patterns. Especially the meanings of "*" and "." are different.
Please consult Wikipedia for more information on Regex [3] and GLOB [4]. A chapter on regex is also found in this wiki.

For a Glob match, use:

   aGlobPattern match:aString

   aGlobPattern match:aString caseSensitive:false

or, reversed argument order, if you prefer:

   aString matches:aGlobPattern

   aString matches:aGlobPattern caseSensitive:false

For a regex match, use:

   aString matchesRegex:aRegexPattern

   aString matchesRegex:aRegexPattern caseSensitive:false

   aString matchesRegex:aRegexPattern ignoringCase:true  "/ an alias to the above

Sorry for the inconsistent method naming which is due to the fact that the regex matcher originated in a public domain package, which was developed independently from the original ST/X system. In order to remain compatible with other Smalltalk dialects, the names were kept.

The CharacterArray class provides a whole bunch of useful match methods (eg. to extract multiple occurrences of a pattern, to do prefix matches and to extract sub patterns from a complex match pattern). Please use the class browser (from the "Extras" → "Tools" menu) to find them.

Code Converting[Bearbeiten]

There are code converters for various character encodings in and below the CharacterEncoder class. Most of them are obsolete or seldom used these days, as now most systems support Unicode (which was not the case, the ST/X was written 30 years ago and switched to Unicode in the 90's!).

In general, you can get an instance of an encoder via a convenient CharacterEncoder interface:

   CharacterEncoder encoderFor:<encoding>

or:

   CharacterEncoder encoderToEncodeFrom:<encoding1> to:<encoding2>

where encoding is a name, such as 'iso8859-1', 'unicode', 'utf8', 'utf16', 'ascii', 'jis7' etc.

Thus, to encode a string from Unicode (which is the internal encoding anyway) to Japanese JIS7, use:

   (CharacterEncoder encode:#'jis') encode:'hello')

or to encode from Microsoft cp1253 to koi8-r, use:

   (CharacterEncoder encoderToEncodeFrom:#'cp1253' to:#'koi8-r') encode:<someStringInCP1253>

You will probably not need that general interface, but use UTF-8 these days. For those, the String class provides easier to use interface:

   aString utf8Encoded

   aString utf8Decoded

Collection Slices[Bearbeiten]

A slice is a reference to a subcollection, which looks and behaves like any other collection, but shares the underlying data with the original collection. Thus modifications in either the original or slice are visible in the other and vice versa.

To get a slice, use one of the "from:", "from:to:", "to:" and "from:count:" methods (the names are similar to the above mentioned "copy*" methods).

For example:

   data := ... some big ByteArray read from a file ...

   record1 := data from:1 to:recordSize.
   record2 := data from:(recordSize+1) to:recordSize*2.
   etc.

gives you the records from the big collection as individual objects. If any modification is made to any of the slices, that modification is actually made in the underlying data collection (i.e. C-Programmers may think of this as a "pointer into the data object").

Slices are especially useful with bulk data processing, to avoid the creation of individual record copies.

However, they are a bit dangerous, as the programmer has to be always aware of possible side effects. If you are uncertain, do not pass a slice to another (unknown) function; instead use the corresponding copy methods.

Stream Handling[Bearbeiten]

Streams can be both internal streams (streaming out-of or into a collection) or external streams. External streams are always byte- or character-oriented and operate on files, sockets, pipes or i/o devices. An internal stream's element type depends on the underlying collection.

The following presents only a very small excerpt of the full stream protocol. Please refer to the online documentation of the Smalltalk/X stream classes.

Creating (Internal Streams)[Bearbeiten]

streams for reading:

rs := ReadStream on:aCollection.

rs := aCollection readStream

streams for writing:

ws := WriteStream on:(String new:initialSize)

ws := '' writeStream

ws := WriteStream on:(Array new)

notice that the underlying collection is reallocated when more elements are added due to nextPut: operations. The reallocations are done by doubling the size, resulting in O(n log n) complexity.

Checking[Bearbeiten]

s atEnd

returns true iff the read stream "s" is positioned at the end.

s size

returns the size of the read stream's buffer, or the number of elements written into a write stream.

Positioning[Bearbeiten]

s position

returns the stream's current read position, starting at 0 when at the beginning. I.e. position represents the number of elements which have already been read or written.

s position:pos

set the position.

s rewind

reset the position to the beginning

s setToEnd

set the position to the end (normally only useful for appending write streams)

s backStep

position one element backwards

s skip:n

skip n elements when reading

Reading[Bearbeiten]

rs next

retrieve the next element from the stream. Nil if there are no more elements.

rs next:count

retrieve the next "n" elements from the stream.

rs nextAvailable:count

retrieve the next "n" elements or whatever number of elements are available.

rs upTo:element

retrieve elements up to an element which is equal to "element"

rs skipTo:element

skip over elements up to an element which is equal to "element"

rs through:element

retrieve elements up-to and including an element which is equal to "element"

Writing[Bearbeiten]

ws nextPut:element

append element to the stream.

ws next:count put:element

append element multiple times to the stream.

ws nextPutAll:aCollectionOfElements

append all elements from the given collection to the stream.

ws println

append a newline character.

ws cr

also

ws nextPutLine:aCollectionOfElements

append all elements from the given collection followed by a newline to the stream.

ws print:anObject

append a printed representation of anObject (for humans) to the stream.

ws store:anObject

append the printed representation of anObject (for the system) to the stream. Unless the object has recursive references, a copy of the element can be reconstructed with Object readFrom:aReadStream

ws println:anObject

append a printed representation of anObject (for humans) to the stream, followed by a newline.

ws printCR:anObject

the same

ws contents

retrieve the stream's contents (typically, the characters buffered so far)

File Streams[Bearbeiten]

aString asFilename readStream

aString asFilename writeStream

See more below in the chapter on file operations.

Numbers[Bearbeiten]

Reading Numbers from a String[Bearbeiten]

To read a number from a string, use the "readFrom:" method provided by the number classes, where the argument is either a string, or a headstream. A readstream is obtained with either "(ReadStream on:aString)" or the more convenient "(aString readStream)".

Thus, in general, code to read a number looks like:

   <class> readFrom:aString

or:

   <class> readFrom:(ReadStream on:aString)

where "<class>" is one of "Integer", "Float", "FixedPoint" or "Number". If "<class>" is "Number", any type of number will be read and returned. Otherwise, only that type of number will be accepted (i.e. "Integer readFrom:String", will report an error if there is a decimal point in the string.

If the string contains nothing but the number string, the above two code expressions behave the same. However, there is an often overlooked difference in case of extra characters after the number:

  • the string-reader expects that the given string contains a representation of a corresponding number object, and NO extra characters before or after it. It will report an error otherwise.
  • in contrast, the stream reader will first skip any spaces, then read as many characters as required from the stream, return the number and leave the stream positioned after the number. It will report an error if no number can be read. More values can be read from the stream if required.

Thus both:

   Integer readFrom:'1234'

and:

   Integer readFrom:(ReadStream on:'1234').

return the Integer object 1234.

Whereas:

   Integer readFrom:'1234bla'

will raise an error, but:

   myStream := ReadStream on:'1234bla'.
   Integer readFrom: myStream.

returns the integer 1234 and leave the stream positioned on the 'bla'. Thus further elements can be read from the stream later.

Notice that "Integer readFrom:" will read an integer, but not a float. Thus:

   Integer readFrom:'1234.5'

will raise an error, but:

   Integer readFrom:(ReadStream on:'1234.5').

will return the Integer object 1234 and leave the stream positioned on the decimal point character.

When expecting an arbitrary number (e.g. with or without decimal point), use:

   Number readFrom: aStringOrStream

which will return either an integer object or a float object depending on what it gets.

If you want to enforce getting a float, use

   Float readFrom: aStringOrStream

which returns the float "1234.0", even when given the string "1234".

Error handling[Bearbeiten]

By default, the readers raise an exception, if any conversion error occurs. This can be caught in an error handler as described elsewhere (hint: catch the error with "on:do:").

However, the conversion routines can also be given a correction function as argument, which is invoked in case of an error, and which provides a replacement value. This is easier and shorter to write then an exception handler.

For this, all of the methods described are also present in a variant with an extra "onError:" argument, which provides that replacement.

For example:

   Integer readFrom:aString onError:[ 0 ]

will return the number as usual if OK, but zero if not.
Of course, arbitrary code can be provided inside this handler - especially it may show an error dialog and ask the user for a replacement, or return from the executed elementary block:

   val := Integer
           readFrom:aString
           onError:[
               |ersatz|
               ersatz := Dialog request:'Please enter a correct number'.
               Integer readFrom:ersatz
           ]
Reading with a Different Decimal Point Character[Bearbeiten]

If the number was originally for a european, it may contain a decimal point different from "." - for example, in Germany you may encouter a monetary amount as "1245,99".

For this, use:

   Float readFrom:aStringOrStream decimalPointCharacters:','

or (better):

   FixedPoint readFrom:aStringOrStream decimalPointCharacters:','

If your code has to deal with both US and German numbers, provide all possible decimal points in the string, as in:

   FixedPoint readFrom:aStringOrStream decimalPointCharacters:',.'
Reading Multiple Numbers from a Stream[Bearbeiten]

A common task is to read multiple numbers from a single long string. Of course, you could first split the string into pieces and use the above "readFrom:" on each.

This may also be a quick&dirty solution, if the individual number strings are contained in fixed-length fields and there are no separators in between the fields (but read below for a better way to do this). Take a look at the string handling examples on how to split strings.

To read multiple numbers, the best solution is to first create a read stream on the string, then read the individual numbers:

   myString := '1234 456.7 0.5 2345.3456 3234234234234234234234234234234234234'.
   myStream := myString readStream.
   n1 := Number readFrom: myStream.      "/ gives the integer 1234 in n1
   n2 := Number readFrom: myStream.      "/ gives the float 456.7 in n2
   n3 := Number readFrom: myStream.      "/ gives the float 0.5 in n3
   n4 := FixedPoint readFrom: myStream.  "/ gives the fixedPoint 2345.3456 in n4
   n5 := Number readFrom: myStream.      "/ gives the large integer 3234...34 in n5

Notice that "readFrom:" first skips any separators (spaces) - therefore no extra code is needed to deal with those.

If extra stuff needs to be skipped, use any of the existing stream functions (skipFor:/skipUntil: etc.). If none does the job, you can look at individual characters and skip until a match is found. For example, to read 3 numbers from the following string '123 bla 456.8|0.5', you could use:

   s := '123 bla 456.8|0.5' readStream.
   n1 := Integer readFrom:s.
   s skipSeparators.
   s nextAlphanumericWord.  "/ skips over the 'bla'
   n2 := Number readFrom:s.
   s next.                  "/ skips over one character
   n3 := Number readFrom:s.

of course, the above code would not handle strings like '123 bla bla 456.8|0.5' or '123 bla 456.8 |0.5'.
Using "peek", which looks at the next character in the stream without consuming it, we could to change the code to:

   n1 := Integer readFrom:s.
   [ s peek isDigit ] whileFalse:[ s next ]. "/ skips over the 'bla bla'
   n2 := Number readFrom:s.
   s next.                                   "/ skips over one character
   n3 := Number readFrom:s.

Notice that "peek" returns nil, if the end of the stream is reached. And that "nil isDigit" will report an error (because only characters can be asked for being a digit). Thus, the code like the above is not prepared to read a variable number of numbers.

Reading with Scanf[Bearbeiten]

There is also a C-like scanf utility, which can read numbers, strings, characters in various formats.

   myString := '1234 456.7 0.5 2345.3456 3234234234234234234234234234234234234'.
   values := '%d %f %f %f %d' scanf: myString.

generates a collection of numbers in "values":

   OrderedCollection(1234 456.7 0.5 2345.3456 3234234234234234234234234234234234234)

The details and format characters are described in the PrintfScanf utility class, here is a short summary:

Conversions (upper case same as lower case):

  • 'b' binary (base 2)
  • 'c' character or (first char of string)
  • 'd' decimal
  • 'e' float
  • 'f' float
  • 'g' float
  • 'i' integer (alias for 'd')
  • 'o' base-8 octal
  • 's' string
  • 'u' integer
  • 'x' base-16 hex

Length prefix:

  • 'h' with float formats: reads as ShortFloat
  • 'L' with float formats: reads as LongFloat

Examples:

  • '%d %x' scanf:'1234 ff00' -> OrderedCollection(1234 65280)
  • '%d %s' scanf:'1234 ff00' -> OrderedCollection(1234 'ff00')
  • '%d %x %b' scanf:'1234 ff00 1001' -> OrderedCollection(1234 65280 9)

Reading a Particular Number of Numbers from a Stream[Bearbeiten]

Assume that you are given a number which defines how many numbers to extract from a given string.

Of course, you can use a "start to: stop do:[:idx |..." loop, but experienced Smalltalk programmers make use of the collection protocol, as in:

   (1 to: count) collect:[:idx | Number readFrom:aStream ]

Such a construct is needed e.g. when you have to parse a string, where the first entry defines how many numbers follow:

   coll := (1 to: (Integer readFrom:myString)) collect:[:idx | Number readFrom:aStream ].

which would parse the string "5 1.0 3.0 2.0 -1.0 .05" into a 5-element collection containing the converted numbers.

Reading a Variable Number of Numbers from a Stream[Bearbeiten]

To do so, we should collect numbers as being read from the stream in a collection:

   collectedNumbers := OrderedCollection new.

   [ aStream atEnd ] whileFalse:[
       collectedNumbers add: ( Number readFrom:aStream ).
   ]

or, if any non-digits and non-spaces between numbers should be skipped, use:

   collectedNumbers := OrderedCollection new.

   [ aStream atEnd ] whileFalse:[
       aStream skipUntil:[:char | char isDigit].
       collectedNumbers add:( Number readFrom:aStream ).
   ]

Using the Pattern Matcher to Extract Parts[Bearbeiten]

In rare situations, complex patterns need to be matched and numeric values retrieved from the matched parts. For this, use a regex pattern matcher to first extract parts from the string, and then convert the substrings. For example, to fetch numbers after certain keywords in a string, you could use:

   "/ assuming that the string is of the form:
   "/    <..arbitrary-text...>key1<number1><...arbitrary text...>key2<number><...arbitrary text...>
   "/ where arbitrary text may even contain key1 and key2, but not followed by a number,
   "/ we first use a regex to match, then convert the matched regex parts:

   myString := 'bla bla key1 bla key11234more bla key2 bla key29876bla bla'.

   parts := myString allRegexMatches:'((key[12]))([0-9]+)'.
   "/ now, parts contains #( 'key11234' 'key29876'
   numbers := parts collect:[:eachPart | Number readFrom: (eachPart copyFrom:5) ].
   "/ now, numbers contains #( 1234 9876 )

notice that parts delivers the matched strings i.e. the collection ('key11234' 'key29876'), so that we have to skip over the 'keyX' prefix before extracting the numbers.

File Operations[Bearbeiten]

Expecco (i.e. Smalltalk) represents file names as instances of a specialized class called "Filename". These look similar to strings, but provide an abstraction over details of the underlying file- and operating system.

This makes it possible to write portable code which runs on Windows, Linux and even VMS, even though these systems use very different filename separators and volume naming schemes.

Thus, we recommend to use filename operations instead of string concatenation, e.g. to construct pathnames. In Smalltalk, the abstract class "Filename" is subclassed by concrete classes named "PCFilename" or "UnixFilename". You should not access those concrete classes explicitly by name! Instead, always refer to "Filename" and let Smalltalk decide, which concrete class to use. (unless you know that you are dealing with a unix-style filename, independent of you operating system, which is e.g. the case if you deal with CVS names or you get file-URLS as strings).

for now, a short summary:

   aString asFilename  - to get a Filename-instance for a given String instance 

   aFilename readStream - to get a readStream on the contents of a file

   aFilename writeStream - to get a writeStream writing to a file

   aFilename appendingWriteStream - to get a writeStream appending to the end of a file

Checking[Bearbeiten]

   aFilename exists - true if the file exists

   aFilename isReadable - true if it is readable

   aFilename isWritable

   aFilename isExecutable - for folders, this means: "can be changed into"

   aFilename isExecutableProgram

   aFilename fileSize

   aFilename isDirectory

   aFilename isNonEmptyDirectory

   aFilename isRegularFile

   aFilename isSymbolicLink

   aFilename info - gets all info; follows any symbolic link. Includes ownerID, groupID, access and modification times etc.

   aFilename linkInfo - gets all info of a symbolic link itself. Includes ownerID, groupID, access and modification times etc.

   aFilename accessTime

   aFilename modificationTime

   aFilename creationTime (same as modificationTime on Unix systems)

   aFilename fileType

   aFilename mimeType

File names[Bearbeiten]

   aFilename pathName

   aFilename directory - the containing directory, as a Filename instance

   aFilename directoryName - ditto, as string

   aFilename baseName

   aFilename suffix

   aFilename hasSuffix:aString

   aFilename isAbsolute

   aFilename isRelative

   aFilename / subPath - yes, "/" is an operator to construct the name of a file inside a folder

alternative:

   aFilename construct:subPath

Operations[Bearbeiten]

   aFilename makeDirectory

   aFilename recursiveMakeDirectory - ensures that all folders along the path are also created.

   aFilename removeDirectory - raises an error, if the folder is not empty

   aFilename recursiveRemoveDirectory - removes everything below also

   aFilename moveTo:newName

   aFilename copyTo:newName

   aFilename recursiveCopyTo:destinationName

Directory Contents[Bearbeiten]

   aFilename directoryContents

   aFilename directoryContentsDo:[:eachFile | ...]

   aFilename filesMatching:aGLOBPatternString

File Contents[Bearbeiten]

   aFilename contents

retrieves the contents as a collection of line strings. Be careful - this should not be used for huge files.

   aFilename contentsAsString

retrieves the contents as one (possibly big) string. Be careful - this should not be used for huge files.

   aFilename binaryContentsOfEntireFile

retrieves the contents as one (possibly big) byte array. Be careful - this should not be used for huge files.

   aFilename readingLinesDo:[:eachLineString | ... ]

better use this for large files. Ensures that the file is closed.

   aFilename readingFileDo:[:stream | ... ]

or that to read inside the code block. Ensures that the file is closed.

Shared Data and Synchronization[Bearbeiten]

A number of mechanisms for synchronisation and mutual access to shared data structures are available: Semaphore, RecursionLock, Monitor, SharedQueue, SharedCollection and the synchronized: method, which is understood by every object. For details, please refer to the Smalltalk/X Online Documentation.

Examples[Bearbeiten]

The following code examples contain versions for Smalltalk, JavaScript, Groovy and Node.js. Notice that Groovy and Node.js actions are invoked via a remote procedure call (RPC) mechanism, which means that they are much slower due to the communication, encoding, decoding and general round trip delays. Therefore, Groovy and Node.js blocks (bridged actions in general) should only be used to access data inside the system under test or to call functions which are not available in the base system (such as additional protocols or hardware access, for which only a JAR or node- or python package is available). Or, functions for which the call overhead is small compared to the runtime (i.e. for heavy computing tasks, it may very well make sense).

Thus, most of the examples below are for demonstration purposes, not for real world scenarios (i.e. it will certainly slow down your test runs, if you do math via Groovy or even Node.js, as shown below).

Also bear in mind, that the underlying numeric representations are less flexible and more error-prone if you do math outside Smalltalk: in Groovy, because there will be no signalling of overflow/underflow situations. Especially with integer arithmetic, which may be outside the 32bit/64bit range. Also, Java has no exact fraction representations, which means that you may loose precision in floating point operations.

Things are even worse in Node.js, which represents all numbers as double precision floats, giving roughly 53 bits of precision. Thus, in Node.js the two integers 12345678901234567890 and 12345678901234567891 will be reported as being equal! (this may change in a future Node version, though).

Point right.png Do NOT use Groovy or Node.js for numeric computations, if large integer values are involved (or use a Bignum package).

Reading/Writing Pin Values[Bearbeiten]

Assuming that the block has two input pins, named "in1" and "in2" and two output pins, named "out1" and "out2", all of String type, the following blocks write concatenated strings to both outputs:

JavaScript[Bearbeiten]

(runs inside expecco when executed)

execute() {
   out1.value( in1.value() + in2.value() );
   out2.value( in2.value() + in1.value() );
}
Smalltalk[Bearbeiten]

(runs inside expecco when executed)

execute
   out1 value:(in1 value , in2 value).
   out2 value:(in2 value , in1 value).
Groovy[Bearbeiten]

(runs inside the JVM, which may be the system under test or another machine running Java)

def execute() {
   out1.value( in1.value() + in2.value() );
   out2.value( in2.value() + in1.value() );
}
Node[Bearbeiten]

(runs inside a node VM, which may be the system under test or another machine running "node")

function execute() {
   out1.value( in1.value() + in2.value() );
   out2.value( in2.value() + in1.value() );
}
Python[Bearbeiten]

(runs inside a Python interpreter, which may be the system under test or another machine running "python")

def execute():
   out1.value( in1.value() + in2.value() )
   out2.value( in2.value() + in1.value() )
C[Bearbeiten]
static void execute() {
   char* s1 = stringValue(in1);
   char* s2 = stringValue(in2);
   char buffer[512];

   snprintf(buffer, sizeof(buffer), "%s%s", s1, s2);
   putString(out1, buffer);

   snprintf(buffer, sizeof(buffer), "%s%s", s2, s1);
   putString(out1, buffer);
}

Note: be aware, that due to the Remote Procedure Call (RPC), the calling overhead of bridged actions is in the order of milliseconds, whereas actions executed inside expecco (Smalltalk and JavaScript) are executed typically within a few hundred nanoseconds.

Thus, bridged actions are typically used to invoke longer running, complex operations or to trigger services, which run autonomous, or to talk to interfaces/devices. They are normally not be used for trivial operations like string manipulation or arithmetic.

Reading/Writing Environment Variables[Bearbeiten]

Assuming that a String-typed environment variable named "IncDec" and an Integer-typed variable named "Counter" exist in the project's environment, and are writable, the following blocks read and write to either variable:

JavaScript[Bearbeiten]

(runs inside expecco, when executed)

execute() {
   if ( environmentAt("IncDec") == "inc" ) {
       environmentAt_put("Counter", environmentAt("Counter") + 1);
   } else {
       environmentAt_put("Counter", environmentAt("Counter") - 1);
   }
}
Smalltalk[Bearbeiten]

(runs inside expecco, when executed)

execute
   (self environmentAt:'IncDec') = 'inc' ifTrue:[
       self environmentAt:'Counter' put:(self environmentAt:'Counter')+1.
   ] ifFalse:[
       self environmentAt:'Counter' put:(self environmentAt:'Counter')-1.
   ]
Groovy[Bearbeiten]

(runs inside the JVM, which may be the system under test or another machine running Java). Notice that the environment-access from inside Groovy involves even more overhead, as those require another RPC call from Groovy back to expecco. When executed, the code below will make 4 full remote-procedure call roundtrips (1 for the call and return value, one for "environmentAt(IncDec)", one for "environmentAt(Counter)" and a fourth one for "environmentAt_put()").

def execute() {
   if ( environmentAt("IncDec") == "inc" ) {
       environmentAt_put("Counter", environmentAt("Counter") + 1);
   } else {
       environmentAt_put("Counter", environmentAt("Counter") - 1);
   }
}

Sending Messages to the Transcript[Bearbeiten]

The Transcript refers to the expecco console, if it has been opened (via the "Extras" → "Tools" menu). If it is not open, "Trnscript" will refer to the standard error stream (Stderr). In place of "Transcript", the following examples also work for "Stdout" and "Stderr".

JavaScript[Bearbeiten]

(runs inside expecco, when executed)

execute() {
   Transcript.showCR("---------------------");
   for (var y=1; y<=10; y++) {
       for (var x=1; x<=10; x++) {
           Transcript.show( x * y );
           Transcript.show( " " );
       }
       Transcript.cr();
   }
}
Smalltalk[Bearbeiten]

(runs inside expecco, when executed)

execute
   Transcript showCR:'---------------------'.
   1 to:10 do:[:y |
       1 to:10 do:[:x |
           Transcript show:(x * y); show:' '.
       ].
       Transcript cr.
   ].
Groovy[Bearbeiten]

A few of the common messages and objects for logging and printing are also made available to Groovy code. Of course, a hidden inter-process mechanism is used, which forwards those calls back to expecco (remember: the Groovy code runs inside the system under test). The Transcript object seen by Groovy code is such a proxy object which implements a subset of the Smalltalk Transcript object but passes the argument strings via IPC back to expecco. So although the code looks similar to the above, the internal implementation (and timing) is completely different, because these calls are implemented as RPC calls from Groovy back to expecco.

(runs inside JavaVM when executed)

def execute() {
   Transcript.showCR("---------------------");
   for (int y=1; y<=10; y++) {
       for (int x=1; x<=10; x++) {
           Transcript.show( x * y );
           Transcript.show( " " );
       }
       Transcript.cr();
   }
}

For compatibility with existing Java/Groovy code, the functions print() and println() can also be used (in addition to the above used show(), showCR() and cr() functions)

Node[Bearbeiten]

A subset of the logging and printing functions are available to Node code. Of course, a hidden inter-process mechanism is used, which forwards those calls back to expecco (remember: the code runs inside the system under test). The Transcript object seen by Node code is such a proxy object which implements a subset of the Smalltalk Transcript object, but passes the argument strings via IPC back to expecco.

So although the code looks similar to the above, the internal implementation (and timing) is completely different, because these calls are implemented as RPC calls from Node back to expecco.

(runs inside Node when executed)

function execute() {
   Transcript.showCR("---------------------");
   Transcript.show( "hello " );
   Transcript.show( "world" );
   Transcript.cr();
   Transcript.show( "A message with embedded args: %1 and %2\n", "foo", 123);
}

For compatibility with existing Node programs, you can also use "console.log()" and "console.error()".

Square Root Block[Bearbeiten]

A function, which computes the square root of its input value, could be implemented like that: (the names of the pins are: in and out, their data type is Number):

JavaScript[Bearbeiten]

(runs inside expecco, when executed)

execute() {
   var inValue;

   inValue = in.value;          // Reading input pin value
   out.value( inValue.sqrt() ); // Writing to output pin
}

alternative:

execute() {
   var inValue;

   inValue = in.value;              // Reading input pin value
   out.value( Math.sqrt(inValue) ); // Writing to output pin
}
Smalltalk[Bearbeiten]

(runs inside expecco, when executed)

execute
   |inValue|

   inValue := in value.       "/ Reading input pin value
   out value: inValue sqrt.   "/ Writing to output pin
Groovy[Bearbeiten]

(runs inside a JVM/the SUT, when executed).
As mentioned above, this kind of operation should definitely be executed in Smalltalk/JavaScript inside expecco, and not in the system under test, due to the RPC overheads.

def execute {
   Object inValue;

   inValue = inPin.value();           // Reading input pin value
   outPin.value(Math.sqrt(inValue));   // Writing to output pin
}

(Notice: "in" is a reserved keyword in Groovy and cannot be used as pin name)

Node[Bearbeiten]

(runs inside Node, when executed).

function execute {
   outPin.value(Math.sqrt(inPin.value()));  
}

(Notice: "in" is a reserved keyword in Node and cannot be used as pin name)

Random-Fail Block[Bearbeiten]

A block, which randomly fails (to demonstrate exception handling), could be implemented like:

JavaScript[Bearbeiten]

(runs inside expecco, when executed)

execute() {
   var dice;

   dice = Random.nextIntegerBetween_and_(1,6);
   if (dice <= 2) {
       fail();
   }
}
Smalltalk[Bearbeiten]

(runs inside expecco, when executed)

execute
   |dice|

   dice := Random nextIntegerBetween:1 and:6.
   (dice <= 2) ifTrue:[
       self fail.
   ].
Groovy[Bearbeiten]

(runs inside a JVM/the SUT, when executed)

execute() {
   Random rand = new Random();
   int dice = rand.nextInt(5)+1;

   if (dice <= 2) {
       fail();
   }
}

Calling other Actions[Bearbeiten]

Script code can call other action blocks via the "call" method/function. Notice, that the call is "by name" or "by ID", and the called function is specified by a string argument. The following examples assume that action blocks named "Action_A" and "Action_B" are present, and that "Action_B" expects two numeric arguments and returns a value through its single output pin.

JavaScript[Bearbeiten]

(runs inside expecco, when executed)

execute() {
   var result;

   call("Action_A");
   result = call("Action_B", 123.0, 9999); 
   Transcript.showCR("got result: %1", result);
}
Smalltalk[Bearbeiten]

(runs inside expecco, when executed)

execute
   |result|

   self call:'Action_A'.
   result := self call:'Action_B' _:123.0 _:9999). 
   Transcript showCR:('got result: %1' bindWith:result).
Groovy[Bearbeiten]

runs inside a JVM/the SUT, when executed, but the called actions are expecco actions; i.e. the call performs a remote-procedure-call back to expecco, to execute the called action, and continue in the JVM's action when finished.

execute() {
   var result;

   call("Action_A");
   result = call("Action_B", 123.0, 9999); 
   Transcript.showCR("got result: %1", result);
}
Node[Bearbeiten]

runs inside the node interpreter, when executed. The call performs a remote-procedure-call back to expecco, to execute the called action, and continue in the node action when finished.

Notice, that due to the single threaded callback oriented nature of node, the value from the called function is passed back via a callback function, not as return value. This is required because the "call" is actually performing a remote procedure call into expecco (i.e. interprocess communication). It is also currently not possible to call another node action from a node action (neither directly, nor indirectly).

function execute() {
   var result;
   
   Transcript.showCR("calling from Node...");
   call("Action_A", function(err, result) {
       call("Action_B", 100, 200, function(err, result) {
           Transcript.showCR("got value: %1", result);
           success();
       });
   });
}

Using the Smalltalk/X Class Library (Smalltalk and JavaScript Blocks Only)[Bearbeiten]

Please note, that besides these direct interfaces with the expecco system described here, you can also use the whole class library of the runtime system. Please check the corresponding Smalltalk/X Documentation as well as the Documentation of the Class APIs.

expecco includes a fully featured class browser, to explore the underlying system's code.

You will see the source code, if you have checked the "Install Source Code" checkbox during the installation procedure (if you did not, redo the installation, and only check this box). You can also access the source code via the public eXept CVS repository.

The class browser is opened via the main-menu's "Extras" → "Tools" → "Class Browser" item. It is documented in full detail in the Smalltalk/X Documentation.

Here are a few more examples, using that class library:

Bulk-Data Reading from a File[Bearbeiten]

The following code reads a measurement data block of 100000 floating point values from file. The file was created beforehand by a recorder device, which was triggered by a dll-callout. For the demonstration, the code below does not read its values from input pins. In a real world application, the size of the file and its fileName would probably be passed in via input pins, of course.

JavaScript[Bearbeiten]
execute() {
   var N;        // should be read from an input-pin
   var fileName; // should be read from an input-pin
   var dataArray;
   var fileStream;

   N = 100000;
   fileName = 'dataFile.dat';
   fileStream = fileName.asFilename().readStream();
   dataArray = Array.new(N);
   for (i=1; i<=N; i++) {
       dataArray[i] = fileStream.nextIEEESingle();
   }
   out.value(dataArray);
}
Smalltalk[Bearbeiten]
execute
   |N fileName dataArray fileStream|

   N := 100000.
   fileName := 'dataFile.dat'.
   fileStream := fileName asFilename readStream.
   dataArray := (1 to:N) collect:[:i | fileStream nextIEEESingle].
   out value:dataArray.

an alternative is:

   ...
   dataArray := N timesCollect:[:i | fileStream nextIEEESingle].
   ...

Opening/Using Smalltalk/X Applications, Dialogs and Components[Bearbeiten]

Any existing Smalltalk/X utility (both the one's which are provided with the base system, and even those coming from additional loaded Smalltalk code) can be called and used. For example, the following uses the builtin file dialog to ask for a directory.

JavaScript[Bearbeiten]
execute() {
   var answer;

   answer = Dialog.requestDirectory("Please select an output folder:");
   if ( answer.notEmptyOrNil() ) {
       out.value( answer );
   }
}
Smalltalk[Bearbeiten]
execute
   |answer|

   answer := Dialog requestDirectory: 'Please select an output folder:'.
   answer notEmptyOrNil ifTrue:[
       out value: answer
   ]

Take a look at the "Dialog" class (using "Extras" - "Tools" - "Class Browser"), where you'll find many common standard dialogs, for confirmations, simple questions, file dialogs etc.

Loading Additional Smalltalk/X Code[Bearbeiten]

You can even create whole library or application packages, compile it as separate package, and load it dynamically to be used from within an elementary block. Assuming that a package named "myPackage.dll" (or "myPackage.so" under Unix/Linux) containing an application class named "MyApplication" has been created and is located in the expecco bin folder, the code can be loaded and executed with:

Smalltalk[Bearbeiten]
execute
   |answer|

   Smalltalk loadPackage:'myPackage'. "/ checks if already loaded, to only load the first time called
   MyApplication open.

To create your own applications or addons, download the free Smalltalk/X development IDE for development and package building.

Expecco and Smalltalk API Stability[Bearbeiten]

The Smalltalk/X class library has been around for more than 30 years now, and of course grew over time. We have always tried hard to keep the API backward compatible, in not removing existing methods, even when new methods appeared, which provided a similar or superset functionality.

Thus, code written 30 years ago can still depend on the interface and it is still working unchanged.

We will continue to keep this backward compatibility in the future, to avoid breaking customer code, whenever possible (there were very few exceptions in the past, where obvious bugs had to be fixed, and customer code already depended on the wrong behavior).

That said, we must emphasize on that being only true for the ST/X class libraries themselves, and any officially published expecco APIs from this document.

If you use internal expecco functionality (which you may find when browsing in the class browser), we cannot guarantee backward compatibility in future versions. If you feel a need for using such an interface, please consult/inform eXept to either get help on a cleaner solution, and/or inform us that the API is used and to get your used interface be marked as "stable EXPECCO_API".

Appendix[Bearbeiten]

Smalltalk Language Syntax (BNF)[Bearbeiten]

See also: Smalltalk Short Description , Smalltalk Basics

;; Notice: an elementary Smalltalk action's "execute" method is a unary method (no argument).
;; there can be only one single such method in an elementary action (private functions should be defined as Smalltalk blocks)
;;
;; This is standard Smalltalk language, with Smalltalk/X language extensions
;;
method ::= selectorSpec body

selectorSpec> ::= unarySelectorSpec
                  | binarySelectorSpec
                  | keywordSelectorSpec

unarySelectorSpec ::= symbol

binarySelectorSpec ::= binopSymbol identifier

keywordSelectorSpec ::= ( keywordSymbol identifier )+

body ::= [ "|" localVarList "|" ]  [statements]

localVarList ::= ( identifier )+

statements ::= statement
               | statement "."
               | statement "." statements

statement ::= expression
              | returnStatement

returnStatement ::= "^" expression

expression ::= identifier ":=" expression
               | keywordExpression

keywordExpression ::= binaryExpression
                      | binaryExpression ( keywordSymbolPart binaryExpression )+

binaryExpression ::= unaryExpression
                     | unaryExpression ( binopSymbol unaryExpression )+

unaryExpression ::= primary
                    | primary ( unarySymbol )+

primary ::= identifier
            | literalConstant
            | expandedStringExpression
            | braceArray
            | "(" expression ")"
            | block
            | "self"
            | "super"

identifier ::= ( "_" | "a"-"z" | "A"-"Z")   ( "_" | "a"-"z" | "A"-"Z" | "0"-"9" )*

literalConstant ::= integerConstant
                    | radixIntegerConstant
                    | floatConstant
                    | arrayConstant
                    | byteArrayConstant
                    | stringConstant
                    | characterConstant
                    | "true"
                    | "false"
                    | "nil"

integerConstant ::= ( "0"-"9" )+

radixIntegerConstant ::= base "r" baseDigits+

base ::= integerConstant (valid are: 2-31)

baseDigits := "0"-"9" | "A"-"Z" | "a"-"z" (valid are "0".."0"+base-1 and "a".."a"+base-10-1 and "A".."A"+base-10-1)

floatConstant ::= [ "-" ] [ digits+ ] "." [ digits+ ] [ ("e" | "E" | "d" | "D") [ "-"] digits+ ]

arrayConstant ::= "#(" literalConstant* ")"

byteArrayConstant ::= "#[" integerConstant(0..255)* "]"

stringConstant ::= ' characters...  ' 
                   | cString

characterConstant ::= "$" character
 
block ::= "[" [ blockArgList ] body "]”

blockArgList ::= (identifier":" )*

comment ::= " " " any-character* " " " | eolComment

unarySymbol ::= identifier

binopSymbol ::= ( "+" | "-" | "*" | "/" | "\" | "," | "@" | "%" | "&" | "=" | "<" | ">" | "~" )+

keywordSymbolPart ::= identifier":"

keywordSymbol ::= keywordSymbolPart+

symbolConstant ::= "#"unarySymbol
                   | "#"binarySymbol
                   | "#"keywordSymbol
                   | "#'"any-character*"'"

Smalltalk/X Language Extensions (BNF)[Bearbeiten]

Smalltalk/X provides a few very useful syntax extensions w.r.t. standard Ansi Smalltalk:

eolComment ::= " "/ " any-character-up-to-end-of-line

tokenComment ::= " "<<"TOKEN
                    ...  
                    any number of lines not beginning with TOKEN
                    (where TOKEN is any valid identifier string)
                    ...
                    TOKEN

braceArray ::= "{" [ expression | expression ( "." expression )+ ] "}" (an array with computed elements)

literalObject ::= "#{" ( fieldName: constant )+ "}"  (a constant object with getters for fields)

immediateObject ::= "{" ( fieldName: expression )+ "}" (an immediate object with getters and setters)

cString ::= c'string with C-escapes'

eString ::= e'string with C-escapes and {embedded expressions}'

iString ::= i'string with C-escapes and {embedded expressions} which is also internationalized'

typedArrayLiteral ::= #u8(...)     "/ unsigned byte array constant
                      | #s8(...)   "/ signed byte array constant
                      | #u16(...)  "/ unsigned int16 array constant
                      | #s16(...)  "/ signed int16 array constant
                      | #u32(...)  "/ unsigned int32 array constant
                      | #s32(...)  "/ signed int32 array constant
                      | #u64(...)  "/ unsigned int64 array constant
                      | #s64(...)  "/ signed int64 array constant
                      | #f32(...)  "/ single precision IEEE float array constant
                      | #f64(...)  "/ double precision IEEE float array constant
 
cStyleInt ::= 0xXXX
              | 0oOOO
              | 0bBBB

indexedAccess ::= array-expr "[" index-expr "]"
                  | array-expr "[" index1-expr "][" index2-expr "]"

indexedStore ::= array-expr "[" index-expr "]" := <expr>
                 | array-expr "[" index1-expr "][" index2-expr "]" := <expr>

JavaScript Language Syntax (BNF)[Bearbeiten]

-- incomplete w.r.t. expression syntax -- to be added

;; Notice: an elementary JavaScript action's "execute" function
;; must be a unary function (no argument).
;; 
;; However, it is possible to define additional (private) helper functions.
;; These will only be visible within that single elementary action's code.
;; 
;; Also notice the syntax extensions for character constants and the "return from" statement.
;; 
method ::= functionSpec functionBody

functionSpec ::= ["async"] "function" identifier "("  [argList] ")"

functionBody ::= "{" (statement)+ "}"

statement ::= varDeclaration
                     | ifStatement
                     | whileStatement
                     | doStatement
                     | forStatement
                     | tryCatchStatement
                     | switchStatement
                     | returnStatement
                     | "{" (statement)+ "}"
                     | expression

varDeclaration ::= "var" identifier ( ","  identifier)* ";"

ifStatement ::= "if" "(" expression ")" statement
                        [ "else" statement ]

whileStatement ::= "while" "(" expression ")" statement
 
doStatement ::= "do" statement "while" "(" expression ")"  ";"

forStatement ::= "for" "(" [expression] ";" [expression] ";" [expression] ")" statement

tryCatchStatement ::= "try" statement (catchClause | finallyClause | catchFinallyClause)

catchClause ::= "catch"  "(" identifier [identifier] ")" statement

finallyClause ::= "finally"  statement

catchFinallyClause ::= catchClause finallyClause

switchStatement ::= "switch" "(" expression ")" "{" ("case constantExpression ":" statement)+ [ "default:" statement+ "}"

returnStatement ::= "return" expression [ "from" identifier ]

expression ::= term ( ("+" | "-") term)*
               | "await" expression

term ::= factor ( ("*" | "/" | "%" ) factor)*

factor ::= powExpr ( "**"  powExpr)*

powExpr ::= "typeof" "(" expression ")"
               | "!" factor
               | "-" factor
               | "++" factor
               | "--" factor
               | primary "--"
               | primary "++"

primary ::= identifier
                  | literalConstant
                  | arrayExpression
                  | "(" expression ")"
                  | block
                  | "this"
                  | "super"
                  | "new" classIdentifier [ "(" constant ")" ]
                  | functionDefinition
                  | lambdaFunction

functionDefinition ::= "function" [functionName] "("  [argList] ")" functionBody

lambdaFunction ::= "("  [argList] ")" "=>" functionBody
                  | arg "=>" functionBody
                  | "("  [argList] ")" "=>" expression
                  | arg "=>" expression
 
identifier ::= ( "_" | "a"-"z" | "A"-"Z")   ( "_" | "a"-"z" | "A"-"Z" | "0"-"9" )*

literalConstant ::= integerConstant
                             | radixIntegerConstant
                             | floatConstant
                             | arrayConstant
                             | byteArrayConstant
                             | stringConstant
                             | characterConstant
                             | "true"
                             | "false"
                             | "null"

integerConstant ::= ( "0"-"9" )+

radixIntegerConstant ::= hexIntegerConstant | octalIntegerConstant | binaryIntegerConstant

hexIntegerConstant ::= "0x"hexDigit+

hexDigit := "0"-"9" | "A"-"F" | "a"-"f"

octalIntegerConstant ::= "0"octalDigit+

octalDigit := "0"-"7"

binaryIntegerConstant ::= "0b"binaryDigit+

binaryDigit := "0" | "1"

floatConstant ::= [ "-" ] [ digits+ ] "." [ digits+ ] [ ("e" | "E" | "d" | "D") [ "-"] digits+ ]

arrayConstant ::= "[" [literalConstant ("," literalConstant)*] "]"

arrayExpression ::= "[" [expression ("," expression)*] "]"

stringConstant ::= apos character* apos 
                 | quote character* quote

characterConstant ::= "$" character

apos ::= "'"' (single quote)

quote ::= " " " (double quote)

comment ::= "/*" any-character* " */ " | eolComment

eolComment ::= " // " any-character-up-to-end-of-line

Notice the nonstandard extension for single character constants


Back to Online_Documentation#Code_API_Overview Online Documentation



Copyright © 2014-2024 eXept Software AG