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 the Bazel Test configuration to support running tests on a single file or a single test #2960

Merged
merged 27 commits into from Jan 17, 2019

Conversation

DaveShuckerow
Copy link
Contributor

@DaveShuckerow DaveShuckerow commented Dec 13, 2018

I have a few questions I need to answer and fixes I need to make before submitting this:

  • How can I test the linecontributor that I added to be sure it will work?
  • Do we have a system for adding this feature behind a configuration option that users can turn off? I don't want to roll this out and have to roll back if it breaks a few bazel users' workflows.
  • Refactor common logic from the test configuration producers and line contributors into a common utility so that we have a smaller surface to test.

I would like an initial review and some thoughts on the 3 questions I have left. Thanks!

@@ -686,7 +686,9 @@
<programRunner implementation="io.flutter.run.bazel.BazelRunner"/>

<configurationType implementation="io.flutter.run.bazelTest.FlutterBazelTestConfigurationType"/>
<runConfigurationProducer implementation="io.flutter.run.bazelTest.BazelTestConfigProducer"/>
Copy link
Member

Choose a reason for hiding this comment

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

This line will get deleted during the build unless it is added to the template. This may be the cause of the travis failure.

Copy link
Member

Choose a reason for hiding this comment

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

Yup, you'll want to contribute the change to resources/META-INF/plugin_template.xml, and then run ./bin/plugin generate.

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.

@devoncarew
Copy link
Member

Do we have a system for adding this feature behind a configuration option that users can turn off? I don't want to roll this out and have to roll back if it breaks a few bazel users' workflows.

We do have an 'experimental' section in our preferences dialog; those normally start out as opt-in. You can see the options from the FlutterSettings class.

@devoncarew
Copy link
Member

devoncarew commented Dec 14, 2018

DBC: can you attach a screen shot for the UI changes? (FlutterBazelTestConfigurationEditorForm.form)

return true;
}

private VirtualFile verifyFlutterTestFile(BazelTestConfig config, ConfigurationContext context, DartFile file) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Possible follow up: @Nullable and @NotNull propagation in IntelliJ can help prevents NPEs

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.

public static final String WIDGET_TEST_FUNCTION = "testWidgets";

