args.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package main
  4. import (
  5. "flag"
  6. "io"
  7. "os"
  8. "slices"
  9. "strings"
  10. "testing"
  11. )
  12. // registerTestFlags registers all flags from the testing package with the
  13. // provided flag set. It does so by calling testing.Init() and then iterating
  14. // over all flags registered on flag.CommandLine.
  15. func registerTestFlags(fs *flag.FlagSet) {
  16. testing.Init()
  17. type bv interface {
  18. IsBoolFlag() bool
  19. }
  20. flag.CommandLine.VisitAll(func(f *flag.Flag) {
  21. if b, ok := f.Value.(bv); ok && b.IsBoolFlag() {
  22. fs.Bool(f.Name, f.DefValue == "true", f.Usage)
  23. if name, ok := strings.CutPrefix(f.Name, "test."); ok {
  24. fs.Bool(name, f.DefValue == "true", f.Usage)
  25. }
  26. return
  27. }
  28. // We don't actually care about the value of the flag, so we just
  29. // register it as a string. The values will be passed to `go test` which
  30. // will parse and validate them anyway.
  31. fs.String(f.Name, f.DefValue, f.Usage)
  32. if name, ok := strings.CutPrefix(f.Name, "test."); ok {
  33. fs.String(name, f.DefValue, f.Usage)
  34. }
  35. })
  36. }
  37. // splitArgs splits args into three parts as consumed by go test.
  38. //
  39. // go test [build/test flags] [packages] [build/test flags & test binary flags]
  40. //
  41. // We return these as three slices of strings [pre] [pkgs] [post].
  42. //
  43. // It is used to split the arguments passed to testwrapper into the arguments
  44. // passed to go test and the arguments passed to the tests.
  45. func splitArgs(args []string) (pre, pkgs, post []string, _ error) {
  46. if len(args) == 0 {
  47. return nil, nil, nil, nil
  48. }
  49. fs := newTestFlagSet()
  50. // Parse stops at the first non-flag argument, so this allows us
  51. // to parse those as values and then reconstruct them as args.
  52. if err := fs.Parse(args); err != nil {
  53. return nil, nil, nil, err
  54. }
  55. fs.Visit(func(f *flag.Flag) {
  56. if f.Value.String() != f.DefValue && f.DefValue != "false" {
  57. pre = append(pre, "-"+f.Name, f.Value.String())
  58. } else {
  59. pre = append(pre, "-"+f.Name)
  60. }
  61. })
  62. // fs.Args() now contains [packages]+[build/test flags & test binary flags],
  63. // to split it we need to find the first non-flag argument.
  64. rem := fs.Args()
  65. ix := slices.IndexFunc(rem, func(s string) bool { return strings.HasPrefix(s, "-") })
  66. if ix == -1 {
  67. return pre, rem, nil, nil
  68. }
  69. pkgs = rem[:ix]
  70. post = rem[ix:]
  71. return pre, pkgs, post, nil
  72. }
  73. func newTestFlagSet() *flag.FlagSet {
  74. fs := flag.NewFlagSet("testwrapper", flag.ContinueOnError)
  75. fs.SetOutput(io.Discard)
  76. // Register all flags from the testing package.
  77. registerTestFlags(fs)
  78. // Also register the -exec flag, which is not part of the testing package.
  79. // TODO(maisem): figure out what other flags we need to register explicitly.
  80. fs.String("exec", "", "Command to run tests with")
  81. fs.Bool("race", false, "build with race detector")
  82. return fs
  83. }
  84. // testingVerbose reports whether the test is being run with verbose logging.
  85. var testingVerbose = func() bool {
  86. verbose := false
  87. // Likely doesn't matter, but to be correct follow the go flag parsing logic
  88. // of overriding previous values.
  89. for _, arg := range os.Args[1:] {
  90. switch arg {
  91. case "-test.v", "--test.v",
  92. "-test.v=true", "--test.v=true",
  93. "-v", "--v",
  94. "-v=true", "--v=true":
  95. verbose = true
  96. case "-test.v=false", "--test.v=false",
  97. "-v=false", "--v=false":
  98. verbose = false
  99. }
  100. }
  101. return verbose
  102. }()