blob: 77b23a4c7736a191cc8a085b45340a6533801f08 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001package spec
2
3import (
4 "fmt"
5 "io"
6 "time"
7
8 "sync"
9
10 "github.com/onsi/ginkgo/internal/containernode"
11 "github.com/onsi/ginkgo/internal/leafnodes"
12 "github.com/onsi/ginkgo/types"
13)
14
15type Spec struct {
16 subject leafnodes.SubjectNode
17 focused bool
18 announceProgress bool
19
20 containers []*containernode.ContainerNode
21
22 state types.SpecState
23 runTime time.Duration
24 startTime time.Time
25 failure types.SpecFailure
26 previousFailures bool
27
28 stateMutex *sync.Mutex
29}
30
31func New(subject leafnodes.SubjectNode, containers []*containernode.ContainerNode, announceProgress bool) *Spec {
32 spec := &Spec{
33 subject: subject,
34 containers: containers,
35 focused: subject.Flag() == types.FlagTypeFocused,
36 announceProgress: announceProgress,
37 stateMutex: &sync.Mutex{},
38 }
39
40 spec.processFlag(subject.Flag())
41 for i := len(containers) - 1; i >= 0; i-- {
42 spec.processFlag(containers[i].Flag())
43 }
44
45 return spec
46}
47
48func (spec *Spec) processFlag(flag types.FlagType) {
49 if flag == types.FlagTypeFocused {
50 spec.focused = true
51 } else if flag == types.FlagTypePending {
52 spec.setState(types.SpecStatePending)
53 }
54}
55
56func (spec *Spec) Skip() {
57 spec.setState(types.SpecStateSkipped)
58}
59
60func (spec *Spec) Failed() bool {
61 return spec.getState() == types.SpecStateFailed || spec.getState() == types.SpecStatePanicked || spec.getState() == types.SpecStateTimedOut
62}
63
64func (spec *Spec) Passed() bool {
65 return spec.getState() == types.SpecStatePassed
66}
67
68func (spec *Spec) Flaked() bool {
69 return spec.getState() == types.SpecStatePassed && spec.previousFailures
70}
71
72func (spec *Spec) Pending() bool {
73 return spec.getState() == types.SpecStatePending
74}
75
76func (spec *Spec) Skipped() bool {
77 return spec.getState() == types.SpecStateSkipped
78}
79
80func (spec *Spec) Focused() bool {
81 return spec.focused
82}
83
84func (spec *Spec) IsMeasurement() bool {
85 return spec.subject.Type() == types.SpecComponentTypeMeasure
86}
87
88func (spec *Spec) Summary(suiteID string) *types.SpecSummary {
89 componentTexts := make([]string, len(spec.containers)+1)
90 componentCodeLocations := make([]types.CodeLocation, len(spec.containers)+1)
91
92 for i, container := range spec.containers {
93 componentTexts[i] = container.Text()
94 componentCodeLocations[i] = container.CodeLocation()
95 }
96
97 componentTexts[len(spec.containers)] = spec.subject.Text()
98 componentCodeLocations[len(spec.containers)] = spec.subject.CodeLocation()
99
100 runTime := spec.runTime
101 if runTime == 0 && !spec.startTime.IsZero() {
102 runTime = time.Since(spec.startTime)
103 }
104
105 return &types.SpecSummary{
106 IsMeasurement: spec.IsMeasurement(),
107 NumberOfSamples: spec.subject.Samples(),
108 ComponentTexts: componentTexts,
109 ComponentCodeLocations: componentCodeLocations,
110 State: spec.getState(),
111 RunTime: runTime,
112 Failure: spec.failure,
113 Measurements: spec.measurementsReport(),
114 SuiteID: suiteID,
115 }
116}
117
118func (spec *Spec) ConcatenatedString() string {
119 s := ""
120 for _, container := range spec.containers {
121 s += container.Text() + " "
122 }
123
124 return s + spec.subject.Text()
125}
126
127func (spec *Spec) Run(writer io.Writer) {
128 if spec.getState() == types.SpecStateFailed {
129 spec.previousFailures = true
130 }
131
132 spec.startTime = time.Now()
133 defer func() {
134 spec.runTime = time.Since(spec.startTime)
135 }()
136
137 for sample := 0; sample < spec.subject.Samples(); sample++ {
138 spec.runSample(sample, writer)
139
140 if spec.getState() != types.SpecStatePassed {
141 return
142 }
143 }
144}
145
146func (spec *Spec) getState() types.SpecState {
147 spec.stateMutex.Lock()
148 defer spec.stateMutex.Unlock()
149 return spec.state
150}
151
152func (spec *Spec) setState(state types.SpecState) {
153 spec.stateMutex.Lock()
154 defer spec.stateMutex.Unlock()
155 spec.state = state
156}
157
158func (spec *Spec) runSample(sample int, writer io.Writer) {
159 spec.setState(types.SpecStatePassed)
160 spec.failure = types.SpecFailure{}
161 innerMostContainerIndexToUnwind := -1
162
163 defer func() {
164 for i := innerMostContainerIndexToUnwind; i >= 0; i-- {
165 container := spec.containers[i]
166 for _, afterEach := range container.SetupNodesOfType(types.SpecComponentTypeAfterEach) {
167 spec.announceSetupNode(writer, "AfterEach", container, afterEach)
168 afterEachState, afterEachFailure := afterEach.Run()
169 if afterEachState != types.SpecStatePassed && spec.getState() == types.SpecStatePassed {
170 spec.setState(afterEachState)
171 spec.failure = afterEachFailure
172 }
173 }
174 }
175 }()
176
177 for i, container := range spec.containers {
178 innerMostContainerIndexToUnwind = i
179 for _, beforeEach := range container.SetupNodesOfType(types.SpecComponentTypeBeforeEach) {
180 spec.announceSetupNode(writer, "BeforeEach", container, beforeEach)
181 s, f := beforeEach.Run()
182 spec.failure = f
183 spec.setState(s)
184 if spec.getState() != types.SpecStatePassed {
185 return
186 }
187 }
188 }
189
190 for _, container := range spec.containers {
191 for _, justBeforeEach := range container.SetupNodesOfType(types.SpecComponentTypeJustBeforeEach) {
192 spec.announceSetupNode(writer, "JustBeforeEach", container, justBeforeEach)
193 s, f := justBeforeEach.Run()
194 spec.failure = f
195 spec.setState(s)
196 if spec.getState() != types.SpecStatePassed {
197 return
198 }
199 }
200 }
201
202 spec.announceSubject(writer, spec.subject)
203 s, f := spec.subject.Run()
204 spec.failure = f
205 spec.setState(s)
206}
207
208func (spec *Spec) announceSetupNode(writer io.Writer, nodeType string, container *containernode.ContainerNode, setupNode leafnodes.BasicNode) {
209 if spec.announceProgress {
210 s := fmt.Sprintf("[%s] %s\n %s\n", nodeType, container.Text(), setupNode.CodeLocation().String())
211 writer.Write([]byte(s))
212 }
213}
214
215func (spec *Spec) announceSubject(writer io.Writer, subject leafnodes.SubjectNode) {
216 if spec.announceProgress {
217 nodeType := ""
218 switch subject.Type() {
219 case types.SpecComponentTypeIt:
220 nodeType = "It"
221 case types.SpecComponentTypeMeasure:
222 nodeType = "Measure"
223 }
224 s := fmt.Sprintf("[%s] %s\n %s\n", nodeType, subject.Text(), subject.CodeLocation().String())
225 writer.Write([]byte(s))
226 }
227}
228
229func (spec *Spec) measurementsReport() map[string]*types.SpecMeasurement {
230 if !spec.IsMeasurement() || spec.Failed() {
231 return map[string]*types.SpecMeasurement{}
232 }
233
234 return spec.subject.(*leafnodes.MeasureNode).MeasurementsReport()
235}