Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

arjunmahishi
Copy link
Collaborator

@arjunmahishi arjunmahishi commented Feb 16, 2024

As pointed out in issue #1520, if the suite is not initialised properly (by calling the Run function), then calling suite.Require() or suite.Assert() will result in a deadlock.

Changes

This commit fixes that by panicking if the suite is not initialised properly. This is justified because the suite is intended to be triggered in the right way. If the user does not do that, this panic will nudge them in the right direction.

It has to be a panic because, at this point, we don't have access to any testing.T context to gracefully call a t.Fail(). Also, these two functions are not expected to return an error.

Related issues

Fixes #1520

@arjunmahishi arjunmahishi added bug pkg-suite Change related to package testify/suite labels Feb 16, 2024
Copy link
Collaborator

@brackendawson brackendawson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change.

Instead, you can fix the deadlock by making two versions of the T method:

// T retrieves the current *testing.T context.
func (suite *Suite) T() *testing.T {
	suite.mu.RLock()
	defer suite.mu.RUnlock()
	return suite._t()
}

func (suite *Suite) _t() *testing.T {
	return suite.t
}

And then calling the internal version from Require and Assert, since they are already protected by the lock.

@arjunmahishi
Copy link
Collaborator Author

This is a breaking change.

How? This would have never worked in the previous version right?

You can fix the deadlock by making two versions of the T method

But just like suite.require, suite.t would also not be initialised without calling Run. So, the user would run into a nil pointer deref panic.

                                          ┌─────────────────────────────────────────┐ 
                                          │func (suite *Suite) SetT(t *testing.T) { │ 
                                          │  suite.mu.Lock()                        │
┌─────┐        ┌─────────────┐            │  defer suite.mu.Unlock()                │
│Run()├───────►│suite.SetT(t)├───────────►│  suite.t = t                            │
└─────┘        └─────────────┘            │  suite.Assertions = assert.New(t)       │
                                          │  suite.require = require.New(t)         │
                                          │}                                        │ 
                                          └─────────────────────────────────────────┘ 

@brackendawson
Copy link
Collaborator

At v1.0 (which is difficult to require from a modern module) SetT exists and does not initialise suite.Suite.Require, and the code path we are modifying was useful, it sucessfully returned a functioning Require to this contrived example:

package kata_test

import (
	"os"
	"testing"

	"github.com/stretchr/testify/suite"
)

type mySuite struct {
	suite.Suite
}

func (suite *mySuite) TestSomething() {
	require := suite.Require()
	require.True(true)
}

func TestSuite(t *testing.T) {
	m := &mySuite{}
	m.SetT(t)
	f, err := os.Create("testfile")
	m.Require().NoError(err)
	defer f.Close()
	suite.Run(t, m)
}

This code works today because SetT also initialises both suite.Suite.Assert and suite.Suite.Require, meaning we don't traverse the code we're modifying, which has at various times been a nil pointer dereference, a deadlock, or now a deliberate panic.

So I think you are right, I can't contrive an example that is broken by this change, it's not a breaking change.

Perhaps though the panic message should encompass something like: "Require must not be called before Run or SetT".

As pointed out in issue stretchr#1520, if the suite is not initialised properly
(buy calling the Run function), then calling suite.Require() or
suite.Assert() will result in a deadlock.

This commit fixes that by panicking if the suite is not initialised
properly. This is justified because, the suite is intended to be
triggered in the right way. If the user does not do that, this panic will
nudge them in the right direction.

It has to be a panic because, at this point, we don't have access to any
testing.T context to gracefully call a t.Fail(). Also, these two
functions are not expected to return an error.

Fixes stretchr#1520
@arjunmahishi
Copy link
Collaborator Author

Ohh got it. Thanks for the context!
Are you on board with panicking though? (I've changed the message)

@brackendawson brackendawson merged commit 14ffa90 into stretchr:master Feb 18, 2024
@arjunmahishi arjunmahishi deleted the fix-suit-deadlock branch February 18, 2024 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug pkg-suite Change related to package testify/suite
Projects
None yet
Development

Successfully merging this pull request may close these issues.

suite.Require deadlock
2 participants