From 291606ef3e236bab7ae5dde7e90a977e3743940f Mon Sep 17 00:00:00 2001 From: Luke Memet Date: Wed, 18 Feb 2026 12:58:22 -0500 Subject: [PATCH] Add test asserting display name setter is accessible on current IntelliJ build Extracts the reflection lookup into a @VisibleForTesting getDisplaySetter() method and adds a test asserting it returns non-null. Serves as a regression guard: if JetBrains removes or renames setDisplayName in a future build, the test will fail loudly rather than the feature silently breaking for users. Follow-up to #8796. --- src/io/flutter/run/LaunchState.java | 40 +++++++++++++++---- .../unit/io/flutter/run/LaunchStateTest.java | 21 ++++++++++ 2 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 testSrc/unit/io/flutter/run/LaunchStateTest.java diff --git a/src/io/flutter/run/LaunchState.java b/src/io/flutter/run/LaunchState.java index 06c0e5efd..90a720a0b 100644 --- a/src/io/flutter/run/LaunchState.java +++ b/src/io/flutter/run/LaunchState.java @@ -60,6 +60,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.BiConsumer; +import org.jetbrains.annotations.VisibleForTesting; /** * Launches a flutter app, showing it in the console. @@ -168,20 +170,42 @@ protected RunContentDescriptor launch(@NotNull ExecutionEnvironment env) throws // The descriptor shows the run configuration name (e.g., `main.dart`) by default; // adding the device name will help users identify the instance when trying to operate a specific one. final String nameWithDeviceName = descriptor.getDisplayName() + " (" + device.deviceName() + ")"; - - try { - // RunContentDescriptor.setDisplayName() is protected, so we use reflection to call it. - final Method setDisplayName = RunContentDescriptor.class.getDeclaredMethod("setDisplayName", String.class); - setDisplayName.setAccessible(true); - setDisplayName.invoke(descriptor, nameWithDeviceName); + final BiConsumer setter = getDisplaySetter(); + if (setter != null) { + setter.accept(descriptor, nameWithDeviceName); } - catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - FlutterUtils.info(LOG, "Error setting display name", e, true); + else { + FlutterUtils.info(LOG, "Could not find a way to set run tab display name", null, true); } return descriptor; } + /** + * Returns a function that sets the display name on a {@link RunContentDescriptor}, or null if unavailable. + * Uses reflection to call the protected {@code setDisplayName()} method. + * Exposed for testing to verify that the method is accessible on the current IntelliJ build. + */ + @VisibleForTesting + @Nullable + static BiConsumer getDisplaySetter() { + try { + final Method m = RunContentDescriptor.class.getDeclaredMethod("setDisplayName", String.class); + m.setAccessible(true); + return (descriptor, name) -> { + try { + m.invoke(descriptor, name); + } + catch (IllegalAccessException | InvocationTargetException e) { + FlutterUtils.info(LOG, "Error setting display name", e, true); + } + }; + } + catch (NoSuchMethodException e) { + return null; + } + } + private static Class classForName(String className) { try { return Class.forName(className); diff --git a/testSrc/unit/io/flutter/run/LaunchStateTest.java b/testSrc/unit/io/flutter/run/LaunchStateTest.java new file mode 100644 index 000000000..0813f294b --- /dev/null +++ b/testSrc/unit/io/flutter/run/LaunchStateTest.java @@ -0,0 +1,21 @@ +/* + * Copyright 2026 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +package io.flutter.run; + +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class LaunchStateTest { + + @Test + public void displaySetterIsAvailable() { + assertNotNull( + "setDisplayName not found on RunContentDescriptor — JetBrains may have removed or renamed it", + LaunchState.getDisplaySetter() + ); + } +}