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

Better exercise var name in tests #627

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
136 changes: 115 additions & 21 deletions dev/src/ExercismDev/ExercismExerciseGenerator.class.st
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
"
I am the source code generator for creating exercism compatible source files that can be checked into the exercism/pharo project for students to download.
I am the source code generator for creating test class with test cases that are generated out of problem specifications. Generated tests are then used for exercism compatible source files that can be checked into the exercism/pharo project for students to download.

You need to have checked out the exercism problem-specifications to point the generator to, to get the test case definitions.

To try: self generate
## Generate new exercises
To prompt user with choosing directory with problem specifications and generate:
`ExercismExerciseGenerator generate`

To generate individual exercise from problem specification:
`ExercismExerciseGenerator new generateExerciseFrom: 'path-to-problem-specifications/exercises/exercise-slug' asFileReference`

## Regenerating existing exercises
Use only when need to update tests from problem specifications, existing class will be used in existing exercise package, only test methods will be overwritten (UUID and other methods will remain same).
`ExercismExerciseGenerator new regenerateExistingExercisesFrom: 'path-to-problem-specifications/exercises' asFileReference`

When working just on specific exercise - its update:
`ExercismExerciseGenerator new regenerateExerciseFrom: 'path-to-problem-specifications/exercises/exercise-slug' asFileReference`
"
Class {
#name : #ExercismExerciseGenerator,
#superclass : #Object,
#instVars : [
'numberGenerated',
'exerciseDirReference',
'testJson'
'testJson',
'regenerateExisting'
],
#classVars : [
'DefaultPath'
Expand Down Expand Up @@ -46,7 +59,7 @@ ExercismExerciseGenerator class >> generate [
chooseDirectory: 'Select the /exercises location in a full Exercism/problem-specifications git project'
path: self defaultPath.

path ifNotNil: [ self new generateFrom: (self defaultPath: path) ]
path ifNotNil: [ self new generateFrom: path ]
]

