#+title: 雲海 "unkai" (sea of clouds)
Go test coverage rendering
#+begin_quote
A sea of clouds covering the landscape
#+end_quote
** Install
#+begin_src shell
go install codeberg.org/clouds666/unkai@latest
#+end_src
** Usage
First the help text and then a more detailed description.
*** Help Text
#+begin_src text
Go test coverage rendering - "Sea of clouds"
Usage:
unkai [flags]
Flags:
-c, --coverage string the coverage file in the textfmt format (default "coverage/textfmt.out")
-f, --files strings files to show (empty means all, example "--files path/to/file1.go:1-5:11-111,path/to/file2.go")
-n, --functions strings functions to show (empty means all, example "--functions fun1,fun2")
-d, --git-diff read git diff from stdin
-h, --help help for unkai
-m, --mode string the output mode (available ansi, html) (default "ansi")
--search strings patterns to search (empty means all, example "--search ./internal/go,./internal/away/...")
-s, --sources string directory of the go sources (default ".")
-v, --version version for unkai
#+end_src
*** Data
Specify the root directory of the go source code with ~--sources~ and the coverage data with ~--coverage~.
The source directory defaults to the working directory and the coverage data defaults to ~coverage/textfmt.out~.
*** Select what is rendered
There are three mutually exclusive ways to select what code is rendered.
- File based
Select option ~--files~
Eg. ~unkai --files path/to/file1.go:1-5:11-111,path/to/file2.go~
- Function based
Select option ~--functions~
Eg. ~unkai --functions fun1,fun2~
To speed up loading of the source code, specify directories to search with ~--search~
Eg. ~unkai --search ./internal/go,./internal/away/... --functions fun1,fun2~
- Git diff based
Select option ~--git-diff~ and pass a git diff to stdin
Eg. ~git diff latest | unkai --git-diff~
*** Output format
Set the format of the output written to stdout with ~--mode~.
The options are ~ansi~ and ~html~.
Defaults to ~ansi~.
** Output
The source code is rendered in different colors depending on its coverage status:
- @@html:<span>no coverage information in the default text color (black or white depending on theme)</span>@@
- @@html:<span style="color: rgb(125, 125, 125)">diff context in gray</span>@@
- @@html:<span style="color: rgb(255, 10, 10)">not covered in red</span>@@
- @@html:<span style="color: rgb(25, 200, 25)">covered in green</span>@@
If the =-covermode= used for computing the coverage is =count= or coverage data from multiple runs with =-covermode atomic= is merged, the color is linearly interpolated between @@html:<span style="color: rgb(25, 100, 25)">dark green</span>@@ for the lowest and @@html:<span style="color: rgb(25, 200, 25)">bright green</span>@@ for the highest count.
So the more often the code was exercised during testing, the brighter the color.
** Examples
*** Forgejo Action
The unkai repository [[./action.yaml][provides a Forgejo Action]].
It can be used [[https://code.forgejo.org/go-chi/session/src/commit/dbccb1ebe821818f5f17b7de38a5aa49d6e5e9e1/.forgejo/workflows/test.yml#L51-L54][after running the tests]] and will [[https://codeberg.org/forgejo/forgejo/pulls/13970#issuecomment-21540035][add or update a comment]] in the pull request with the coverage report.
For instance:
#+begin_src yaml
on:
pull_request:
jobs:
test:
runs-on: docker
steps:
- name: test
run: go test -cover -coverpkg ./... -coverprofile=textfmt.out -v ./...
- uses: https://codeberg.org/clouds666/unkai@latest
with:
coverage: textfmt.out
token: ${{ forgejo.TOKEN }}
#+end_src
*** In this project
**** Terminal output
#+begin_src shell
go run . --sources ./data/example-project --coverage data/cover.out --functions SomeFunction,OtherFunction
#+end_src
**** HTML output
#+begin_src shell
go run . --sources ./data/example-project --coverage data/cover.out --functions SomeFunction,OtherFunction --mode html | wl-copy
#+end_src
Produces:
#+begin_export html
<pre>
<span>file: codeberg.org/clouds666/example-project/lib.go
</span><span style="color: rgb(125, 125, 125)"> 7: </span><span>func SomeFunction(doSmth bool) </span><span style="color: rgb(25, 200, 25)">{</span>
<span style="color: rgb(125, 125, 125)"> 8: </span><span style="color: rgb(25, 200, 25)"> if doSmth </span><span style="color: rgb(255, 25, 25)">{</span>
<span style="color: rgb(125, 125, 125)"> 9: </span><span style="color: rgb(255, 25, 25)"> fmt.Println("not covered")</span>
<span style="color: rgb(125, 125, 125)"> 10: </span><span style="color: rgb(255, 25, 25)"> }</span><span></span>
<span style="color: rgb(125, 125, 125)"> 11: </span><span></span>
<span style="color: rgb(125, 125, 125)"> 12: </span><span> </span><span style="color: rgb(25, 200, 25)">fmt.Println("covered")</span><span></span>
<span style="color: rgb(125, 125, 125)"> 13: </span><span>}</span>
<span style="color: rgb(125, 125, 125)"> ...
</span><span style="color: rgb(125, 125, 125)"> 15: </span><span>func OtherFunction() </span><span style="color: rgb(255, 25, 25)">{</span>
<span style="color: rgb(125, 125, 125)"> 16: </span><span style="color: rgb(255, 25, 25)"> fmt.Println("not covered")</span>
<span style="color: rgb(125, 125, 125)"> 17: </span><span style="color: rgb(255, 25, 25)">}</span><span></span>
</pre>
#+end_export
*** Forgejo
Make sure you have the file ~coverage/textfmt.out~.
For this example, the unit test coverage is enough.
In the [[https://codeberg.org/forgejo/forgejo][forgejo]] project root, run:
#+begin_src shell
unkai --functions AddDeletedBranch,GetDeletedBranchByID
#+end_src
** Developing
Using [[https://github.com/casey/just#installation][just]]:
*** Run tests
#+begin_src shell
just test
#+end_src
*** Show coverage
#+begin_src shell
just cover-show-files main.go:10-100
just cover-show-functions GetFunctions,functionFilter
just cover-show-diff latest
#+end_src