Skip to content

Commit

Permalink
add increments 16,17,18
Browse files Browse the repository at this point in the history
  • Loading branch information
bbrodriges committed Jul 12, 2023
1 parent 096f0f4 commit d7db192
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 0 deletions.
102 changes: 102 additions & 0 deletions cmd/shortenertestbeta/iteration16_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package main

// Basic imports
import (
"context"
"os"
"os/exec"
"strings"
"time"

"github.com/google/pprof/profile"
"github.com/stretchr/testify/suite"
)

// Iteration16Suite является сьютом с тестами и состоянием для инкремента
type Iteration16Suite struct {
suite.Suite
}

// SetupSuite подготавливает необходимые зависимости
func (suite *Iteration16Suite) SetupSuite() {
// check required flags
suite.Require().NotEmpty(flagTargetSourcePath, "-source-path non-empty flag required")
// pprof flags
// suite.Require().NotEmpty(flagBaseProfilePath, "-base-profile-path non-empty flag required")
// suite.Require().NotEmpty(flagResultProfilePath, "-result-profile-path non-empty flag required")
// suite.Require().NotEmpty(flagPackageName, "-package-name non-empty flag required")
}

// TestBenchmarksPresence пробует запустить бенчмарки и получить результаты используя стандартный тулинг
func (suite *Iteration16Suite) TestBenchmarksPresence() {
sourcePath := strings.TrimRight(flagTargetSourcePath, "/") + "/..."

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()

// запускаем команду стандартного тулинга
cmd := exec.CommandContext(ctx, "go", "test", "-bench=.", "-benchmem", "-benchtime=100ms", "-run=^$", sourcePath)
cmd.Env = os.Environ() // pass parent envs
out, err := cmd.CombinedOutput()
suite.Assert().NoError(err, "Невозможно получить результат выполнения команды: %s. Вывод:\n\n %s", cmd, out)

// проверяем наличие в выводе ключевых слов
matched := strings.Contains(string(out), "ns/op") && strings.Contains(string(out), "B/op")
found := suite.Assert().True(matched, "Отсутствует информация о наличии бенчмарков в коде, команда: %s", cmd)

if !found {
suite.T().Logf("Вывод команды:\n\n%s", string(out))
}
}

// TestProfilesDiff пробует получить разницу между двумя результатами запуска pprof
func (suite *Iteration16Suite) TestProfilesDiff() {
// тест пока не работает
suite.T().Skip("not implemented")

// открываем базовый профиль
baseFd, err := os.Open(flagBaseProfilePath)
suite.Require().NoError(err, "Невозможно открыть файл с базовым профилем: %s", flagBaseProfilePath)
defer baseFd.Close()

// открываем новый профиль
resultFd, err := os.Open(flagResultProfilePath)
suite.Require().NoError(err, "Невозможно открыть файл с результирующим профилем: %s", flagResultProfilePath)
defer resultFd.Close()

// парсим профили
baseProfile, err := profile.Parse(baseFd)
suite.Assert().NoError(err, "Невозможно распарсить базовый профиль")

resultProfile, err := profile.Parse(resultFd)
suite.Assert().NoError(err, "Невозможно распарсить результирующий профиль")

// инвертируем значения базового профиля, чтобы получить положительную динамику
baseProfile.Scale(-1)
mergedProfile, err := profile.Merge([]*profile.Profile{resultProfile, baseProfile})

// проверяем только функции нашего пакета
for i, sample := range mergedProfile.Sample {
if len(mergedProfile.Function) < i {
break
}

fn := mergedProfile.Function[i]
fName := strings.ToLower(fn.Name)

// пропускаем тестовые функции
if !strings.Contains(fName, flagPackageName) ||
strings.Contains(fName, "test_run") {
continue
}

for _, value := range sample.Value {
// нашли улучшение
if value < 0 {
return
}
}
}

suite.T().Error("Не удалось обнаружить положительных изменений в результирующем профиле")
}
72 changes: 72 additions & 0 deletions cmd/shortenertestbeta/iteration17_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package main

// Basic imports
import (
"context"
"fmt"
"os"
"os/exec"
"time"

"github.com/stretchr/testify/suite"
)

// Iteration17Suite является сьютом с тестами и состоянием для инкремента
type Iteration17Suite struct {
suite.Suite
}

// SetupSuite подготавливает необходимые зависимости
func (suite *Iteration17Suite) SetupSuite() {
suite.Require().NotEmpty(flagTargetSourcePath, "-source-path non-empty flag required")
}

// TestStylingDiff пробует проверить правильность форматирования кода в проекте
func (suite *Iteration17Suite) TestStylingDiff() {
// проверяем форматирование с помощью gofmt
gofmtErr := checkGofmtStyling(flagTargetSourcePath)
// проверяем форматирование с помощью goimports
goimportsErr := checkGoimportsStyling(flagTargetSourcePath)

// нас устраивает любой один форматтер, которые не вернул ошибку
if gofmtErr == nil || goimportsErr == nil {
return
}

suite.Assert().NoError(gofmtErr, "Ошибка проверки форматирования с помощью gofmt")
suite.Assert().NoError(goimportsErr, "Ошибка проверки форматирования с помощью goimports")
}

// checkGofmtStyling возвращает ошибку, если файл не отформатирован согласно правилам gofmt
func checkGofmtStyling(path string) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

cmd := exec.CommandContext(ctx, "gofmt", "-l", "-s", path)
cmd.Env = os.Environ() // pass parent envs
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("Невозможно получить результат выполнения команды: %s. Ошибка: %w", cmd, err)
}
if len(out) > 0 {
return fmt.Errorf("Найдены неотформатированные файлы:\n\n%s", cmd)
}
return nil
}

