github.com/smithx10/nomad@v0.9.1-rc1/e2e/framework/framework.go (about) 1 package framework 2 3 import ( 4 "flag" 5 "fmt" 6 "reflect" 7 "strings" 8 "testing" 9 ) 10 11 var fEnv = flag.String("env", "", "name of the environment executing against") 12 var fProvider = flag.String("env.provider", "", "cloud provider for which environment is executing against") 13 var fOS = flag.String("env.os", "", "operating system for which the environment is executing against") 14 var fArch = flag.String("env.arch", "", "cpu architecture for which the environment is executing against") 15 var fTags = flag.String("env.tags", "", "comma delimited list of tags associated with the environment") 16 var fLocal = flag.Bool("local", false, "denotes execution is against a local environment") 17 var fSlow = flag.Bool("slow", false, "toggles execution of slow test suites") 18 var fForceRun = flag.Bool("forceRun", false, "if set, skips all environment checks when filtering test suites") 19 20 var pkgFramework = New() 21 22 type Framework struct { 23 suites []*TestSuite 24 provisioner Provisioner 25 env Environment 26 27 isLocalRun bool 28 slow bool 29 force bool 30 } 31 32 // Environment includes information about the target environment the test 33 // framework is targeting. During 'go run', these fields are populated by 34 // the following flags: 35 // 36 // -env <string> "name of the environment executing against" 37 // -env.provider <string> "cloud provider for which the environment is executing against" 38 // -env.os <string> "operating system of environment" 39 // -env.arch <string> "cpu architecture of environment" 40 // -env.tags <string> "comma delimited list of environment tags" 41 // 42 // These flags are not needed when executing locally 43 type Environment struct { 44 Name string 45 Provider string 46 OS string 47 Arch string 48 Tags map[string]struct{} 49 } 50 51 // New creates a Framework 52 func New() *Framework { 53 env := Environment{ 54 Name: *fEnv, 55 Provider: *fProvider, 56 OS: *fOS, 57 Arch: *fArch, 58 Tags: map[string]struct{}{}, 59 } 60 for _, tag := range strings.Split(*fTags, ",") { 61 env.Tags[tag] = struct{}{} 62 } 63 return &Framework{ 64 provisioner: DefaultProvisioner, 65 env: env, 66 isLocalRun: *fLocal, 67 slow: *fSlow, 68 force: *fForceRun, 69 } 70 } 71 72 // AddSuites adds a set of test suites to a Framework 73 func (f *Framework) AddSuites(s ...*TestSuite) *Framework { 74 f.suites = append(f.suites, s...) 75 return f 76 } 77 78 // AddSuites adds a set of test suites to the package scoped Framework 79 func AddSuites(s ...*TestSuite) *Framework { 80 pkgFramework.AddSuites(s...) 81 return pkgFramework 82 } 83 84 // Run starts the test framework, running each TestSuite 85 func (f *Framework) Run(t *testing.T) { 86 for _, s := range f.suites { 87 t.Run(s.Component, func(t *testing.T) { 88 skip, err := f.runSuite(t, s) 89 if skip { 90 t.Skipf("skipping suite '%s': %v", s.Component, err) 91 return 92 } 93 if err != nil { 94 t.Errorf("error starting suite '%s': %v", s.Component, err) 95 } 96 }) 97 } 98 99 } 100 101 // Run starts the package scoped Framework, running each TestSuite 102 func Run(t *testing.T) { 103 pkgFramework.Run(t) 104 } 105 106 // runSuite is called from Framework.Run inside of a sub test for each TestSuite. 107 // If skip is returned as true, the test suite is skipped with the error text added 108 // to the Skip reason 109 // If skip is false and an error is returned, the test suite is failed. 110 func (f *Framework) runSuite(t *testing.T, s *TestSuite) (skip bool, err error) { 111 112 // If -forceRun is set, skip all constraint checks 113 if !f.force { 114 // If this is a local run, check that the suite supports running locally 115 if !s.CanRunLocal && f.isLocalRun { 116 return true, fmt.Errorf("local run detected and suite cannot run locally") 117 } 118 119 // Check that constraints are met 120 if err := s.Constraints.matches(f.env); err != nil { 121 return true, fmt.Errorf("constraint failed: %v", err) 122 } 123 124 // Check the slow toggle and if the suite's slow flag is that same 125 if f.slow != s.Slow { 126 return true, fmt.Errorf("framework slow suite configuration is %v but suite is %v", f.slow, s.Slow) 127 } 128 } 129 130 for _, c := range s.Cases { 131 // The test name is set to the name of the implementing type, including package 132 name := fmt.Sprintf("%T", c) 133 134 // Each TestCase is provisioned a Nomad cluster to test against. 135 // This varies by the provisioner implementation used, currently 136 // the default behavior is for every Test case to use a single shared 137 // Nomad cluster. 138 info, err := f.provisioner.ProvisionCluster(ProvisionerOptions{ 139 Name: name, 140 ExpectConsul: s.Consul, 141 ExpectVault: s.Vault, 142 }) 143 if err != nil { 144 return false, fmt.Errorf("could not provision cluster for case: %v", err) 145 } 146 defer f.provisioner.DestroyCluster(info.ID) 147 c.setClusterInfo(info) 148 149 // Each TestCase runs as a subtest of the TestSuite 150 t.Run(c.Name(), func(t *testing.T) { 151 // If the TestSuite has Parallel set, all cases run in parallel 152 if s.Parallel { 153 t.Parallel() 154 } 155 156 f := newF(t) 157 158 // Check if the case includes a before all function 159 if beforeAllTests, ok := c.(BeforeAllTests); ok { 160 beforeAllTests.BeforeAll(f) 161 } 162 163 // Check if the case includes an after all function at the end 164 defer func() { 165 if afterAllTests, ok := c.(AfterAllTests); ok { 166 afterAllTests.AfterAll(f) 167 } 168 }() 169 170 // Here we need to iterate through the methods of the case to find 171 // ones that are test functions 172 reflectC := reflect.TypeOf(c) 173 for i := 0; i < reflectC.NumMethod(); i++ { 174 method := reflectC.Method(i) 175 if ok := isTestMethod(method.Name); !ok { 176 continue 177 } 178 // Each test is run as its own sub test of the case 179 // Test cases are never parallel 180 t.Run(method.Name, func(t *testing.T) { 181 182 cF := newFFromParent(f, t) 183 if BeforeEachTest, ok := c.(BeforeEachTest); ok { 184 BeforeEachTest.BeforeEach(cF) 185 } 186 defer func() { 187 if afterEachTest, ok := c.(AfterEachTest); ok { 188 afterEachTest.AfterEach(cF) 189 } 190 }() 191 192 //Call the method 193 method.Func.Call([]reflect.Value{reflect.ValueOf(c), reflect.ValueOf(cF)}) 194 }) 195 } 196 }) 197 198 } 199 200 return false, nil 201 } 202 203 func isTestMethod(m string) bool { 204 if !strings.HasPrefix(m, "Test") { 205 return false 206 } 207 // THINKING: adding flag to target a specific test or test regex? 208 return true 209 }