Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a test runner #40

Merged
merged 2 commits into from Oct 26, 2020
Merged

Add a test runner #40

merged 2 commits into from Oct 26, 2020

Conversation

rabbbit
Copy link
Contributor

@rabbbit rabbbit commented Oct 19, 2020

Something that came up in #39 - there's a fair bit of duplication
in the tests. A runner make current setup a bit easier/shorter,
but would make #39 much smaller.

There's no functional changes.

Something that came up in #39 - there's a fair bit of duplication
in the tests. A runner make current setup a bit easier/shorter,
but would make #39 much smaller.

There's no functional changes.
assert.InDelta(t, 200, r.count.Load(), 10, "count within rate limit")
})

r.wg.Add(1)

Choose a reason for hiding this comment

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

a test function being able to modify the state of the runner directly feels a little odd. would it be a better idea to pass in a runner interface instead? exposing the waitgroup directly allows for crazy things like calling wg.Wait inside the test function which can cause a deadlock. not that we expect people to do that, but it's better to reduce the API surface to avoid accidental problems

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm.

Agree that having a smaller surface is a good thing, but not sure if this won't over-complicate things here. Like,

  • there are literally two tests (one of which tests nothing, I think)
  • we're unlikely to have more tests
  • if we cause a deadlock than it's on us ;)

Maybe I should have called the runner just args - would have been closer to my goal of just reducing the noise.

If I pass in the interface I also have to take away access to r.count, r.doneCh (test below) and I'll need to pass in a function as an argument to run (since TestDelayedRateLimiter) is doing some custom work below.

Anyway, can take a stab, why not.

Copy link
Contributor Author

@rabbbit rabbbit Oct 26, 2020

Choose a reason for hiding this comment

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

Okay, so if we passed in the interface we kinda need more methods:

type runner interface {
	// startTaking tries to Take() on passed in limiters in a loop/goroutine.
	startTaking(rls ...ratelimit.Limiter)
	// assertCountAt asserts the limiters have Taken() a number of times at the given time.
	// It's a thin wrapper around afterFunc to reduce boilerplate code.
	assertCountAt(d time.Duration, count int)
	// getClock returns the test clock.
	getClock() ratelimit.Clock
	// afterFunc executes a func at a given time.
	afterFunc(d time.Duration, fn func())
}

It's a bit verbose ... but I'm starting to like it - the actual test code looks quite clear.

Copy link
Contributor Author

@rabbbit rabbbit Oct 26, 2020

Choose a reason for hiding this comment

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

I'm not sure whether we'll need afterFunc once we fix TestDelayedRateLimiter

But for now it should stay here.

@cinchurge
Copy link

I like the idea, but we should probably do some encapsulation of the runner to reduce potential problems

@rabbbit rabbbit force-pushed the pawel/testrunner branch 3 times, most recently from 0568cd0 to 9fc8b72 Compare October 26, 2020 15:25

// startTaking tries to Take() on passed in limiters in a loop/goroutine.
func (r *runnerImpl) startTaking(rls ...ratelimit.Limiter) {
go func() {

Choose a reason for hiding this comment

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

do we need to add this to the waitgroup?

Copy link
Contributor Author

@rabbbit rabbbit Oct 26, 2020

Choose a reason for hiding this comment

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

So, technically, I think we should, but it's not needed right now - clock.afterFunc does time.sleep(1*millisecond) so we get it for free.

Given that it works now, and the old code was doing this, I'd prefer to keep it as is - just to minimize changes in this PR.

#39 actually adds a separate wg for starting jobs (it's out of date now, I'll update it after this PR) - I'd prefer to iterate & update it in follow ups.

But yeah, more wgs will be needed if we don't want to rely on sleeps (and to deprecate the internal clock fork), good observation :)

Copy link

@cinchurge cinchurge left a comment

Choose a reason for hiding this comment

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

lgtm!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants