github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/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/hashicorp/nomad/e2e/framework/provisioning" 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 force default no-op provisioning 21 -skipTests skips all tests and only provisions 22 -slow include execution of slow test suites 23 -suite run specified test suite 24 -showHelp shows this help text 25 26 Provisioning flags tell the test runner to pre-provision the cluster before 27 running all tests. These flags can be passed to 'go test'. If no 28 '-provision.*' flag is set, the test runner assumes the cluster has already 29 been configured and uses the test environment's env vars to connect to the 30 cluster. 31 32 -provision.terraform=string pass file generated by terraform output 33 -provision.vagrant=string provision to a single-node vagrant box 34 35 Nomad version flags tell the provisioner to deploy a specific version of 36 Nomad. These flags are all ignored if no '-provision.*' flag is set. 37 Otherwise at most one should be set. 38 39 -nomad.local_file=string provision this specific local binary of Nomad 40 -nomad.sha=string provision this specific sha from S3 41 -nomad.version=string provision this version from releases.hashicorp.com 42 43 TestSuites can request Constraints on the Framework.Environment so that tests 44 are only run in the appropriate conditions. These environment flags provide 45 the information for those constraints. 46 47 -env=string name of the environment 48 -env.arch=string cpu architecture of the targets 49 -env.os=string operating system of the targets 50 -env.provider=string cloud provider of the environment 51 -env.tags=string comma delimited list of tags for the environment 52 53 ` 54 55 var fHelp = flag.Bool("showHelp", false, "print the help screen") 56 var fLocal = flag.Bool("local", false, 57 "denotes execution is against a local environment, forcing default no-op provisioning") 58 var fSlow = flag.Bool("slow", false, "toggles execution of slow test suites") 59 var fForceRun = flag.Bool("forceRun", false, 60 "if set, skips all environment checks when filtering test suites") 61 var fSkipTests = flag.Bool("skipTests", false, "skip all tests and only provision") 62 var fSuite = flag.String("suite", "", "run specified test suite") 63 64 // Provisioning flags 65 var fProvisionVagrant = flag.String("provision.vagrant", "", 66 "run pre-provision to a single-node vagrant host") 67 var fProvisionTerraform = flag.String("provision.terraform", "", 68 "run pre-provision from file generated by 'terraform output provisioning'") 69 70 // Nomad version flags 71 // TODO: these override each other. local_file > sha > version 72 // but we should assert at most 1 is set. 73 var fProvisionNomadLocalBinary = flag.String("nomad.local_file", "", 74 "provision this specific local binary of Nomad (ignored for no-op provisioning).") 75 var fProvisionNomadSha = flag.String("nomad.sha", "", 76 "provision this specific sha of Nomad (ignored for no-op provisioning)") 77 var fProvisionNomadVersion = flag.String("nomad.version", "", 78 "provision this specific release of Nomad (ignored for no-op provisioning)") 79 80 // Environment flags 81 // TODO: 82 // if we have a provisioner, each target has its own environment. it'd 83 // be nice if we could match that environment against the tests so that 84 // we always avoid running tests that don't apply against the 85 // environment, and then have these flags override that behavior. 86 var fEnv = flag.String("env", "", "name of the environment executing against") 87 var fProvider = flag.String("env.provider", "", 88 "cloud provider for which environment is executing against") 89 var fOS = flag.String("env.os", "", 90 "operating system for which the environment is executing against") 91 var fArch = flag.String("env.arch", "", 92 "cpu architecture for which the environment is executing against") 93 var fTags = flag.String("env.tags", "", 94 "comma delimited list of tags associated with the environment") 95 96 type Framework struct { 97 suites []*TestSuite 98 provisioner provisioning.Provisioner 99 env Environment 100 101 isLocalRun bool 102 slow bool 103 force bool 104 skipAll bool 105 suite string 106 } 107 108 // Environment contains information about the test target environment, used 109 // to constrain the set of tests run. See the environment flags above. 110 type Environment struct { 111 Name string 112 Provider string 113 OS string 114 Arch string 115 Tags map[string]struct{} 116 } 117 118 // New creates a Framework 119 func New() *Framework { 120 flag.Parse() 121 if *fHelp { 122 log.Fatal(frameworkHelp) 123 } 124 env := Environment{ 125 Name: *fEnv, 126 Provider: *fProvider, 127 OS: *fOS, 128 Arch: *fArch, 129 Tags: map[string]struct{}{}, 130 } 131 for _, tag := range strings.Split(*fTags, ",") { 132 env.Tags[tag] = struct{}{} 133 } 134 return &Framework{ 135 provisioner: provisioning.NewProvisioner(provisioning.ProvisionerConfig{ 136 IsLocal: *fLocal, 137 VagrantBox: *fProvisionVagrant, 138 TerraformConfig: *fProvisionTerraform, 139 NomadLocalBinary: *fProvisionNomadLocalBinary, 140 NomadSha: *fProvisionNomadSha, 141 NomadVersion: *fProvisionNomadVersion, 142 }), 143 env: env, 144 isLocalRun: *fLocal, 145 slow: *fSlow, 146 force: *fForceRun, 147 skipAll: *fSkipTests, 148 suite: *fSuite, 149 } 150 } 151 152 // AddSuites adds a set of test suites to a Framework 153 func (f *Framework) AddSuites(s ...*TestSuite) *Framework { 154 f.suites = append(f.suites, s...) 155 return f 156 } 157 158 var pkgSuites []*TestSuite 159 160 // AddSuites adds a set of test suites to the package scoped Framework 161 func AddSuites(s ...*TestSuite) { 162 pkgSuites = append(pkgSuites, s...) 163 } 164 165 // Run starts the test framework, running each TestSuite 166 func (f *Framework) Run(t *testing.T) { 167 info, err := f.provisioner.SetupTestRun(t, provisioning.SetupOptions{}) 168 if err != nil { 169 t.Fatalf("could not provision cluster: %v", err) 170 } 171 defer f.provisioner.TearDownTestRun(t, info.ID) 172 173 if f.skipAll { 174 t.Skip("Skipping all tests, -skipTests set") 175 } 176 177 for _, s := range f.suites { 178 t.Run(s.Component, func(t *testing.T) { 179 skip, err := f.runSuite(t, s) 180 if skip { 181 t.Skipf("skipping suite '%s': %v", s.Component, err) 182 return 183 } 184 if err != nil { 185 t.Errorf("error starting suite '%s': %v", s.Component, err) 186 } 187 }) 188 } 189 } 190 191 // Run starts the package scoped Framework, running each TestSuite 192 func Run(t *testing.T) { 193 f := New() 194 f.AddSuites(pkgSuites...) 195 f.Run(t) 196 } 197 198 // runSuite is called from Framework.Run inside of a sub test for each TestSuite. 199 // If skip is returned as true, the test suite is skipped with the error text added 200 // to the Skip reason 201 // If skip is false and an error is returned, the test suite is failed. 202 func (f *Framework) runSuite(t *testing.T, s *TestSuite) (skip bool, err error) { 203 204 // If -forceRun is set, skip all constraint checks 205 if !f.force { 206 // If this is a local run, check that the suite supports running locally 207 if !s.CanRunLocal && f.isLocalRun { 208 return true, fmt.Errorf("local run detected and suite cannot run locally") 209 } 210 211 // Check that constraints are met 212 if err := s.Constraints.matches(f.env); err != nil { 213 return true, fmt.Errorf("constraint failed: %v", err) 214 } 215 216 // Check the slow toggle and if the suite's slow flag is that same 217 if f.slow != s.Slow { 218 return true, fmt.Errorf("framework slow suite configuration is %v but suite is %v", f.slow, s.Slow) 219 } 220 } 221 222 // If -suite is set, skip any suite that is not the one specified. 223 if f.suite != "" && f.suite != s.Component { 224 return true, fmt.Errorf("only running suite %q", f.suite) 225 } 226 227 info, err := f.provisioner.SetupTestSuite(t, provisioning.SetupOptions{ 228 Name: s.Component, 229 ExpectConsul: s.Consul, 230 ExpectVault: s.Vault, 231 }) 232 if err != nil { 233 t.Fatalf("could not provision cluster: %v", err) 234 } 235 defer f.provisioner.TearDownTestSuite(t, info.ID) 236 237 for _, c := range s.Cases { 238 f.runCase(t, s, c) 239 } 240 241 return false, nil 242 } 243 244 func (f *Framework) runCase(t *testing.T, s *TestSuite, c TestCase) { 245 246 // The test name is set to the name of the implementing type, including package 247 name := fmt.Sprintf("%T", c) 248 249 // The ClusterInfo handle should be used by each TestCase to isolate 250 // job/task state created during the test. 251 info, err := f.provisioner.SetupTestCase(t, provisioning.SetupOptions{ 252 Name: name, 253 ExpectConsul: s.Consul, 254 ExpectVault: s.Vault, 255 }) 256 if err != nil { 257 t.Errorf("could not provision cluster for case: %v", err) 258 } 259 defer f.provisioner.TearDownTestCase(t, info.ID) 260 c.setClusterInfo(info) 261 262 // Each TestCase runs as a subtest of the TestSuite 263 t.Run(c.Name(), func(t *testing.T) { 264 // If the TestSuite has Parallel set, all cases run in parallel 265 if s.Parallel { 266 t.Parallel() 267 } 268 269 f := newF(t) 270 271 // Check if the case includes a before all function 272 if beforeAllTests, ok := c.(BeforeAllTests); ok { 273 beforeAllTests.BeforeAll(f) 274 } 275 276 // Check if the case includes an after all function at the end 277 defer func() { 278 if afterAllTests, ok := c.(AfterAllTests); ok { 279 afterAllTests.AfterAll(f) 280 } 281 }() 282 283 // Here we need to iterate through the methods of the case to find 284 // ones that are test functions 285 reflectC := reflect.TypeOf(c) 286 for i := 0; i < reflectC.NumMethod(); i++ { 287 method := reflectC.Method(i) 288 if ok := isTestMethod(method.Name); !ok { 289 continue 290 } 291 // Each test is run as its own sub test of the case 292 // Test cases are never parallel 293 t.Run(method.Name, func(t *testing.T) { 294 295 cF := newFFromParent(f, t) 296 if BeforeEachTest, ok := c.(BeforeEachTest); ok { 297 BeforeEachTest.BeforeEach(cF) 298 } 299 defer func() { 300 if afterEachTest, ok := c.(AfterEachTest); ok { 301 afterEachTest.AfterEach(cF) 302 } 303 }() 304 305 //Call the method 306 method.Func.Call([]reflect.Value{reflect.ValueOf(c), reflect.ValueOf(cF)}) 307 }) 308 } 309 }) 310 } 311 312 func isTestMethod(m string) bool { 313 if !strings.HasPrefix(m, "Test") { 314 return false 315 } 316 317 // THINKING: adding flag to target a specific test or test regex? 318 return true 319 }