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

feat(test): add mock clock API #18

Closed

Conversation

schomatis
Copy link

@schomatis schomatis commented Jun 30, 2022

Went with a more specialized UseMockClock API to clearly signal this is for tests and not something you normally mess with but can revert to the more general SetClock if preferred.

Closes #17.

sweeper.go Outdated Show resolved Hide resolved
sweeper.go Outdated Show resolved Hide resolved
@schomatis
Copy link
Author

@Stebalien PTAL

@marten-seemann
Copy link
Contributor

Next step will be to use this mock clock in all the test here that currently do time.Sleeps. We can do that in a follow-up PR, if you prefer.

@schomatis
Copy link
Author

Yes, let's land this first please.

sweeper.go Outdated Show resolved Hide resolved
@schomatis
Copy link
Author

@marten-seemann @Stebalien It seems running the tests many times (--count 2) does keep some global/static context (and hence the clock). I had a sanity check to enforce the mock clock was being set only once but I think I'll have to remove it then. Flag if there's an alternative as I'd prefer to keep that check.

@schomatis
Copy link
Author

Even removing the overwrite check, running two tests in a row (is it really sequential?) still fails locally:

go test . -v --run TestMockClock --count 2

=== RUN   TestMockClock
--- PASS: TestMockClock (0.94s)
=== RUN   TestMockClock
    flow_test.go:204: expected rate 25000 (±1000), got 0
    flow_test.go:204: expected rate 5000 (±200), got 0
    flow_test.go:198: expected total 340000, got 0
--- FAIL: TestMockClock (0.85s)

So it seems I'm clearly missing something of how multiple runs of same test share context that I'll need to look closer into.

@marten-seemann
Copy link
Contributor

Did current tests pass with count=2?

@schomatis
Copy link
Author

I think anything that is run in the same go test call (be it many of the same or each of different tests) use the same context/clock and that is why even remote tests (no --count > 1) also fails.

If this is confirmed then the expectation of #17 (comment) is clearly unmet and we should re-discuss in the issue how is the mock clock going to be used.

@schomatis
Copy link
Author

Yeah, we're definitely sharing the sweeper across tests. Blocking on #17 (comment).

@Stebalien
Copy link
Member

  • You could have a global clock, and mock it in an init.
  • You could swap out the globalSweeper on test init/cleanup.

@schomatis
Copy link
Author

Having race issues with the swapping, will try some more this approach (with more care of stopping goroutines first) and attempt the alternative if I can't make progress here.

@schomatis schomatis force-pushed the schomatis/feat/add-mock-clock branch from f04ec7a to 9725a73 Compare July 5, 2022 00:31
@schomatis schomatis force-pushed the schomatis/feat/add-mock-clock branch from 9725a73 to f34d616 Compare July 5, 2022 00:34
@schomatis
Copy link
Author

Added a stop signal when swapping sweepers.

Copy link
Member

@Stebalien Stebalien left a comment

Choose a reason for hiding this comment

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

This probably works, but being able to "stop" adds complexity.

You encountered a race because the globalSweeper is by-value. If we make it by-reference, we should be able to replace it without interfering with the previous sweeper (which will die on its own).

sweeper.go Outdated Show resolved Hide resolved
Comment on lines +26 to +39
// SetClock puts a global clock in place for testing purposes only.
// It should be called only once and before using any of the Meter or
// MeterRegistry APIs of this package and should be restored with
// RestoreClock after the test.
func SetClock(clock clock.Clock) {
globalSweeper.stop()
globalSweeper = sweeper{}
globalSweeper.clock = clock
}

func RestoreClock() {
globalSweeper.stop()
globalSweeper = sweeper{}
}
Copy link
Member

Choose a reason for hiding this comment

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

These are testing functions and don't need to be exported.

Copy link
Author

Choose a reason for hiding this comment

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

The idea would be to use this in libp2p/go-libp2p-core#267 so I think we still want to export this, or something similar to it.

mockClock.Add(40 * time.Millisecond)
}
actual := m.Snapshot()
checkApproxEq(t, actual.Rate, 25000, 100)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I've said this before, but there's no need for approximations. We're using a mock clock, so timing is precise, and so should be the measurement.

@marten-seemann
Copy link
Contributor

I'm getting the impression that this PR is moving in the wrong direction.
@schomatis, thanks for your work here. I'll be taking over from here on.

Co-authored-by: Steven Allen <steven@stebalien.com>
@schomatis
Copy link
Author

If we make it by-reference

That makes sense.

@schomatis
Copy link
Author

I'm getting the impression that this PR is moving in the wrong direction.
@schomatis, thanks for your work here. I'll be taking over from here on.

@marten-seemann Your call. This PR is already passing and I'm aligned with Steven's input, so I'm not sure which wrong direction has this taken. The error margin comment is valid but it's not central to the mock clock implementation (but how we leverage it later), so I prioritized fixing the data race which was the blocker here.

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.

Add a mock clock
3 participants