Merge branch '16-test-coverage' into 'master'
Resolve "test coverage" Closes #16 See merge request slon/shad-go-private!20
This commit is contained in:
commit
629e9b3c92
44 changed files with 1020 additions and 1 deletions
85
coverme/README.md
Normal file
85
coverme/README.md
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
## coverme
|
||||||
|
|
||||||
|
В этой задаче нужно покрыть простой todo-app http сервис unit тестами.
|
||||||
|
|
||||||
|
Необходимо покрыть все sub-package'и.
|
||||||
|
Package main можно не тестировать.
|
||||||
|
|
||||||
|
Существующие файлы менять не нужно.
|
||||||
|
Нужно создавать новые файлы с тестами.
|
||||||
|
|
||||||
|
Тестирующая система будет проверять code coverage.
|
||||||
|
Порог задан в [coverage_test.go](./app/coverage_test.go)
|
||||||
|
|
||||||
|
Важно понимать, что coverage 100% - не решение всех проблем.
|
||||||
|
В коде по-прежнему могут быть ошибки.
|
||||||
|
Coverage 100% говорит ровно о том, что все строки кода выполнялись.
|
||||||
|
Хорошие тесты в первую очередь тестируют функциональность.
|
||||||
|
|
||||||
|
Как посмотреть coverage:
|
||||||
|
```
|
||||||
|
go test -v -cover ./coverme/...
|
||||||
|
```
|
||||||
|
|
||||||
|
Coverage можно выводить в html (см. ссылки), и эта функциональность поддерживается в Goland.
|
||||||
|
|
||||||
|
## Ссылки
|
||||||
|
|
||||||
|
1. cover: https://blog.golang.org/cover
|
||||||
|
2. [gomock](https://github.com/golang/mock) для создания мока базы данных при тестировании серевера
|
||||||
|
3. [httptest.ResponseRecorder](https://golang.org/pkg/net/http/httptest/#ResponseRecorder) для тестирования handler'ов сервера
|
||||||
|
4. [httptest.Server](https://golang.org/pkg/net/http/httptest/#Server) для тестирования клинета
|
||||||
|
5. Если вы ждёте, когда же выложат лекцию: https://www.youtube.com/watch?v=ndmB0bj7eyw
|
||||||
|
|
||||||
|
## O сервисе
|
||||||
|
|
||||||
|
Todo-app с минимальной функциональностью + client.
|
||||||
|
|
||||||
|
Запуск:
|
||||||
|
```
|
||||||
|
✗ go run ./coverme/main.go -port 6029
|
||||||
|
```
|
||||||
|
|
||||||
|
Health check:
|
||||||
|
```
|
||||||
|
✗ curl -i -X GET localhost:6029/
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
Date: Thu, 19 Mar 2020 21:46:02 GMT
|
||||||
|
Content-Length: 24
|
||||||
|
|
||||||
|
"API is up and working!"
|
||||||
|
```
|
||||||
|
|
||||||
|
Создать новое todo:
|
||||||
|
```
|
||||||
|
✗ curl -i localhost:6029/todo/create -d '{"title":"A","content":"a"}'
|
||||||
|
HTTP/1.1 201 Created
|
||||||
|
Content-Type: application/json
|
||||||
|
Date: Thu, 19 Mar 2020 21:41:31 GMT
|
||||||
|
Content-Length: 51
|
||||||
|
|
||||||
|
{"id":0,"title":"A","content":"a","finished":false}
|
||||||
|
```
|
||||||
|
|
||||||
|
Получить todo по id:
|
||||||
|
```
|
||||||
|
✗ curl -i localhost:6029/todo/0
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
Date: Thu, 19 Mar 2020 21:44:17 GMT
|
||||||
|
Content-Length: 51
|
||||||
|
|
||||||
|
{"id":0,"title":"A","content":"a","finished":false}
|
||||||
|
```
|
||||||
|
|
||||||
|
Получить все todo:
|
||||||
|
```
|
||||||
|
✗ curl -i -X GET localhost:6029/todo
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
Date: Thu, 19 Mar 2020 21:44:37 GMT
|
||||||
|
Content-Length: 53
|
||||||
|
|
||||||
|
[{"id":0,"title":"A","content":"a","finished":false}]
|
||||||
|
```
|
99
coverme/app/app.go
Normal file
99
coverme/app/app.go
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/handlers"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
"gitlab.com/slon/shad-go/coverme/models"
|
||||||
|
"gitlab.com/slon/shad-go/coverme/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
router *mux.Router
|
||||||
|
db models.Storage
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(db models.Storage) *App {
|
||||||
|
return &App{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) Start(port int) {
|
||||||
|
app.initRoutes()
|
||||||
|
app.run(fmt.Sprintf(":%d", port))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) initRoutes() {
|
||||||
|
app.router = mux.NewRouter()
|
||||||
|
app.router.HandleFunc("/", app.status).Methods("Get")
|
||||||
|
app.router.HandleFunc("/todo", app.list).Methods("Get")
|
||||||
|
app.router.HandleFunc("/todo/{id:[0-9]+}", app.getTodo).Methods("Get")
|
||||||
|
app.router.HandleFunc("/todo/create", app.addTodo).Methods("Post")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) run(addr string) {
|
||||||
|
loggedRouter := handlers.LoggingHandler(os.Stderr, app.router)
|
||||||
|
_ = http.ListenAndServe(addr, loggedRouter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) list(w http.ResponseWriter, r *http.Request) {
|
||||||
|
todos, err := app.db.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
utils.ServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = utils.RespondJSON(w, http.StatusOK, todos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) addTodo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
req := &models.AddRequest{}
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
if err := decoder.Decode(&req); err != nil {
|
||||||
|
utils.BadRequest(w, "payload is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() { _ = r.Body.Close() }()
|
||||||
|
|
||||||
|
if req.Title == "" {
|
||||||
|
utils.BadRequest(w, "title is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
todo, err := app.db.AddTodo(req.Title, req.Content)
|
||||||
|
if err != nil {
|
||||||
|
utils.ServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = utils.RespondJSON(w, http.StatusCreated, todo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) getTodo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id, err := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/todo/"))
|
||||||
|
if err != nil {
|
||||||
|
utils.BadRequest(w, "ID must be an int")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
todo, err := app.db.GetTodo(models.ID(id))
|
||||||
|
if err != nil {
|
||||||
|
utils.ServerError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = utils.RespondJSON(w, http.StatusOK, todo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *App) status(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_ = utils.RespondJSON(w, http.StatusOK, "API is up and working!")
|
||||||
|
}
|
5
coverme/app/coverage_test.go
Normal file
5
coverme/app/coverage_test.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
// min coverage: 90%
|
70
coverme/client/client.go
Normal file
70
coverme/client/client.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitlab.com/slon/shad-go/coverme/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(addr string) *Client {
|
||||||
|
return &Client{addr: addr}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Add(r *models.AddRequest) (*models.Todo, error) {
|
||||||
|
data, _ := json.Marshal(r)
|
||||||
|
|
||||||
|
resp, err := http.Post(c.addr+"/create", "application/json", bytes.NewReader(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusCreated {
|
||||||
|
return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var todo *models.Todo
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&todo)
|
||||||
|
return todo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Get(id models.ID) (*models.Todo, error) {
|
||||||
|
resp, err := http.Get(c.addr + fmt.Sprintf("/todo/%d", id))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var todo *models.Todo
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&todo)
|
||||||
|
return todo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) List() ([]*models.Todo, error) {
|
||||||
|
resp, err := http.Get(c.addr + "/todo")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var todos []*models.Todo
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&todos)
|
||||||
|
return todos, err
|
||||||
|
}
|
18
coverme/main.go
Normal file
18
coverme/main.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
|
||||||
|
"gitlab.com/slon/shad-go/coverme/app"
|
||||||
|
"gitlab.com/slon/shad-go/coverme/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
port := flag.Int("port", 8080, "port to listen")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
db := models.NewInMemoryStorage()
|
||||||
|
app.New(db).Start(*port)
|
||||||
|
}
|
70
coverme/models/storage.go
Normal file
70
coverme/models/storage.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Storage interface {
|
||||||
|
AddTodo(string, string) (*Todo, error)
|
||||||
|
GetTodo(ID) (*Todo, error)
|
||||||
|
GetAll() ([]*Todo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type InMemoryStorage struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
todos map[ID]*Todo
|
||||||
|
|
||||||
|
nextID ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInMemoryStorage() *InMemoryStorage {
|
||||||
|
return &InMemoryStorage{
|
||||||
|
todos: make(map[ID]*Todo),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *InMemoryStorage) AddTodo(title, content string) (*Todo, error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
id := s.nextID
|
||||||
|
s.nextID++
|
||||||
|
|
||||||
|
todo := &Todo{
|
||||||
|
ID: id,
|
||||||
|
Title: title,
|
||||||
|
Content: content,
|
||||||
|
Finished: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.todos[todo.ID] = todo
|
||||||
|
|
||||||
|
return todo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *InMemoryStorage) GetTodo(id ID) (*Todo, error) {
|
||||||
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
|
||||||
|
todo, ok := s.todos[id]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("todo %d not found", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return todo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *InMemoryStorage) GetAll() ([]*Todo, error) {
|
||||||
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
|
||||||
|
var out []*Todo
|
||||||
|
for _, todo := range s.todos {
|
||||||
|
out = append(out, todo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
25
coverme/models/todo.go
Normal file
25
coverme/models/todo.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
type ID int
|
||||||
|
|
||||||
|
type AddRequest struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Todo struct {
|
||||||
|
ID ID `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Finished bool `json:"finished"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Todo) MarkFinished() {
|
||||||
|
t.Finished = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Todo) MarkUnfished() {
|
||||||
|
t.Finished = false
|
||||||
|
}
|
32
coverme/utils/httputils.go
Normal file
32
coverme/utils/httputils.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RespondJSON(w http.ResponseWriter, status int, data interface{}) error {
|
||||||
|
response, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(status)
|
||||||
|
|
||||||
|
_, _ = w.Write(response)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerError(w http.ResponseWriter) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = w.Write([]byte("Server encountered an error."))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BadRequest(w http.ResponseWriter, message string) {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
_, _ = w.Write([]byte(message))
|
||||||
|
}
|
2
go.mod
2
go.mod
|
@ -6,6 +6,8 @@ require (
|
||||||
github.com/go-resty/resty/v2 v2.1.0
|
github.com/go-resty/resty/v2 v2.1.0
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible
|
github.com/gofrs/uuid v3.2.0+incompatible
|
||||||
github.com/golang/mock v1.4.1
|
github.com/golang/mock v1.4.1
|
||||||
|
github.com/gorilla/handlers v1.4.2
|
||||||
|
github.com/gorilla/mux v1.7.4
|
||||||
github.com/spf13/cobra v0.0.5
|
github.com/spf13/cobra v0.0.5
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
go.uber.org/goleak v1.0.0
|
go.uber.org/goleak v1.0.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -26,6 +26,10 @@ github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhS
|
||||||
github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=
|
github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=
|
||||||
github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
|
github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
|
||||||
github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||||
|
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
|
||||||
|
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||||
|
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||||
|
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
|
96
tools/testtool/commands/coverage.go
Normal file
96
tools/testtool/commands/coverage.go
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/tools/cover"
|
||||||
|
)
|
||||||
|
|
||||||
|
// coverageCommentPrefix is a prefix of coverage comment.
|
||||||
|
//
|
||||||
|
// Coverage comment has the following form:
|
||||||
|
//
|
||||||
|
// // min coverage: 80.5%
|
||||||
|
const coverageCommentPrefix = "min coverage: "
|
||||||
|
|
||||||
|
type CoverageRequirements struct {
|
||||||
|
Enabled bool
|
||||||
|
Percent float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCoverageRequirements searches for comment in test files
|
||||||
|
// that specifies test coverage requirements.
|
||||||
|
//
|
||||||
|
// Stops on first matching comment.
|
||||||
|
func getCoverageRequirements(rootPackage string) *CoverageRequirements {
|
||||||
|
files := listTestFiles(rootPackage)
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
if r, _ := searchCoverageComment(f); r.Enabled {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CoverageRequirements{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// searchCoverageComment searches for the first occurrence of the comment of the form
|
||||||
|
//
|
||||||
|
// // min coverage: 80.5%
|
||||||
|
//
|
||||||
|
// Stops on the first matching comment.
|
||||||
|
func searchCoverageComment(fname string) (*CoverageRequirements, error) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
|
||||||
|
f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range f.Comments {
|
||||||
|
t := c.Text()
|
||||||
|
if !strings.HasPrefix(t, coverageCommentPrefix) || !strings.HasSuffix(t, "%\n") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t = strings.TrimPrefix(t, coverageCommentPrefix)
|
||||||
|
t = strings.TrimSuffix(t, "%\n")
|
||||||
|
percent, err := strconv.ParseFloat(t, 64)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if percent < 0 || percent > 100.0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return &CoverageRequirements{Enabled: true, Percent: percent}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CoverageRequirements{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calCoverage calculates coverage percent for given coverage profile.
|
||||||
|
func calCoverage(profile string) (float64, error) {
|
||||||
|
profiles, err := cover.ParseProfiles(profile)
|
||||||
|
if err != nil {
|
||||||
|
return 0.0, fmt.Errorf("cannot parse coverage profile file %s: %w", profile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var total, covered int
|
||||||
|
for _, p := range profiles {
|
||||||
|
for _, block := range p.Blocks {
|
||||||
|
total += block.NumStmt
|
||||||
|
if block.Count > 0 {
|
||||||
|
covered += block.NumStmt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if total == 0 {
|
||||||
|
return 0.0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return float64(covered) / float64(total) * 100, nil
|
||||||
|
}
|
13
tools/testtool/commands/coverage_test.go
Normal file
13
tools/testtool/commands/coverage_test.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_getCoverageRequirements(t *testing.T) {
|
||||||
|
r := getCoverageRequirements("../testdata/coverage/sum")
|
||||||
|
require.True(t, r.Enabled)
|
||||||
|
require.Equal(t, 90.0, r.Percent)
|
||||||
|
}
|
|
@ -242,12 +242,21 @@ func runTests(testDir, privateRepo, problem string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
coverageReq := getCoverageRequirements(privateRepo)
|
||||||
|
if coverageReq.Enabled {
|
||||||
|
log.Printf("required coverage: %.2f%%", coverageReq.Percent)
|
||||||
|
}
|
||||||
|
|
||||||
binariesJSON, _ := json.Marshal(binaries)
|
binariesJSON, _ := json.Marshal(binaries)
|
||||||
|
|
||||||
for testPkg := range testPkgs {
|
for testPkg := range testPkgs {
|
||||||
binPath := filepath.Join(binCache, randomName())
|
binPath := filepath.Join(binCache, randomName())
|
||||||
testBinaries[testPkg] = binPath
|
testBinaries[testPkg] = binPath
|
||||||
if err := runGo("test", "-mod", "readonly", "-tags", "private", "-c", "-o", binPath, testPkg); err != nil {
|
cmd := []string{"test", "-mod", "readonly", "-tags", "private", "-c", "-o", binPath, testPkg}
|
||||||
|
if coverageReq.Enabled {
|
||||||
|
cmd = append(cmd, "-cover")
|
||||||
|
}
|
||||||
|
if err := runGo(cmd...); err != nil {
|
||||||
return fmt.Errorf("error building test in %s: %w", testPkg, err)
|
return fmt.Errorf("error building test in %s: %w", testPkg, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,6 +266,9 @@ func runTests(testDir, privateRepo, problem string) error {
|
||||||
|
|
||||||
{
|
{
|
||||||
cmd := exec.Command(testBinary)
|
cmd := exec.Command(testBinary)
|
||||||
|
if coverageReq.Enabled {
|
||||||
|
cmd = exec.Command(testBinary, "-test.coverprofile", "c.out")
|
||||||
|
}
|
||||||
if currentUserIsRoot() {
|
if currentUserIsRoot() {
|
||||||
if err := sandbox(cmd); err != nil {
|
if err := sandbox(cmd); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -273,6 +285,20 @@ func runTests(testDir, privateRepo, problem string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if coverageReq.Enabled {
|
||||||
|
log.Printf("checking coverage is at least %.2f%% for %s", coverageReq.Percent, testPkg)
|
||||||
|
|
||||||
|
percent, err := calCoverage(filepath.Join(testDir, relPath, "c.out"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if percent < coverageReq.Percent {
|
||||||
|
return fmt.Errorf("poor coverage %.2f%%; expected at least %.2f%%",
|
||||||
|
percent, coverageReq.Percent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
benchCmd := exec.Command(testBinary, "-test.bench=.", "-test.run=^$")
|
benchCmd := exec.Command(testBinary, "-test.bench=.", "-test.run=^$")
|
||||||
if currentUserIsRoot() {
|
if currentUserIsRoot() {
|
||||||
|
|
22
tools/testtool/testdata/coverage/sum/subpkg/empty_test.go
vendored
Normal file
22
tools/testtool/testdata/coverage/sum/subpkg/empty_test.go
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// This is subpkg package comment.
|
||||||
|
package subpkg
|
||||||
|
|
||||||
|
// Incorrect coverage comments:
|
||||||
|
|
||||||
|
// min coverage: -1%
|
||||||
|
|
||||||
|
// min coverage: 100.001%
|
||||||
|
|
||||||
|
// min coverage: 100 %
|
||||||
|
|
||||||
|
// min coverage:10%
|
||||||
|
|
||||||
|
// min coverage: 19%
|
||||||
|
|
||||||
|
// Correct coverage comment:
|
||||||
|
|
||||||
|
// min coverage: 90%
|
||||||
|
|
||||||
|
// Testtool uses first matching comment.
|
||||||
|
|
||||||
|
// min coverage: 91%
|
2
tools/testtool/testdata/coverage/sum/sum.go
vendored
Normal file
2
tools/testtool/testdata/coverage/sum/sum.go
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// This is package comment.
|
||||||
|
package sum
|
8
tools/testtool/testdata/coverage/sum/sum_test.go
vendored
Normal file
8
tools/testtool/testdata/coverage/sum/sum_test.go
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package sum
|
||||||
|
|
||||||
|
// This is a single line comment.
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is multiline
|
||||||
|
comment!
|
||||||
|
*/
|
77
tools/testtool/testdata/submissions/correct/coverme/private/.golangci.yml
vendored
Normal file
77
tools/testtool/testdata/submissions/correct/coverme/private/.golangci.yml
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
# options for analysis running
|
||||||
|
run:
|
||||||
|
# default concurrency is a available CPU number
|
||||||
|
concurrency: 8
|
||||||
|
|
||||||
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
|
deadline: 5m
|
||||||
|
|
||||||
|
# exit code when at least one issue was found, default is 1
|
||||||
|
issues-exit-code: 1
|
||||||
|
|
||||||
|
# include test files or not, default is true
|
||||||
|
tests: true
|
||||||
|
|
||||||
|
|
||||||
|
# output configuration options
|
||||||
|
output:
|
||||||
|
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
|
||||||
|
format: colored-line-number
|
||||||
|
|
||||||
|
# print lines of code with issue, default is true
|
||||||
|
print-issued-lines: true
|
||||||
|
|
||||||
|
# print linter name in the end of issue text, default is true
|
||||||
|
print-linter-name: true
|
||||||
|
|
||||||
|
|
||||||
|
# all available settings of specific linters
|
||||||
|
linters-settings:
|
||||||
|
govet:
|
||||||
|
# report about shadowed variables
|
||||||
|
check-shadowing: true
|
||||||
|
golint:
|
||||||
|
# minimal confidence for issues, default is 0.8
|
||||||
|
min-confidence: 0.8
|
||||||
|
gofmt:
|
||||||
|
# simplify code: gofmt with `-s` option, true by default
|
||||||
|
simplify: true
|
||||||
|
goimports:
|
||||||
|
# put imports beginning with prefix after 3rd-party packages;
|
||||||
|
# it's a comma-separated list of prefixes
|
||||||
|
local-prefixes: gitlab.com
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- errcheck
|
||||||
|
- gofmt
|
||||||
|
- golint
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- scopelint
|
||||||
|
- staticcheck
|
||||||
|
- typecheck
|
||||||
|
- unconvert
|
||||||
|
|
||||||
|
|
||||||
|
issues:
|
||||||
|
# List of regexps of issue texts to exclude, empty list by default.
|
||||||
|
# But independently from this option we use default exclude patterns,
|
||||||
|
# it can be disabled by `exclude-use-default: false`. To list all
|
||||||
|
# excluded by default patterns execute `golangci-lint run --help`
|
||||||
|
exclude:
|
||||||
|
- Using the variable on range scope .* in function literal
|
||||||
|
|
||||||
|
# Independently from option `exclude` we use default exclude patterns,
|
||||||
|
# it can be disabled by this option. To list all
|
||||||
|
# excluded by default patterns execute `golangci-lint run --help`.
|
||||||
|
# Default value for this option is true.
|
||||||
|
exclude-use-default: true
|
||||||
|
|
||||||
|
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||||
|
max-per-linter: 0
|
||||||
|
|
||||||
|
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||||
|
max-same-issues: 0
|
17
tools/testtool/testdata/submissions/correct/coverme/private/coverme/coverage_test.go
vendored
Normal file
17
tools/testtool/testdata/submissions/correct/coverme/private/coverme/coverage_test.go
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package coverme_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"gitlab.com/slon/shad-go/coverme"
|
||||||
|
)
|
||||||
|
|
||||||
|
// min coverage: 70%
|
||||||
|
|
||||||
|
func TestSum(t *testing.T) {
|
||||||
|
require.Equal(t, int64(2), coverme.Sum(1, 1))
|
||||||
|
}
|
14
tools/testtool/testdata/submissions/correct/coverme/private/coverme/service.go
vendored
Normal file
14
tools/testtool/testdata/submissions/correct/coverme/private/coverme/service.go
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package coverme
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func Sum(a, b int64) int64 {
|
||||||
|
if a == 0 {
|
||||||
|
return b
|
||||||
|
} else if a == http.StatusOK {
|
||||||
|
return http.StatusCreated + b - 1
|
||||||
|
}
|
||||||
|
return a + b
|
||||||
|
}
|
15
tools/testtool/testdata/submissions/correct/coverme/private/coverme/solution_test.go
vendored
Normal file
15
tools/testtool/testdata/submissions/correct/coverme/private/coverme/solution_test.go
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// +build solution
|
||||||
|
|
||||||
|
package coverme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSum(t *testing.T) {
|
||||||
|
require.Equal(t, int64(2), Sum(1, 1))
|
||||||
|
require.Equal(t, int64(1), Sum(0, 1))
|
||||||
|
require.Equal(t, int64(202), Sum(200, 2))
|
||||||
|
}
|
12
tools/testtool/testdata/submissions/correct/coverme/private/coverme/subpkg/f.go
vendored
Normal file
12
tools/testtool/testdata/submissions/correct/coverme/private/coverme/subpkg/f.go
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package subpkg
|
||||||
|
|
||||||
|
func AddOne(n int) int {
|
||||||
|
if n == 0 {
|
||||||
|
return 1
|
||||||
|
} else if n == 1 {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
return n + 1
|
||||||
|
}
|
13
tools/testtool/testdata/submissions/correct/coverme/private/coverme/subpkg/f_test.go
vendored
Normal file
13
tools/testtool/testdata/submissions/correct/coverme/private/coverme/subpkg/f_test.go
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package subpkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// min coverage: 100%
|
||||||
|
|
||||||
|
func TestAddOne(t *testing.T) {
|
||||||
|
require.Equal(t, 1, AddOne(0))
|
||||||
|
}
|
5
tools/testtool/testdata/submissions/correct/coverme/private/coverme/subpkg/g.go
vendored
Normal file
5
tools/testtool/testdata/submissions/correct/coverme/private/coverme/subpkg/g.go
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package subpkg
|
||||||
|
|
||||||
|
func AddTwo(n int) int {
|
||||||
|
return AddOne(n + 1)
|
||||||
|
}
|
5
tools/testtool/testdata/submissions/correct/coverme/private/go.mod
vendored
Normal file
5
tools/testtool/testdata/submissions/correct/coverme/private/go.mod
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module gitlab.com/slon/shad-go
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require github.com/stretchr/testify v1.5.1
|
11
tools/testtool/testdata/submissions/correct/coverme/private/go.sum
vendored
Normal file
11
tools/testtool/testdata/submissions/correct/coverme/private/go.sum
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
11
tools/testtool/testdata/submissions/correct/coverme/student/coverme/additional_test.go
vendored
Normal file
11
tools/testtool/testdata/submissions/correct/coverme/student/coverme/additional_test.go
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package coverme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSum2(t *testing.T) {
|
||||||
|
require.Equal(t, int64(202), Sum(200, 2))
|
||||||
|
}
|
17
tools/testtool/testdata/submissions/correct/coverme/student/coverme/coverage_test.go
vendored
Normal file
17
tools/testtool/testdata/submissions/correct/coverme/student/coverme/coverage_test.go
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package coverme_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"gitlab.com/slon/shad-go/coverme"
|
||||||
|
)
|
||||||
|
|
||||||
|
// min coverage: 70%
|
||||||
|
|
||||||
|
func TestSum(t *testing.T) {
|
||||||
|
require.Equal(t, int64(2), coverme.Sum(1, 1))
|
||||||
|
}
|
14
tools/testtool/testdata/submissions/correct/coverme/student/coverme/service.go
vendored
Normal file
14
tools/testtool/testdata/submissions/correct/coverme/student/coverme/service.go
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package coverme
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func Sum(a, b int64) int64 {
|
||||||
|
if a == 0 {
|
||||||
|
return b
|
||||||
|
} else if a == http.StatusOK {
|
||||||
|
return http.StatusCreated + b - 1
|
||||||
|
}
|
||||||
|
return a + b
|
||||||
|
}
|
16
tools/testtool/testdata/submissions/correct/coverme/student/coverme/subpkg/additional_test.go
vendored
Normal file
16
tools/testtool/testdata/submissions/correct/coverme/student/coverme/subpkg/additional_test.go
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package subpkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddOne_2(t *testing.T) {
|
||||||
|
require.Equal(t, 2, AddOne(1))
|
||||||
|
require.Equal(t, 30, AddOne(29))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddTwo(t *testing.T) {
|
||||||
|
require.Equal(t, 4, AddTwo(2))
|
||||||
|
}
|
12
tools/testtool/testdata/submissions/correct/coverme/student/coverme/subpkg/f.go
vendored
Normal file
12
tools/testtool/testdata/submissions/correct/coverme/student/coverme/subpkg/f.go
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package subpkg
|
||||||
|
|
||||||
|
func AddOne(n int) int {
|
||||||
|
if n == 0 {
|
||||||
|
return 1
|
||||||
|
} else if n == 1 {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
return n + 1
|
||||||
|
}
|
13
tools/testtool/testdata/submissions/correct/coverme/student/coverme/subpkg/f_test.go
vendored
Normal file
13
tools/testtool/testdata/submissions/correct/coverme/student/coverme/subpkg/f_test.go
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package subpkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// min coverage: 100%
|
||||||
|
|
||||||
|
func TestAddOne(t *testing.T) {
|
||||||
|
require.Equal(t, 1, AddOne(0))
|
||||||
|
}
|
5
tools/testtool/testdata/submissions/correct/coverme/student/coverme/subpkg/g.go
vendored
Normal file
5
tools/testtool/testdata/submissions/correct/coverme/student/coverme/subpkg/g.go
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package subpkg
|
||||||
|
|
||||||
|
func AddTwo(n int) int {
|
||||||
|
return AddOne(n + 1)
|
||||||
|
}
|
5
tools/testtool/testdata/submissions/correct/coverme/student/go.mod
vendored
Normal file
5
tools/testtool/testdata/submissions/correct/coverme/student/go.mod
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module gitlab.com/slon/shad-go
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require github.com/stretchr/testify v1.5.1
|
10
tools/testtool/testdata/submissions/correct/coverme/student/go.sum
vendored
Normal file
10
tools/testtool/testdata/submissions/correct/coverme/student/go.sum
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
77
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/.golangci.yml
vendored
Normal file
77
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/.golangci.yml
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
# options for analysis running
|
||||||
|
run:
|
||||||
|
# default concurrency is a available CPU number
|
||||||
|
concurrency: 8
|
||||||
|
|
||||||
|
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||||
|
deadline: 5m
|
||||||
|
|
||||||
|
# exit code when at least one issue was found, default is 1
|
||||||
|
issues-exit-code: 1
|
||||||
|
|
||||||
|
# include test files or not, default is true
|
||||||
|
tests: true
|
||||||
|
|
||||||
|
|
||||||
|
# output configuration options
|
||||||
|
output:
|
||||||
|
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
|
||||||
|
format: colored-line-number
|
||||||
|
|
||||||
|
# print lines of code with issue, default is true
|
||||||
|
print-issued-lines: true
|
||||||
|
|
||||||
|
# print linter name in the end of issue text, default is true
|
||||||
|
print-linter-name: true
|
||||||
|
|
||||||
|
|
||||||
|
# all available settings of specific linters
|
||||||
|
linters-settings:
|
||||||
|
govet:
|
||||||
|
# report about shadowed variables
|
||||||
|
check-shadowing: true
|
||||||
|
golint:
|
||||||
|
# minimal confidence for issues, default is 0.8
|
||||||
|
min-confidence: 0.8
|
||||||
|
gofmt:
|
||||||
|
# simplify code: gofmt with `-s` option, true by default
|
||||||
|
simplify: true
|
||||||
|
goimports:
|
||||||
|
# put imports beginning with prefix after 3rd-party packages;
|
||||||
|
# it's a comma-separated list of prefixes
|
||||||
|
local-prefixes: gitlab.com
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- errcheck
|
||||||
|
- gofmt
|
||||||
|
- golint
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- scopelint
|
||||||
|
- staticcheck
|
||||||
|
- typecheck
|
||||||
|
- unconvert
|
||||||
|
|
||||||
|
|
||||||
|
issues:
|
||||||
|
# List of regexps of issue texts to exclude, empty list by default.
|
||||||
|
# But independently from this option we use default exclude patterns,
|
||||||
|
# it can be disabled by `exclude-use-default: false`. To list all
|
||||||
|
# excluded by default patterns execute `golangci-lint run --help`
|
||||||
|
exclude:
|
||||||
|
- Using the variable on range scope .* in function literal
|
||||||
|
|
||||||
|
# Independently from option `exclude` we use default exclude patterns,
|
||||||
|
# it can be disabled by this option. To list all
|
||||||
|
# excluded by default patterns execute `golangci-lint run --help`.
|
||||||
|
# Default value for this option is true.
|
||||||
|
exclude-use-default: true
|
||||||
|
|
||||||
|
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||||
|
max-per-linter: 0
|
||||||
|
|
||||||
|
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||||
|
max-same-issues: 0
|
5
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/go.mod
vendored
Normal file
5
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/go.mod
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module gitlab.com/slon/shad-go
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require github.com/stretchr/testify v1.5.1
|
11
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/go.sum
vendored
Normal file
11
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/go.sum
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
16
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/poorcoverage/coverage_test.go
vendored
Normal file
16
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/poorcoverage/coverage_test.go
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package poorcoverage_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gitlab.com/slon/shad-go/poorcoverage"
|
||||||
|
)
|
||||||
|
|
||||||
|
// min coverage: 100%
|
||||||
|
|
||||||
|
func TestSum(t *testing.T) {
|
||||||
|
require.Equal(t, int64(2), poorcoverage.Sum(1, 1))
|
||||||
|
}
|
10
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/poorcoverage/service.go
vendored
Normal file
10
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/poorcoverage/service.go
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package poorcoverage
|
||||||
|
|
||||||
|
func Sum(a, b int64) int64 {
|
||||||
|
if a == 0 {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return a + b
|
||||||
|
}
|
14
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/poorcoverage/solution_test.go
vendored
Normal file
14
tools/testtool/testdata/submissions/incorrect/poorcoverage/private/poorcoverage/solution_test.go
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// +build solution
|
||||||
|
|
||||||
|
package poorcoverage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSum(t *testing.T) {
|
||||||
|
require.Equal(t, int64(2), Sum(1, 1))
|
||||||
|
require.Equal(t, int64(1), Sum(0, 1))
|
||||||
|
}
|
5
tools/testtool/testdata/submissions/incorrect/poorcoverage/student/go.mod
vendored
Normal file
5
tools/testtool/testdata/submissions/incorrect/poorcoverage/student/go.mod
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module gitlab.com/slon/shad-go
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require github.com/stretchr/testify v1.5.1
|
6
tools/testtool/testdata/submissions/incorrect/poorcoverage/student/go.sum
vendored
Normal file
6
tools/testtool/testdata/submissions/incorrect/poorcoverage/student/go.sum
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
16
tools/testtool/testdata/submissions/incorrect/poorcoverage/student/poorcoverage/coverage_test.go
vendored
Normal file
16
tools/testtool/testdata/submissions/incorrect/poorcoverage/student/poorcoverage/coverage_test.go
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package poorcoverage_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"gitlab.com/slon/shad-go/poorcoverage"
|
||||||
|
)
|
||||||
|
|
||||||
|
// min coverage: 100%
|
||||||
|
|
||||||
|
func TestSum(t *testing.T) {
|
||||||
|
require.Equal(t, int64(2), poorcoverage.Sum(1, 1))
|
||||||
|
}
|
10
tools/testtool/testdata/submissions/incorrect/poorcoverage/student/poorcoverage/service.go
vendored
Normal file
10
tools/testtool/testdata/submissions/incorrect/poorcoverage/student/poorcoverage/service.go
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// +build !change
|
||||||
|
|
||||||
|
package poorcoverage
|
||||||
|
|
||||||
|
func Sum(a, b int64) int64 {
|
||||||
|
if a == 0 {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return a + b
|
||||||
|
}
|
Loading…
Reference in a new issue