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

refactor: re-arrange some globals and add clock #2343

Merged
merged 15 commits into from
Dec 1, 2021
Merged

Conversation

JadenSimon
Copy link
Contributor

@JadenSimon JadenSimon commented Nov 29, 2021

Problem

The insiders version of VS Code broke our tests due to our reliance on 'fake timers'. The library itself isn't bad, it's just we're stubbing the global scheduling functions.

Solution

Add a 'clock' object to our extension globals. This should be used in 'production' code while test code may use either the 'real' functions or the scoped-down version depending on the use case. Also refactored the globals code a bit to be more ergonomic by removing the namespace and using pure modules. Some work still needs to be done to make the initialization cleaner which should help a lot with making tests cleaner and easier to write.

License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@JadenSimon JadenSimon requested a review from a team as a code owner November 29, 2021 21:19
@@ -99,29 +98,42 @@ describe('timeoutUtils', async function () {
})

it('Correctly reports elapsed time with refresh', async function () {
const longTimer = new timeoutUtils.Timeout(10)
clock.tick(5)
// TODO: fake timers `refresh` is not implemented correctly for the non-global case
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I found a bug with fake timers that I'll need to upstream a fix for. It only happens when using a different global object to fake.

this.pollTimer = undefined
}
}

public startPolling(id: string): void {
this.pollingNodes.add(id)
this.pollTimer = this.pollTimer ?? setInterval(this.refresh.bind(this), POLLING_INTERVAL)
this.pollTimer = this.pollTimer ?? awsToolkit.clock.setInterval(this.refresh.bind(this), POLLING_INTERVAL)
Copy link
Contributor

Choose a reason for hiding this comment

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

should we have a lint rule to prevent direct usage of the window.setInterval / setTimeout / etc?

Copy link
Contributor Author

@JadenSimon JadenSimon Nov 30, 2021

Choose a reason for hiding this comment

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

I don't think it should be required, at least not yet. It boils down to where if you want to test your code with the fake clock then you must use the toolkit-owned clock. But using the real thing has no effect if you are not mocking the clock.

I'll still dig up the linting PR so we can at least have a means to enforce some of these things through code. But I consider it relatively low priority.

Copy link
Contributor

Choose a reason for hiding this comment

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

But using the real thing has no effect if you are not mocking the clock.

Main concern for me is if someone mocks the clock but doesn't use this global wrapper, then has weird effects and doesn't know why.

But I consider it relatively low priority.

Not a blocker here. Backlogged: #2347

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Main concern for me is if someone mocks the clock but doesn't use this global wrapper, then has weird effects and doesn't know why.

Yup that's the biggest concern. I'll add a big warning for installFakeClock for now until linting can be added.

@@ -40,7 +40,7 @@ export async function deleteLambda({
if (!deleteParams.functionName) {
return
}
const startTime = new Date()
const startTime = new awsToolkit.clock.Date()
Copy link
Contributor

Choose a reason for hiding this comment

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

lint rule for this?

/**
* Namespace for common variables used globally in the extension.
* All variables here must be initialized in the activate() method of extension.ts
*/
export namespace ext {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed the namespace since namespaces are mostly considered 'legacy', at least for Node applications. Best just to use ES6 module syntax directly.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks. This has been on our TODO list for awhile :)

@@ -97,56 +96,54 @@ let resultLimit = Number.MAX_VALUE

// After the server has started the client sends an initialize request. The server receives
// in the passed params the rootPath of the workspace plus the client capabilities.
connection.onInitialize(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this file hadn't been run with prettier so it got moved around quite a bit.

ext.visualizationResourcePaths.stateMachineCustomThemeCSS = vscode.Uri.file(
context.asAbsolutePath(join('media', 'css', 'stateMachineRender.css'))
)
return {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the most part we shouldn't assign directly to the global object. Just looking at the code that uses ext I can tell there's tons of potential avenues for bugs during testing because we assign in two places.

@@ -16,6 +16,7 @@ import { ApiGatewayClient } from '../../shared/clients/apiGatewayClient'
import { RestApi } from 'aws-sdk/clients/apigateway'
import { toArrayAsync, toMap, updateInPlace } from '../../shared/utilities/collectionUtils'
import { RestApiNode } from './apiNodes'
import globals from '../../shared/extensionGlobals'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

When I changed the import it ended up getting placed at the bottom. All well I guess. I wonder if we can have prettier sort the imports or something.

@JadenSimon
Copy link
Contributor Author

Apparently describe blocks are executed on module load in the minimum VS Code version. Really not a big fan of having two separate Date references. Need to either add a tiny bit of abstraction to the scoped clock or figure out a different way to separate them.

@JadenSimon
Copy link
Contributor Author

JadenSimon commented Dec 1, 2021

/runintegrationtests ...

@JadenSimon JadenSimon merged commit 6f1eabb into master Dec 1, 2021
@JadenSimon JadenSimon deleted the sijaden/clocks branch December 1, 2021 22:59
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.

2 participants