func featureInfoFromFeature(f types.Feature) types.FeatureInfo {
	return types.FeatureInfo{
		Name:   f.Name(),
		Labels: f.Labels(),
		Steps:  f.Steps(),
	}
}
featureInfoFromFeature just copies the values from the Feature but, as a new type,
it has less risk of abuse and side-effects. Meant to solely be informational.
package env
import (
	"context"
	"fmt"
	"log"
	"testing"
	"sigs.k8s.io/e2e-framework/pkg/envconf"
	"sigs.k8s.io/e2e-framework/pkg/features"
	"sigs.k8s.io/e2e-framework/pkg/internal/types"
)
type (
	Environment = types.Environment
	Func        = types.EnvFunc
	FeatureFunc = types.FeatureEnvFunc
	actionRole uint8
)
type testEnv struct {
	ctx     context.Context
	cfg     *envconf.Config
	actions []action
}
func New() types.Environment {
	return newTestEnv()
}
func NewWithConfig(cfg *envconf.Config) types.Environment {
	env := newTestEnv()
	env.cfg = cfg
	return env
}
func NewWithKubeConfig(kubeconfigfile string) types.Environment {
	env := newTestEnv()
	cfg := envconf.NewWithKubeConfig(kubeconfigfile)
	env.cfg = cfg
	return env
}
func NewInClusterConfig() types.Environment {
	env := newTestEnv()
	cfg := envconf.NewWithKubeConfig("")
	env.cfg = cfg
	return env
}
func NewWithContext(ctx context.Context, cfg *envconf.Config) (types.Environment, error) {
	if ctx == nil {
		return nil, fmt.Errorf("context is nil")
	}
	if cfg == nil {
		return nil, fmt.Errorf("environment config is nil")
	}
	return &testEnv{ctx: ctx, cfg: cfg}, nil
}
func newTestEnv() *testEnv {
	return &testEnv{
		ctx: context.Background(),
		cfg: envconf.New(),
	}
}
func (e *testEnv) WithContext(ctx context.Context) types.Environment {
	if ctx == nil {
		panic("nil context")  
	}
	env := &testEnv{
		ctx: ctx,
		cfg: e.cfg,
	}
	env.actions = append(env.actions, e.actions...)
	return env
}
func (e *testEnv) Setup(funcs ...Func) types.Environment {
	if len(funcs) == 0 {
		return e
	}
	e.actions = append(e.actions, action{role: roleSetup, funcs: funcs})
	return e
}
func (e *testEnv) BeforeEachTest(funcs ...types.TestEnvFunc) types.Environment {
	if len(funcs) == 0 {
		return e
	}
	e.actions = append(e.actions, action{role: roleBeforeTest, testFuncs: funcs})
	return e
}
func (e *testEnv) BeforeEachFeature(funcs ...FeatureFunc) types.Environment {
	if len(funcs) == 0 {
		return e
	}
	e.actions = append(e.actions, action{role: roleBeforeFeature, featureFuncs: funcs})
	return e
}
func (e *testEnv) AfterEachFeature(funcs ...FeatureFunc) types.Environment {
	if len(funcs) == 0 {
		return e
	}
	e.actions = append(e.actions, action{role: roleAfterFeature, featureFuncs: funcs})
	return e
}
func (e *testEnv) AfterEachTest(funcs ...types.TestEnvFunc) types.Environment {
	if len(funcs) == 0 {
		return e
	}
	e.actions = append(e.actions, action{role: roleAfterTest, testFuncs: funcs})
	return e
}
func (e *testEnv) Test(t *testing.T, testFeatures ...types.Feature) {
	if e.ctx == nil {
		panic("context not set")  
	}
	if len(testFeatures) == 0 {
		t.Log("No test testFeatures provided, skipping test")
		return
	}
	beforeTestActions := e.getBeforeTestActions()
	var err error
	for _, action := range beforeTestActions {
		if e.ctx, err = action.runWithT(e.ctx, e.cfg, t); err != nil {
			t.Fatalf("BeforeEachTest failure: %s", err)
		}
	}
	beforeFeatureActions := e.getBeforeFeatureActions()
	afterFeatureActions := e.getAfterFeatureActions()
	for _, feature := range testFeatures {
		for _, action := range beforeFeatureActions {
			if e.ctx, err = action.runWithFeature(e.ctx, e.cfg, featureInfoFromFeature(feature)); err != nil {
				t.Fatalf("BeforeEachTest failure: %s", err)
			}
		}
		e.ctx = e.execFeature(e.ctx, t, feature)
		for _, action := range afterFeatureActions {
			if e.ctx, err = action.runWithFeature(e.ctx, e.cfg, featureInfoFromFeature(feature)); err != nil {
				t.Fatalf("BeforeEachTest failure: %s", err)
			}
		}
	}
	afterTestActions := e.getAfterTestActions()
	for _, action := range afterTestActions {
		if e.ctx, err = action.runWithT(e.ctx, e.cfg, t); err != nil {
			t.Fatalf("AfterEachTest failure: %s", err)
		}
	}
}
func (e *testEnv) Finish(funcs ...Func) types.Environment {
	if len(funcs) == 0 {
		return e
	}
	e.actions = append(e.actions, action{role: roleFinish, funcs: funcs})
	return e
}
func (e *testEnv) Run(m *testing.M) int {
	if e.ctx == nil {
		panic("context not set")  
	}
	setups := e.getSetupActions()
	var err error
	for _, setup := range setups {
		if e.ctx, err = setup.run(e.ctx, e.cfg); err != nil {
			log.Fatal(err)
		}
	}
	exitCode := m.Run()  
	finishes := e.getFinishActions()
	for _, fin := range finishes {
		if e.ctx, err = fin.run(e.ctx, e.cfg); err != nil {
			log.Println(err)
		}
	}
	return exitCode
}
func (e *testEnv) getActionsByRole(r actionRole) []action {
	if e.actions == nil {
		return nil
	}
	var result []action
	for _, a := range e.actions {
		if a.role == r {
			result = append(result, a)
		}
	}
	return result
}
func (e *testEnv) getSetupActions() []action {
	return e.getActionsByRole(roleSetup)
}
func (e *testEnv) getBeforeTestActions() []action {
	return e.getActionsByRole(roleBeforeTest)
}
func (e *testEnv) getBeforeFeatureActions() []action {
	return e.getActionsByRole(roleBeforeFeature)
}
func (e *testEnv) getAfterFeatureActions() []action {
	return e.getActionsByRole(roleAfterFeature)
}
func (e *testEnv) getAfterTestActions() []action {
	return e.getActionsByRole(roleAfterTest)
}
func (e *testEnv) getFinishActions() []action {
	return e.getActionsByRole(roleFinish)
}
func (e *testEnv) execFeature(ctx context.Context, t *testing.T, f types.Feature) context.Context {
	featName := f.Name()
	t.Run(featName, func(t *testing.T) {
		if e.cfg.SkipFeatureRegex() != nil && e.cfg.SkipFeatureRegex().MatchString(featName) {
			t.Skipf(`Skipping feature "%s": name matched`, featName)
		}
		if e.cfg.FeatureRegex() != nil && !e.cfg.FeatureRegex().MatchString(featName) {
			t.Skipf(`Skipping feature "%s": name not matched`, featName)
		}
		for k, v := range e.cfg.Labels() {
			if f.Labels()[k] != v {
				t.Skipf(`Skipping feature "%s": unmatched label "%s=%s"`, featName, k, f.Labels()[k])
			}
		}
		for k, v := range e.cfg.SkipLabels() {
			if f.Labels()[k] == v {
				t.Skipf(`Skipping feature "%s": matched label provided in --skip-lables "%s=%s"`, featName, k, f.Labels()[k])
			}
		}
		setups := features.GetStepsByLevel(f.Steps(), types.LevelSetup)
		for _, setup := range setups {
			ctx = setup.Func()(ctx, t, e.cfg)
		}
		assessments := features.GetStepsByLevel(f.Steps(), types.LevelAssess)
		for _, assess := range assessments {
			t.Run(assess.Name(), func(t *testing.T) {
				if e.cfg.SkipAssessmentRegex() != nil && e.cfg.SkipAssessmentRegex().MatchString(assess.Name()) {
					t.Skipf(`Skipping assessment "%s": name matched`, assess.Name())
				}
				if e.cfg.AssessmentRegex() != nil && !e.cfg.AssessmentRegex().MatchString(assess.Name()) {
					t.Skipf(`Skipping assessment "%s": name not matched`, assess.Name())
				}
				ctx = assess.Func()(ctx, t, e.cfg)
			})
		}
		teardowns := features.GetStepsByLevel(f.Steps(), types.LevelTeardown)
		for _, teardown := range teardowns {
			ctx = teardown.Func()(ctx, t, e.cfg)
		}
	})
	return ctx
}