public static boolean isFlutterTest(DartFile file) {
return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should there be a TODO in this method??? If not, perhaps a comment as to why.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This wasn't complete; I've gone back and finished this code. PTAL.

For this section, it was definitely worth fixing instead of leaving it as a TODO.

fields.setBazelTarget(StringUtil.nullize(myBuildTarget.getText().trim(), true));
fields.setLaunchingScript(StringUtil.nullize(FileUtil.toSystemIndependentName(myLaunchingScript.getText().trim()), true));
final String testName = StringUtil.nullize(this.myTestName.getText().trim(), true);
final String entryFile = StringUtil.nullize(FileUtil.toSystemIndependentName(myEntryFile.getText().trim()), true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Good call wrapping this with FileUtil.toSystemIndependentName(), when we have Windows behaving differently is is typically because I forgot to wrap with this method.

fields.setEntryFile(StringUtil.nullize(FileUtil.toSystemIndependentName(myEntryFile.getText().trim()), true));
fields.setBazelTarget(StringUtil.nullize(myBuildTarget.getText().trim(), true));
fields.setLaunchingScript(StringUtil.nullize(FileUtil.toSystemIndependentName(myLaunchingScript.getText().trim()), true));
final String testName = StringUtil.nullize(this.myTestName.getText().trim(), true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't the nullize be called on this.myTestName, if myTestName is null, trim would throw a NPE.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No. Nullize is the opposite of notNullize: it makes empty strings null.

The text fields, so far as I understand, have @NotNull text fields.

fields.setLaunchingScript(StringUtil.nullize(FileUtil.toSystemIndependentName(myLaunchingScript.getText().trim()), true));
final String testName = StringUtil.nullize(this.myTestName.getText().trim(), true);
final String entryFile = StringUtil.nullize(FileUtil.toSystemIndependentName(myEntryFile.getText().trim()), true);
final String bazelTarget = StringUtil.nullize(myBuildTarget.getText().trim(), true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, shouldn't nullize() be called on the next before trim() is called?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same.

I've refactored this out into a helper method so that the behavior is easier to keep consistent.

}
else if (next != Scope.TARGET_PATTERN) {
if (myEntryFile.getText().isEmpty()) {
myEntryFile.setText(myBuildTarget.getText().replace("//", ""));
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you need a call to StringUtil.nullize() like you have in line 84?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not necessary, but it's a good idea.

This if statement should semantically mean that the text in myEntryFile will be the same as passing a null value. To achieve this, it's best to use the same logic for determining its value as we were using in the other line.

@@ -34,7 +34,7 @@
@Nullable
public static TestType asTestCall(@NotNull PsiElement element) {
final DartFile file = FlutterUtils.getDartFile(element);
if (FlutterUtils.isInTestDir(file) && FlutterUtils.isInFlutterProject(element)) {
if (FlutterUtils.isInTestDir(file)/* && FlutterUtils.isInFlutterProject(element)*/) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove commented out code

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.

@jwren
Copy link
Contributor

jwren commented Dec 14, 2018

@DaveShuckerow LGTM

On your questions:

  1. line contributor -- lots of manual testing. Even if it was easy and we had bunch of tests for run configurations, lots of manual testing is required here.

  2. System to have work behind a flag-- Devon's experimental comment counts only for things behind that flag/ setting. Since this PR adds to the plugin XML, realize that the IntelliJ framework will come along and start to instantiate the appropriate objects. If you do use the experimental flag make sure that you disable everything you intend to. Again-- lots of manual testing.

  3. Refactoring task. If it is simply a nice refactoring, perhaps follow up with a purely refactoring PR later?

Copy link
Contributor Author

@DaveShuckerow DaveShuckerow left a comment

Choose a reason for hiding this comment

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

I added in tests for the command line calls getting generated by the flutter test script; this will help us prevent regressions against the g3 tooling it invokes.

These changes reflect answers to my three questions from the beginning of the PR.

  • Tests for the linecontributor are manual for now 😢
  • I added in an experiment to turn on the new bazel test runner.
  • Pub and bazel logic is different enough that I feel more comfortable separating the utilities than sharing them. I did refactor out some common logic, like the run configuration serialization.

@@ -686,7 +686,9 @@
<programRunner implementation="io.flutter.run.bazel.BazelRunner"/>

<configurationType implementation="io.flutter.run.bazelTest.FlutterBazelTestConfigurationType"/>
<runConfigurationProducer implementation="io.flutter.run.bazelTest.BazelTestConfigProducer"/>
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.

fields.setEntryFile(StringUtil.nullize(FileUtil.toSystemIndependentName(myEntryFile.getText().trim()), true));
fields.setBazelTarget(StringUtil.nullize(myBuildTarget.getText().trim(), true));
fields.setLaunchingScript(StringUtil.nullize(FileUtil.toSystemIndependentName(myLaunchingScript.getText().trim()), true));
final String testName = StringUtil.nullize(this.myTestName.getText().trim(), true);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

No. Nullize is the opposite of notNullize: it makes empty strings null.

The text fields, so far as I understand, have @NotNull text fields.

fields.setLaunchingScript(StringUtil.nullize(FileUtil.toSystemIndependentName(myLaunchingScript.getText().trim()), true));
final String testName = StringUtil.nullize(this.myTestName.getText().trim(), true);
final String entryFile = StringUtil.nullize(FileUtil.toSystemIndependentName(myEntryFile.getText().trim()), true);
final String bazelTarget = StringUtil.nullize(myBuildTarget.getText().trim(), true);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same.

I've refactored this out into a helper method so that the behavior is easier to keep consistent.

}
else if (next != Scope.TARGET_PATTERN) {
if (myEntryFile.getText().isEmpty()) {
myEntryFile.setText(myBuildTarget.getText().replace("//", ""));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not necessary, but it's a good idea.

This if statement should semantically mean that the text in myEntryFile will be the same as passing a null value. To achieve this, it's best to use the same logic for determining its value as we were using in the other line.

@@ -34,7 +34,7 @@
@Nullable
public static TestType asTestCall(@NotNull PsiElement element) {
final DartFile file = FlutterUtils.getDartFile(element);
if (FlutterUtils.isInTestDir(file) && FlutterUtils.isInFlutterProject(element)) {
if (FlutterUtils.isInTestDir(file)/* && FlutterUtils.isInFlutterProject(element)*/) {
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.

public static final String WIDGET_TEST_FUNCTION = "testWidgets";

public static boolean isFlutterTest(DartFile file) {
return true;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This wasn't complete; I've gone back and finished this code. PTAL.

For this section, it was definitely worth fixing instead of leaving it as a TODO.

Copy link
Member

@devoncarew devoncarew left a comment

Choose a reason for hiding this comment

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

@DaveShuckerow, I made a bunch of drive-by comments.

@Nullable String launchScript,
@Nullable String testScript
) {
final Fields fields = new Fields();
Copy link
Member

Choose a reason for hiding this comment

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

Should we add an additional ctor to Fields here? It would mean less assignment below.

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

import java.util.Arrays;
import java.util.List;

public class BazelTestConfigUtils {
Copy link
Member

Choose a reason for hiding this comment

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

@DaveShuckerow, how much of this code is from TestConfigUtils? Can we share more here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A lot is shared. I've refactored these to have a common abstract superclass.

}

// The value to use for the bazel test runner setting if no FlutterSettings are available.
// TODO: set up a FlutterSettings implementation that we can use in tests.
Copy link
Member

Choose a reason for hiding this comment

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

We should use TODO(username): here, so people will know who to ask for context.

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.

src/io/flutter/run/bazelTest/BazelTestRunner.java Outdated Show resolved Hide resolved
@@ -1,94 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
Copy link
Member

Choose a reason for hiding this comment

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

Can you attach a screen shot for this UI?

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 shared a screenshot for this internally.

import java.util.Map;


public class FlutterBazelTestLineMarkerContributor extends RunLineMarkerContributor {
Copy link
Member

Choose a reason for hiding this comment

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

How much of this can we share with FlutterTestLineMarkerContributor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All of it 😄

src/io/flutter/sdk/FlutterSettingsConfigurable.form Outdated Show resolved Hide resolved
src/io/flutter/settings/FlutterSettings.java Outdated Show resolved Hide resolved
Copy link
Contributor

@jwren jwren left a comment

Choose a reason for hiding this comment

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

LGTM

// The order we do the checks here determines priority.

final DartSdk sdk = DartPlugin.getDartSdk(project);
if (sdk == null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you need a call to sdk.getVersion().flutterTestSupportsFiltering(); here as well, like above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, and for two reasons:

  • bazel test filtering is managed separately from the Flutter SDK AFAIK. Test filtering support was added at a later change in g3 than when it was added to the Flutter SDK.
  • when the test runner is not available, all test configurations will use the legacy behavior, which doesn't attempt to filter targets.

src/io/flutter/run/bazelTest/BazelTestFields.java Outdated Show resolved Hide resolved
final int lineNumber = document.getLineNumber(textOffset);

// e.g., dart_location:///Users/pq/IdeaProjects/untitled1298891289891/test/unit_test.dart,3,2,["my first unit test"]
final String path = containingFile.getVirtualFile().getPath();
Copy link
Contributor

Choose a reason for hiding this comment

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

Paths should be processed through FileUtil.toSystemIndependentName(). If such a call breaks some string assumption here, document the reason.

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

@@ -0,0 +1,41 @@
/*
* Copyright 2018 The Chromium Authors. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: copyrights can be 2019 now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Update all files or just newly added ones?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From discussion, it looks like we leave copyrights on files as the year the file was created, so new files will be copyright 2019, files I created in 2018 will be copyright 2018, and other files should have their copyrights untouched.

@DaveShuckerow
Copy link
Contributor Author

We need to leave a warning when the bazel test runner experiment is turned on and the test runner is not available so that users can sync their clients.

Copy link
Contributor Author

@DaveShuckerow DaveShuckerow left a comment

Choose a reason for hiding this comment

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

I believe I've responded to all the comments.

Please take another look.

}

// The value to use for the bazel test runner setting if no FlutterSettings are available.
// TODO: set up a FlutterSettings implementation that we can use in tests.
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.

import java.util.Arrays;
import java.util.List;

public class BazelTestConfigUtils {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A lot is shared. I've refactored these to have a common abstract superclass.

return true;
}

private VirtualFile verifyFlutterTestFile(BazelTestConfig config, ConfigurationContext context, DartFile file) {
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.

import java.util.Map;


public class FlutterBazelTestLineMarkerContributor extends RunLineMarkerContributor {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All of it 😄

final int lineNumber = document.getLineNumber(textOffset);

// e.g., dart_location:///Users/pq/IdeaProjects/untitled1298891289891/test/unit_test.dart,3,2,["my first unit test"]
final String path = containingFile.getVirtualFile().getPath();
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

src/io/flutter/run/bazelTest/BazelTestFields.java Outdated Show resolved Hide resolved
src/io/flutter/run/bazelTest/BazelTestRunner.java Outdated Show resolved Hide resolved
@@ -1,94 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
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 shared a screenshot for this internally.

src/io/flutter/sdk/FlutterSettingsConfigurable.form Outdated Show resolved Hide resolved
src/io/flutter/settings/FlutterSettings.java Outdated Show resolved Hide resolved
@DaveShuckerow DaveShuckerow merged commit c307e0e into flutter:master Jan 17, 2019
@DaveShuckerow DaveShuckerow deleted the bazel-test branch February 4, 2019 23:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants