Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shuffle tests #15581

Merged
merged 10 commits into from
Dec 22, 2023
29 changes: 25 additions & 4 deletions src/JenkinsTools-Core/HDTestReport.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Class {
'shouldSerializeError'
],
#classVars : [
'CurrentStageName'
'CurrentStageName',
'ShuffleSeed'
],
#category : 'JenkinsTools-Core',
#package : 'JenkinsTools-Core'
Expand All @@ -39,18 +40,32 @@ HDTestReport class >> runClasses: aCollectionOfClasses named: packageName [

| suite classes time result |
suite := TestSuite named: packageName.
classes := (aCollectionOfClasses select: [ :class | class isTestCase and: [ class isAbstract not ] ]) asSortedCollection: [ :a :b | a name <= b name ].

"Use the configured shuffle seed if any, or a random one otherwise"
ShuffleSeed ifNotNil: [ suite shuffleSeed: ShuffleSeed asInteger ].

classes := (aCollectionOfClasses select: [ :class |
class isTestCase and: [ class isAbstract not ] ])
asSortedCollection: [ :a :b | a name <= b name ].

classes ifEmpty: [ ^ nil ].

classes do: [ :class | suite addTests: class buildSuite tests ].

time := DateAndTime now.
Transcript << 'Beginning to run tests of ' << packageName << OSPlatform current lineEnding.
Transcript
<< 'Beginning to run tests of ' << packageName
<< ' with random seed ' << ShuffleSeed asString
<< OSPlatform current lineEnding.
"We flush so that if a crash happens during the tests, we print in which package is the naughty test in the logs."
Transcript flush.

result := self runSuite: suite.
Transcript << 'Finished to run tests of ' << packageName << ' in ' << (DateAndTime now - time) humanReadablePrintString << OSPlatform current lineEnding.

Transcript
<< 'Finished to run tests of ' << packageName << ' in '
<< (DateAndTime now - time) humanReadablePrintString
<< OSPlatform current lineEnding.
Transcript flush.

^ result
Expand All @@ -68,6 +83,11 @@ HDTestReport class >> runSuite: aTestSuite [
^ self new runSuite: aTestSuite
]

{ #category : 'running' }
HDTestReport class >> shuffleSeed: aSeed [
ShuffleSeed := aSeed
]

{ #category : 'private' }
HDTestReport >> calculateNodeName [
| environmentClass name bitString |
Expand Down Expand Up @@ -327,6 +347,7 @@ HDTestReport >> setUp [
nextPutAll: 'name="'; nextPutAll: (self encode: suite name); nextPutAll: '" ';
nextPutAll: 'tests="'; print: suite tests size; nextPutAll: '" ';
nextPutAll: 'timestamp="'; print: Time now; nextPutAll: '" ';
nextPutAll: 'seed="'; print: suite shuffleSeed; nextPutAll: '" ';
nextPutAll: '>'.

"Now this is ugly. We want to update the time and the number of failures and errors, but still at the same time stream a valid XML. So remember this position and add some whitespace, that we can fill later."
Expand Down
26 changes: 18 additions & 8 deletions src/JenkinsTools-Core/TestCommandLineHandler.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Usage: test [--junit-xml-output] [--fail-on-failure] [<package> ...]
--stage-name=aName
it adds a prefix to the xml generated, this is useful
when running in the CI infrastructure
--shuffle-seed an integer specifying the seed used to shuffle the tests
<package> a String matching a package name

Examples:
Expand Down Expand Up @@ -130,13 +131,22 @@ TestCommandLineHandler >> runPackages [

{ #category : 'private' }
TestCommandLineHandler >> testRunner [

(self hasOption: 'junit-xml-output') ifTrue: [
HDTestReport currentStageName: ((self hasOption: 'stage-name') ifTrue: [ self optionAt: 'stage-name' ] ifFalse: [ '' ]).

(self hasOption: 'junit-xml-output') ifTrue: [
HDTestReport shuffleSeed: ((self hasOption: 'shuffle-seed')
ifTrue: [ self optionAt: 'shuffle-seed' ]
ifFalse: [ nil ]).
HDTestReport currentStageName: ((self hasOption: 'stage-name')
ifTrue: [ self optionAt: 'stage-name' ]
ifFalse: [ '' ]).
^ HDTestReport ].

self class environment at: #CommandLineTestRunner ifPresent: [ :commandLineTestRunner |
(self hasOption: 'no-xterm') ifTrue: [ ^ commandLineTestRunner ].
^ self class environment at: #VTermTestRunner
] ifAbsent: [ self error: 'no tests output available, try to use the option --junit-xml-output' ]

self class environment
at: #CommandLineTestRunner
ifPresent: [ :commandLineTestRunner |
(self hasOption: 'no-xterm') ifTrue: [ ^ commandLineTestRunner ].
^ self class environment at: #VTermTestRunner ]
ifAbsent: [
self error:
'no tests output available, try to use the option --junit-xml-output' ]
]
8 changes: 8 additions & 0 deletions src/Random-Core/SequenceableCollection.extension.st
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,11 @@ SequenceableCollection >> shuffleBy: aRandom [
SequenceableCollection >> shuffled [
^ self copy shuffle
]

{ #category : '*Random-Core' }
SequenceableCollection >> shuffledBy: aRandom [
"Durstenfeld's version of the Fisher-Yates shuffle"
"({1. 2. 3. 4. 5} shuffleBy: (Random seed: 42)) >>> #(1 2 5 4 3)"

^ self copy shuffleBy: aRandom
]
61 changes: 34 additions & 27 deletions src/SUnit-Core/TestSuite.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ Class {
'tests',
'resources',
'name',
'announcer'
'announcer',
'randomGenerator'
],
#category : 'SUnit-Core-Kernel',
#package : 'SUnit-Core',
Expand All @@ -38,12 +39,6 @@ TestSuite >> , aTestSuite [
yourself
]

{ #category : 'dependencies' }
TestSuite >> addDependentToHierachy: anObject [
self addDependent: anObject.
self tests do: [ :each | each addDependentToHierachy: anObject]
]

{ #category : 'accessing' }
TestSuite >> addTest: aTest [
self tests add: aTest
Expand All @@ -61,16 +56,8 @@ TestSuite >> announceTest: aTest [

{ #category : 'running' }
TestSuite >> debug [
self setUp.
[
self tests do: [:each |
each debug.
self announceTest: each.
self changed: each.
]
] ensure:[
self tearDown.
]

self runWith: [ :test | test debug ]
]

{ #category : 'accessing' }
Expand All @@ -85,6 +72,13 @@ TestSuite >> defaultResources [
coll]
]

{ #category : 'initialization' }
TestSuite >> initialize [

super initialize.
randomGenerator := Random new
]

{ #category : 'accessing' }
TestSuite >> name [

Expand All @@ -97,12 +91,6 @@ TestSuite >> name: aString [
name := aString
]

{ #category : 'dependencies' }
TestSuite >> removeDependentFromHierachy: anObject [
self removeDependent: anObject.
self tests do: [ :each | each removeDependentFromHierachy: anObject]
]

{ #category : 'running' }
TestSuite >> resourceClass [

Expand Down Expand Up @@ -138,16 +126,17 @@ TestSuite >> run [

{ #category : 'running' }
TestSuite >> run: aResult [

CurrentExecutionEnvironment runTestsBy: [
self runUnmanaged: aResult ]
self runWith: [ :test | test run: aResult ] ]
]

{ #category : 'running' }
TestSuite >> runUnmanaged: aResult [
TestSuite >> runWith: aBlock [
self setUp.
[ self tests
[ self shuffledTests
do: [ :each |
each run: aResult.
aBlock value: each.
self announceTest: each.
self changed: each ] ]
ensure: [ self tearDown ]
Expand All @@ -159,6 +148,24 @@ TestSuite >> setUp [
each isAvailable ifFalse: [ each signalInitializationError ]]
]

{ #category : 'running' }
TestSuite >> shuffleSeed [

^ randomGenerator seed
]

{ #category : 'running' }
TestSuite >> shuffleSeed: aSeed [

randomGenerator := Random seed: aSeed
]

{ #category : 'running' }
TestSuite >> shuffledTests [

^ self tests shuffledBy: randomGenerator
]

{ #category : 'running' }
TestSuite >> tearDown [
self resourceClass resetResources: self resources
Expand Down
7 changes: 4 additions & 3 deletions src/SUnit-Tests/Package.extension.st
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ Extension { #name : 'Package' }
Package >> testSuite [

| suite |

suite := TestSuite named: self packageName.
suite addTests: ((self classes select: #isTestCase) flatCollect: [:aTestClass | aTestClass suite tests]).
suite := TestSuite named: self name.
suite addTests:
((self classes select: #isTestCase) flatCollect: [ :aTestClass |
aTestClass suite tests ]).

^ suite
]