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