// checkGoimportsStyling возвращает ошибку, если файл не отформатирован согласно правилам goimports
func checkGoimportsStyling(path string) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

cmd := exec.CommandContext(ctx, "goimports", "-l", path)
cmd.Env = os.Environ() // pass parent envs
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("Невозможно получить результат выполнения команды: %s. Ошибка: %w", cmd, err)
}
if len(out) > 0 {
return fmt.Errorf("Найдены неотформатированные файлы:\n\n%s", cmd)
}
return nil
}
174 changes: 174 additions & 0 deletions cmd/shortenertestbeta/iteration18_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package main

// Basic imports
import (
"errors"
"go/ast"
"go/parser"
"go/token"
"io/fs"
"path/filepath"
"strings"
"testing"

"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)

// Iteration18Suite является сьютом с тестами и состоянием для инкремента
type Iteration18Suite struct {
suite.Suite
}

// SetupSuite подготавливает необходимые зависимости
func (suite *Iteration18Suite) SetupSuite() {
// check required flags
suite.Require().NotEmpty(flagTargetSourcePath, "-source-path non-empty flag required")
}

// TestDocsComments пробует проверить налиция документационных комментариев в коде
func (suite *Iteration18Suite) TestDocsComments() {
var reports []string
err := filepath.WalkDir(flagTargetSourcePath, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}

if d.IsDir() {
// пропускаем служебные директории
if d.Name() == "vendor" || d.Name() == ".git" {
return filepath.SkipDir
}
// проваливаемся в директорию
return nil
}

// проускаем не Go файлы и Go тесты
if !strings.HasSuffix(d.Name(), ".go") ||
strings.HasSuffix(d.Name(), "_test.go") {
return nil
}

reported := undocumentedNodes(suite.T(), path)
if len(reported) > 0 {
reports = append(reports, reported...)
}

return nil
})

suite.NoError(err, "Неожиданная ошибка")
if len(reports) > 0 {
suite.Failf("Найдены файлы с недокументированной сущностями",
strings.Join(reports, "\n"),
)
}
}

// TestExamplePresence пробует рекурсивно найти хотя бы один файл example_test.go в директории с исходным кодом проекта
func (suite *Iteration18Suite) TestExamplePresence() {
err := filepath.WalkDir(flagTargetSourcePath, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}

if d.IsDir() {
// пропускаем служебные директории
if d.Name() == "vendor" || d.Name() == ".git" {
return filepath.SkipDir
}
// проваливаемся в директорию
return nil
}

// проверяем имя файла
if strings.HasSuffix(d.Name(), "example_test.go") {
// возвращаем сигнальную ошибку
return errUsageFound
}

return nil
})

// проверяем сигнальную ошибку
if errors.Is(err, errUsageFound) {
// найден хотя бы один файл
return
}

if err == nil {
suite.T().Error("Не найден ни один файл example_test.go")
return
}
suite.T().Errorf("Неожиданная ошибка при поиске файла example_test.go: %s", err)
}

func undocumentedNodes(t *testing.T, filepath string) []string {
t.Helper()

fset := token.NewFileSet()
sf, err := parser.ParseFile(fset, filepath, nil, parser.ParseComments)
require.NoError(t, err)

// пропускаем автоматически сгенерированные файлы
if isGenerated(sf) {
return nil
}

var reports []string

for _, decl := range sf.Decls {
switch node := decl.(type) {
case *ast.GenDecl:
if undocumentedGenDecl(node) {
reports = append(reports, fset.Position(node.Pos()).String())
}
case *ast.FuncDecl:
if node.Name.IsExported() && node.Doc == nil {
reports = append(reports, fset.Position(node.Pos()).String())
}
}
}

return reports
}

// undocumentedGenDecl проверяет, что экспортированная декларация является недокументированной
func undocumentedGenDecl(decl *ast.GenDecl) bool {
for _, spec := range decl.Specs {
switch st := spec.(type) {
case *ast.TypeSpec:
if st.Name.IsExported() && decl.Doc == nil {
return true
}
case *ast.ValueSpec:
for _, name := range st.Names {
if name.IsExported() && decl.Doc == nil {
return true
}
}
}
}
return false
}

// isGenerated проверяет сгенерирован ли файл автоматически
// на основании правил, описанных в https://golang.org/s/generatedcode.
func isGenerated(file *ast.File) bool {
const (
genCommentPrefix = "// Code generated "
genCommentSuffix = " DO NOT EDIT."
)

for _, group := range file.Comments {
for _, comment := range group.List {
if strings.HasPrefix(comment.Text, genCommentPrefix) &&
strings.HasSuffix(comment.Text, genCommentSuffix) &&
len(comment.Text) > len(genCommentPrefix)+len(genCommentSuffix) {
return true
}
}
}

return false
}
15 changes: 15 additions & 0 deletions cmd/shortenertestbeta/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,18 @@ func TestIteration15(t *testing.T) {
// Запускает тест-сьют для пятнадцатой итерации
suite.Run(t, new(Iteration15Suite))
}

func TestIteration16(t *testing.T) {
// Запускает тест-сьют для шестнадцатой итерации
suite.Run(t, new(Iteration16Suite))
}

func TestIteration17(t *testing.T) {
// Запускает тест-сьют для семнадцатой итерации
suite.Run(t, new(Iteration17Suite))
}

func TestIteration18(t *testing.T) {
// Запускает тест-сьют для восемнадцатой итерации
suite.Run(t, new(Iteration18Suite))
}

0 comments on commit d7db192

Please sign in to comment.