| package spec | 
 |  | 
 | import ( | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"time" | 
 |  | 
 | 	"github.com/onsi/ginkgo/internal/containernode" | 
 | 	"github.com/onsi/ginkgo/internal/leafnodes" | 
 | 	"github.com/onsi/ginkgo/types" | 
 | ) | 
 |  | 
 | type Spec struct { | 
 | 	subject          leafnodes.SubjectNode | 
 | 	focused          bool | 
 | 	announceProgress bool | 
 |  | 
 | 	containers []*containernode.ContainerNode | 
 |  | 
 | 	state            types.SpecState | 
 | 	runTime          time.Duration | 
 | 	failure          types.SpecFailure | 
 | 	previousFailures bool | 
 | } | 
 |  | 
 | func New(subject leafnodes.SubjectNode, containers []*containernode.ContainerNode, announceProgress bool) *Spec { | 
 | 	spec := &Spec{ | 
 | 		subject:          subject, | 
 | 		containers:       containers, | 
 | 		focused:          subject.Flag() == types.FlagTypeFocused, | 
 | 		announceProgress: announceProgress, | 
 | 	} | 
 |  | 
 | 	spec.processFlag(subject.Flag()) | 
 | 	for i := len(containers) - 1; i >= 0; i-- { | 
 | 		spec.processFlag(containers[i].Flag()) | 
 | 	} | 
 |  | 
 | 	return spec | 
 | } | 
 |  | 
 | func (spec *Spec) processFlag(flag types.FlagType) { | 
 | 	if flag == types.FlagTypeFocused { | 
 | 		spec.focused = true | 
 | 	} else if flag == types.FlagTypePending { | 
 | 		spec.state = types.SpecStatePending | 
 | 	} | 
 | } | 
 |  | 
 | func (spec *Spec) Skip() { | 
 | 	spec.state = types.SpecStateSkipped | 
 | } | 
 |  | 
 | func (spec *Spec) Failed() bool { | 
 | 	return spec.state == types.SpecStateFailed || spec.state == types.SpecStatePanicked || spec.state == types.SpecStateTimedOut | 
 | } | 
 |  | 
 | func (spec *Spec) Passed() bool { | 
 | 	return spec.state == types.SpecStatePassed | 
 | } | 
 |  | 
 | func (spec *Spec) Flaked() bool { | 
 | 	return spec.state == types.SpecStatePassed && spec.previousFailures | 
 | } | 
 |  | 
 | func (spec *Spec) Pending() bool { | 
 | 	return spec.state == types.SpecStatePending | 
 | } | 
 |  | 
 | func (spec *Spec) Skipped() bool { | 
 | 	return spec.state == types.SpecStateSkipped | 
 | } | 
 |  | 
 | func (spec *Spec) Focused() bool { | 
 | 	return spec.focused | 
 | } | 
 |  | 
 | func (spec *Spec) IsMeasurement() bool { | 
 | 	return spec.subject.Type() == types.SpecComponentTypeMeasure | 
 | } | 
 |  | 
 | func (spec *Spec) Summary(suiteID string) *types.SpecSummary { | 
 | 	componentTexts := make([]string, len(spec.containers)+1) | 
 | 	componentCodeLocations := make([]types.CodeLocation, len(spec.containers)+1) | 
 |  | 
 | 	for i, container := range spec.containers { | 
 | 		componentTexts[i] = container.Text() | 
 | 		componentCodeLocations[i] = container.CodeLocation() | 
 | 	} | 
 |  | 
 | 	componentTexts[len(spec.containers)] = spec.subject.Text() | 
 | 	componentCodeLocations[len(spec.containers)] = spec.subject.CodeLocation() | 
 |  | 
 | 	return &types.SpecSummary{ | 
 | 		IsMeasurement:          spec.IsMeasurement(), | 
 | 		NumberOfSamples:        spec.subject.Samples(), | 
 | 		ComponentTexts:         componentTexts, | 
 | 		ComponentCodeLocations: componentCodeLocations, | 
 | 		State:        spec.state, | 
 | 		RunTime:      spec.runTime, | 
 | 		Failure:      spec.failure, | 
 | 		Measurements: spec.measurementsReport(), | 
 | 		SuiteID:      suiteID, | 
 | 	} | 
 | } | 
 |  | 
 | func (spec *Spec) ConcatenatedString() string { | 
 | 	s := "" | 
 | 	for _, container := range spec.containers { | 
 | 		s += container.Text() + " " | 
 | 	} | 
 |  | 
 | 	return s + spec.subject.Text() | 
 | } | 
 |  | 
 | func (spec *Spec) Run(writer io.Writer) { | 
 | 	if spec.state == types.SpecStateFailed { | 
 | 		spec.previousFailures = true | 
 | 	} | 
 |  | 
 | 	startTime := time.Now() | 
 | 	defer func() { | 
 | 		spec.runTime = time.Since(startTime) | 
 | 	}() | 
 |  | 
 | 	for sample := 0; sample < spec.subject.Samples(); sample++ { | 
 | 		spec.runSample(sample, writer) | 
 |  | 
 | 		if spec.state != types.SpecStatePassed { | 
 | 			return | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func (spec *Spec) runSample(sample int, writer io.Writer) { | 
 | 	spec.state = types.SpecStatePassed | 
 | 	spec.failure = types.SpecFailure{} | 
 | 	innerMostContainerIndexToUnwind := -1 | 
 |  | 
 | 	defer func() { | 
 | 		for i := innerMostContainerIndexToUnwind; i >= 0; i-- { | 
 | 			container := spec.containers[i] | 
 | 			for _, afterEach := range container.SetupNodesOfType(types.SpecComponentTypeAfterEach) { | 
 | 				spec.announceSetupNode(writer, "AfterEach", container, afterEach) | 
 | 				afterEachState, afterEachFailure := afterEach.Run() | 
 | 				if afterEachState != types.SpecStatePassed && spec.state == types.SpecStatePassed { | 
 | 					spec.state = afterEachState | 
 | 					spec.failure = afterEachFailure | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	}() | 
 |  | 
 | 	for i, container := range spec.containers { | 
 | 		innerMostContainerIndexToUnwind = i | 
 | 		for _, beforeEach := range container.SetupNodesOfType(types.SpecComponentTypeBeforeEach) { | 
 | 			spec.announceSetupNode(writer, "BeforeEach", container, beforeEach) | 
 | 			spec.state, spec.failure = beforeEach.Run() | 
 | 			if spec.state != types.SpecStatePassed { | 
 | 				return | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	for _, container := range spec.containers { | 
 | 		for _, justBeforeEach := range container.SetupNodesOfType(types.SpecComponentTypeJustBeforeEach) { | 
 | 			spec.announceSetupNode(writer, "JustBeforeEach", container, justBeforeEach) | 
 | 			spec.state, spec.failure = justBeforeEach.Run() | 
 | 			if spec.state != types.SpecStatePassed { | 
 | 				return | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	spec.announceSubject(writer, spec.subject) | 
 | 	spec.state, spec.failure = spec.subject.Run() | 
 | } | 
 |  | 
 | func (spec *Spec) announceSetupNode(writer io.Writer, nodeType string, container *containernode.ContainerNode, setupNode leafnodes.BasicNode) { | 
 | 	if spec.announceProgress { | 
 | 		s := fmt.Sprintf("[%s] %s\n  %s\n", nodeType, container.Text(), setupNode.CodeLocation().String()) | 
 | 		writer.Write([]byte(s)) | 
 | 	} | 
 | } | 
 |  | 
 | func (spec *Spec) announceSubject(writer io.Writer, subject leafnodes.SubjectNode) { | 
 | 	if spec.announceProgress { | 
 | 		nodeType := "" | 
 | 		switch subject.Type() { | 
 | 		case types.SpecComponentTypeIt: | 
 | 			nodeType = "It" | 
 | 		case types.SpecComponentTypeMeasure: | 
 | 			nodeType = "Measure" | 
 | 		} | 
 | 		s := fmt.Sprintf("[%s] %s\n  %s\n", nodeType, subject.Text(), subject.CodeLocation().String()) | 
 | 		writer.Write([]byte(s)) | 
 | 	} | 
 | } | 
 |  | 
 | func (spec *Spec) measurementsReport() map[string]*types.SpecMeasurement { | 
 | 	if !spec.IsMeasurement() || spec.Failed() { | 
 | 		return map[string]*types.SpecMeasurement{} | 
 | 	} | 
 |  | 
 | 	return spec.subject.(*leafnodes.MeasureNode).MeasurementsReport() | 
 | } |