Skip to content

Commit

Permalink
refactor usesKnownPackage helper (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
bbrodriges authored May 12, 2023
1 parent 1941b38 commit e39eea6
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 128 deletions.
69 changes: 41 additions & 28 deletions cmd/metricstest/helpers.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package main

import (
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/fs"
Expand All @@ -16,9 +14,32 @@ import (
"golang.org/x/tools/go/ast/astutil"
)

// PackageRules это набор правил для поиска используемых пакетов
type PackageRules []PackageRule

// PackageList возвращает строковый список пакетов из правил
func (p PackageRules) PackageList() string {
var b strings.Builder
for i, r := range p {
if i > 0 {
b.WriteByte('\n')
}
b.WriteString(r.Name)
}
return b.String()
}

// PackageRule это правило для поиска используемого пакета
type PackageRule struct {
// имя пакета для поиска
Name string
// разрешать ли импортирование через "_"
AllowBlank bool
}

// usesKnownPackage проверяет, что хотя бы в одном файле, начиная с указанной директории rootdir,
// содержится хотя бы один пакет из списка knownPackages
func usesKnownPackage(t *testing.T, rootdir string, knownPackages []string) error {
func usesKnownPackage(t *testing.T, rootdir string, rules ...PackageRule) error {
// запускаем рекурсивное прохождение по дереву директорий начиная с rootdir
err := filepath.WalkDir(rootdir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
Expand All @@ -42,59 +63,51 @@ func usesKnownPackage(t *testing.T, rootdir string, knownPackages []string) erro
return nil
}

// пытаемся получить import запись из файла с исходным кодом
spec, err := importsKnownPackage(t, path, knownPackages)
if err != nil {
return fmt.Errorf("невозможно проинспектировать файл %s: %w", path, err)
}
// запись не пустая и импортирована явно
if spec != nil && spec.Name.String() != "_" {
// возвращаем специализированную ошибку, сообщающую о нахождении импорта
return errUsageFound
}

// продолжаем сканирование файлов
return nil
// проверяем файл на наличие искомых пакетов
return importsKnownPackage(t, path, rules...)
})

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

// importsKnownPackage возвращает import запись первого найденного импорта из списка knownPackages в файле filepath
func importsKnownPackage(t *testing.T, filepath string, knownPackages []string) (*ast.ImportSpec, error) {
func importsKnownPackage(t *testing.T, filepath string, rules ...PackageRule) error {
t.Helper()

// парсим файл с исходным кодом
fset := token.NewFileSet()
sf, err := parser.ParseFile(fset, filepath, nil, parser.ImportsOnly)
if err != nil {
return nil, fmt.Errorf("невозможно распарсить файл: %w", err)
return fmt.Errorf("невозможно распарсить файл: %w", err)
}

// итерируемся по import записям файла
importSpecs := astutil.Imports(fset, sf)
// импорты могут быть объединены в группы внутри круглых скобок
for _, paragraph := range importSpecs {
for _, importSpec := range paragraph {
for _, knownImport := range knownPackages {
// проверяем совпадение с искомым импортом
if strings.Contains(importSpec.Path.Value, knownImport) {
return importSpec, nil
for _, rule := range rules {
// пропускаем не подходящий импорт
if !strings.Contains(importSpec.Path.Value, rule.Name) {
continue
}
// найден "пустой" импорт, хотя это запрещено правилом
if importSpec.Name != nil && importSpec.Name.String() == "_" && !rule.AllowBlank {
return nil
}
// возвращаем специализированную ошибку, сообщающую о нахождении импорта
return errUsageFound
}
}
}

return nil, nil
return nil
}

// dumpRequest - это httputil.DumpRequest, который возвращает только байты запроса
Expand Down
14 changes: 7 additions & 7 deletions cmd/metricstest/iteration10a_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Iteration10ASuite struct {
serverAddress string
serverPort string
serverProcess *fork.BackgroundProcess
knownLibraries []string
knownLibraries PackageRules

rnd *rand.Rand
}
Expand All @@ -46,11 +46,11 @@ func (suite *Iteration10ASuite) SetupSuite() {

serverArgs := []string{}

suite.knownLibraries = []string{
"database/sql",
"github.com/jackc/pgx",
"github.com/lib/pq",
"github.com/jmoiron/sqlx",
suite.knownLibraries = PackageRules{
{Name: "database/sql", AllowBlank: true},
{Name: "github.com/jackc/pgx", AllowBlank: true},
{Name: "github.com/lib/pq", AllowBlank: true},
{Name: "github.com/jmoiron/sqlx"},
}

ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
Expand Down Expand Up @@ -115,7 +115,7 @@ func (suite *Iteration10ASuite) serverShutdown() {

// TestLibraryUsage пробует рекурсивно найти использование database/sql хотя бы в одном файле с исходным кодом проекта
func (suite *Iteration10ASuite) TestLibraryUsage() {
err := usesKnownPackage(suite.T(), flagTargetSourcePath, suite.knownLibraries)
err := usesKnownPackage(suite.T(), flagTargetSourcePath, suite.knownLibraries...)
if errors.Is(err, errUsageFound) {
return
}
Expand Down
158 changes: 79 additions & 79 deletions cmd/metricstest/iteration3a_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
type Iteration3ASuite struct {
suite.Suite

knownFrameworks []string
restrictedFrameworks []string
knownFrameworks PackageRules
restrictedFrameworks PackageRules
}

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

// список известных фреймворков
suite.knownFrameworks = []string{
"aahframework.org",
"confetti-framework.com",
"github.com/abahmed/gearbox",
"github.com/aerogo/aero",
"github.com/aisk/vox",
"github.com/ant0ine/go-json-rest",
"github.com/aofei/air",
"github.com/appist/appy",
"github.com/astaxie/beego",
"github.com/beatlabs/patron",
"github.com/bnkamalesh/webgo",
"github.com/claygod/Bxog",
"github.com/claygod/microservice",
"github.com/dimfeld/httptreemux",
"github.com/dinever/golf",
"github.com/fulldump/golax",
"github.com/gernest/alien",
"github.com/gernest/utron",
"github.com/gin-gonic/gin",
"github.com/go-chi/chi",
"github.com/go-goyave/goyave",
"github.com/go-macaron/macaron",
"github.com/go-ozzo/ozzo-routing",
"github.com/go-playground/lars",
"github.com/go-playground/pure",
"github.com/go-zoo/bone",
"github.com/goa-go/goa",
"github.com/goadesign/goa",
"github.com/goanywhere/rex",
"github.com/gocraft/web",
"github.com/gofiber/fiber",
"github.com/goji/goji",
"github.com/gookit/rux",
"github.com/gorilla/mux",
"github.com/goroute/route",
"github.com/gotuna/gotuna",
"github.com/gowww/router",
"github.com/GuilhermeCaruso/bellt",
"github.com/hidevopsio/hiboot",
"github.com/husobee/vestigo",
"github.com/i-love-flamingo/flamingo",
"github.com/i-love-flamingo/flamingo-commerce",
"github.com/ivpusic/neo",
"github.com/julienschmidt/httprouter",
"github.com/labstack/echo",
"github.com/lunny/tango",
"github.com/mustafaakin/gongular",
"github.com/nbari/violetear",
"github.com/nsheremet/banjo",
"github.com/NYTimes/gizmo",
"github.com/paulbellamy/mango",
"github.com/rainycape/gondola",
"github.com/razonyang/fastrouter",
"github.com/rcrowley/go-tigertonic",
"github.com/resoursea/api",
"github.com/revel/revel",
"github.com/rs/xmux",
"github.com/twharmon/goweb",
"github.com/uadmin/uadmin",
"github.com/ungerik/go-rest",
"github.com/vardius/gorouter",
"github.com/VividCortex/siesta",
"github.com/xujiajun/gorouter",
"github.com/xxjwxc/ginrpc",
"github.com/yarf-framework/yarf",
"github.com/zpatrick/fireball",
"gobuffalo.io",
"rest-layer.io",
suite.knownFrameworks = PackageRules{
{Name: "aahframework.org"},
{Name: "confetti-framework.com"},
{Name: "github.com/abahmed/gearbox"},
{Name: "github.com/aerogo/aero"},
{Name: "github.com/aisk/vox"},
{Name: "github.com/ant0ine/go-json-rest"},
{Name: "github.com/aofei/air"},
{Name: "github.com/appist/appy"},
{Name: "github.com/astaxie/beego"},
{Name: "github.com/beatlabs/patron"},
{Name: "github.com/bnkamalesh/webgo"},
{Name: "github.com/claygod/Bxog"},
{Name: "github.com/claygod/microservice"},
{Name: "github.com/dimfeld/httptreemux"},
{Name: "github.com/dinever/golf"},
{Name: "github.com/fulldump/golax"},
{Name: "github.com/gernest/alien"},
{Name: "github.com/gernest/utron"},
{Name: "github.com/gin-gonic/gin"},
{Name: "github.com/go-chi/chi"},
{Name: "github.com/go-goyave/goyave"},
{Name: "github.com/go-macaron/macaron"},
{Name: "github.com/go-ozzo/ozzo-routing"},
{Name: "github.com/go-playground/lars"},
{Name: "github.com/go-playground/pure"},
{Name: "github.com/go-zoo/bone"},
{Name: "github.com/goa-go/goa"},
{Name: "github.com/goadesign/goa"},
{Name: "github.com/goanywhere/rex"},
{Name: "github.com/gocraft/web"},
{Name: "github.com/gofiber/fiber"},
{Name: "github.com/goji/goji"},
{Name: "github.com/gookit/rux"},
{Name: "github.com/gorilla/mux"},
{Name: "github.com/goroute/route"},
{Name: "github.com/gotuna/gotuna"},
{Name: "github.com/gowww/router"},
{Name: "github.com/GuilhermeCaruso/bellt"},
{Name: "github.com/hidevopsio/hiboot"},
{Name: "github.com/husobee/vestigo"},
{Name: "github.com/i-love-flamingo/flamingo"},
{Name: "github.com/i-love-flamingo/flamingo-commerce"},
{Name: "github.com/ivpusic/neo"},
{Name: "github.com/julienschmidt/httprouter"},
{Name: "github.com/labstack/echo"},
{Name: "github.com/lunny/tango"},
{Name: "github.com/mustafaakin/gongular"},
{Name: "github.com/nbari/violetear"},
{Name: "github.com/nsheremet/banjo"},
{Name: "github.com/NYTimes/gizmo"},
{Name: "github.com/paulbellamy/mango"},
{Name: "github.com/rainycape/gondola"},
{Name: "github.com/razonyang/fastrouter"},
{Name: "github.com/rcrowley/go-tigertonic"},
{Name: "github.com/resoursea/api"},
{Name: "github.com/revel/revel"},
{Name: "github.com/rs/xmux"},
{Name: "github.com/twharmon/goweb"},
{Name: "github.com/uadmin/uadmin"},
{Name: "github.com/ungerik/go-rest"},
{Name: "github.com/vardius/gorouter"},
{Name: "github.com/VividCortex/siesta"},
{Name: "github.com/xujiajun/gorouter"},
{Name: "github.com/xxjwxc/ginrpc"},
{Name: "github.com/yarf-framework/yarf"},
{Name: "github.com/zpatrick/fireball"},
{Name: "gobuffalo.io"},
{Name: "rest-layer.io"},
}

// список запрещенных фреймворков
suite.restrictedFrameworks = []string{
"github.com/valyala/fasthttp",
"github.com/fasthttp/router",
suite.restrictedFrameworks = PackageRules{
{Name: "github.com/valyala/fasthttp"},
{Name: "github.com/fasthttp/router"},
}
}

// TestFrameworkUsage пробует рекурсивно найти хотя бы одно использование известных фреймворков в директории с исходным кодом проекта
func (suite *Iteration3ASuite) TestFrameworkUsage() {
// проверяем наличие запрещенных фреймворков
err := usesKnownPackage(suite.T(), flagTargetSourcePath, suite.restrictedFrameworks)
if err == nil {
err := usesKnownPackage(suite.T(), flagTargetSourcePath, suite.restrictedFrameworks...)
if errors.Is(err, errUsageFound) {
suite.T().Errorf("Найдено использование одного из не рекомендуемых фреймворков по пути %s: %s",
flagTargetSourcePath, suite.restrictedFrameworks)
flagTargetSourcePath, suite.restrictedFrameworks.PackageList())
return
}

// проверяем наличие известных фреймворков
err = usesKnownPackage(suite.T(), flagTargetSourcePath, suite.knownFrameworks)
if err == nil {
err = usesKnownPackage(suite.T(), flagTargetSourcePath, suite.knownFrameworks...)
if errors.Is(err, errUsageFound) {
return
}
if errors.Is(err, errUsageNotFound) {
Expand Down
14 changes: 7 additions & 7 deletions cmd/metricstest/iteration6_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
type Iteration6Suite struct {
suite.Suite

knownLoggers []string
knownLoggers PackageRules
}

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

// список известных логгеров
suite.knownLoggers = []string{
"github.com/rs/zerolog",
"go.uber.org/zap",
"github.com/sirupsen/logrus",
suite.knownLoggers = PackageRules{
{Name: "github.com/rs/zerolog"},
{Name: "go.uber.org/zap"},
{Name: "github.com/sirupsen/logrus"},
// "github.com/apex",
// "github.com/go-kit/kit/log",
// "github.com/golang/glog",
Expand All @@ -37,8 +37,8 @@ func (suite *Iteration6Suite) SetupSuite() {
// TestLoggerUsage пробует рекурсивно найти хотя бы одно использование известных логгеров в директории с исходным кодом проекта
func (suite *Iteration6Suite) TestLoggerUsage() {
// проверяем наличие известных фреймворков
err := usesKnownPackage(suite.T(), flagTargetSourcePath, suite.knownLoggers)
if err == nil {
err := usesKnownPackage(suite.T(), flagTargetSourcePath, suite.knownLoggers...)
if errors.Is(err, errUsageFound) {
return
}
if errors.Is(err, errUsageNotFound) {
Expand Down
Loading

0 comments on commit e39eea6

Please sign in to comment.