@@ -5,7 +5,13 @@ import (
55 "bytes"
66 "encoding/json"
77 "fmt"
8+ "go/ast"
9+ "go/parser"
10+ "go/printer"
11+ "go/token"
12+ "os"
813 "os/exec"
14+ "path/filepath"
915 "strings"
1016
1117 "github.com/rs/zerolog/log"
@@ -197,3 +203,89 @@ func FilterPackagesWithTests(pkgs []string) []string {
197203 }
198204 return testPkgs
199205}
206+
207+ // SkipTest is a struct that contains the package and name of the test to skip
208+ type SkipTest struct {
209+ Package string
210+ Name string
211+ }
212+
213+ // SkipTests finds all package/test pairs provided and skips them
214+ func SkipTests (repoPath string , testsToSkip []SkipTest ) error {
215+ for _ , testToSkip := range testsToSkip {
216+ cmd := exec .Command ("go" , "list" , "-json" , testToSkip .Package )
217+ out , err := cmd .CombinedOutput ()
218+ if err != nil {
219+ return fmt .Errorf ("error getting package path for %s: %w" , testToSkip .Package , err )
220+ }
221+ packagePath := strings .TrimSpace (string (out ))
222+ log .Debug ().Str ("package" , testToSkip .Package ).Str ("test" , testToSkip .Name ).Str ("packagePath" , packagePath ).Msg ("Skipping test" )
223+
224+ err = filepath .Walk (filepath .Join (repoPath , packagePath ), func (path string , info os.FileInfo , err error ) error {
225+ if err != nil {
226+ return err
227+ }
228+ if info .IsDir () || ! strings .HasSuffix (info .Name (), "_test.go" ) {
229+ return nil
230+ }
231+
232+ fset := token .NewFileSet ()
233+ fileAst , err := parser .ParseFile (fset , path , nil , parser .ParseComments )
234+ if err != nil {
235+ return err
236+ }
237+
238+ found := false
239+ ast .Inspect (fileAst , func (n ast.Node ) bool {
240+ fn , ok := n .(* ast.FuncDecl )
241+ if ! ok || fn .Name .Name != testToSkip .Name {
242+ return true
243+ }
244+ // Check if first parameter is *testing.T
245+ if fn .Type .Params != nil && len (fn .Type .Params .List ) > 0 {
246+ param := fn .Type .Params .List [0 ]
247+ if starExpr , ok := param .Type .(* ast.StarExpr ); ok {
248+ if sel , ok := starExpr .X .(* ast.SelectorExpr ); ok && sel .Sel .Name == "T" {
249+ // Insert t.Skip as first statement
250+ skipStmt := & ast.ExprStmt {
251+ X : & ast.CallExpr {
252+ Fun : & ast.SelectorExpr {
253+ X : ast .NewIdent (param .Names [0 ].Name ),
254+ Sel : ast .NewIdent ("Skip" ),
255+ },
256+ Args : []ast.Expr {
257+ & ast.BasicLit {
258+ Kind : token .STRING ,
259+ Value : "\" skipped by flakeguard\" " ,
260+ },
261+ },
262+ },
263+ }
264+ fn .Body .List = append ([]ast.Stmt {skipStmt }, fn .Body .List ... )
265+ found = true
266+ return false // stop
267+ }
268+ }
269+ }
270+ return true
271+ })
272+
273+ if found {
274+ // Write back the file
275+ var out strings.Builder
276+ if err := printer .Fprint (& out , fset , fileAst ); err != nil {
277+ return err
278+ }
279+ if err := os .WriteFile (path , []byte (out .String ()), info .Mode ()); err != nil {
280+ return err
281+ }
282+ return filepath .SkipDir // stop walking
283+ }
284+ return fmt .Errorf ("test function %s not found in package %s" , testToSkip .Name , testToSkip .Package )
285+ })
286+ if err != nil {
287+ return err
288+ }
289+ }
290+ return nil
291+ }
0 commit comments