Skip to content

Conversation

@jonahwilliams
Copy link
Contributor

@jonahwilliams jonahwilliams commented Nov 27, 2019

Description

Removes direct usage of context getters from ProjectFileInvalidator. Allows testing without testbed or context.

@fluttergithubbot fluttergithubbot added the tool Affects the "flutter" command-line tool. See also t: labels. label Nov 27, 2019
@jonahwilliams jonahwilliams changed the title Add hybrid watching strategy to speed up dependency checking [flutter_tool] restructure ProjectFileInvalidator to no longer directly depend on context Dec 12, 2019
@jonahwilliams
Copy link
Contributor Author

FYI @christopherfujino @blasten @jmagman @zanderso

For an example of removing direct context dependencies. I think the tests are a lot easier to read without a testing zone.

void _testProjectFileInvalidator({@required bool asyncScanning}) {
const ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator();
for (bool asyncScanning in <bool>[true, false]) {
test('No last compile', () async {
Copy link
Contributor

@christopherfujino christopherfujino Dec 12, 2019

Choose a reason for hiding this comment

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

should these tests have unique names (i.e. interpolate asyncScanning)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call, fixed

@christopherfujino
Copy link
Contributor

This is pretty cool. I agree, the test is more readable and explicit.

Copy link
Member

@zanderso zanderso left a comment

Choose a reason for hiding this comment

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

Generally like the idea.

void _testProjectFileInvalidator({@required bool asyncScanning}) {
const ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator();
for (bool asyncScanning in <bool>[true, false]) {
test('No last compile, asyncScanning: $asyncScanning', () async {
Copy link
Member

Choose a reason for hiding this comment

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

These probably need to use testUsingContext() with a context that maps everything to a fake implementation that crashes or a Mock with no behavior to ensure that, e.g. some library called by the ProjectFileInvalidator now or in the future doesn't use default globals, like for the file system:

context.get<FileSystem>() ?? _kLocalFs,

Copy link
Contributor Author

@jonahwilliams jonahwilliams Dec 12, 2019

Choose a reason for hiding this comment

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

We could wrap in a test context, possible fiddle with context a bit so that it threw if accessed at all? I think that would be preferable to creating a bunch of throwing mocks

Copy link
Member

Choose a reason for hiding this comment

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

sgtm

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Giving this a shot by wrapping the existing test method ... we'll see if this works. else we could name it testWithoutContext ... or hopefully something shorter

class ProjectFileInvalidator {
const ProjectFileInvalidator();
ProjectFileInvalidator(
this._fileSystem,
Copy link

Choose a reason for hiding this comment

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

I tend to prefer named parameters when a method/constructor involves more than one parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems like a good idea. Generally there is a bit more boilerplate involved if we want to use named parameters but still have private fields which is why I avoided it.

Copy link

Choose a reason for hiding this comment

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

fair :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Still updated to use named parameters & private fields, its really a minor complaint. Plus it reads a lot better

}

@isTest
void test(String description, FutureOr<void> body(), {
Copy link
Member

Choose a reason for hiding this comment

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

Will this wrapper be in place for all tool tests that just say test() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we could take this opportunity to enforce some reasonable default behavior in the future if necessary, such as providing zone based throwing implementations of the real filesystem or network behavior.

Map<String, dynamic> onPlatform,
int retry,
}) {
return runZoned(() {
Copy link
Member

Choose a reason for hiding this comment

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

Will the test body run in this zone?


@override
T get<T>() {
throw UnsupportedError('context.get is not supported in this test.');
Copy link
Member

Choose a reason for hiding this comment

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

Consider including $T in the exception message.

class ProjectFileInvalidator {
const ProjectFileInvalidator();
ProjectFileInvalidator({
@required FileSystem fileSystem,
Copy link

Choose a reason for hiding this comment

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

nice!

});

testWithoutContext('Throws if accessing the Zone', () {
expect(() => fs, throwsA(isInstanceOf<UnsupportedError>()));
Copy link
Member

Choose a reason for hiding this comment

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

Should this test just do a context.get() directly so that it isn't dependent on how the fs getter is implemented? A separate test could check that fs checks the context, but I'm not sure how valuable that test would be.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

}

@isTest
void testWithoutContext(String description, FutureOr<void> body(), {
Copy link
Member

Choose a reason for hiding this comment

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

This needs some comments about when to use and/or not use this. I think it would also be good to have a code health issue filed on github linked from here that describes what the next steps are, and maybe suggests some candidate components that could benefit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Filled #47161 and added a doc comment

@jonahwilliams jonahwilliams merged commit a723c94 into flutter:master Dec 17, 2019
@jonahwilliams jonahwilliams deleted the switching_watcher_strategy branch December 17, 2019 02:12
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

tool Affects the "flutter" command-line tool. See also t: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants