1- // Copyright (c) 2012 The Gocov Authors.
2- //
3- // Permission is hereby granted, free of charge, to any person obtaining a copy
4- // of this software and associated documentation files (the "Software"), to
5- // deal in the Software without restriction, including without limitation the
6- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7- // sell copies of the Software, and to permit persons to whom the Software is
8- // furnished to do so, subject to the following conditions:
9- //
10- // The above copyright notice and this permission notice shall be included in
11- // all copies or substantial portions of the Software.
12- //
13- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19- // IN THE SOFTWARE.
20-
21- package cov
1+ package themes
222
233import (
244 "encoding/base64"
255 "encoding/json"
266 "fmt"
7+ "go/token"
8+ "html"
279 "io"
2810 "io/ioutil"
2911 "os"
12+ "path/filepath"
3013 "sort"
3114 "strings"
3215 "time"
3316
3417 "github.com/axw/gocov"
35- "github.com/matm/gocov-html/pkg/themes"
36- "github.com/matm/gocov-html/pkg/types"
3718 "github.com/rotisserie/eris"
3819)
3920
@@ -97,10 +78,10 @@ func (r *report) clear() {
9778 r .packages = nil
9879}
9980
100- func buildReportPackage (pkg * gocov.Package , r * report ) types. ReportPackage {
101- rv := types. ReportPackage {
81+ func buildReportPackage (pkg * gocov.Package , r * report ) reportPackage {
82+ rv := reportPackage {
10283 Pkg : pkg ,
103- Functions : make (types. ReportFunctionList , 0 ),
84+ Functions : make (reportFunctionList , 0 ),
10485 }
10586 for _ , fn := range pkg .Functions {
10687 reached := 0
@@ -109,7 +90,7 @@ func buildReportPackage(pkg *gocov.Package, r *report) types.ReportPackage {
10990 reached ++
11091 }
11192 }
112- rf := types. ReportFunction {Function : fn , StatementsReached : reached }
93+ rf := reportFunction {Function : fn , StatementsReached : reached }
11394 covp := rf .CoveragePercent ()
11495 if covp >= float64 (r .CoverageMin ) && covp <= float64 (r .CoverageMax ) {
11596 rv .Functions = append (rv .Functions , rf )
@@ -127,8 +108,7 @@ func buildReportPackage(pkg *gocov.Package, r *report) types.ReportPackage {
127108
128109// printReport prints a coverage report to the given writer.
129110func printReport (w io.Writer , r * report ) error {
130- theme := themes .Current ()
131- data := theme .Data ()
111+ data := curTheme .Data ()
132112
133113 // Base64 decoding of style data and script.
134114 s , err := base64 .StdEncoding .DecodeString (data .Style )
@@ -154,7 +134,7 @@ func printReport(w io.Writer, r *report) error {
154134 }
155135 css = string (style )
156136 }
157- reportPackages := make (types. ReportPackageList , len (r .packages ))
137+ reportPackages := make (reportPackageList , len (r .packages ))
158138 pkgNames := make ([]string , len (r .packages ))
159139 for i , pkg := range r .packages {
160140 reportPackages [i ] = buildReportPackage (pkg , r )
@@ -170,7 +150,7 @@ func printReport(w io.Writer, r *report) error {
170150 )
171151
172152 if len (reportPackages ) > 1 {
173- rv := types. ReportPackage {
153+ rv := reportPackage {
174154 Pkg : & gocov.Package {Name : "Report Total" },
175155 }
176156 for _ , rp := range reportPackages {
@@ -179,7 +159,7 @@ func printReport(w io.Writer, r *report) error {
179159 }
180160 data .Overview = & rv
181161 }
182- err = theme .Template ().Execute (w , data )
162+ err = curTheme .Template ().Execute (w , data )
183163 return eris .Wrap (err , "execute template" )
184164}
185165
@@ -227,3 +207,154 @@ func HTMLReportCoverage(r io.Reader, opts ReportOptions) error {
227207 fmt .Fprintf (os .Stderr , "Took %v\n " , time .Since (t0 ))
228208 return eris .Wrap (err , "HTML report" )
229209}
210+
211+ // ProjectURL is the project's site on GitHub.
212+ const ProjectURL = "https://github.com/matm/gocov-html"
213+
214+ const (
215+ hitPrefix = " "
216+ missPrefix = "MISS"
217+ )
218+
219+ type reportPackageList []reportPackage
220+
221+ type reportPackage struct {
222+ Pkg * gocov.Package
223+ Functions reportFunctionList
224+ TotalStatements int
225+ ReachedStatements int
226+ }
227+
228+ // PercentageReached computes the percentage of reached statements by the tests
229+ // for a package.
230+ func (rp * reportPackage ) PercentageReached () float64 {
231+ var rv float64
232+ if rp .TotalStatements > 0 {
233+ rv = float64 (rp .ReachedStatements ) / float64 (rp .TotalStatements ) * 100
234+ }
235+ return rv
236+ }
237+
238+ // reportFunction is a gocov Function with some added stats.
239+ type reportFunction struct {
240+ * gocov.Function
241+ StatementsReached int
242+ }
243+
244+ // functionLine holds the line of code, its line number in the source file
245+ // and whether the tests reached it.
246+ type functionLine struct {
247+ Code string
248+ LineNumber int
249+ Missed bool
250+ }
251+
252+ // CoveragePercent is the percentage of code coverage for a function. Returns 100
253+ // if the function has no statement.
254+ func (f reportFunction ) CoveragePercent () float64 {
255+ reached := f .StatementsReached
256+ var stmtPercent float64 = 0
257+ if len (f .Statements ) > 0 {
258+ stmtPercent = float64 (reached ) / float64 (len (f .Statements )) * 100
259+ } else if len (f .Statements ) == 0 {
260+ stmtPercent = 100
261+ }
262+ return stmtPercent
263+ }
264+
265+ // ShortFileName returns the base path of the function's file name. Provided for
266+ // convenience to be used in the HTML template of the theme.
267+ func (f reportFunction ) ShortFileName () string {
268+ return filepath .Base (f .File )
269+ }
270+
271+ // Lines returns information about all a function's Lines of code.
272+ func (f reportFunction ) Lines () []functionLine {
273+ type annotator struct {
274+ fset * token.FileSet
275+ files map [string ]* token.File
276+ }
277+ a := & annotator {}
278+ a .fset = token .NewFileSet ()
279+ a .files = make (map [string ]* token.File )
280+
281+ // Load the file for line information. Probably overkill, maybe
282+ // just compute the lines from offsets in here.
283+ setContent := false
284+ file := a .files [f .File ]
285+ if file == nil {
286+ info , err := os .Stat (f .File )
287+ if err != nil {
288+ panic (err )
289+ }
290+ file = a .fset .AddFile (f .File , a .fset .Base (), int (info .Size ()))
291+ setContent = true
292+ }
293+
294+ data , err := ioutil .ReadFile (f .File )
295+ if err != nil {
296+ panic (err )
297+ }
298+
299+ if setContent {
300+ // This processes the content and records line number info.
301+ file .SetLinesForContent (data )
302+ }
303+
304+ statements := f .Statements [:]
305+ lineno := file .Line (file .Pos (f .Start ))
306+ lines := strings .Split (string (data )[f .Start :f .End ], "\n " )
307+ fls := make ([]functionLine , len (lines ))
308+
309+ for i , line := range lines {
310+ lineno := lineno + i
311+ statementFound := false
312+ hit := false
313+ for j := 0 ; j < len (statements ); j ++ {
314+ start := file .Line (file .Pos (statements [j ].Start ))
315+ if start == lineno {
316+ statementFound = true
317+ if ! hit && statements [j ].Reached > 0 {
318+ hit = true
319+ }
320+ statements = append (statements [:j ], statements [j + 1 :]... )
321+ }
322+ }
323+ hitmiss := hitPrefix
324+ if statementFound && ! hit {
325+ hitmiss = missPrefix
326+ }
327+ fls [i ] = functionLine {
328+ Missed : hitmiss == missPrefix ,
329+ LineNumber : lineno ,
330+ Code : html .EscapeString (strings .Replace (line , "\t " , " " , - 1 )),
331+ }
332+ }
333+ return fls
334+ }
335+
336+ // reportFunctionList is a list of functions for a report.
337+ type reportFunctionList []reportFunction
338+
339+ func (l reportFunctionList ) Len () int {
340+ return len (l )
341+ }
342+
343+ // TODO make sort method configurable?
344+ func (l reportFunctionList ) Less (i , j int ) bool {
345+ var left , right float64
346+ if len (l [i ].Statements ) > 0 {
347+ left = float64 (l [i ].StatementsReached ) / float64 (len (l [i ].Statements ))
348+ }
349+ if len (l [j ].Statements ) > 0 {
350+ right = float64 (l [j ].StatementsReached ) / float64 (len (l [j ].Statements ))
351+ }
352+ if left < right {
353+ return true
354+ }
355+ return left == right && len (l [i ].Statements ) < len (l [j ].Statements )
356+ }
357+
358+ func (l reportFunctionList ) Swap (i , j int ) {
359+ l [i ], l [j ] = l [j ], l [i ]
360+ }
0 commit comments