1 package types
2
3 import (
4 "encoding/json"
5 "fmt"
6 "sort"
7 "strings"
8 "time"
9 )
10
11 const GINKGO_FOCUS_EXIT_CODE = 197
12 const GINKGO_TIME_FORMAT = "01/02/06 15:04:05.999"
13
14
15 type Report struct {
16
17 SuitePath string
18
19
20 SuiteDescription string
21
22
23 SuiteLabels []string
24
25
26
27
28 SuiteSucceeded bool
29
30
31
32 SuiteHasProgrammaticFocus bool
33
34
35
36
37
38
39
40
41 SpecialSuiteFailureReasons []string
42
43
44
45
46 PreRunStats PreRunStats
47
48
49 StartTime time.Time
50 EndTime time.Time
51
52
53 RunTime time.Duration
54
55
56
57
58 SuiteConfig SuiteConfig
59
60
61
62 SpecReports SpecReports
63 }
64
65
66
67
68 type PreRunStats struct {
69 TotalSpecs int
70 SpecsThatWillRun int
71 }
72
73
74
75 func (report Report) Add(other Report) Report {
76 report.SuiteSucceeded = report.SuiteSucceeded && other.SuiteSucceeded
77
78 if other.StartTime.Before(report.StartTime) {
79 report.StartTime = other.StartTime
80 }
81
82 if other.EndTime.After(report.EndTime) {
83 report.EndTime = other.EndTime
84 }
85
86 specialSuiteFailureReasons := []string{}
87 reasonsLookup := map[string]bool{}
88 for _, reasons := range [][]string{report.SpecialSuiteFailureReasons, other.SpecialSuiteFailureReasons} {
89 for _, reason := range reasons {
90 if !reasonsLookup[reason] {
91 reasonsLookup[reason] = true
92 specialSuiteFailureReasons = append(specialSuiteFailureReasons, reason)
93 }
94 }
95 }
96 report.SpecialSuiteFailureReasons = specialSuiteFailureReasons
97 report.RunTime = report.EndTime.Sub(report.StartTime)
98
99 reports := make(SpecReports, len(report.SpecReports)+len(other.SpecReports))
100 copy(reports, report.SpecReports)
101 offset := len(report.SpecReports)
102 for i := range other.SpecReports {
103 reports[i+offset] = other.SpecReports[i]
104 }
105
106 report.SpecReports = reports
107 return report
108 }
109
110
111 type SpecReport struct {
112
113
114 ContainerHierarchyTexts []string
115
116
117
118 ContainerHierarchyLocations []CodeLocation
119
120
121
122 ContainerHierarchyLabels [][]string
123
124
125
126
127 LeafNodeType NodeType
128 LeafNodeLocation CodeLocation
129 LeafNodeLabels []string
130 LeafNodeText string
131
132
133 State SpecState
134
135
136 IsSerial bool
137
138
139 IsInOrderedContainer bool
140
141
142 StartTime time.Time
143 EndTime time.Time
144
145
146 RunTime time.Duration
147
148
149 ParallelProcess int
150
151
152 RunningInParallel bool
153
154
155
156 Failure Failure
157
158
159
160
161 NumAttempts int
162
163
164 MaxFlakeAttempts int
165
166
167 MaxMustPassRepeatedly int
168
169
170 CapturedGinkgoWriterOutput string
171
172
173
174
175 CapturedStdOutErr string
176
177
178 ReportEntries ReportEntries
179
180
181 ProgressReports []ProgressReport
182
183
184 AdditionalFailures []AdditionalFailure
185
186
187 SpecEvents SpecEvents
188 }
189
190 func (report SpecReport) MarshalJSON() ([]byte, error) {
191
192 out := struct {
193 ContainerHierarchyTexts []string
194 ContainerHierarchyLocations []CodeLocation
195 ContainerHierarchyLabels [][]string
196 LeafNodeType NodeType
197 LeafNodeLocation CodeLocation
198 LeafNodeLabels []string
199 LeafNodeText string
200 State SpecState
201 StartTime time.Time
202 EndTime time.Time
203 RunTime time.Duration
204 ParallelProcess int
205 Failure *Failure `json:",omitempty"`
206 NumAttempts int
207 MaxFlakeAttempts int
208 MaxMustPassRepeatedly int
209 CapturedGinkgoWriterOutput string `json:",omitempty"`
210 CapturedStdOutErr string `json:",omitempty"`
211 ReportEntries ReportEntries `json:",omitempty"`
212 ProgressReports []ProgressReport `json:",omitempty"`
213 AdditionalFailures []AdditionalFailure `json:",omitempty"`
214 SpecEvents SpecEvents `json:",omitempty"`
215 }{
216 ContainerHierarchyTexts: report.ContainerHierarchyTexts,
217 ContainerHierarchyLocations: report.ContainerHierarchyLocations,
218 ContainerHierarchyLabels: report.ContainerHierarchyLabels,
219 LeafNodeType: report.LeafNodeType,
220 LeafNodeLocation: report.LeafNodeLocation,
221 LeafNodeLabels: report.LeafNodeLabels,
222 LeafNodeText: report.LeafNodeText,
223 State: report.State,
224 StartTime: report.StartTime,
225 EndTime: report.EndTime,
226 RunTime: report.RunTime,
227 ParallelProcess: report.ParallelProcess,
228 Failure: nil,
229 ReportEntries: nil,
230 NumAttempts: report.NumAttempts,
231 MaxFlakeAttempts: report.MaxFlakeAttempts,
232 MaxMustPassRepeatedly: report.MaxMustPassRepeatedly,
233 CapturedGinkgoWriterOutput: report.CapturedGinkgoWriterOutput,
234 CapturedStdOutErr: report.CapturedStdOutErr,
235 }
236
237 if !report.Failure.IsZero() {
238 out.Failure = &(report.Failure)
239 }
240 if len(report.ReportEntries) > 0 {
241 out.ReportEntries = report.ReportEntries
242 }
243 if len(report.ProgressReports) > 0 {
244 out.ProgressReports = report.ProgressReports
245 }
246 if len(report.AdditionalFailures) > 0 {
247 out.AdditionalFailures = report.AdditionalFailures
248 }
249 if len(report.SpecEvents) > 0 {
250 out.SpecEvents = report.SpecEvents
251 }
252
253 return json.Marshal(out)
254 }
255
256
257
258
259 func (report SpecReport) CombinedOutput() string {
260 if report.CapturedStdOutErr == "" {
261 return report.CapturedGinkgoWriterOutput
262 }
263 if report.CapturedGinkgoWriterOutput == "" {
264 return report.CapturedStdOutErr
265 }
266 return report.CapturedStdOutErr + "\n" + report.CapturedGinkgoWriterOutput
267 }
268
269
270
271 func (report SpecReport) Failed() bool {
272 return report.State.Is(SpecStateFailureStates)
273 }
274
275
276 func (report SpecReport) FullText() string {
277 texts := []string{}
278 texts = append(texts, report.ContainerHierarchyTexts...)
279 if report.LeafNodeText != "" {
280 texts = append(texts, report.LeafNodeText)
281 }
282 return strings.Join(texts, " ")
283 }
284
285
286 func (report SpecReport) Labels() []string {
287 out := []string{}
288 seen := map[string]bool{}
289 for _, labels := range report.ContainerHierarchyLabels {
290 for _, label := range labels {
291 if !seen[label] {
292 seen[label] = true
293 out = append(out, label)
294 }
295 }
296 }
297 for _, label := range report.LeafNodeLabels {
298 if !seen[label] {
299 seen[label] = true
300 out = append(out, label)
301 }
302 }
303
304 return out
305 }
306
307
308 func (report SpecReport) MatchesLabelFilter(query string) (bool, error) {
309 filter, err := ParseLabelFilter(query)
310 if err != nil {
311 return false, err
312 }
313 return filter(report.Labels()), nil
314 }
315
316
317 func (report SpecReport) FileName() string {
318 return report.LeafNodeLocation.FileName
319 }
320
321
322 func (report SpecReport) LineNumber() int {
323 return report.LeafNodeLocation.LineNumber
324 }
325
326
327 func (report SpecReport) FailureMessage() string {
328 return report.Failure.Message
329 }
330
331
332 func (report SpecReport) FailureLocation() CodeLocation {
333 return report.Failure.Location
334 }
335
336
337 func (report SpecReport) Timeline() Timeline {
338 timeline := Timeline{}
339 if !report.Failure.IsZero() {
340 timeline = append(timeline, report.Failure)
341 if report.Failure.AdditionalFailure != nil {
342 timeline = append(timeline, *(report.Failure.AdditionalFailure))
343 }
344 }
345 for _, additionalFailure := range report.AdditionalFailures {
346 timeline = append(timeline, additionalFailure)
347 }
348 for _, reportEntry := range report.ReportEntries {
349 timeline = append(timeline, reportEntry)
350 }
351 for _, progressReport := range report.ProgressReports {
352 timeline = append(timeline, progressReport)
353 }
354 for _, specEvent := range report.SpecEvents {
355 timeline = append(timeline, specEvent)
356 }
357 sort.Sort(timeline)
358 return timeline
359 }
360
361 type SpecReports []SpecReport
362
363
364 func (reports SpecReports) WithLeafNodeType(nodeTypes NodeType) SpecReports {
365 count := 0
366 for i := range reports {
367 if reports[i].LeafNodeType.Is(nodeTypes) {
368 count++
369 }
370 }
371
372 out := make(SpecReports, count)
373 j := 0
374 for i := range reports {
375 if reports[i].LeafNodeType.Is(nodeTypes) {
376 out[j] = reports[i]
377 j++
378 }
379 }
380 return out
381 }
382
383
384 func (reports SpecReports) WithState(states SpecState) SpecReports {
385 count := 0
386 for i := range reports {
387 if reports[i].State.Is(states) {
388 count++
389 }
390 }
391
392 out, j := make(SpecReports, count), 0
393 for i := range reports {
394 if reports[i].State.Is(states) {
395 out[j] = reports[i]
396 j++
397 }
398 }
399 return out
400 }
401
402
403 func (reports SpecReports) CountWithState(states SpecState) int {
404 n := 0
405 for i := range reports {
406 if reports[i].State.Is(states) {
407 n += 1
408 }
409 }
410 return n
411 }
412
413
414 func (reports SpecReports) CountOfFlakedSpecs() int {
415 n := 0
416 for i := range reports {
417 if reports[i].MaxFlakeAttempts > 1 && reports[i].State.Is(SpecStatePassed) && reports[i].NumAttempts > 1 {
418 n += 1
419 }
420 }
421 return n
422 }
423
424
425 func (reports SpecReports) CountOfRepeatedSpecs() int {
426 n := 0
427 for i := range reports {
428 if reports[i].MaxMustPassRepeatedly > 1 && reports[i].State.Is(SpecStateFailureStates) && reports[i].NumAttempts > 1 {
429 n += 1
430 }
431 }
432 return n
433 }
434
435
436 type TimelineLocation struct {
437
438 Offset int `json:",omitempty"`
439
440
441
442 Order int `json:",omitempty"`
443
444 Time time.Time
445 }
446
447
448
449 type TimelineEvent interface {
450 GetTimelineLocation() TimelineLocation
451 }
452
453 type Timeline []TimelineEvent
454
455 func (t Timeline) Len() int { return len(t) }
456 func (t Timeline) Less(i, j int) bool {
457 return t[i].GetTimelineLocation().Order < t[j].GetTimelineLocation().Order
458 }
459 func (t Timeline) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
460 func (t Timeline) WithoutHiddenReportEntries() Timeline {
461 out := Timeline{}
462 for _, event := range t {
463 if reportEntry, isReportEntry := event.(ReportEntry); isReportEntry && reportEntry.Visibility == ReportEntryVisibilityNever {
464 continue
465 }
466 out = append(out, event)
467 }
468 return out
469 }
470
471 func (t Timeline) WithoutVeryVerboseSpecEvents() Timeline {
472 out := Timeline{}
473 for _, event := range t {
474 if specEvent, isSpecEvent := event.(SpecEvent); isSpecEvent && specEvent.IsOnlyVisibleAtVeryVerbose() {
475 continue
476 }
477 out = append(out, event)
478 }
479 return out
480 }
481
482
483 type Failure struct {
484
485
486
487
488 Message string
489
490
491
492 Location CodeLocation
493
494 TimelineLocation TimelineLocation
495
496
497
498 ForwardedPanic string `json:",omitempty"`
499
500
501
502
503
504
505
506
507
508 FailureNodeContext FailureNodeContext `json:",omitempty"`
509
510 FailureNodeType NodeType `json:",omitempty"`
511
512 FailureNodeLocation CodeLocation `json:",omitempty"`
513
514 FailureNodeContainerIndex int `json:",omitempty"`
515
516
517 ProgressReport ProgressReport `json:",omitempty"`
518
519
520 AdditionalFailure *AdditionalFailure `json:",omitempty"`
521 }
522
523 func (f Failure) IsZero() bool {
524 return f.Message == "" && (f.Location == CodeLocation{})
525 }
526
527 func (f Failure) GetTimelineLocation() TimelineLocation {
528 return f.TimelineLocation
529 }
530
531
532 type FailureNodeContext uint
533
534 const (
535 FailureNodeContextInvalid FailureNodeContext = iota
536
537 FailureNodeIsLeafNode
538 FailureNodeAtTopLevel
539 FailureNodeInContainer
540 )
541
542 var fncEnumSupport = NewEnumSupport(map[uint]string{
543 uint(FailureNodeContextInvalid): "INVALID FAILURE NODE CONTEXT",
544 uint(FailureNodeIsLeafNode): "leaf-node",
545 uint(FailureNodeAtTopLevel): "top-level",
546 uint(FailureNodeInContainer): "in-container",
547 })
548
549 func (fnc FailureNodeContext) String() string {
550 return fncEnumSupport.String(uint(fnc))
551 }
552 func (fnc *FailureNodeContext) UnmarshalJSON(b []byte) error {
553 out, err := fncEnumSupport.UnmarshJSON(b)
554 *fnc = FailureNodeContext(out)
555 return err
556 }
557 func (fnc FailureNodeContext) MarshalJSON() ([]byte, error) {
558 return fncEnumSupport.MarshJSON(uint(fnc))
559 }
560
561
562
563
564 type AdditionalFailure struct {
565 State SpecState
566 Failure Failure
567 }
568
569 func (f AdditionalFailure) GetTimelineLocation() TimelineLocation {
570 return f.Failure.TimelineLocation
571 }
572
573
574
575 type SpecState uint
576
577 const (
578 SpecStateInvalid SpecState = 0
579
580 SpecStatePending SpecState = 1 << iota
581 SpecStateSkipped
582 SpecStatePassed
583 SpecStateFailed
584 SpecStateAborted
585 SpecStatePanicked
586 SpecStateInterrupted
587 SpecStateTimedout
588 )
589
590 var ssEnumSupport = NewEnumSupport(map[uint]string{
591 uint(SpecStateInvalid): "INVALID SPEC STATE",
592 uint(SpecStatePending): "pending",
593 uint(SpecStateSkipped): "skipped",
594 uint(SpecStatePassed): "passed",
595 uint(SpecStateFailed): "failed",
596 uint(SpecStateAborted): "aborted",
597 uint(SpecStatePanicked): "panicked",
598 uint(SpecStateInterrupted): "interrupted",
599 uint(SpecStateTimedout): "timedout",
600 })
601
602 func (ss SpecState) String() string {
603 return ssEnumSupport.String(uint(ss))
604 }
605 func (ss SpecState) GomegaString() string {
606 return ssEnumSupport.String(uint(ss))
607 }
608 func (ss *SpecState) UnmarshalJSON(b []byte) error {
609 out, err := ssEnumSupport.UnmarshJSON(b)
610 *ss = SpecState(out)
611 return err
612 }
613 func (ss SpecState) MarshalJSON() ([]byte, error) {
614 return ssEnumSupport.MarshJSON(uint(ss))
615 }
616
617 var SpecStateFailureStates = SpecStateFailed | SpecStateTimedout | SpecStateAborted | SpecStatePanicked | SpecStateInterrupted
618
619 func (ss SpecState) Is(states SpecState) bool {
620 return ss&states != 0
621 }
622
623
624 type ProgressReport struct {
625 Message string `json:",omitempty"`
626 ParallelProcess int `json:",omitempty"`
627 RunningInParallel bool `json:",omitempty"`
628
629 ContainerHierarchyTexts []string `json:",omitempty"`
630 LeafNodeText string `json:",omitempty"`
631 LeafNodeLocation CodeLocation `json:",omitempty"`
632 SpecStartTime time.Time `json:",omitempty"`
633
634 CurrentNodeType NodeType `json:",omitempty"`
635 CurrentNodeText string `json:",omitempty"`
636 CurrentNodeLocation CodeLocation `json:",omitempty"`
637 CurrentNodeStartTime time.Time `json:",omitempty"`
638
639 CurrentStepText string `json:",omitempty"`
640 CurrentStepLocation CodeLocation `json:",omitempty"`
641 CurrentStepStartTime time.Time `json:",omitempty"`
642
643 AdditionalReports []string `json:",omitempty"`
644
645 CapturedGinkgoWriterOutput string `json:",omitempty"`
646 TimelineLocation TimelineLocation `json:",omitempty"`
647
648 Goroutines []Goroutine `json:",omitempty"`
649 }
650
651 func (pr ProgressReport) IsZero() bool {
652 return pr.CurrentNodeType == NodeTypeInvalid
653 }
654
655 func (pr ProgressReport) Time() time.Time {
656 return pr.TimelineLocation.Time
657 }
658
659 func (pr ProgressReport) SpecGoroutine() Goroutine {
660 for _, goroutine := range pr.Goroutines {
661 if goroutine.IsSpecGoroutine {
662 return goroutine
663 }
664 }
665 return Goroutine{}
666 }
667
668 func (pr ProgressReport) HighlightedGoroutines() []Goroutine {
669 out := []Goroutine{}
670 for _, goroutine := range pr.Goroutines {
671 if goroutine.IsSpecGoroutine || !goroutine.HasHighlights() {
672 continue
673 }
674 out = append(out, goroutine)
675 }
676 return out
677 }
678
679 func (pr ProgressReport) OtherGoroutines() []Goroutine {
680 out := []Goroutine{}
681 for _, goroutine := range pr.Goroutines {
682 if goroutine.IsSpecGoroutine || goroutine.HasHighlights() {
683 continue
684 }
685 out = append(out, goroutine)
686 }
687 return out
688 }
689
690 func (pr ProgressReport) WithoutCapturedGinkgoWriterOutput() ProgressReport {
691 out := pr
692 out.CapturedGinkgoWriterOutput = ""
693 return out
694 }
695
696 func (pr ProgressReport) WithoutOtherGoroutines() ProgressReport {
697 out := pr
698 filteredGoroutines := []Goroutine{}
699 for _, goroutine := range pr.Goroutines {
700 if goroutine.IsSpecGoroutine || goroutine.HasHighlights() {
701 filteredGoroutines = append(filteredGoroutines, goroutine)
702 }
703 }
704 out.Goroutines = filteredGoroutines
705 return out
706 }
707
708 func (pr ProgressReport) GetTimelineLocation() TimelineLocation {
709 return pr.TimelineLocation
710 }
711
712 type Goroutine struct {
713 ID uint64
714 State string
715 Stack []FunctionCall
716 IsSpecGoroutine bool
717 }
718
719 func (g Goroutine) IsZero() bool {
720 return g.ID == 0
721 }
722
723 func (g Goroutine) HasHighlights() bool {
724 for _, fc := range g.Stack {
725 if fc.Highlight {
726 return true
727 }
728 }
729
730 return false
731 }
732
733 type FunctionCall struct {
734 Function string
735 Filename string
736 Line int
737 Highlight bool `json:",omitempty"`
738 Source []string `json:",omitempty"`
739 SourceHighlight int `json:",omitempty"`
740 }
741
742
743 type NodeType uint
744
745 const (
746 NodeTypeInvalid NodeType = 0
747
748 NodeTypeContainer NodeType = 1 << iota
749 NodeTypeIt
750
751 NodeTypeBeforeEach
752 NodeTypeJustBeforeEach
753 NodeTypeAfterEach
754 NodeTypeJustAfterEach
755
756 NodeTypeBeforeAll
757 NodeTypeAfterAll
758
759 NodeTypeBeforeSuite
760 NodeTypeSynchronizedBeforeSuite
761 NodeTypeAfterSuite
762 NodeTypeSynchronizedAfterSuite
763
764 NodeTypeReportBeforeEach
765 NodeTypeReportAfterEach
766 NodeTypeReportBeforeSuite
767 NodeTypeReportAfterSuite
768
769 NodeTypeCleanupInvalid
770 NodeTypeCleanupAfterEach
771 NodeTypeCleanupAfterAll
772 NodeTypeCleanupAfterSuite
773 )
774
775 var NodeTypesForContainerAndIt = NodeTypeContainer | NodeTypeIt
776 var NodeTypesForSuiteLevelNodes = NodeTypeBeforeSuite | NodeTypeSynchronizedBeforeSuite | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite | NodeTypeCleanupAfterSuite
777 var NodeTypesAllowedDuringCleanupInterrupt = NodeTypeAfterEach | NodeTypeJustAfterEach | NodeTypeAfterAll | NodeTypeAfterSuite | NodeTypeSynchronizedAfterSuite | NodeTypeCleanupAfterEach | NodeTypeCleanupAfterAll | NodeTypeCleanupAfterSuite
778 var NodeTypesAllowedDuringReportInterrupt = NodeTypeReportBeforeEach | NodeTypeReportAfterEach | NodeTypeReportBeforeSuite | NodeTypeReportAfterSuite
779
780 var ntEnumSupport = NewEnumSupport(map[uint]string{
781 uint(NodeTypeInvalid): "INVALID NODE TYPE",
782 uint(NodeTypeContainer): "Container",
783 uint(NodeTypeIt): "It",
784 uint(NodeTypeBeforeEach): "BeforeEach",
785 uint(NodeTypeJustBeforeEach): "JustBeforeEach",
786 uint(NodeTypeAfterEach): "AfterEach",
787 uint(NodeTypeJustAfterEach): "JustAfterEach",
788 uint(NodeTypeBeforeAll): "BeforeAll",
789 uint(NodeTypeAfterAll): "AfterAll",
790 uint(NodeTypeBeforeSuite): "BeforeSuite",
791 uint(NodeTypeSynchronizedBeforeSuite): "SynchronizedBeforeSuite",
792 uint(NodeTypeAfterSuite): "AfterSuite",
793 uint(NodeTypeSynchronizedAfterSuite): "SynchronizedAfterSuite",
794 uint(NodeTypeReportBeforeEach): "ReportBeforeEach",
795 uint(NodeTypeReportAfterEach): "ReportAfterEach",
796 uint(NodeTypeReportBeforeSuite): "ReportBeforeSuite",
797 uint(NodeTypeReportAfterSuite): "ReportAfterSuite",
798 uint(NodeTypeCleanupInvalid): "DeferCleanup",
799 uint(NodeTypeCleanupAfterEach): "DeferCleanup (Each)",
800 uint(NodeTypeCleanupAfterAll): "DeferCleanup (All)",
801 uint(NodeTypeCleanupAfterSuite): "DeferCleanup (Suite)",
802 })
803
804 func (nt NodeType) String() string {
805 return ntEnumSupport.String(uint(nt))
806 }
807 func (nt *NodeType) UnmarshalJSON(b []byte) error {
808 out, err := ntEnumSupport.UnmarshJSON(b)
809 *nt = NodeType(out)
810 return err
811 }
812 func (nt NodeType) MarshalJSON() ([]byte, error) {
813 return ntEnumSupport.MarshJSON(uint(nt))
814 }
815
816 func (nt NodeType) Is(nodeTypes NodeType) bool {
817 return nt&nodeTypes != 0
818 }
819
820
823 type SpecEvent struct {
824 SpecEventType SpecEventType
825
826 CodeLocation CodeLocation
827 TimelineLocation TimelineLocation
828
829 Message string `json:",omitempty"`
830 Duration time.Duration `json:",omitempty"`
831 NodeType NodeType `json:",omitempty"`
832 Attempt int `json:",omitempty"`
833 }
834
835 func (se SpecEvent) GetTimelineLocation() TimelineLocation {
836 return se.TimelineLocation
837 }
838
839 func (se SpecEvent) IsOnlyVisibleAtVeryVerbose() bool {
840 return se.SpecEventType.Is(SpecEventByEnd | SpecEventNodeStart | SpecEventNodeEnd)
841 }
842
843 func (se SpecEvent) GomegaString() string {
844 out := &strings.Builder{}
845 out.WriteString("[" + se.SpecEventType.String() + " SpecEvent] ")
846 if se.Message != "" {
847 out.WriteString("Message=")
848 out.WriteString(`"` + se.Message + `",`)
849 }
850 if se.Duration != 0 {
851 out.WriteString("Duration=" + se.Duration.String() + ",")
852 }
853 if se.NodeType != NodeTypeInvalid {
854 out.WriteString("NodeType=" + se.NodeType.String() + ",")
855 }
856 if se.Attempt != 0 {
857 out.WriteString(fmt.Sprintf("Attempt=%d", se.Attempt) + ",")
858 }
859 out.WriteString("CL=" + se.CodeLocation.String() + ",")
860 out.WriteString(fmt.Sprintf("TL.Offset=%d", se.TimelineLocation.Offset))
861
862 return out.String()
863 }
864
865 type SpecEvents []SpecEvent
866
867 func (se SpecEvents) WithType(seType SpecEventType) SpecEvents {
868 out := SpecEvents{}
869 for _, event := range se {
870 if event.SpecEventType.Is(seType) {
871 out = append(out, event)
872 }
873 }
874 return out
875 }
876
877 type SpecEventType uint
878
879 const (
880 SpecEventInvalid SpecEventType = 0
881
882 SpecEventByStart SpecEventType = 1 << iota
883 SpecEventByEnd
884 SpecEventNodeStart
885 SpecEventNodeEnd
886 SpecEventSpecRepeat
887 SpecEventSpecRetry
888 )
889
890 var seEnumSupport = NewEnumSupport(map[uint]string{
891 uint(SpecEventInvalid): "INVALID SPEC EVENT",
892 uint(SpecEventByStart): "By",
893 uint(SpecEventByEnd): "By (End)",
894 uint(SpecEventNodeStart): "Node",
895 uint(SpecEventNodeEnd): "Node (End)",
896 uint(SpecEventSpecRepeat): "Repeat",
897 uint(SpecEventSpecRetry): "Retry",
898 })
899
900 func (se SpecEventType) String() string {
901 return seEnumSupport.String(uint(se))
902 }
903 func (se *SpecEventType) UnmarshalJSON(b []byte) error {
904 out, err := seEnumSupport.UnmarshJSON(b)
905 *se = SpecEventType(out)
906 return err
907 }
908 func (se SpecEventType) MarshalJSON() ([]byte, error) {
909 return seEnumSupport.MarshJSON(uint(se))
910 }
911
912 func (se SpecEventType) Is(specEventTypes SpecEventType) bool {
913 return se&specEventTypes != 0
914 }
915
View as plain text