// Code generated by mockery v1.0.0. DO NOT EDIT.
package main
import mock "github.com/stretchr/testify/mock"
// mockExecuter is an autogenerated mock type for the executer type
type mockExecuter struct {
mock.Mock
}
// covertOutputToCoverage provides a mock function with given fields: termOutput
func (_m *mockExecuter) covertOutputToCoverage(termOutput string) ([]testLine, error) {
ret := _m.Called(termOutput)
var r0 []testLine
if rf, ok := ret.Get(0).(func(string) []testLine); ok {
r0 = rf(termOutput)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]testLine)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(termOutput)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// runGoTest provides a mock function with given fields:
func (_m *mockExecuter) runGoTest() (string, error) {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// validateTestOutput provides a mock function with given fields: tl, o
func (_m *mockExecuter) validateTestOutput(tl []testLine, o string) error {
ret := _m.Called(tl, o)
var r0 error
if rf, ok := ret.Get(0).(func([]testLine, string) error); ok {
r0 = rf(tl, o)
} else {
r0 = ret.Error(0)
}
return r0
}
// Package main contains code to do with ensuring coverage is over 80%
package main
import (
"fmt"
"log"
"os/exec"
"strconv"
"strings"
)
const (
minPercentCov = 80.0
coverageStringNotFound = -1.0
firstItemIndex = 1
floatByteSize = 64
emptySliceLen = 0
lenOfPercentChar = 1
indexOfEmptyLine = 1
)
var excludedPkgs = map[string]bool{
"golang-repo-template": true,
"golang-repo-template/pkg/fruit": true,
"github.com/gowhale/go-snake": true,
"github.com/gowhale/go-snake/pkg/game": true,
}
func main() {
if err := run(&execute{}); err != nil {
log.Fatalln(err)
}
log.Println("Tests=PASS Coverage=PASS")
}
//go:generate go run github.com/vektra/mockery/cmd/mockery -name executer -inpkg --filename executer_mock.go
type executer interface {
runGoTest() (string, error)
covertOutputToCoverage(termOutput string) ([]testLine, error)
validateTestOutput(tl []testLine, o string) error
}
func run(e executer) error {
output, err := e.runGoTest()
if err != nil {
log.Println(output)
return err
}
tl, err := e.covertOutputToCoverage(output)
if err != nil {
return err
}
return e.validateTestOutput(tl, output)
}
var execCommand = exec.Command
type execute struct{}
func (*execute) runGoTest() (string, error) {
cmd := execCommand("go", "test", "./...", "--cover")
output, err := cmd.CombinedOutput()
termOutput := string(output)
return termOutput, err
}
type testLine struct {
pkgName string
coverage float64
coverLine bool
}
func getCoverage(line string) (testLine, error) {
if !strings.Contains(line, "go: downloading") {
pkgName := strings.Fields(line)[firstItemIndex]
if _, ok := excludedPkgs[pkgName]; !ok {
coverageIndex := strings.Index(line, "coverage: ")
if coverageIndex != coverageStringNotFound {
lineFields := strings.Fields(line[coverageIndex:])
pkgPercentStr := lineFields[firstItemIndex][:len(lineFields[firstItemIndex])-lenOfPercentChar]
pkgPercentFloat, err := strconv.ParseFloat(pkgPercentStr, floatByteSize)
if err != nil {
return testLine{}, err
}
log.Println(pkgPercentStr)
return testLine{pkgName: pkgName, coverage: pkgPercentFloat, coverLine: true}, nil
}
return testLine{pkgName: pkgName, coverage: coverageStringNotFound, coverLine: true}, nil
}
}
return testLine{coverLine: false}, nil
}
func (*execute) covertOutputToCoverage(termOutput string) ([]testLine, error) {
testStruct := []testLine{}
lines := strings.Split(termOutput, "\n")
for _, line := range lines[:len(lines)-indexOfEmptyLine] {
tl, err := getCoverage(line)
if err != nil {
return nil, err
}
if tl.coverLine {
testStruct = append(testStruct, tl)
}
}
return testStruct, nil
}
func (*execute) validateTestOutput(tl []testLine, o string) error {
invalidOutputs := []string{}
for _, line := range tl {
switch {
case !line.coverLine:
invalidOutputs = append(invalidOutputs, fmt.Sprintf("pkg=%s is missing tests", line.pkgName))
case line.coverage < minPercentCov:
invalidOutputs = append(invalidOutputs, fmt.Sprintf("pkg=%s cov=%f under the %f%% minimum line coverage", line.pkgName, line.coverage, minPercentCov))
}
}
if len(invalidOutputs) == emptySliceLen {
return nil
}
log.Println(o)
log.Println("###############################")
log.Println("invalid pkg's:")
for i, invalid := range invalidOutputs {
log.Printf("id=%d problem=%s", i, invalid)
}
log.Println("###############################")
return fmt.Errorf("the following pkgs are not valid: %+v", invalidOutputs)
}
// Package canvas is regarding the thing the snake is placed on
package canvas
import "github.com/gowhale/go-snake/pkg/snake"
// Canvas represents the canvas which the snake is placed
type Canvas struct {
width, height int
snk *snake.Snake
}
// NewCanvas returns a Canvas based on the height, width and snake passed
func NewCanvas(width, height int, snk *snake.Snake) Canvas {
return Canvas{
width: width,
height: height,
snk: snk,
}
}
// GetMatrix returns a matrix made up of 1 and 0
// 1's mean the snake's body lays there. BEWARE!
func (c *Canvas) GetMatrix() [][]int {
matrix := [][]int{}
for y := 0; y < c.height; y++ {
matrix = append(matrix, []int{})
for x := 0; x < c.width; x++ {
matrix[y] = append(matrix[y], 0)
}
}
snakeCords := c.snk.Body()
for _, cord := range snakeCords {
matrix[cord[1]][cord[0]] = 1
}
return matrix
}
// Package snake contains code to do with the Snake 'character'
package snake
import "log"
const (
North = "N" // North means the snake is going up the matrix
South = "S" // South means the snake is down up the matrix
East = "E" // East means the snake is right accross the matrix
West = "W" // West means the snake is left accross the matrix
)
// Snake represents the game character
// Has attributes such as direction and position etc.
type Snake struct {
direction string
xLimit, yLimit int
headCord []int
tailCord [][]int
moves int
grow bool
}
// NewSnake creates a new instance of snake to be place on Canvas
func NewSnake(headCord []int, tailCord [][]int, xLimit, yLimit int) Snake {
return Snake{
direction: North,
headCord: headCord,
tailCord: tailCord,
xLimit: xLimit,
yLimit: yLimit,
moves: 0,
grow: false,
}
}
// Body returns all coords the snake is at
// tail and head!
func (s *Snake) Body() [][]int {
return append([][]int{s.headCord}, s.tailCord...)
}
// SetGrow marks the snake as ready to grow
func (s *Snake) SetGrow(g bool) {
s.grow = g
}
// GetGrow returns if the snake needs to grow
func (s *Snake) GetGrow() bool {
return s.grow
}
func (s *Snake) println() {
log.Printf("direction=%s head=%+v tail=%+v", s.direction, s.headCord, s.tailCord)
}
func (s *Snake) updatePositions(x, y int) {
if !s.GetGrow() {
s.tailCord = s.tailCord[:len(s.tailCord)-1]
} else {
s.SetGrow(false)
}
s.moves++
s.tailCord = append([][]int{{s.headCord[0], s.headCord[1]}}, s.tailCord...)
headX, headY := s.headCord[0]+x, s.headCord[1]+y
if headX < 0 {
headX = s.xLimit - 1
}
if headY < 0 {
headY = s.yLimit - 1
s.headCord[1] = headY
}
if headX > s.xLimit-1 {
headX = 0
}
if headY > s.yLimit-1 {
headY = 0
}
s.headCord = []int{headX, headY}
}
// Score returns the snakes score
func (s *Snake) Score() int {
return s.moves
}
// Dead returns if the snake is dead as it has collided with itself
func (s *Snake) Dead() bool {
allCords := s.Body()
for i := range allCords {
for j := i + 1; j < len(allCords); j++ {
if allCords[i][0] == allCords[j][0] && allCords[i][1] == allCords[j][1] {
return true
}
}
}
return false
}
// NextMove updates the snakes cords based on direction
func (s *Snake) NextMove() {
switch s.direction {
case North:
s.updatePositions(0, -1)
case South:
s.updatePositions(0, 1)
case East:
s.updatePositions(1, 0)
case West:
s.updatePositions(-1, 0)
}
}
var possibleDirections = map[string]map[string]bool{
North: {
North: true,
East: true,
West: true,
},
South: {
South: true,
East: true,
West: true,
},
East: {
North: true,
East: true,
South: true,
},
West: {
North: true,
South: true,
West: true,
},
}
// ChangeDirection changes the direction the snake is heading
// Note: Snake cannot turn in opposite way
func (s *Snake) ChangeDirection(direction string) {
if _, ok := possibleDirections[s.direction][direction]; ok {
s.direction = direction
}
}