{ #category : #examples }
Expand All @@ -72,6 +85,12 @@ ExercismExerciseGenerator class >> writeLegacyPackageBaselineNames [
show: '''Exercise@' , n , '''' ]
]

{ #category : #internal }
ExercismExerciseGenerator >> canOverwriteExisting [

^ self regenerateExisting or: [self exerciseTestAlreadyExists not]
]

{ #category : #internal }
ExercismExerciseGenerator >> compile: src for: aClass selector: aSelector protocol: aName [

Expand Down Expand Up @@ -111,6 +130,14 @@ ExercismExerciseGenerator >> exerciseDirReference: anObject [
exerciseDirReference := anObject
]

{ #category : #generation }
ExercismExerciseGenerator >> exerciseDirectoriesDo: exerciseGenerateBlock [

self class defaultPath entries
select: #isDirectory
thenDo: [:dirEntry | exerciseGenerateBlock value: dirEntry ]
]

{ #category : #internal }
ExercismExerciseGenerator >> exerciseIsDeprecated [

Expand All @@ -123,6 +150,12 @@ ExercismExerciseGenerator >> exerciseTestAlreadyExists [
^ Smalltalk hasClassNamed: self testClassName
]

{ #category : #internal }
ExercismExerciseGenerator >> exerciseVariableName [

^ self testNameCamelCased asValidSelector asString
]

{ #category : #generation }
ExercismExerciseGenerator >> generateExerciseCommentFor: testClass [
| comment |
Expand All @@ -146,35 +179,39 @@ ExercismExerciseGenerator >> generateExerciseFrom: aFileSystemReference [
"this is needed, from exercise directory all artefacts will be obtained"
self exerciseDirReference: aFileSystemReference.

self hasValidTestDecriptions ifFalse: [^ self log: 'does not contain any test descriptions (skipping).' for: aFileSystemReference basename ].

self exerciseIsDeprecated
ifTrue: [ ^self log: 'is deprecated (skipping)' for: self testClassName ].

self exerciseTestAlreadyExists ifTrue: [ ^self log: 'already exists (skipping)' for: self testClassName ].

self canOverwriteExisting
ifFalse: [ ^self log: 'exercise test class already exists (skipping).' for: self testClassName ].

testClass := self generateTestClass.
self generateSetupFor: testClass.
self generateTestMethodsFor: testClass.
self generateMetaDataFor: testClass.

self numberGenerated: self numberGenerated + 1.
self log: 'successfully created' for: self testClassName
self log: 'successfully created.' for: self testClassName




]

{ #category : #generation }
ExercismExerciseGenerator >> generateFrom: filePathReference [

"set default path from parameter"
self class defaultPath: filePathReference.

"create WIP package for exercises, if missing"
self ensureCreateExerciseWIPPackage.

self traceCr: 'Generating new TestCases from specification: ', filePathReference printString.

self numberGenerated: 0.
filePathReference entries
do: [ :entry | self generateExerciseFrom: entry reference ].

self exerciseDirectoriesDo: [:dirEntry | self generateExerciseFrom: dirEntry reference ].

self
traceCr: ('Generation complete. Created {1} Tests!'
Expand All @@ -187,6 +224,9 @@ ExercismExerciseGenerator >> generateMetaDataFor: testClass [
"write commment with exercise info to class"
self generateExerciseCommentFor: testClass.

"Create UUID and version, only if creating new exercise (not regenerating existing)"
self regenerateExisting ifTrue: [ ^ self ].

"compile method with uuid"
self generateUUIDMethodFor: testClass.

Expand All @@ -202,7 +242,7 @@ ExercismExerciseGenerator >> generateSetupFor: testClass [
outStream
<< 'setUp'; cr;
tab; << 'super setUp.'; cr;
tab; << self testVariableName; << ' := '; << self testNameCamelCased; << ' new'.
tab; << self exerciseVariableName; << ' := '; << self testNameCamelCased; << ' new'.
].

self compile: src for: testClass selector: #setUp protocol: 'running'
Expand All @@ -213,10 +253,10 @@ ExercismExerciseGenerator >> generateSetupFor: testClass [
ExercismExerciseGenerator >> generateTestClass [

^ ExercismTest << self testClassName asSymbol
slots: {self testVariableName asSymbol};
slots: {self exerciseVariableName asSymbol};
sharedVariables: {};
tag: self testNameCamelCased;
package: 'ExercismWIP';
package: self packageNameForTestClass;
install
]

Expand All @@ -235,7 +275,7 @@ ExercismExerciseGenerator >> generateTestMethodsFor: testClass [
testMethodGenerator
testClass: testClass;
testCaseJson: testCaseJson;
testVariable: self testVariableName;
testVariable: self exerciseVariableName;
testPrefix: '';
generateTests.
]
Expand Down Expand Up @@ -270,12 +310,23 @@ ExercismExerciseGenerator >> generateVersionMethodFor: testClass [

]

{ #category : #generation }
ExercismExerciseGenerator >> hasValidTestDecriptions [

"answer true, if directory with problem description contains valid test description - must contain file canonical-data.json"

^ self exerciseDirReference fileNames includes: 'canonical-data.json'
]

{ #category : #initialization }
ExercismExerciseGenerator >> initialize [

super initialize.
"reset number of generated test classes"
self numberGenerated: 0.

"by default don not regenerate existing exercises, new exercise generation is default"
self regenerateExisting: false.
]

{ #category : #internal }
Expand All @@ -301,6 +352,55 @@ ExercismExerciseGenerator >> numberGenerated: anObject [
numberGenerated := anObject
]

{ #category : #internal }
ExercismExerciseGenerator >> packageNameForTestClass [

"if regenerating existing class, use exercise package like 'Exercise@SlugName', otherwise just WIP package"
self regenerateExisting ifTrue: [
^ 'Exercise@{1}' format: {self testNameCamelCased}
].
^ self defaultPackageName
]

{ #category : #generation }
ExercismExerciseGenerator >> regenerateExerciseFrom: aFileSystemReference [

self regenerateExisting: true.
self generateExerciseFrom: aFileSystemReference
]

{ #category : #accessing }
ExercismExerciseGenerator >> regenerateExisting [

^ regenerateExisting
]

{ #category : #accessing }
ExercismExerciseGenerator >> regenerateExisting: aBool [

regenerateExisting := aBool
]

{ #category : #generation }
ExercismExerciseGenerator >> regenerateExistingExercisesFrom: filePathReference [

"this will regenerate already existing exercises from problem specifications"
self traceCr: 'Regenerating existing TestCases from specification: ', filePathReference printString.

self numberGenerated: 0.
ExercismExercise allExercises do: [:existingExercise |
filePathReference entries
do: [ :entry |
entry name = existingExercise name ifTrue: [
self regenerateExerciseFrom: entry reference
]
]
].
self
traceCr: ('Existing exercises sucessfully regenerated with: {1} Tests!'
format: {self numberGenerated})
]

{ #category : #internal }
ExercismExerciseGenerator >> testClassName [

Expand Down Expand Up @@ -334,12 +434,6 @@ ExercismExerciseGenerator >> testNameCamelCased [

]

{ #category : #internal }
ExercismExerciseGenerator >> testVariableName [

^ (self testNameCamelCased, 'Calculator') asValidSelector asString
]

{ #category : #internal }
ExercismExerciseGenerator >> updateCategorisation [
"utility script to fix categorisations"
Expand Down
Loading