• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

moosetechnology / GitProjectHealth / 9682577336

26 Jun 2024 03:32PM UTC coverage: 29.519% (+2.6%) from 26.896%
9682577336

Pull #7

github

web-flow
Merge 0d36202b7 into dfd76b711
Pull Request #7: Optimize code and writting tests

514 of 1154 new or added lines in 11 files covered. (44.54%)

17 existing lines in 3 files now uncovered.

2177 of 7375 relevant lines covered (29.52%)

0.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.6
/src/GitLabHealth-Model-Analysis/GitAnalyzer.class.st
1
Class {
2
        #name : #GitAnalyzer,
3
        #superclass : #Object,
4
        #instVars : [
5
                'glModel',
6
                'fromCommit',
7
                'glhImporter',
8
                'onProject',
9
                'maxChildCommits'
10
        ],
11
        #category : #'GitLabHealth-Model-Analysis'
12
}
13

14
{ #category : #analyze }
15
GitAnalyzer >> analyseAmandment [
1✔
16
        "return the first commit that modify the same lines of code as the fromCommit"
1✔
17

1✔
18
        | churn res access|
1✔
19
        
1✔
20
        access := ('' join: {
1✔
21
                                   'amandment'.
1✔
22
                                   maxChildCommits }) asSymbol.
1✔
23
        
1✔
24
        ^ fromCommit cacheAt: access ifPresent: [ :v | v ] ifAbsentPut: [
1✔
25
        
1✔
26
        ('GitAnalyzer, analyse amandment onProject: ', onProject printString )
1✔
27
                recordInfo.
1✔
28
        
1✔
29
        churn := self analyseChurn.
1✔
30

1✔
31
        res := self firstAmandmentFromChrun: churn.
1✔
32
         res]
1✔
33
]
1✔
34

35
{ #category : #analyze }
36
GitAnalyzer >> analyseChurn [
1✔
37

1✔
38
        | commitFiles totalContribution childCommits access |
1✔
39
        access := ('' join: {
1✔
40
                                   'churn'.
1✔
41
                                   maxChildCommits }) asSymbol.
1✔
42

1✔
43
        ^ fromCommit cacheAt: access ifAbsentPut: [
1✔
44
                  ('GitAnalyzer, analyse chrun onProject: ' , onProject printString)
1✔
45
                          recordInfo.
1✔
46
                  childCommits := Set new.
1✔
47
                  totalContribution := self
1✔
48
                                               visitChildCommits: fromCommit childCommits
1✔
49
                                               toStoreThemIn: childCommits
1✔
50
                                               upto: self maxChildCommits.
1✔
51
                  totalContribution := totalContribution sum: [ :commit | "nil if merge request commit"
1✔
52
                                               commit additions ifNil: [ 0 ] ].
1✔
53
                  commitFiles := self
1✔
54
                                         impactedFilesInFollowUpCommitsOf: fromCommit
1✔
55
                                         withMaxCommits: self maxChildCommits.
1✔
56
                        "a churned line is a line added on top of an already added line"
1✔
57
                  (self computeChurnOnFiles: commitFiles)
1✔
58
                          at: #totalContribution ifAbsentPut: totalContribution;
1✔
59
                          yourself ]
1✔
60
]
1✔
61

62
{ #category : #analyze }
63
GitAnalyzer >> analyseCommentContributions [
1✔
64

1✔
65
        | numberOfComments |
1✔
66
        ('GitAnalyzer, analyse comment contributions onProject: '
1✔
67
         , onProject printString) recordInfo.
1✔
68
        numberOfComments := 0.
1✔
69
        fromCommit diffs do: [ :diff |
1✔
70
                diff diffRanges do: [ :range |
1✔
71
                        range changes do: [ :change |
1✔
72
                                ((change isKindOf: GLPHEAddition) and: [
1✔
73
                                         (change sourceCode withoutPrefix: '+') trimLeft
1✔
74
                                                 beginsWithAnyOf: { '#'. '//'. '/*'. '*'. '*/' } ]) ifTrue: [
1✔
75
                                        numberOfComments := numberOfComments + 1 ] ] ] ].
1✔
76
        ^ numberOfComments
1✔
77
]
1✔
78

79
{ #category : #commit }
80
GitAnalyzer >> analyseCommitContribution [
×
81
        
×
82
        
×
83
        ('GitAnalyzer, analyse commit contribution of: ', fromCommit printString )
×
84
                recordInfo.
×
85
        
×
86
        ^ { (#addition -> fromCommit additions).
×
87
          (#deletion -> fromCommit deletions). } asDictionary 
×
88
]
×
89

90
{ #category : #analyze }
91
GitAnalyzer >> analyseCommitFrequencyFromCommits: initialCommits [
×
92

×
93
        | commits response |
×
NEW
94
        ('GitAnalyzer, analyse commit Frequency on: ' , onProject printString)
×
95
                recordInfo.
×
NEW
96

×
NEW
97
        response := {
×
NEW
98
                            (#numberOfCommit -> nil).
×
99
                            (#frequency -> nil) } asDictionary.
×
100

×
101

×
102
        commits := self arrangeCommitsByDate: initialCommits.
×
103
        commits := (commits associations sortAscending: [ :entry |
×
104
                            entry key asDate ]) asOrderedDictionary.
×
105

×
106
        ^ commits
×
107
]
×
108

109
{ #category : #analyze }
110
GitAnalyzer >> analyseCommitFrequencySince: since until: until [ 
1✔
111

1✔
112
        | commits response |
1✔
113
        
1✔
114
        ('GitAnalyzer, analyse commit Frequency on: ', onProject printString )
1✔
115
                recordInfo.
1✔
116
        
1✔
117
        response := {
1✔
118
                            (#numberOfCommit -> nil).
1✔
119
                            (#frequency -> nil) } asDictionary.
1✔
120

1✔
121
        commits := glhImporter
1✔
122
                           importCommitsOProject: onProject
1✔
123
                           since: since
1✔
124
                           until: until.
1✔
125

1✔
126
        commits := self arrangeCommitsByDate: commits.
1✔
127
        commits := (commits associations sortAscending: [ :entry |
1✔
128
                            entry key asDate ]) asOrderedDictionary.
1✔
129

1✔
130
        ^ commits
1✔
131
]
1✔
132

133
{ #category : #analyze }
134
GitAnalyzer >> analyseMergeResquestValidation: aGLHPMergeRequest [
1✔
135

1✔
136
        | creationDate mergedDate response |
1✔
137
        ('GitAnalyzer, analyse merge request delay of: '
1✔
138
         , aGLHPMergeRequest printString) recordInfo.
1✔
139

1✔
140
        ^ aGLHPMergeRequest
1✔
141
                  cacheAt: #mergeRequestValidation
1✔
142
                  ifPresent: [ :v | v ]
1✔
143
                  ifAbsentPut: [
1✔
144
                          response := {
1✔
145
                                              (#id_merge_resquest -> aGLHPMergeRequest iid).
1✔
146
                                              (#id_merge_commit -> nil).
1✔
147
                                              (#created_at -> aGLHPMergeRequest created_at).
1✔
148
                                              (#merged_at -> nil).
1✔
149
                                              (#duration -> nil).
1✔
150
                                              (#status -> aGLHPMergeRequest merge_status) }
1✔
151
                                              asDictionary.
1✔
152

1✔
153
                          creationDate := aGLHPMergeRequest created_at asDateAndTime.
1✔
154

1✔
155
                          mergedDate := aGLHPMergeRequest merged_at ifNil: [ ^ response ].
1✔
156

1✔
157
                          response
1✔
158
                                  at: #duration put: mergedDate - creationDate;
1✔
159
                                  at: #id_merge_commit put: aGLHPMergeRequest merge_commit_sha;
1✔
160
                                  at: #merged_at put: aGLHPMergeRequest merged_at;
1✔
161
                                  yourself ]
1✔
162
]
1✔
163

164
{ #category : #filter }
165
GitAnalyzer >> arrangeCommitsByDate: commits [
1✔
166

1✔
167
        | date2commits |
1✔
168
        date2commits := Dictionary new.
1✔
169

1✔
170
        commits do: [ :commit |
1✔
171
                | date |
1✔
172
                date := commit created_at asDate.
1✔
173
                date2commits
1✔
174
                        at: date printString
1✔
175
                        ifPresent: [ :v | v add: commit ]
1✔
176
                        ifAbsentPut: [
1✔
177
                                OrderedCollection new
1✔
178
                                        add: commit;
1✔
179
                                        yourself ] ].
1✔
180
        ^ date2commits
1✔
181
]
1✔
182

183
{ #category : #churn }
184
GitAnalyzer >> computeChurnOnFiles: aCollection [
1✔
185

1✔
186
        | changesDic perFileChanges churns |
1✔
187
        "1 -> (a GLPHEChange -> NumberOfChurnDetected)"
1✔
188
        changesDic := Dictionary new.
1✔
189

1✔
190

1✔
191
        perFileChanges := aCollection associations collect: [ :assoc |
1✔
192
                                  assoc key
1✔
193
                                  -> (self computeSpecificChurnOf: assoc value) ].
1✔
194

1✔
195

1✔
196
        churns := perFileChanges collect: [ :assoc |
1✔
197
                          | churn file results |
1✔
198
                          file := assoc key.
1✔
199
                          results := assoc value.
1✔
200
                          results values ifEmpty: [ churn := nil ] ifNotEmpty: [
1✔
201
                                  | churnedContribution locContributed |
1✔
202
                                  "churn contribution are the LoC that were affected by a churn"
1✔
203
                                  churnedContribution := results values select: [ :a |
1✔
204
                                                                 a value > 1 ].
1✔
205
                                  locContributed := results values sum: [ :a | a value ].
1✔
206

1✔
207
                                  churn := {
1✔
208
                                                   (#churnLoC
1✔
209
                                                    ->
1✔
210
                                                            ((churnedContribution sum: [ :a | a value ])
1✔
211
                                                             - churnedContribution size)).
1✔
212
                                                   (#locContributed -> locContributed) }
1✔
213
                                                   asDictionary ].
1✔
214

1✔
215
                          file -> churn ].
1✔
216
        churns := churns reject: [ :file2churn | file2churn value isNil ].
1✔
217

1✔
218
        ^ {
1✔
219
                  (#churns -> churns).
1✔
220
                  (#details -> perFileChanges) } asDictionary
1✔
221
]
1✔
222

223
{ #category : #churn }
224
GitAnalyzer >> computeSpecificChurnOf: commit2Changes [
1✔
225

1✔
226
        | changesDic |
1✔
227
        "1 -> (a GLPHEChange -> NumberOfChurnDetected)"
1✔
228
        changesDic := OrderedDictionary new.
1✔
229

1✔
230

1✔
231
        (commit2Changes sortAscending: [ :assoc | assoc key created_at ])
1✔
232
                do: [ :entry |
1✔
233
                        | commit diffRanges |
1✔
234
                        commit := entry key.
1✔
235
                        diffRanges := entry value.
1✔
236

1✔
237
                        diffRanges do: [ :diff |
1✔
238
                                | from |
1✔
239
                                from := (diff originalLineRange
1✔
240
                                                 copyFrom: (diff originalLineRange indexOf: $-) + 1
1✔
241
                                                 to: (diff originalLineRange
1✔
242
                                                                  indexOf: $,
1✔
243
                                                                  ifAbsent: [ diff originalLineRange size + 1 ]) - 1)
1✔
244
                                                asString asNumber.
1✔
245
                                from = 0 ifTrue: [ from := 1 ].
1✔
246
                                self insertDiff: diff into: changesDic startingFrom: from ] ].
1✔
247

1✔
248

1✔
249

1✔
250
        ^ self sortChangeDic: changesDic
1✔
251
]
1✔
252

253
{ #category : #accessing }
254
GitAnalyzer >> firstAmandmentFromChrun: aChurnAnalysis [ 
1✔
255
        |details whereChangesOccurs firstCommitsPerFile|
1✔
256
        
1✔
257
        whereChangesOccurs := (aChurnAnalysis at: #churns ) select: [ :file | (file value at: #churnLoC) > 0 ].
1✔
258
        
1✔
259
        details := whereChangesOccurs collect: [ :file |
1✔
260
                ((aChurnAnalysis at: #details) detect: [ :entry | entry key = file key] )
1✔
261
                 ].
1✔
262
        
1✔
263
        firstCommitsPerFile := details collect: [ :perFile |
1✔
264
                |changes firstCommits first|
1✔
265
                changes := perFile value.
1✔
266
                changes := changes select: [ :line2changes | line2changes value value > 1  ].
1✔
267
                firstCommits := (changes collect: [ :line2changes |  line2changes key second diffRange diff commit ]) values. 
1✔
268
                first := (firstCommits sortAscending: [:c | c created_at ]) first.
1✔
269
                 ].
1✔
270
        
1✔
271

1✔
272
        ^ (firstCommitsPerFile sortAscending: [:c | c created_at ]) ifEmpty: nil ifNotEmpty: [ :v | v first ]  . 
1✔
273

1✔
274
]
1✔
275

276
{ #category : #accessing }
277
GitAnalyzer >> fromCommit: aCommit [
1✔
278
        fromCommit := aCommit. 
1✔
279
]
1✔
280

281
{ #category : #accessing }
282
GitAnalyzer >> glhImporter: anImporter [
1✔
283
        glhImporter := anImporter .
1✔
284
]
1✔
285

286
{ #category : #'as yet unclassified' }
287
GitAnalyzer >> impactedFilesInFollowUpCommitsOf: aGLHCommit [
×
288

×
289
        ^ self
×
290
                  impactedFilesInFollowUpCommitsOf: aGLHCommit
×
291
                  withMaxCommits: self maxChildCommits. 
×
292
]
×
293

294
{ #category : #churn }
295
GitAnalyzer >> impactedFilesInFollowUpCommitsOf: aGLHCommit withMaxCommits: max [
1✔
296

1✔
297
        | commitFiles |
1✔
298
        commitFiles := (fromCommit diffs collect: [ :diff |
1✔
299
                                diff new_path -> (Set new
1✔
300
                                         add: aGLHCommit -> diff diffRanges;
1✔
301
                                         yourself) ]) asDictionary.
1✔
302

1✔
303
        self
1✔
304
                visitChildCommits: fromCommit childCommits
1✔
305
                lookingForFiles: commitFiles upto: max.
1✔
306

1✔
307
        ^ commitFiles
1✔
308
]
1✔
309

310
{ #category : #initialization }
311
GitAnalyzer >> initialize [
1✔
312

1✔
313
        glModel := GLPHEModel new.
1✔
314
        fromCommit := GLHCommit new.
1✔
315
        glhImporter := GLPHModelImporter new.
1✔
316
        onProject := GLHProject new.
1✔
317
        maxChildCommits := -1
1✔
318
]
1✔
319

320
{ #category : #insertion }
321
GitAnalyzer >> insertDiff: aGLPHEDiffRange into: fileChangesDic startingFrom: from [ 
1✔
322
        |index|
1✔
323
        index := from. 
1✔
324
        aGLPHEDiffRange changes do: [ :aChange |
1✔
325
        
1✔
326
                aChange isAddition ifTrue: [ 
1✔
327
                        fileChangesDic at: index ifPresent: [ :current | 
1✔
328
                         
1✔
329
                        current key add: aChange.
1✔
330
                        current value: current value + 1.  ] ifAbsentPut: [((OrderedCollection new add: aChange; yourself) -> 1 ) ].
1✔
331
                         ].
1✔
332
                
1✔
333
                aChange isDeletion ifFalse: [ index := index + 1 ]. 
1✔
334
                
1✔
335
                 ]
1✔
336
]
1✔
337

338
{ #category : #accessing }
339
GitAnalyzer >> maxChildCommit: max [ 
1✔
340
        maxChildCommits := max
1✔
341
]
1✔
342

343
{ #category : #accessing }
344
GitAnalyzer >> maxChildCommits [
1✔
345
        ^ maxChildCommits
1✔
346
]
1✔
347

348
{ #category : #'as yet unclassified' }
349
GitAnalyzer >> onModel: agitHealthModel [
1✔
350
        glModel := agitHealthModel
1✔
351
]
1✔
352

353
{ #category : #accessing }
354
GitAnalyzer >> onProject: aGLHProject [ 
1✔
355
        onProject := aGLHProject
1✔
356
]
1✔
357

358
{ #category : #sorting }
359
GitAnalyzer >> sortChangeDic: aCollection [ 
1✔
360
        ^ (aCollection associations sortAscending: [ :e | e key ] ) asOrderedDictionary 
1✔
361
]
1✔
362

363
{ #category : #visiting }
364
GitAnalyzer >> visitChildCommits: commits lookingForFiles: commitFiles [
×
365

×
366
        ^ self visitChildCommits:  commits lookingForFiles: commitFiles upto: -1 
×
367
]
×
368

369
{ #category : #visiting }
370
GitAnalyzer >> visitChildCommits: commits lookingForFiles: commitFiles upto: nCommits [
1✔
371

1✔
372
        commits ifEmpty: [ ^ commitFiles ].
1✔
373
        (nCommits = 0) ifTrue: [ ^ commitFiles ].
1✔
374

1✔
375
        commits do: [ :commit |
1✔
376
                | files |
1✔
377
                files := commit diffs collect: [ :diff | diff ].
1✔
378

1✔
379
                files do: [ :diff |
1✔
380
                        commitFiles
1✔
381
                                at: diff new_path
1✔
382
                                ifPresent: [ :v | v add: commit -> diff diffRanges ]
1✔
383
                                ifAbsent: [  ] ].
1✔
384

1✔
385
                self
1✔
386
                        visitChildCommits: commit childCommits
1✔
387
                        lookingForFiles: commitFiles
1✔
388
                        upto: nCommits - 1 ].
1✔
389

1✔
390
        ^ commitFiles
1✔
391
]
1✔
392

393
{ #category : #visiting }
394
GitAnalyzer >> visitChildCommits: commits toStoreThemIn: commitsFound upto: nCommits [
1✔
395

1✔
396
        commits ifEmpty: [ ^ commitsFound ].
1✔
397
        nCommits = 0 ifTrue: [ ^ commitsFound ].
1✔
398

1✔
399
        commits do: [ :commit |
1✔
400
                commitsFound add: commit.
1✔
401

1✔
402
                self
1✔
403
                        visitChildCommits: commit childCommits
1✔
404
                        toStoreThemIn: commitsFound
1✔
405
                        upto: nCommits - 1 ].
1✔
406

1✔
407
        ^ commitsFound
1✔
408
]
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc