<b>=============== Summary ===============</b><br>
<br>
Change Set: TestCoverage-traits<br>
Date: 9 September 2022<br>
Author: Christoph Thiede<br>
<br>
Adds proper support for trait methods to SUnit's test coverage collection.<br>
<br>
Details:<br>
* Adds #eventualUsers/#allUsers protocol on TraitDescription and MethodReference for browing users of a trait and its methods.<br>
* Tests the new traits protocol.<br>
* TestRunner: Excludes trait methods (i.e., methods copied from a trait) from test coverage methods. Installs extra wrappers for all eventual users of each method defined in a trait and synchronizes them via TestCoverage>>#mark.<br>
* Implements proper #repondsTo: on TestCoverage.<br>
<br>
<b>=============== Diff ===============</b><br>
<br>
<b>CompiledMethod>>eventualUsers {*Traits-NanoKernel} · TraitTest 9/9/2022 23:37</b><br>
<font color="#FF0000">+ eventualUsers<br>
+ <br>
+ ^ self methodClass eventualUsersForSelector: self selector</font><br>
<br>
<b>MethodReference>>eventualUserReferences {*Traits-NanoKernel} · ct 9/9/2022 22:12</b><br>
<font color="#FF0000">+ eventualUserReferences<br>
+ <br>
+ ^ self eventualUsers collect: [:classDescription |<br>
+ (classDescription >> self selector) methodReference]</font><br>
<br>
<b>MethodReference>>eventualUsers {*Traits-NanoKernel} · ct 9/9/2022 20:11</b><br>
<font color="#FF0000">+ eventualUsers<br>
+ <br>
+ ^ self compiledMethod eventualUsers</font><br>
<br>
<b>TestCoverage>>mark {private} · ct 9/9/2022 22:22 (changed)</b><br>
mark<br>
<s><font color="#0000FF">- hasRun := true<br>
</font></s><font color="#FF0000">+ hasRun := true.<br>
+ <br>
+ self isTraitMethod ifTrue:<br>
+ [| original |<br>
+ original := self originalTraitMethod methodReference compiledMethod.<br>
+ (original respondsTo: #mark) ifTrue:<br>
+ [original mark; uninstall]].</font><br>
<br>
<b>TestCoverage>>respondsTo: {private} · ct 9/9/2022 22:22</b><br>
<font color="#FF0000">+ respondsTo: aMessage<br>
+ <br>
+ ^ (self class canUnderstand: aMessage)<br>
+ or: [method respondsTo: aMessage]</font><br>
<br>
<b>TestRunner>>addMethodsUnderTestIn:to: {actions} · ct 9/9/2022 22:16 (changed)</b><br>
addMethodsUnderTestIn: packages to: methods <br>
packages<br>
do: [:package | package isNil<br>
ifFalse: [package methods<br>
do: [:method | ((#(#packageNamesUnderTest #classNamesNotUnderTest ) includes: method methodSymbol)<br>
<s><font color="#0000FF">- or: [method compiledMethod isAbstract<br>
- or: [method compiledMethod hasLiteral: #ignoreForCoverage]])<br>
</font></s><font color="#FF0000">+ or: [method compiledMethod isAbstract]<br>
+ or: [method compiledMethod isTraitMethod]<br>
+ or: [method compiledMethod hasLiteral: #ignoreForCoverage])<br>
</font> ifFalse: [methods add: method]]]]<br>
<br>
<b>TestRunner>>collectCoverageFor: {actions} · ct 9/9/2022 23:00 (changed)</b><br>
collectCoverageFor: methods<br>
<s><font color="#0000FF">- | wrappers suite |<br>
- wrappers := methods collect: [ :each | TestCoverage on: each ].<br>
</font></s><font color="#FF0000">+ | methodsWithTraits wrappers suite |<br>
+ methodsWithTraits := methods , ((methods select: [ :each | each actualClass isTrait ]) gather: [ :each | each eventualUserReferences ]).<br>
+ wrappers := methodsWithTraits collect: [ :each | TestCoverage on: each ].<br>
</font> suite := self<br>
reset;<br>
suiteAll.<br>
<br>
[ wrappers do: [ :each | each install ].<br>
[ self runSuite: suite ] ensure: [ wrappers do: [ :each | each uninstall ] ] ] valueUnpreemptively.<br>
<s><font color="#0000FF">- wrappers := wrappers reject: [ :each | each hasRun ].<br>
</font></s><font color="#FF0000">+ wrappers := wrappers reject: [ :each | each hasRun or: [ each isTraitMethod ] ].<br>
+ <br>
</font> wrappers isEmpty <br>
ifTrue: <br>
[ UIManager default inform: 'Congratulations. Your tests cover all code under analysis.' ]<br>
ifFalse: <br>
[ ToolSet <br>
browseMessageSet: (wrappers collect: [ :each | each reference ])<br>
name: 'Not Covered Code (' , (100 - (100 * wrappers size // methods size)) printString , '% Code Coverage)'<br>
autoSelect: nil ].<br>
self saveResultInHistory<br>
<br>
<b>TraitDescription>>allUsers {accessing} · ct 9/9/2022 23:06</b><br>
<font color="#FF0000">+ allUsers<br>
+ "Answer all ClassDescriptions that directly or indirectly (i.e., through a path of other traits) use the receiver."<br>
+ <br>
+ ^ self users gather: [:classDescription |<br>
+ classDescription isTrait<br>
+ ifTrue: [classDescription allUsers copyWithFirst: classDescription]<br>
+ ifFalse: [{classDescription}]]</font><br>
<br>
<b>TraitDescription>>eventualUsers {accessing} · ct 9/9/2022 23:07</b><br>
<font color="#FF0000">+ eventualUsers<br>
+ "Answer all non-trait ClassDescriptions that directly or indirectly (i.e., through a path of other traits) use the receiver."<br>
+ <br>
+ ^ self users gather: [:classDescription |<br>
+ classDescription isTrait<br>
+ ifTrue: [classDescription eventualUsers]<br>
+ ifFalse: [{classDescription}]]</font><br>
<br>
<b>TraitDescription>>eventualUsersForSelector: {accessing} · TraitTest 9/9/2022 23:26</b><br>
<font color="#FF0000">+ eventualUsersForSelector: aSymbol<br>
+ "Answer all eventual users that use the implementation of for aSymbol of the receiver, i.e., exclude all users that override this message."<br>
+ <br>
+ | originalMethod |<br>
+ originalMethod := self >> aSymbol.<br>
+ ^ self eventualUsers select: [:classDescription |<br>
+ | userMethod |<br>
+ (userMethod := classDescription compiledMethodAt: aSymbol ifAbsent: []) notNil and:<br>
+ [userMethod originalTraitMethod == originalMethod]]</font><br>
<br>
<b>TraitTest>>testAllUsers {testing} · TraitTest 9/9/2022 23:17</b><br>
<font color="#FF0000">+ testAllUsers<br>
+ self assert: self t1 allUsers size = 4.<br>
+ self assert: (self t1 allUsers includesAllOf: {self t4. self t5. self c2. self t6 }).<br>
+ self assert: self t3 allUsers isEmpty.<br>
+ self assert: self t5 allUsers size = 1.<br>
+ self assert: self t5 allUsers anyOne = self c2.<br>
+ self c2 uses: self t1 + self t5.<br>
+ self assert: self t5 allUsers size = 1.<br>
+ self assert: self t5 allUsers anyOne = self c2.<br>
+ self c2 uses: self t2 asTraitComposition.<br>
+ self assert: self t5 allUsers isEmpty</font><br>
<br>
<b>TraitTest>>testEventualUserReferences {testing} · TraitTest 9/9/2022 23:41</b><br>
<font color="#FF0000">+ testEventualUserReferences<br>
+ self assert: (self t1 >> #m11) methodReference eventualUserReferences isEmpty.<br>
+ self assert: (self t1 >> #m12) methodReference eventualUserReferences isEmpty.<br>
+ self assert: (self t2 >> #m21) methodReference eventualUserReferences isEmpty.<br>
+ self assert: (self t3 >> #m31) methodReference eventualUserReferences isEmpty.<br>
+ self assert: (self t5 >> #m11) methodReference eventualUserReferences isEmpty.<br>
+ self assert: (self t5 >> #m12) methodReference eventualUserReferences size = 1.<br>
+ self assert: (self t5 >> #m12) methodReference eventualUserReferences anyOne = (self c2 >> #m12) methodReference.<br>
+ self assert: (self t5 >> #m21) methodReference eventualUserReferences size = 1.<br>
+ self assert: (self t5 >> #m21) methodReference eventualUserReferences anyOne = (self c2 >> #m21) methodReference.<br>
+ self assert: (self t5 >> #m51) methodReference eventualUserReferences size = 1.<br>
+ self assert: (self t5 >> #m51) methodReference eventualUserReferences anyOne = (self c2 >> #m51) methodReference.</font><br>
<br>
<b>TraitTest>>testEventualUsers {testing} · TraitTest 9/9/2022 23:18</b><br>
<font color="#FF0000">+ testEventualUsers<br>
+ self assert: self t5 eventualUsers size = 1.<br>
+ self assert: self t5 eventualUsers anyOne = self c2.<br>
+ self assert: self t1 eventualUsers size = 1.<br>
+ self assert: self t1 eventualUsers anyOne = self c2.<br>
+ self assert: self t3 eventualUsers isEmpty.</font><br>
<br>
["TestCoverage-traits.1.cs"]<br>
<br>
<font color="#808080">---<br>
</font><font color="#808080"><i>Sent from </i></font><font color="#808080"><i><a href="https://github.com/hpi-swa-lab/squeak-inbox-talk"><u><font color="#808080">Squeak Inbox Talk</font></u></a></i></font>