Execution environment usage for running examples
This page describes how the execution environment is specifically used when running GT examples. For a general overview of execution environments, see the page Execution environment overview.
There are three main ways examples get executed:
1. Interactive/IDE - When running individual examples from the UI via GtExample>>#run
run
<return: #GtExampleResult>
^ self evaluator result
or GtExample>>#debug
debug
^ self debugger result
2. Command Line/CI - When running examples in batch via GtExamplesHudsonReport
GtExamplesTestingHudsonReport << #GtExamplesHudsonReport
slots: {};
tag: 'Testing';
package: 'GToolkit-Examples'
3. Remote runner - When running examples in the remote runner using GtRrExamplesTask
GtRrAbstractExamplesTask << #GtRrExamplesTask
slots: { #ignoreNoTest };
tag: 'Tests';
package: 'RemoteRunner'
. This delegates the running directly to the GtExample
Object << #GtExample
slots: {
#providerClass .
#methodClass .
#selector .
#label .
#description .
#form .
#exceptions .
#subjects .
#children .
#properties .
#after .
#problems .
#timeLimit };
tag: 'Core';
package: 'GToolkit-Examples'
instance.
The core class for example execution is
GtExampleEvaluator
GtExampleProcessor << #GtExampleEvaluator
slots: { #result };
tag: 'Runtime';
package: 'GToolkit-Examples'
. It provides two execution modes:
- GtExampleEvaluator>>#runManaged
runManaged
<return: #GtExampleResult>
^ CurrentExecutionEnvironment runExampleEvaluator: self
- Runs with watchdog monitoring (time limits enforced)
- GtExampleEvaluator>>#runUnmanaged
runUnmanaged
<return: #GtExampleResult>
"The ideal case would be to updade the example runner to use runManaged"
^ CurrentExecutionEnvironment runUnmanagedExampleEvaluator: self
- Runs without watchdog but still tracks the example in the test environment
Both methods delegate to CurrentExecutionEnvironment
ProcessLocalVariable << #CurrentExecutionEnvironment
slots: {};
tag: 'Processes';
package: 'Kernel'
:
"Managed execution with watchdog"
CurrentExecutionEnvironment runExampleEvaluator: anEvaluator.
"Unmanaged execution without watchdog"
CurrentExecutionEnvironment runUnmanagedExampleEvaluator: anEvaluator.
When you run an example interactively:
1. GtExample>>#run
run
<return: #GtExampleResult>
^ self evaluator result
creates a GtExampleEvaluator
GtExampleProcessor << #GtExampleEvaluator
slots: { #result };
tag: 'Runtime';
package: 'GToolkit-Examples'
2. GtExampleEvaluator>>#result
result
<return: #GtExampleResult>
self
do: [ result := self runUnmanaged ]
on: GtExampleResult signalableExceptions
do: [ :anException |
result := (self newResultFor: self example)
exampleException: (GtSystemUtility freeze: anException) ].
^ result
calls GtExampleEvaluator>>#runUnmanaged
runUnmanaged
<return: #GtExampleResult>
"The ideal case would be to updade the example runner to use runManaged"
^ CurrentExecutionEnvironment runUnmanagedExampleEvaluator: self
3. This calls CurrentExecutionEnvironment>>#runUnmanagedExampleEvaluator:
runUnmanagedExampleEvaluator: anExampleEvaluator
^ self value runUnmanagedExampleEvaluator: anExampleEvaluator
4. Since no test environment is pre-installed, DefaultExecutionEnvironment>>#runUnmanagedExampleEvaluator:
runUnmanagedExampleEvaluator: anExampleEvaluator
| testEnv exampleResult |
testEnv := TestExecutionEnvironment new.
exampleResult := nil.
testEnv beActiveDuring: [
exampleResult := testEnv runUnmanagedExampleEvaluator: anExampleEvaluator ].
^ exampleResult
creates and activates a new TestExecutionEnvironment
ExecutionEnvironment << #TestExecutionEnvironment
slots: { #watchDogProcess . #watchDogSemaphore . #testCase . #maxTimeForTest . #testCompleted . #services . #mainTestProcess };
tag: 'Kernel';
package: 'SUnit-Core'
5. TestExecutionEnvironment>>#runUnmanagedExampleEvaluator:
runUnmanagedExampleEvaluator: anExampleEvaluator
| exampleResult |
testCase := anExampleEvaluator.
testCompleted := false.
[ exampleResult := anExampleEvaluator value ] ensure: [
testCompleted := true ].
^ exampleResult
sets the evaluator as the current testCase and executes it
"From DefaultExecutionEnvironment>>#runUnmanagedExampleEvaluator:"
testEnv := TestExecutionEnvironment new.
testEnv beActiveDuring: [
exampleResult := testEnv runUnmanagedExampleEvaluator: anExampleEvaluator ].
When running examples in CI via
GtExamplesHudsonReport
GtExamplesTestingHudsonReport << #GtExamplesHudsonReport
slots: {};
tag: 'Testing';
package: 'GToolkit-Examples'
:
1. GtExamplesTestingHudsonReport>>#runAll
runAll
CurrentExecutionEnvironment runTestsBy: [
self testCasesToRun do: [ :each |
self runTestCase: each ] ]
calls CurrentExecutionEnvironment runTestsBy: [...]
2. This installs a TestExecutionEnvironment
ExecutionEnvironment << #TestExecutionEnvironment
slots: { #watchDogProcess . #watchDogSemaphore . #testCase . #maxTimeForTest . #testCompleted . #services . #mainTestProcess };
tag: 'Kernel';
package: 'SUnit-Core'
for the entire batch run via DefaultExecutionEnvironment>>#runTestsBy:
runTestsBy: aBlock
TestExecutionEnvironment new beActiveDuring: aBlock
3. Each example is then run via GtExamplesHudsonReport>>#runExample:
runExample: anExample
| errorDetails stacks runStatus retryCount |
errorDetails := nil.
retryCount := 3.
stacks := OrderedCollection new.
self beginTestCase: anExample runBlock: [
[ anExample debug ]
on: self signalableExceptions
do: [ :err |
retryCount := retryCount - 1.
GtExamplesReportRetrySignal new
example: anExample;
exception: err;
retryCount: retryCount;
emit.
retryCount > 0 ifTrue:[
stacks add: (self stackTraceString: err of: anExample).
3 timesRepeat: [ Smalltalk garbageCollect ].
3 seconds wait.
err retry ]
ifFalse:
[ errorDetails := Dictionary new.
self initializeErrorDetails: errorDetails forError: err.
(anExample exceptions handles: err) ifFalse: [
stacks add: (self stackTraceString: err of: anExample) ] ] ] ].
errorDetails
ifNil: [ runStatus := #success ]
ifNotNil: [
| stackReport |
stackReport := stacks size = 1
ifTrue: [ stacks first ]
ifFalse: [
String streamContents: [ :aStream |
| stackIndex |
stackIndex := 1.
stacks
do: [ :aStack |
aStream
<< 'Stack ';
print: stackIndex;
lf;
<< aStack.
stackIndex := stackIndex +1 ]
separatedBy: [
aStream lf; lf ] ] ].
((errorDetails at: #errorClass) includesBehavior: AssertionFailure)
ifTrue: [
runStatus := #failure.
self writeFailure: errorDetails stack: stackReport ]
ifFalse: [
runStatus := #error.
self writeError: errorDetails stack: stackReport ] ].
self endTestCase.
GtExamplesReportRunExampleSignal new
example: anExample;
runStatus: runStatus;
emit.
which calls GtExample>>#debug
debug
^ self debugger result
4. Since a test environment is already active, TestExecutionEnvironment>>#runUnmanagedExampleEvaluator:
runUnmanagedExampleEvaluator: anExampleEvaluator
| exampleResult |
testCase := anExampleEvaluator.
testCompleted := false.
[ exampleResult := anExampleEvaluator value ] ensure: [
testCompleted := true ].
^ exampleResult
is called directly (no new environment created)
"From GtExamplesTestingHudsonReport>>#runAll"
CurrentExecutionEnvironment runTestsBy: [
self testCasesToRun do: [ :each |
self runTestCase: each ] ]
Managed execution (TestExecutionEnvironment>>#runExampleEvaluator:
runExampleEvaluator: anExampleEvaluator
| exampleResult |
testCase := anExampleEvaluator.
maxTimeForTest := anExampleEvaluator timeLimit.
testCompleted := false.
watchDogSemaphore signal. "signal about new test case"
[ exampleResult := self runExampleUnderWatchdogUsingEvaluator: anExampleEvaluator] ensure: [
testCompleted := true.
watchDogSemaphore signal. "signal that test case is completed"
self cleanUpAfterTest ].
^ exampleResult
):
- Signals the watchdog semaphore to start timing
- Calls TestExecutionEnvironment>>#runExampleUnderWatchdogUsingEvaluator:
runExampleUnderWatchdogUsingEvaluator: anEvaluator
| exampleResult |
exampleResult := nil.
[
[ exampleResult := anEvaluator runUnmanaged ] ensure: [
"Terminated test is not considered as completed (user just closed a debugger for example)"
mainTestProcess isTerminating ifFalse: [
self handleCompletedTest ]]
] on: Exception do: [ :err |
self handleException: err
].
^ exampleResult
- Will signal TestTookTooMuchTime if the example exceeds its time limit
- Calls handleCompletedTest on services when done
Unmanaged execution (TestExecutionEnvironment>>#runUnmanagedExampleEvaluator:
runUnmanagedExampleEvaluator: anExampleEvaluator
| exampleResult |
testCase := anExampleEvaluator.
testCompleted := false.
[ exampleResult := anExampleEvaluator value ] ensure: [
testCompleted := true ].
^ exampleResult
):
- Sets the evaluator as the current testCase (for Epicea logging checks)
- Executes the evaluator directly without watchdog timing
- Does NOT trigger the watchdog or call service handlers