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 GtExampleEvaluator

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 ].
  

Execution Flow from Command Line

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