1package suite
2
3import (
4	"errors"
5	"io/ioutil"
6	"os"
7	"testing"
8	"time"
9
10	"github.com/stretchr/testify/assert"
11	"github.com/stretchr/testify/require"
12)
13
14// SuiteRequireTwice is intended to test the usage of suite.Require in two
15// different tests
16type SuiteRequireTwice struct{ Suite }
17
18// TestSuiteRequireTwice checks for regressions of issue #149 where
19// suite.requirements was not initialised in suite.SetT()
20// A regression would result on these tests panicking rather than failing.
21func TestSuiteRequireTwice(t *testing.T) {
22	ok := testing.RunTests(
23		allTestsFilter,
24		[]testing.InternalTest{{
25			Name: "TestSuiteRequireTwice",
26			F: func(t *testing.T) {
27				suite := new(SuiteRequireTwice)
28				Run(t, suite)
29			},
30		}},
31	)
32	assert.Equal(t, false, ok)
33}
34
35func (s *SuiteRequireTwice) TestRequireOne() {
36	r := s.Require()
37	r.Equal(1, 2)
38}
39
40func (s *SuiteRequireTwice) TestRequireTwo() {
41	r := s.Require()
42	r.Equal(1, 2)
43}
44
45type panickingSuite struct {
46	Suite
47	panicInSetupSuite    bool
48	panicInSetupTest     bool
49	panicInBeforeTest    bool
50	panicInTest          bool
51	panicInAfterTest     bool
52	panicInTearDownTest  bool
53	panicInTearDownSuite bool
54}
55
56func (s *panickingSuite) SetupSuite() {
57	if s.panicInSetupSuite {
58		panic("oops in setup suite")
59	}
60}
61
62func (s *panickingSuite) SetupTest() {
63	if s.panicInSetupTest {
64		panic("oops in setup test")
65	}
66}
67
68func (s *panickingSuite) BeforeTest(_, _ string) {
69	if s.panicInBeforeTest {
70		panic("oops in before test")
71	}
72}
73
74func (s *panickingSuite) Test() {
75	if s.panicInTest {
76		panic("oops in test")
77	}
78}
79
80func (s *panickingSuite) AfterTest(_, _ string) {
81	if s.panicInAfterTest {
82		panic("oops in after test")
83	}
84}
85
86func (s *panickingSuite) TearDownTest() {
87	if s.panicInTearDownTest {
88		panic("oops in tear down test")
89	}
90}
91
92func (s *panickingSuite) TearDownSuite() {
93	if s.panicInTearDownSuite {
94		panic("oops in tear down suite")
95	}
96}
97
98func TestSuiteRecoverPanic(t *testing.T) {
99	ok := true
100	panickingTests := []testing.InternalTest{
101		{
102			Name: "TestPanicInSetupSuite",
103			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInSetupSuite: true}) },
104		},
105		{
106			Name: "TestPanicInSetupTest",
107			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInSetupTest: true}) },
108		},
109		{
110			Name: "TestPanicInBeforeTest",
111			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInBeforeTest: true}) },
112		},
113		{
114			Name: "TestPanicInTest",
115			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTest: true}) },
116		},
117		{
118			Name: "TestPanicInAfterTest",
119			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInAfterTest: true}) },
120		},
121		{
122			Name: "TestPanicInTearDownTest",
123			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownTest: true}) },
124		},
125		{
126			Name: "TestPanicInTearDownSuite",
127			F:    func(t *testing.T) { Run(t, &panickingSuite{panicInTearDownSuite: true}) },
128		},
129	}
130
131	require.NotPanics(t, func() {
132		ok = testing.RunTests(allTestsFilter, panickingTests)
133	})
134
135	assert.False(t, ok)
136}
137
138// This suite is intended to store values to make sure that only
139// testing-suite-related methods are run.  It's also a fully
140// functional example of a testing suite, using setup/teardown methods
141// and a helper method that is ignored by testify.  To make this look
142// more like a real world example, all tests in the suite perform some
143// type of assertion.
144type SuiteTester struct {
145	// Include our basic suite logic.
146	Suite
147
148	// Keep counts of how many times each method is run.
149	SetupSuiteRunCount    int
150	TearDownSuiteRunCount int
151	SetupTestRunCount     int
152	TearDownTestRunCount  int
153	TestOneRunCount       int
154	TestTwoRunCount       int
155	TestSubtestRunCount   int
156	NonTestMethodRunCount int
157
158	SuiteNameBefore []string
159	TestNameBefore  []string
160
161	SuiteNameAfter []string
162	TestNameAfter  []string
163
164	TimeBefore []time.Time
165	TimeAfter  []time.Time
166}
167
168type SuiteSkipTester struct {
169	// Include our basic suite logic.
170	Suite
171
172	// Keep counts of how many times each method is run.
173	SetupSuiteRunCount    int
174	TearDownSuiteRunCount int
175}
176
177// The SetupSuite method will be run by testify once, at the very
178// start of the testing suite, before any tests are run.
179func (suite *SuiteTester) SetupSuite() {
180	suite.SetupSuiteRunCount++
181}
182
183func (suite *SuiteTester) BeforeTest(suiteName, testName string) {
184	suite.SuiteNameBefore = append(suite.SuiteNameBefore, suiteName)
185	suite.TestNameBefore = append(suite.TestNameBefore, testName)
186	suite.TimeBefore = append(suite.TimeBefore, time.Now())
187}
188
189func (suite *SuiteTester) AfterTest(suiteName, testName string) {
190	suite.SuiteNameAfter = append(suite.SuiteNameAfter, suiteName)
191	suite.TestNameAfter = append(suite.TestNameAfter, testName)
192	suite.TimeAfter = append(suite.TimeAfter, time.Now())
193}
194
195func (suite *SuiteSkipTester) SetupSuite() {
196	suite.SetupSuiteRunCount++
197	suite.T().Skip()
198}
199
200// The TearDownSuite method will be run by testify once, at the very
201// end of the testing suite, after all tests have been run.
202func (suite *SuiteTester) TearDownSuite() {
203	suite.TearDownSuiteRunCount++
204}
205
206func (suite *SuiteSkipTester) TearDownSuite() {
207	suite.TearDownSuiteRunCount++
208}
209
210// The SetupTest method will be run before every test in the suite.
211func (suite *SuiteTester) SetupTest() {
212	suite.SetupTestRunCount++
213}
214
215// The TearDownTest method will be run after every test in the suite.
216func (suite *SuiteTester) TearDownTest() {
217	suite.TearDownTestRunCount++
218}
219
220// Every method in a testing suite that begins with "Test" will be run
221// as a test.  TestOne is an example of a test.  For the purposes of
222// this example, we've included assertions in the tests, since most
223// tests will issue assertions.
224func (suite *SuiteTester) TestOne() {
225	beforeCount := suite.TestOneRunCount
226	suite.TestOneRunCount++
227	assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount+1)
228	suite.Equal(suite.TestOneRunCount, beforeCount+1)
229}
230
231// TestTwo is another example of a test.
232func (suite *SuiteTester) TestTwo() {
233	beforeCount := suite.TestTwoRunCount
234	suite.TestTwoRunCount++
235	assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount)
236	suite.NotEqual(suite.TestTwoRunCount, beforeCount)
237}
238
239func (suite *SuiteTester) TestSkip() {
240	suite.T().Skip()
241}
242
243// NonTestMethod does not begin with "Test", so it will not be run by
244// testify as a test in the suite.  This is useful for creating helper
245// methods for your tests.
246func (suite *SuiteTester) NonTestMethod() {
247	suite.NonTestMethodRunCount++
248}
249
250func (suite *SuiteTester) TestSubtest() {
251	suite.TestSubtestRunCount++
252
253	for _, t := range []struct {
254		testName string
255	}{
256		{"first"},
257		{"second"},
258	} {
259		suiteT := suite.T()
260		suite.Run(t.testName, func() {
261			// We should get a different *testing.T for subtests, so that
262			// go test recognizes them as proper subtests for output formatting
263			// and running individual subtests
264			subTestT := suite.T()
265			suite.NotEqual(subTestT, suiteT)
266		})
267		suite.Equal(suiteT, suite.T())
268	}
269}
270
271// TestRunSuite will be run by the 'go test' command, so within it, we
272// can run our suite using the Run(*testing.T, TestingSuite) function.
273func TestRunSuite(t *testing.T) {
274	suiteTester := new(SuiteTester)
275	Run(t, suiteTester)
276
277	// Normally, the test would end here.  The following are simply
278	// some assertions to ensure that the Run function is working as
279	// intended - they are not part of the example.
280
281	// The suite was only run once, so the SetupSuite and TearDownSuite
282	// methods should have each been run only once.
283	assert.Equal(t, suiteTester.SetupSuiteRunCount, 1)
284	assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1)
285
286	assert.Equal(t, len(suiteTester.SuiteNameAfter), 4)
287	assert.Equal(t, len(suiteTester.SuiteNameBefore), 4)
288	assert.Equal(t, len(suiteTester.TestNameAfter), 4)
289	assert.Equal(t, len(suiteTester.TestNameBefore), 4)
290
291	assert.Contains(t, suiteTester.TestNameAfter, "TestOne")
292	assert.Contains(t, suiteTester.TestNameAfter, "TestTwo")
293	assert.Contains(t, suiteTester.TestNameAfter, "TestSkip")
294	assert.Contains(t, suiteTester.TestNameAfter, "TestSubtest")
295
296	assert.Contains(t, suiteTester.TestNameBefore, "TestOne")
297	assert.Contains(t, suiteTester.TestNameBefore, "TestTwo")
298	assert.Contains(t, suiteTester.TestNameBefore, "TestSkip")
299	assert.Contains(t, suiteTester.TestNameBefore, "TestSubtest")
300
301	for _, suiteName := range suiteTester.SuiteNameAfter {
302		assert.Equal(t, "SuiteTester", suiteName)
303	}
304
305	for _, suiteName := range suiteTester.SuiteNameBefore {
306		assert.Equal(t, "SuiteTester", suiteName)
307	}
308
309	for _, when := range suiteTester.TimeAfter {
310		assert.False(t, when.IsZero())
311	}
312
313	for _, when := range suiteTester.TimeBefore {
314		assert.False(t, when.IsZero())
315	}
316
317	// There are four test methods (TestOne, TestTwo, TestSkip, and TestSubtest), so
318	// the SetupTest and TearDownTest methods (which should be run once for
319	// each test) should have been run four times.
320	assert.Equal(t, suiteTester.SetupTestRunCount, 4)
321	assert.Equal(t, suiteTester.TearDownTestRunCount, 4)
322
323	// Each test should have been run once.
324	assert.Equal(t, suiteTester.TestOneRunCount, 1)
325	assert.Equal(t, suiteTester.TestTwoRunCount, 1)
326	assert.Equal(t, suiteTester.TestSubtestRunCount, 1)
327
328	// Methods that don't match the test method identifier shouldn't
329	// have been run at all.
330	assert.Equal(t, suiteTester.NonTestMethodRunCount, 0)
331
332	suiteSkipTester := new(SuiteSkipTester)
333	Run(t, suiteSkipTester)
334
335	// The suite was only run once, so the SetupSuite and TearDownSuite
336	// methods should have each been run only once, even though SetupSuite
337	// called Skip()
338	assert.Equal(t, suiteSkipTester.SetupSuiteRunCount, 1)
339	assert.Equal(t, suiteSkipTester.TearDownSuiteRunCount, 1)
340
341}
342
343func TestSuiteGetters(t *testing.T) {
344	suite := new(SuiteTester)
345	suite.SetT(t)
346	assert.NotNil(t, suite.Assert())
347	assert.Equal(t, suite.Assertions, suite.Assert())
348	assert.NotNil(t, suite.Require())
349	assert.Equal(t, suite.require, suite.Require())
350}
351
352type SuiteLoggingTester struct {
353	Suite
354}
355
356func (s *SuiteLoggingTester) TestLoggingPass() {
357	s.T().Log("TESTLOGPASS")
358}
359
360func (s *SuiteLoggingTester) TestLoggingFail() {
361	s.T().Log("TESTLOGFAIL")
362	assert.NotNil(s.T(), nil) // expected to fail
363}
364
365type StdoutCapture struct {
366	oldStdout *os.File
367	readPipe  *os.File
368}
369
370func (sc *StdoutCapture) StartCapture() {
371	sc.oldStdout = os.Stdout
372	sc.readPipe, os.Stdout, _ = os.Pipe()
373}
374
375func (sc *StdoutCapture) StopCapture() (string, error) {
376	if sc.oldStdout == nil || sc.readPipe == nil {
377		return "", errors.New("StartCapture not called before StopCapture")
378	}
379	os.Stdout.Close()
380	os.Stdout = sc.oldStdout
381	bytes, err := ioutil.ReadAll(sc.readPipe)
382	if err != nil {
383		return "", err
384	}
385	return string(bytes), nil
386}
387
388func TestSuiteLogging(t *testing.T) {
389	suiteLoggingTester := new(SuiteLoggingTester)
390	capture := StdoutCapture{}
391	internalTest := testing.InternalTest{
392		Name: "SomeTest",
393		F: func(subT *testing.T) {
394			Run(subT, suiteLoggingTester)
395		},
396	}
397	capture.StartCapture()
398	testing.RunTests(allTestsFilter, []testing.InternalTest{internalTest})
399	output, err := capture.StopCapture()
400	require.NoError(t, err, "Got an error trying to capture stdout and stderr!")
401	require.NotEmpty(t, output, "output content must not be empty")
402
403	// Failed tests' output is always printed
404	assert.Contains(t, output, "TESTLOGFAIL")
405
406	if testing.Verbose() {
407		// In verbose mode, output from successful tests is also printed
408		assert.Contains(t, output, "TESTLOGPASS")
409	} else {
410		assert.NotContains(t, output, "TESTLOGPASS")
411	}
412}
413