|  | 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() | 
|  | } |