-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Added telemetry for usage of activateEnvInCurrentTerminal setting #8654
Added telemetry for usage of activateEnvInCurrentTerminal setting #8654
Conversation
Codecov Report
@@ Coverage Diff @@
## master #8654 +/- ##
==========================================
+ Coverage 59.18% 59.19% +0.01%
==========================================
Files 521 522 +1
Lines 23977 23987 +10
Branches 3873 3870 -3
==========================================
+ Hits 14190 14200 +10
- Misses 8870 8871 +1
+ Partials 917 916 -1
Continue to review full report at Codecov.
|
@@ -158,12 +160,15 @@ suite('Terminal Provider', () => { | |||
let configService: TypeMoq.IMock<IConfigurationService>; | |||
let terminalActivator: TypeMoq.IMock<ITerminalActivator>; | |||
let terminal: TypeMoq.IMock<Terminal>; | |||
// tslint:disable-next-line:no-any | |||
let getActiveResource: sinon.SinonStub<any>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For my info, why sinon
? feels like typemoq
could achieve the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I am trying to mock the methods of the class I am testing itself.
Typemoq can be used to mock methods of dependencies of a class just fine, but not the methods of the class I am testing. (I don't have mocked objects of the sort TypeMoq.IMock<InterfaceName>
for the class I am testing)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea of mocking out a method of the class against which I'm testing is a little mind-melting. There must be a simpler way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mostly LGTM
My suggestions are all about formatting and wording in comments. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Telemetry is being captured in the wrong place, only when extension is opened! (Rejecting PR because of this).
Terminal name can contain PII, also we don't need this (not to my knowledge). - optional if others think it's not PII (there's no guarantee it's not)
my bad folks, I should have clarified what kind of telemetry I was expecting. It's just to track usage (i.e. how many people have the setting set to true), just to help with decisions in the future in terms of maintenance. Let's drop the terminal name since we can't guarantee it won't ever collect PII. |
Then i'd track this feature usage as part of the extension load, instead of tracking activated terminals when they are opened, etc. |
I changed telemetry after consulting with @luabud . We want to track the usage of the setting (if it's present or not) and also the times when the setting is actually used to activate the terminal. |
f70b382
to
85755b1
Compare
Co-Authored-By: Eric Snow <ericsnowcurrently@gmail.com>
eb9ad68
to
1e4b232
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd still like to see the PR changed to tracking the setting as opposed to its usage.
It could be used elsewhere in the future.
This way its obvious, right now we're tracking a feature and not a setting. Hence looking at the code the telemetry isn't obvious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The actual fix LGTM.
However, I have a bunch of comments related to the effort to factor out getting the "active" resource. I strongly recommend moving that to a separate PR.
src/client/activation/types.ts
Outdated
@@ -169,3 +169,8 @@ export const IExtensionSingleActivationService = Symbol('IExtensionSingleActivat | |||
export interface IExtensionSingleActivationService { | |||
activate(): Promise<void>; | |||
} | |||
|
|||
export const IActiveResourceService = Symbol('IActiveResourceService'); | |||
export interface IActiveResourceService { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a doc comment explaining the purpose of this type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Though I don't think we even need an interface for this.
We could just create the class and inject just the class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DonJayamanne For the cases where serviceContainer is used,
this.activeResourceService = this.serviceContainer.get<IActiveResourceService>(IActiveResourceService);
we can't inject the class, can we? How do we initialize this.activeResourceService
in these cases?
}); | ||
|
||
test('Return document uri if a saved document is currently opened', async () => { | ||
const activeTextEditor = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a unit test I would expect use of mocks for both activeTextEditor
and activeTextEditor.document
. Then you would verify that isUntitled
and uri
were both used and that nothing else was. So I suppose the test would look more like this:
test('Return document uri if a saved document is currently opened', async () => {
const expected = Uri.parse('a');
const activeTextEditor: TextEditor = mock(TextEditor);
const document: TextDocument = mock(TextDocument);
when(document.isUntitled).thenReturn(false);
when(document.uri).thenReturn(expected);
when(activeTextEditor.document).thenReturn(document);
when(documentManager.activeTextEditor).thenReturn(activeTextEditor);
//when(workspaceService.workspaceFolders).thenReturn([]);
const activeResource = activeResourceService.getActiveResource();
assert.deepEqual(activeResource, expected);
verify(document.isUntitled).once();
verify(document.uri).once();
verify(activeTextEditor.document).once();
verify(documentManager.activeTextEditor).atLeast(1);
verify(workspaceService.workspaceFolders).never();
});
The trick is ensuring that you verify everything properly. That is a lot easier with TypeMoq:
suite('Active resource service', () => {
let document: TypeMoq.IMock<vscode.TextDocument>;
let editor: TypeMoq.IMock<vscode.TextEditor>;
let documentManager: TypeMoq.IMock<IDocumentManager>;
let workspaceService: TypeMoq.IMock<IWorkspaceService>;
let activeResourceService: ActiveResourceService;
setup(() => {
document = TypeMoq.Mock.ofType<vscode.TextDocument>(undefined, TypeMoq.MockBehavior.Strict);
editor = TypeMoq.Mock.ofType<vscode.TextEditor>(undefined, TypeMoq.MockBehavior.Strict);
documentManager = TypeMoq.Mock.ofType<IDocumentManager>(undefined, TypeMoq.MockBehavior.Strict);
workspaceService = TypeMoq.Mock.ofType<IWorkspaceService>(undefined, TypeMoq.MockBehavior.Strict);
activeResourceService = new ActiveResourceService(
documentManager.object,
workspaceService.object
);
});
function verifyAll() {
document.verifyAll();
editor.verifyAll();
documentManager.verifyAll();
workspaceService.verifyAll();
}
test('Return document uri if a saved document is currently opened', async () => {
const expected = Uri.parse('a');
documentManager.setup(dm => dm.activeTextEditor)
.returns(() => editor);
editor.setup(e => e.document)
.returns(() => document);
document.setup(d => d.isUntitled)
.returns(() => false);
document.setup(d => d.uri)
.returns(() => expected);
const activeResource = activeResourceService.getActiveResource();
assert.deepEqual(activeResource, expected);
verifyAll();
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still like ts-mokito
:) and the old test was good enough for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just worried that we aren't checking for unexpected behavior (spurious calls). Does ts-mokito offer a way to verify that only the calls you expect actually happened?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do the same in ts-mokito
, but I am thinking that the old test was good enough.
Do we really need to verify every little aspect? Seems a bit too much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ericsnowcurrently I have some concerns that verifying every count can lead to high coupling. Until we have a team discussion regarding best testing practices, I would go with the existing approach. Please approve the PR if you find everything else okay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine. :)
I was aiming to track usage of the feature for the purpose of future decision making in terms of maintenance and feature work. I understand that if we change the behaviour of the feature and/or setting, we'll probably need to change the telemetry as well, but I don't think we're saving up so much time by tracking something else now. Of course we should always aim for extensible solutions but in this case it's telemetry being captured, not feature or bug fixing work. Does that make sense? |
@luabud not sure what u mean. |
The original requirement was Ie there's a disconnect between the requirement and the solution implementation.. |
@DonJayamanne We're tracking both, Don. Please have a look at my earlier comment. The telemetry event is sent only if the setting is on, and even if the user is not actually using the setting. |
Co-Authored-By: Eric Snow <ericsnowcurrently@gmail.com>
Co-Authored-By: Eric Snow <ericsnowcurrently@gmail.com>
Just waiting for your review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Honestly, this would have landed a lot sooner if the ActiveResourceService
stuff had been done in a separate PR. :)
For #8004
The heart of the change is this file
src/client/providers/terminalProvider.ts
. I am just adding telemetry and passing in the active resource.Other changes are related to #8654 (comment) which just involves refactoring the method
getActiveResource()
.[ ] Test plan is updated as appropriate[ ]package-lock.json
has been regenerated by runningnpm install
(if dependencies have changed)[ ] The wiki is updated with any design decisions/details.