Skip to content

[flutter_tool] restructure ProjectFileInvalidator to no longer directly depend on context - #45739

Merged
jonahwilliams merged 16 commits into
flutter:masterfrom
jonahwilliams:switching_watcher_strategy
Dec 17, 2019
Merged

jonahwilliams merged 16 commits into
flutter:masterfrom
jonahwilliams:switching_watcher_strategy

Conversation

@jonahwilliams

@jonahwilliams jonahwilliams commented Nov 27, 2019 •

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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 {

@christopherfujino christopherfujino Dec 12, 2019 •

Copy link
Copy Markdown
Contributor

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
Copy Markdown
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
Copy Markdown
Contributor

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

@zanderso zanderso left a comment

Copy link
Copy Markdown
Member

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
Copy Markdown
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,

@jonahwilliams jonahwilliams Dec 12, 2019 •

Copy link
Copy Markdown
Contributor Author

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
Copy Markdown
Member

Choose a reason for hiding this comment

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

sgtm

Copy link
Copy Markdown
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
Copy Markdown

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
Copy Markdown
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
Copy Markdown

Choose a reason for hiding this comment

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

fair :)

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown

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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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