bazelbuild / intellij

IntelliJ plugin for Bazel projects
https://ij.bazel.build/
Apache License 2.0
752 stars 296 forks source link

[Golang] Bazel plugin doesn't filter go tests implemented as receiver methods #5455

Open jyyeh77 opened 8 months ago

jyyeh77 commented 8 months ago

Description of the bug:

Running individual tests implemented as receiver methods of a struct through Run configurations auto-generated by the Bazel for IntelliJ plugin don't recognize the methods filtered by the --test-filter flag, leading to failing tests appearing to pass when they don't execute.

Example auto-generated configuration:

Screenshot 2023-10-11 at 11 04 20 AM

What's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.

How to reproduce

Given BUILD.bazel:

//...

go_test(
    name = "go_default_test",
    srcs = [
        "foo_test.go",
    ],
    embed = [":go_default_library"],
    deps = [
        "@com_github_stretchr_testify//suite:go_default_library",
    ],
)

Given test file:

package testpkg

import (
    "github.com/stretchr/testify/suite"
    "testing"
)

type BazTestSuite struct {
    suite.Suite
}

func TestBazTestSuite(t *testing.T) {
    suite.Run(t, new(BazTestSuite))
}

func (t *BazTestSuite) TestFoo() {
    t.Equal("foo", "baz")
}

Click on the green 'Run' arrow next to the test:

Screenshot 2023-10-11 at 11 06 03 AM

Which Intellij IDE are you using? Please provide the specific version.

2022.3 (Ultimate)

What programming languages and tools are you using? Please provide specific versions.

Golang 1.20

What Bazel plugin version are you using?

2023.07.04.0.1-api-version-223

Have you found anything relevant by searching the web?

No response

Any other information, logs, or outputs that you want to share?

No response

aczapszys-apex commented 1 month ago

Note that in this case, intellij should pass --test_arg=-testify.m in place of --test_filter. That would look like

--test_env=GO_TEST_WRAP_TESTV=1
--test_arg=-testify.m=^TestFoo$
aczapszys-apex commented 1 month ago

I'm not sure how to build this plugin, but I put together an inspirational hack for this support.

diff --git base/src/com/google/idea/blaze/base/command/BlazeFlags.java base/src/com/google/idea/blaze/base/command/BlazeFlags.java
index 8925b3485..4ec3797b1 100644
--- base/src/com/google/idea/blaze/base/command/BlazeFlags.java
+++ base/src/com/google/idea/blaze/base/command/BlazeFlags.java
@@ -48,6 +48,8 @@ public final class BlazeFlags {
   public static final String DISABLE_TEST_SHARDING = "--test_sharding_strategy=disabled";
   // Filters the unit tests that are run (used with regexp for Java/Robolectric tests).
   public static final String TEST_FILTER = "--test_filter";
+  // Filters the unit tests that are run via a Testify test suite.
+  public static final String TEST_FILTER_TESTIFY = "--test_arg=-testify.m";
   // Re-run the test even if the results are cached.
   public static final String NO_CACHE_TEST_RESULTS = "--nocache_test_results";
   // Environment variables for the test runner
diff --git base/src/com/google/idea/blaze/base/run/producers/TestContext.java base/src/com/google/idea/blaze/base/run/producers/TestContext.java
index 030af25d4..38050e542 100644
--- base/src/com/google/idea/blaze/base/run/producers/TestContext.java
+++ base/src/com/google/idea/blaze/base/run/producers/TestContext.java
@@ -170,8 +170,28 @@ public abstract class TestContext implements RunConfigurationContext {
         @Override
         public boolean matchesConfigState(RunConfigurationFlagsState state) {
           return state
-              .getRawFlags()
-              .contains(BlazeFlags.TEST_FILTER + "=" + BlazeParametersListUtil.encodeParam(filter));
+                  .getRawFlags()
+                  .contains(BlazeFlags.TEST_FILTER + "=" + BlazeParametersListUtil.encodeParam(filter));
+        }
+      };
+    }
+
+    static BlazeFlagsModification testFilterForTestifySuite(String filter) {
+      return new BlazeFlagsModification() {
+        @Override
+        public void modifyFlags(List<String> flags) {
+          // remove old test filter flag if present
+          flags.removeIf((flag) -> flag.startsWith(BlazeFlags.TEST_FILTER_TESTIFY));
+          if (filter != null) {
+            flags.add(BlazeFlags.TEST_FILTER_TESTIFY + "=" + BlazeParametersListUtil.encodeParam(filter));
+          }
+        }
+
+        @Override
+        public boolean matchesConfigState(RunConfigurationFlagsState state) {
+          return state
+                  .getRawFlags()
+                  .contains(BlazeFlags.TEST_FILTER_TESTIFY + "=" + BlazeParametersListUtil.encodeParam(filter));
         }
       };
     }
@@ -234,9 +254,13 @@ public abstract class TestContext implements RunConfigurationContext {
     }

     @CanIgnoreReturnValue
-    public Builder setTestFilter(@Nullable String filter) {
+    public Builder setTestFilter(@Nullable String filter, boolean isMethod) {
       if (filter != null) {
-        blazeFlags.add(BlazeFlagsModification.testFilter(filter));
+        if (isMethod) {
+          blazeFlags.add(BlazeFlagsModification.testFilterForTestifySuite(filter));
+        } else {
+          blazeFlags.add(BlazeFlagsModification.testFilter(filter));
+        }
       }
       return this;
     }
diff --git golang/src/com/google/idea/blaze/golang/run/producers/GoTestContextProvider.java golang/src/com/google/idea/blaze/golang/run/producers/GoTestContextProvider.java
index 14f87ac6e..ce723941c 100644
--- golang/src/com/google/idea/blaze/golang/run/producers/GoTestContextProvider.java
+++ golang/src/com/google/idea/blaze/golang/run/producers/GoTestContextProvider.java
@@ -19,6 +19,7 @@ import com.goide.execution.testing.GoTestFinder;
 import com.goide.execution.testing.GoTestRunConfigurationProducerBase;
 import com.goide.psi.GoFile;
 import com.goide.psi.GoFunctionOrMethodDeclaration;
+import com.goide.psi.GoMethodDeclaration;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.idea.blaze.base.dependencies.TargetInfo;
@@ -110,11 +111,12 @@ class GoTestContextProvider implements TestContextProvider {
           .setDescription(file.getName())
           .build();
     }
+    boolean isMethod = function.getClass().isAssignableFrom(GoMethodDeclaration.class);
     String testFilterRegex = regexifyTestFilter(calculateRawTestFilterForElement(element, function));
     return TestContext.builder(/* sourceElement= */ function, ExecutorType.DEBUG_SUPPORTED_TYPES)
         .addTestEnv(GO_TEST_WRAP_TESTV)
         .setTarget(target)
-        .setTestFilter(testFilterRegex)
+        .setTestFilter(testFilterRegex, isMethod)
         .setDescription(String.format("%s#%s", file.getName(), function.getName()))
         .build();
   }