github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/e2e/framework/framework.go (about) 1 package framework 2 3 import ( 4 "flag" 5 "fmt" 6 "log" 7 "reflect" 8 "strings" 9 "testing" 10 11 "github.com/stretchr/testify/require" 12 ) 13 14 const frameworkHelp = ` 15 Usage: go test -v ./e2e [options] 16 17 These flags are coarse overrides for the test environment. 18 19 -forceRun skip all environment checks when filtering test suites 20 -local denotes execution is against a local environment 21 -slow include execution of slow test suites 22 -suite run specified test suite 23 -showHelp shows this help text 24 25 TestSuites can request Constraints on the Framework.Environment so that tests 26 are only run in the appropriate conditions. These environment flags provide 27 the information for those constraints. 28 29 -env=string name of the environment 30 -env.arch=string cpu architecture of the targets 31 -env.os=string operating system of the targets 32 -env.provider=string cloud provider of the environment 33 -env.tags=string comma delimited list of tags for the environment 34 35 ` 36 37 var fHelp = flag.Bool("showHelp", false, "print the help screen") 38 var fLocal = flag.Bool("local", false, 39 "denotes execution is against a local environment") 40 var fSlow = flag.Bool("slow", false, "toggles execution of slow test suites") 41 var fForceRun = flag.Bool("forceRun", false, 42 "if set, skips all environment checks when filtering test suites") 43 var fSuite = flag.String("suite", "", "run specified test suite") 44 45 // Environment flags 46 // TODO: 47 // It would be nice if we could match the target environment against 48 // the tests automatically so that we always avoid running tests that 49 // don't apply, and then have these flags override that behavior. 50 var fEnv = flag.String("env", "", "name of the environment executing against") 51 var fProvider = flag.String("env.provider", "", 52 "cloud provider for which environment is executing against") 53 var fOS = flag.String("env.os", "", 54 "operating system for which the environment is executing against") 55 var fArch = flag.String("env.arch", "", 56 "cpu architecture for which the environment is executing against") 57 var fTags = flag.String("env.tags", "", 58 "comma delimited list of tags associated with the environment") 59 60 type Framework struct { 61 suites []*TestSuite 62 provisioner Provisioner 63 env Environment 64 65 isLocalRun bool 66 slow bool 67 force bool 68 suite string 69 } 70 71 // Environment contains information about the test target environment, used 72 // to constrain the set of tests run. See the environment flags above. 73 type Environment struct { 74 Name string 75 Provider string 76 OS string 77 Arch string 78 Tags map[string]struct{} 79 } 80 81 // New creates a Framework 82 func New() *Framework { 83 flag.Parse() 84 if *fHelp { 85 log.Fatal(frameworkHelp) 86 } 87 env := Environment{ 88 Name: *fEnv, 89 Provider: *fProvider, 90 OS: *fOS, 91 Arch: *fArch, 92 Tags: map[string]struct{}{}, 93 } 94 for _, tag := range strings.Split(*fTags, ",") { 95 env.Tags[tag] = struct{}{} 96 } 97 return &Framework{ 98 provisioner: DefaultProvisioner, 99 env: env, 100 isLocalRun: *fLocal, 101 slow: *fSlow, 102 force: *fForceRun, 103 suite: *fSuite, 104 } 105 } 106 107 // AddSuites adds a set of test suites to a Framework 108 func (f *Framework) AddSuites(s ...*TestSuite) *Framework { 109 f.suites = append(f.suites, s...) 110 return f 111 } 112 113 var pkgSuites []*TestSuite 114 115 // AddSuites adds a set of test suites to the package scoped Framework 116 func AddSuites(s ...*TestSuite) { 117 pkgSuites = append(pkgSuites, s...) 118 } 119 120 // Run starts the test framework, running each TestSuite 121 func (f *Framework) Run(t *testing.T) { 122 info, err := f.provisioner.SetupTestRun(t, SetupOptions{}) 123 if err != nil { 124 require.NoError(t, err, "could not provision cluster") 125 } 126 defer f.provisioner.TearDownTestRun(t, info.ID) 127 128 for _, s := range f.suites { 129 t.Run(s.Component, func(t *testing.T) { 130 skip, err := f.runSuite(t, s) 131 if skip { 132 t.Skipf("skipping suite '%s': %v", s.Component, err) 133 return 134 } 135 if err != nil { 136 t.Errorf("error starting suite '%s': %v", s.Component, err) 137 } 138 }) 139 } 140 } 141 142 // Run starts the package scoped Framework, running each TestSuite 143 func Run(t *testing.T) { 144 f := New() 145 f.AddSuites(pkgSuites...) 146 f.Run(t) 147 } 148 149 // runSuite is called from Framework.Run inside of a sub test for each TestSuite. 150 // If skip is returned as true, the test suite is skipped with the error text added 151 // to the Skip reason 152 // If skip is false and an error is returned, the test suite is failed. 153 func (f *Framework) runSuite(t *testing.T, s *TestSuite) (skip bool, err error) { 154 155 // If -forceRun is set, skip all constraint checks 156 if !f.force { 157 // If this is a local run, check that the suite supports running locally 158 if !s.CanRunLocal && f.isLocalRun { 159 return true, fmt.Errorf("local run detected and suite cannot run locally") 160 } 161 162 // Check that constraints are met 163 if err := s.Constraints.matches(f.env); err != nil { 164 return true, fmt.Errorf("constraint failed: %v", err) 165 } 166 167 // Check the slow toggle and if the suite's slow flag is that same 168 if f.slow != s.Slow { 169 return true, fmt.Errorf("framework slow suite configuration is %v but suite is %v", f.slow, s.Slow) 170 } 171 } 172 173 // If -suite is set, skip any suite that is not the one specified. 174 if f.suite != "" && f.suite != s.Component { 175 return true, fmt.Errorf("only running suite %q", f.suite) 176 } 177 178 info, err := f.provisioner.SetupTestSuite(t, SetupOptions{ 179 Name: s.Component, 180 ExpectConsul: s.Consul, 181 ExpectVault: s.Vault, 182 }) 183 require.NoError(t, err, "could not provision cluster") 184 defer f.provisioner.TearDownTestSuite(t, info.ID) 185 186 for _, c := range s.Cases { 187 f.runCase(t, s, c) 188 } 189 190 return false, nil 191 } 192 193 func (f *Framework) runCase(t *testing.T, s *TestSuite, c TestCase) { 194 195 // The test name is set to the name of the implementing type, including package 196 name := fmt.Sprintf("%T", c) 197 198 // The ClusterInfo handle should be used by each TestCase to isolate 199 // job/task state created during the test. 200 info, err := f.provisioner.SetupTestCase(t, SetupOptions{ 201 Name: name, 202 ExpectConsul: s.Consul, 203 ExpectVault: s.Vault, 204 }) 205 if err != nil { 206 t.Errorf("could not provision cluster for case: %v", err) 207 } 208 defer f.provisioner.TearDownTestCase(t, info.ID) 209 c.setClusterInfo(info) 210 211 // Each TestCase runs as a subtest of the TestSuite 212 t.Run(c.Name(), func(t *testing.T) { 213 // If the TestSuite has Parallel set, all cases run in parallel 214 if s.Parallel { 215 t.Parallel() 216 } 217 218 f := newF(t) 219 220 // Check if the case includes a before all function 221 if beforeAllTests, ok := c.(BeforeAllTests); ok { 222 beforeAllTests.BeforeAll(f) 223 } 224 225 // Check if the case includes an after all function at the end 226 defer func() { 227 if afterAllTests, ok := c.(AfterAllTests); ok { 228 afterAllTests.AfterAll(f) 229 } 230 }() 231 232 // Here we need to iterate through the methods of the case to find 233 // ones that are test functions 234 reflectC := reflect.TypeOf(c) 235 for i := 0; i < reflectC.NumMethod(); i++ { 236 method := reflectC.Method(i) 237 if ok := isTestMethod(method.Name); !ok { 238 continue 239 } 240 // Each test is run as its own sub test of the case 241 // Test cases are never parallel 242 t.Run(method.Name, func(t *testing.T) { 243 244 cF := newFFromParent(f, t) 245 if BeforeEachTest, ok := c.(BeforeEachTest); ok { 246 BeforeEachTest.BeforeEach(cF) 247 } 248 defer func() { 249 if afterEachTest, ok := c.(AfterEachTest); ok { 250 afterEachTest.AfterEach(cF) 251 } 252 }() 253 254 //Call the method 255 method.Func.Call([]reflect.Value{reflect.ValueOf(c), reflect.ValueOf(cF)}) 256 }) 257 } 258 }) 259 } 260 261 func isTestMethod(m string) bool { 262 return strings.HasPrefix(m, "Test") 263 }