1 package reporters_test
2
3 import (
4 "encoding/xml"
5 "fmt"
6 "os"
7 "path/filepath"
8 "time"
9
10 . "github.com/onsi/ginkgo/v2"
11 . "github.com/onsi/gomega"
12
13 "github.com/onsi/ginkgo/v2/reporters"
14 "github.com/onsi/ginkgo/v2/types"
15 )
16
17 var _ = Describe("JunitReport", func() {
18 var report types.Report
19
20 BeforeEach(func() {
21 report = types.Report{
22 SuiteDescription: "My Suite",
23 SuitePath: "/path/to/suite",
24 PreRunStats: types.PreRunStats{SpecsThatWillRun: 15, TotalSpecs: 20},
25 SuiteConfig: types.SuiteConfig{RandomSeed: 17, ParallelTotal: 1},
26 RunTime: time.Minute,
27 SpecReports: types.SpecReports{
28 S(types.NodeTypeIt, Label("cat", "dog"), CLabels(Label("dolphin"), Label("gorilla", "cow")), CTS("A", "B"), CLS(cl0, cl1), "C", cl2, types.SpecStateTimedout, STD("some captured stdout\n"), GW("ginkgowriter\noutput\ncleanup!"), SE(types.SpecEventByStart, "a by step", cl0),
29 SE(types.SpecEventNodeStart, types.NodeTypeIt, "C", cl2, TL(0)),
30 F("failure\nmessage", cl3, types.FailureNodeIsLeafNode, FailureNodeLocation(cl2), types.NodeTypeIt, TL("ginkgowriter\n"), AF(types.SpecStatePanicked, cl4, types.FailureNodeIsLeafNode, FailureNodeLocation(cl2), types.NodeTypeIt, TL("ginkgowriter\noutput\n"), ForwardedPanic("the panic!"))),
31 SE(types.SpecEventNodeEnd, types.NodeTypeIt, "C", cl2, TL("ginkgowriter\noutput\n"), time.Microsecond*87230),
32 RE("a report entry", cl1, TL("ginkgowriter\noutput\n")),
33 RE("a hidden report entry", cl1, TL("ginkgowriter\noutput\n"), types.ReportEntryVisibilityNever),
34 AF(types.SpecStateFailed, "a subsequent failure", types.FailureNodeInContainer, FailureNodeLocation(cl3), types.NodeTypeAfterEach, 0, TL("ginkgowriter\noutput\ncleanup!")),
35 ),
36 S(types.NodeTypeIt, "A", cl0, STD("some captured stdout\n"), GW("some GinkgoWriter\noutput is interspersed\nhere and there\n"), Label("cat", "owner:frank", "OWNer:bob"),
37 SE(types.SpecEventNodeStart, types.NodeTypeIt, "A", cl0),
38 PR("my progress report", LeafNodeText("A"), TL("some GinkgoWriter\n")),
39 SE(types.SpecEventByStart, "My Step", cl1, TL("some GinkgoWriter\n")),
40 RE("my entry", cl1, types.ReportEntryVisibilityFailureOrVerbose, TL("some GinkgoWriter\noutput is interspersed\n")),
41 RE("my hidden entry", cl1, types.ReportEntryVisibilityNever, TL("some GinkgoWriter\noutput is interspersed\n")),
42 SE(types.SpecEventByEnd, "My Step", cl1, time.Millisecond*200, TL("some GinkgoWriter\noutput is interspersed\n")),
43 SE(types.SpecEventNodeEnd, types.NodeTypeIt, "A", cl0, time.Millisecond*300, TL("some GinkgoWriter\noutput is interspersed\nhere and there\n")),
44 ),
45 S(types.NodeTypeIt, "A", cl0, types.SpecStatePending, CLabels(Label("owner:org")), Label("owner:team")),
46 S(types.NodeTypeIt, "A", cl0, types.SpecStatePanicked, CLabels(Label("owner:org")), STD("some captured stdout\n"),
47 SE(types.SpecEventNodeStart, types.NodeTypeIt, "A", cl0),
48 F("failure\nmessage", cl1, types.FailureNodeIsLeafNode, FailureNodeLocation(cl0), types.NodeTypeIt, ForwardedPanic("the panic")),
49 SE(types.SpecEventNodeEnd, types.NodeTypeIt, "A", cl0, time.Millisecond*300, TL("some GinkgoWriter\noutput is interspersed\nhere and there\n")),
50 ),
51 S(types.NodeTypeBeforeSuite, "A", cl0, types.SpecStatePassed),
52 },
53 }
54 })
55
56 Describe("default behavior", func() {
57 var generated reporters.JUnitTestSuites
58
59 BeforeEach(func() {
60 fname := fmt.Sprintf("./report-%d", GinkgoParallelProcess())
61 Ω(reporters.GenerateJUnitReport(report, fname)).Should(Succeed())
62 DeferCleanup(os.Remove, fname)
63
64 generated = reporters.JUnitTestSuites{}
65 f, err := os.Open(fname)
66 Ω(err).ShouldNot(HaveOccurred())
67 Ω(xml.NewDecoder(f).Decode(&generated)).Should(Succeed())
68 })
69
70 It("generates a Junit report and writes it to disk", func() {
71 Ω(generated.Tests).Should(Equal(5))
72 Ω(generated.Disabled).Should(Equal(1))
73 Ω(generated.Errors).Should(Equal(1))
74 Ω(generated.Failures).Should(Equal(1))
75 Ω(generated.Time).Should(Equal(60.0))
76 Ω(generated.TestSuites).Should(HaveLen(1))
77
78 suite := generated.TestSuites[0]
79 Ω(suite.Name).Should(Equal("My Suite"))
80 Ω(suite.Package).Should(Equal("/path/to/suite"))
81 Ω(suite.Properties.WithName("SuiteSucceeded")).Should(Equal("false"))
82 Ω(suite.Properties.WithName("RandomSeed")).Should(Equal("17"))
83
84 Ω(suite.Tests).Should(Equal(5))
85 Ω(suite.Disabled).Should(Equal(1))
86 Ω(suite.Errors).Should(Equal(1))
87 Ω(suite.Failures).Should(Equal(1))
88 Ω(suite.Time).Should(Equal(60.0))
89
90 Ω(suite.TestCases).Should(HaveLen(5))
91
92 failingSpec := suite.TestCases[0]
93 Ω(failingSpec.Name).Should(Equal("[It] A B C [dolphin, gorilla, cow, cat, dog]"))
94 Ω(failingSpec.Classname).Should(Equal("My Suite"))
95 Ω(failingSpec.Status).Should(Equal("timedout"))
96 Ω(failingSpec.Skipped).Should(BeNil())
97 Ω(failingSpec.Error).Should(BeNil())
98 Ω(failingSpec.Owner).Should(Equal(""))
99 Ω(failingSpec.Failure.Message).Should(Equal("failure\nmessage"))
100 Ω(failingSpec.Failure.Type).Should(Equal("timedout"))
101 Ω(failingSpec.Failure.Description).Should(MatchLines(
102 "[TIMEDOUT] failure",
103 "message",
104 spr("In [It] at: cl3.go:103 @ %s", FORMATTED_TIME),
105 "",
106 "[PANICKED] ",
107 spr("In [It] at: cl4.go:144 @ %s", FORMATTED_TIME),
108 "",
109 "the panic!",
110 "",
111 "Full Stack Trace",
112 " full-trace",
113 " cl-4",
114 "",
115 "There were additional failures detected after the initial failure. These are visible in the timeline",
116 "",
117 ))
118 Ω(failingSpec.SystemOut).Should(Equal("some captured stdout\n"))
119 Ω(failingSpec.SystemErr).Should(MatchLines(
120 spr("STEP: a by step - cl0.go:12 @ %s", FORMATTED_TIME),
121 spr("> Enter [It] C - cl2.go:80 @ %s", FORMATTED_TIME),
122 "ginkgowriter",
123 "[TIMEDOUT] failure",
124 "message",
125 spr("In [It] at: cl3.go:103 @ %s", FORMATTED_TIME),
126 "output",
127 "[PANICKED] ",
128 spr("In [It] at: cl4.go:144 @ %s", FORMATTED_TIME),
129 "",
130 "the panic!",
131 "",
132 "Full Stack Trace",
133 " full-trace",
134 " cl-4",
135 spr("< Exit [It] C - cl2.go:80 @ %s (87ms)", FORMATTED_TIME),
136 spr("a report entry - cl1.go:37 @ %s", FORMATTED_TIME),
137 spr("a hidden report entry - cl1.go:37 @ %s", FORMATTED_TIME),
138 "cleanup!",
139 "[FAILED] a subsequent failure",
140 spr("In [AfterEach] at: :0 @ %s", FORMATTED_TIME),
141 "",
142 ))
143
144 passingSpec := suite.TestCases[1]
145 Ω(passingSpec.Name).Should(Equal("[It] A [cat, owner:frank, OWNer:bob]"))
146 Ω(passingSpec.Classname).Should(Equal("My Suite"))
147 Ω(passingSpec.Status).Should(Equal("passed"))
148 Ω(passingSpec.Skipped).Should(BeNil())
149 Ω(passingSpec.Error).Should(BeNil())
150 Ω(passingSpec.Failure).Should(BeNil())
151 Ω(passingSpec.Owner).Should(Equal("bob"))
152 Ω(passingSpec.SystemOut).Should(Equal("some captured stdout\n"))
153 Ω(passingSpec.SystemErr).Should(MatchLines(
154 spr("> Enter [It] A - cl0.go:12 @ %s", FORMATTED_TIME),
155 "some GinkgoWriter",
156 "my progress report",
157 " A (Spec Runtime: 5s)",
158 " cl0.go:12",
159 spr("STEP: My Step - cl1.go:37 @ %s", FORMATTED_TIME),
160 "output is interspersed",
161 spr("my entry - cl1.go:37 @ %s", FORMATTED_TIME),
162 spr("my hidden entry - cl1.go:37 @ %s", FORMATTED_TIME),
163 spr("END STEP: My Step - cl1.go:37 @ %s (200ms)", FORMATTED_TIME),
164 "here and there",
165 spr("< Exit [It] A - cl0.go:12 @ %s (300ms)", FORMATTED_TIME),
166 "",
167 ))
168
169 pendingSpec := suite.TestCases[2]
170 Ω(pendingSpec.Name).Should(Equal("[It] A [owner:org, owner:team]"))
171 Ω(pendingSpec.Classname).Should(Equal("My Suite"))
172 Ω(pendingSpec.Status).Should(Equal("pending"))
173 Ω(pendingSpec.Skipped.Message).Should(Equal("pending"))
174 Ω(pendingSpec.Error).Should(BeNil())
175 Ω(pendingSpec.Owner).Should(Equal("team"))
176 Ω(pendingSpec.Failure).Should(BeNil())
177 Ω(pendingSpec.SystemOut).Should(BeEmpty())
178 Ω(pendingSpec.SystemErr).Should(BeEmpty())
179
180 panickedSpec := suite.TestCases[3]
181 Ω(panickedSpec.Name).Should(Equal("[It] A [owner:org]"))
182 Ω(panickedSpec.Classname).Should(Equal("My Suite"))
183 Ω(panickedSpec.Status).Should(Equal("panicked"))
184 Ω(panickedSpec.Skipped).Should(BeNil())
185 Ω(panickedSpec.Owner).Should(Equal("org"))
186 Ω(panickedSpec.Error.Message).Should(Equal("the panic"))
187 Ω(panickedSpec.Error.Type).Should(Equal("panicked"))
188 Ω(panickedSpec.Error.Description).Should(MatchLines(
189 "[PANICKED] failure",
190 "message",
191 spr("In [It] at: cl1.go:37 @ %s", FORMATTED_TIME),
192 "",
193 "the panic",
194 "",
195 "Full Stack Trace",
196 " full-trace",
197 " cl-1",
198 "",
199 ))
200 Ω(panickedSpec.Failure).Should(BeNil())
201 Ω(panickedSpec.SystemOut).Should(Equal("some captured stdout\n"))
202 Ω(panickedSpec.SystemErr).Should(MatchLines(
203 spr("> Enter [It] A - cl0.go:12 @ %s", FORMATTED_TIME),
204 "[PANICKED] failure",
205 "message",
206 spr("In [It] at: cl1.go:37 @ %s", FORMATTED_TIME),
207 "",
208 "the panic",
209 "",
210 "Full Stack Trace",
211 " full-trace",
212 " cl-1",
213 spr("< Exit [It] A - cl0.go:12 @ %s (300ms)", FORMATTED_TIME),
214 "",
215 ))
216
217 beforeSuiteSpec := suite.TestCases[4]
218 Ω(beforeSuiteSpec.Name).Should(Equal("[BeforeSuite] A"))
219 Ω(beforeSuiteSpec.Classname).Should(Equal("My Suite"))
220 Ω(beforeSuiteSpec.Status).Should(Equal("passed"))
221 Ω(beforeSuiteSpec.Skipped).Should(BeNil())
222 Ω(beforeSuiteSpec.Error).Should(BeNil())
223 Ω(beforeSuiteSpec.Failure).Should(BeNil())
224 Ω(beforeSuiteSpec.SystemOut).Should(BeEmpty())
225 Ω(beforeSuiteSpec.SystemErr).Should(BeEmpty())
226 })
227 })
228
229 Describe("when configured to omit all the omittables", func() {
230 var generated reporters.JUnitTestSuites
231
232 BeforeEach(func() {
233 fname := fmt.Sprintf("./report-%d", GinkgoParallelProcess())
234 Ω(reporters.GenerateJUnitReportWithConfig(report, fname, reporters.JunitReportConfig{
235 OmitTimelinesForSpecState: types.SpecStatePassed,
236 OmitFailureMessageAttr: true,
237 OmitCapturedStdOutErr: true,
238 OmitSpecLabels: true,
239 OmitLeafNodeType: true,
240 OmitSuiteSetupNodes: true,
241 })).Should(Succeed())
242 DeferCleanup(os.Remove, fname)
243
244 generated = reporters.JUnitTestSuites{}
245 f, err := os.Open(fname)
246 Ω(err).ShouldNot(HaveOccurred())
247 Ω(xml.NewDecoder(f).Decode(&generated)).Should(Succeed())
248 })
249
250 It("generates a Junit report and writes it to disk", func() {
251 Ω(generated.Tests).Should(Equal(4))
252 Ω(generated.Disabled).Should(Equal(1))
253 Ω(generated.Errors).Should(Equal(1))
254 Ω(generated.Failures).Should(Equal(1))
255 Ω(generated.Time).Should(Equal(60.0))
256 Ω(generated.TestSuites).Should(HaveLen(1))
257
258 suite := generated.TestSuites[0]
259 Ω(suite.Name).Should(Equal("My Suite"))
260 Ω(suite.Package).Should(Equal("/path/to/suite"))
261 Ω(suite.Properties.WithName("SuiteSucceeded")).Should(Equal("false"))
262 Ω(suite.Properties.WithName("RandomSeed")).Should(Equal("17"))
263
264 Ω(suite.Tests).Should(Equal(4))
265 Ω(suite.Disabled).Should(Equal(1))
266 Ω(suite.Errors).Should(Equal(1))
267 Ω(suite.Failures).Should(Equal(1))
268 Ω(suite.Time).Should(Equal(60.0))
269
270 Ω(suite.TestCases).Should(HaveLen(4))
271
272 failingSpec := suite.TestCases[0]
273 Ω(failingSpec.Name).Should(Equal("A B C"))
274 Ω(failingSpec.Classname).Should(Equal("My Suite"))
275 Ω(failingSpec.Status).Should(Equal("timedout"))
276 Ω(failingSpec.Skipped).Should(BeNil())
277 Ω(failingSpec.Error).Should(BeNil())
278 Ω(failingSpec.Failure.Message).Should(BeEmpty())
279 Ω(failingSpec.Failure.Type).Should(Equal("timedout"))
280 Ω(failingSpec.Failure.Description).Should(MatchLines(
281 "[TIMEDOUT] failure",
282 "message",
283 spr("In [It] at: cl3.go:103 @ %s", FORMATTED_TIME),
284 "",
285 "[PANICKED] ",
286 spr("In [It] at: cl4.go:144 @ %s", FORMATTED_TIME),
287 "",
288 "the panic!",
289 "",
290 "Full Stack Trace",
291 " full-trace",
292 " cl-4",
293 "",
294 "There were additional failures detected after the initial failure. These are visible in the timeline",
295 "",
296 ))
297 Ω(failingSpec.SystemOut).Should(BeEmpty())
298 Ω(failingSpec.SystemErr).Should(MatchLines(
299 spr("STEP: a by step - cl0.go:12 @ %s", FORMATTED_TIME),
300 spr("> Enter [It] C - cl2.go:80 @ %s", FORMATTED_TIME),
301 "ginkgowriter",
302 "[TIMEDOUT] failure",
303 "message",
304 spr("In [It] at: cl3.go:103 @ %s", FORMATTED_TIME),
305 "output",
306 "[PANICKED] ",
307 spr("In [It] at: cl4.go:144 @ %s", FORMATTED_TIME),
308 "",
309 "the panic!",
310 "",
311 "Full Stack Trace",
312 " full-trace",
313 " cl-4",
314 spr("< Exit [It] C - cl2.go:80 @ %s (87ms)", FORMATTED_TIME),
315 spr("a report entry - cl1.go:37 @ %s", FORMATTED_TIME),
316 spr("a hidden report entry - cl1.go:37 @ %s", FORMATTED_TIME),
317 "cleanup!",
318 "[FAILED] a subsequent failure",
319 spr("In [AfterEach] at: :0 @ %s", FORMATTED_TIME),
320 "",
321 ))
322
323 passingSpec := suite.TestCases[1]
324 Ω(passingSpec.Name).Should(Equal("A"))
325 Ω(passingSpec.Classname).Should(Equal("My Suite"))
326 Ω(passingSpec.Status).Should(Equal("passed"))
327 Ω(passingSpec.Skipped).Should(BeNil())
328 Ω(passingSpec.Error).Should(BeNil())
329 Ω(passingSpec.Failure).Should(BeNil())
330 Ω(passingSpec.SystemOut).Should(BeEmpty())
331 Ω(passingSpec.SystemErr).Should(BeEmpty())
332
333 pendingSpec := suite.TestCases[2]
334 Ω(pendingSpec.Name).Should(Equal("A"))
335 Ω(pendingSpec.Classname).Should(Equal("My Suite"))
336 Ω(pendingSpec.Status).Should(Equal("pending"))
337 Ω(pendingSpec.Skipped.Message).Should(Equal("pending"))
338 Ω(pendingSpec.Error).Should(BeNil())
339 Ω(pendingSpec.Failure).Should(BeNil())
340 Ω(pendingSpec.SystemOut).Should(BeEmpty())
341 Ω(pendingSpec.SystemErr).Should(BeEmpty())
342
343 panickedSpec := suite.TestCases[3]
344 Ω(panickedSpec.Name).Should(Equal("A"))
345 Ω(panickedSpec.Classname).Should(Equal("My Suite"))
346 Ω(panickedSpec.Status).Should(Equal("panicked"))
347 Ω(panickedSpec.Skipped).Should(BeNil())
348 Ω(panickedSpec.Error.Message).Should(BeEmpty())
349 Ω(panickedSpec.Error.Type).Should(Equal("panicked"))
350 Ω(panickedSpec.Error.Description).Should(MatchLines(
351 "[PANICKED] failure",
352 "message",
353 spr("In [It] at: cl1.go:37 @ %s", FORMATTED_TIME),
354 "",
355 "the panic",
356 "",
357 "Full Stack Trace",
358 " full-trace",
359 " cl-1",
360 "",
361 ))
362 Ω(panickedSpec.Failure).Should(BeNil())
363 Ω(panickedSpec.SystemOut).Should(BeEmpty())
364 Ω(panickedSpec.SystemErr).Should(MatchLines(
365 spr("> Enter [It] A - cl0.go:12 @ %s", FORMATTED_TIME),
366 "[PANICKED] failure",
367 "message",
368 spr("In [It] at: cl1.go:37 @ %s", FORMATTED_TIME),
369 "",
370 "the panic",
371 "",
372 "Full Stack Trace",
373 " full-trace",
374 " cl-1",
375 spr("< Exit [It] A - cl0.go:12 @ %s (300ms)", FORMATTED_TIME),
376 "",
377 ))
378 })
379 })
380
381 Describe("when configured to write the report inside a folder", func() {
382 var folderPath string
383 var filePath string
384
385 BeforeEach(func() {
386 folderPath = filepath.Join(fmt.Sprintf("test_outputs_%d", GinkgoParallelProcess()))
387 fileName := fmt.Sprintf("report-%d", GinkgoParallelProcess())
388 filePath = filepath.Join(folderPath, fileName)
389
390 Ω(reporters.GenerateJUnitReport(report, filePath)).Should(Succeed())
391 DeferCleanup(os.RemoveAll, folderPath)
392 })
393
394 It("creates the folder and the report file", func() {
395 _, err := os.Stat(folderPath)
396 Ω(err).Should(Succeed(), "Parent folder should be created")
397 _, err = os.Stat(filePath)
398 Ω(err).Should(Succeed(), "Report file should be created")
399 })
400 })
401 })
402
View as plain text