@@ -89,47 +89,44 @@ func (s *GithubPullRequest) createCommentBody(changedLinesWithCoverage domain.So
8989
9090 modules := collectModules (changedLinesWithCoverage )
9191
92- summaryLines := []string {}
93-
94- if len (modules ) > 0 {
95- summaryLines = append (summaryLines , fmt .Sprintf ("*Modules: %v*\n \n " , strings .Join (modules , ", " )))
92+ bodyLines := []string {
93+ "## 📊 Code Coverage — Changed Lines\n " ,
94+ "\n " ,
95+ "> Coverage is measured **only for the lines this PR changes**, not the whole file or repo.\n " ,
96+ "\n " ,
9697 }
97- var missedInstructions string
9898
99- for _ , r := range changedLinesWithCoverage {
100- if r .MissedInstructionCount > 0 {
101- missedInstructions += fmt .Sprintf ("--- %v\n " , lineDescription (r .SourceLine ))
102- missedInstructions += fmt .Sprintf ("%v\n " , r .LineValue )
103- }
99+ if len (modules ) > 0 {
100+ bodyLines = append (bodyLines , fmt .Sprintf ("*Modules: %v*\n \n " , strings .Join (modules , ", " )))
104101 }
105102
106- summaryLines = append (summaryLines , generateSummaryLines (changedLinesWithCoverage , func (linesWithDataCount int , linesWithoutDataCount int , covered int , missed int ) []string {
103+ bodyLines = append (bodyLines , generateSummaryLines (changedLinesWithCoverage , func (linesWithDataCount int , linesWithoutDataCount int , covered int , missed int ) []string {
107104 totalLines := linesWithDataCount + linesWithoutDataCount
108105 totalInstructions := covered + missed
109106
110- result := make ([]string , 5 )
111-
112- result [0 ] = "Code Coverage Summary:\n \n "
113- result [1 ] = fmt .Sprintf ("Lines Without Coverage Data -> %.f%% (%d)\n " , toPercent (safeDiv (float32 (linesWithoutDataCount ), float32 (totalLines ), 0 )), linesWithoutDataCount )
114- result [2 ] = fmt .Sprintf ("Lines With Coverage Data -> %.f%% (%d)\n " , toPercent (safeDiv (float32 (linesWithDataCount ), float32 (totalLines ), 1 )), linesWithDataCount )
115- result [3 ] = fmt .Sprintf ("Covered Instructions -> **%.f%%** (%d)\n " , toPercent (safeDiv (float32 (covered ), float32 (totalInstructions ), 1 )), covered )
116- result [4 ] = fmt .Sprintf ("Missed Instructions -> %.f%% (%d)\n " , toPercent (safeDiv (float32 (missed ), float32 (totalInstructions ), 0 )), missed )
117-
118- return result
107+ coveredPct := toPercent (safeDiv (float32 (covered ), float32 (totalInstructions ), 1 ))
108+ missedPct := toPercent (safeDiv (float32 (missed ), float32 (totalInstructions ), 0 ))
109+ withDataPct := toPercent (safeDiv (float32 (linesWithDataCount ), float32 (totalLines ), 1 ))
110+ withoutDataPct := toPercent (safeDiv (float32 (linesWithoutDataCount ), float32 (totalLines ), 0 ))
111+
112+ return []string {
113+ fmt .Sprintf ("### %v Covered Instructions: %.f%% (%d)\n " , coverageStatusEmoji (coveredPct ), coveredPct , covered ),
114+ "\n " ,
115+ "| Metric | Result | What it means |\n " ,
116+ "| :-- | :-: | :-- |\n " ,
117+ fmt .Sprintf ("| 🟢 **Covered Instructions** | **%.f%%** (%d) | Changed code your tests executed. Higher is better. |\n " , coveredPct , covered ),
118+ fmt .Sprintf ("| 🔴 **Missed Instructions** | %.f%% (%d) | Changed code your tests never ran. Lower is better. |\n " , missedPct , missed ),
119+ fmt .Sprintf ("| 📈 Lines With Coverage Data | %.f%% (%d) | Changed lines the coverage tool could track. |\n " , withDataPct , linesWithDataCount ),
120+ fmt .Sprintf ("| ⚪ Lines Without Coverage Data | %.f%% (%d) | Changed lines with no data: comments, blanks, declarations. |\n " , withoutDataPct , linesWithoutDataCount ),
121+ }
119122 })... )
120123
121- var summary string
122- if missedInstructions == "" {
123- summary = strings .Join (summaryLines , "" )
124- } else {
125-
126- summaryWithoutInstructions := strings .Join (summaryLines , "" )
127- summary = summaryWithoutInstructions + "\n <details><summary>Missed Instructions summary</summary>\n \n " + "```\n " + missedInstructions + "```" +
128- "\n </details>"
129- }
124+ body := strings .Join (bodyLines , "" )
125+ body += missedInstructionsSection (changedLinesWithCoverage )
126+ body += "\n <sub>🤖 Generated by <a href=\" https://github.com/target/pull-request-code-coverage\" >pull-request-code-coverage</a> — coverage for changed lines only.</sub>\n "
130127
131128 data := map [string ]string {
132- "body" : summary ,
129+ "body" : body ,
133130 }
134131
135132 dataBytes , marshalErr := s .jsonClient .Marshal (data )
@@ -141,6 +138,41 @@ func (s *GithubPullRequest) createCommentBody(changedLinesWithCoverage domain.So
141138 return bytes .NewBuffer (dataBytes ), nil
142139}
143140
141+ // missedInstructionsSection renders a collapsible block listing each changed
142+ // line that was not executed by tests. Returns "" when nothing was missed.
143+ func missedInstructionsSection (changedLinesWithCoverage domain.SourceLineCoverageReport ) string {
144+ var missedInstructions string
145+ missedLineCount := 0
146+
147+ for _ , r := range changedLinesWithCoverage {
148+ if r .MissedInstructionCount > 0 {
149+ missedLineCount ++
150+ missedInstructions += fmt .Sprintf ("--- %v\n " , lineDescription (r .SourceLine ))
151+ missedInstructions += fmt .Sprintf ("%v\n " , r .LineValue )
152+ }
153+ }
154+
155+ if missedInstructions == "" {
156+ return ""
157+ }
158+
159+ return fmt .Sprintf ("\n <details><summary>🔍 Missed instructions (%d)</summary>\n \n " , missedLineCount ) +
160+ "```\n " + missedInstructions + "```" + "\n </details>\n "
161+ }
162+
163+ // coverageStatusEmoji maps a covered-instruction percentage to a traffic-light
164+ // status icon, so the headline reads at a glance.
165+ func coverageStatusEmoji (coveredPct float32 ) string {
166+ switch {
167+ case coveredPct >= 80 :
168+ return "🟢"
169+ case coveredPct >= 50 :
170+ return "🟡"
171+ default :
172+ return "🔴"
173+ }
174+ }
175+
144176func collectModules (changedLinesWithCoverage domain.SourceLineCoverageReport ) []string {
145177 collector := map [string ]bool {}
146178
0 commit comments