Skip to content

Commit 712e40d

Browse files
authored
Refactor FlutterStudioProjectOpenProcessor to improve openProjectAsync robustness with a project fallback mechanism, extracting helper methods, and adding a unit test. (#8681)
Follow up on: #8629 Related issue: #8661
1 parent 77a77fd commit 712e40d

2 files changed

Lines changed: 83 additions & 8 deletions

File tree

src/io/flutter/editor/FlutterStudioProjectOpenProcessor.kt

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import io.flutter.utils.FlutterModuleUtils
2424
*
2525
* Converted to Kotlin to support `openProjectAsync`.
2626
*/
27-
class FlutterStudioProjectOpenProcessor : FlutterProjectOpenProcessor() {
27+
open class FlutterStudioProjectOpenProcessor : FlutterProjectOpenProcessor() {
2828
override val name: String
2929
get() = "Flutter Studio"
3030

@@ -50,23 +50,38 @@ class FlutterStudioProjectOpenProcessor : FlutterProjectOpenProcessor() {
5050
val project = importProvider.openProjectAsync(virtualFile, projectToClose, forceOpenInNewFrame)
5151

5252
// A callback may have caused the project to be reloaded. Find the new Project object.
53-
val newProject = FlutterUtils.findProject(virtualFile.path)
53+
var newProject = findProject(virtualFile.path)
5454
if (newProject == null || newProject.isDisposed) {
55-
return newProject
55+
// Fallback to the original project if re-lookup fails.
56+
if (project != null && !project.isDisposed) {
57+
newProject = project
58+
} else {
59+
return null
60+
}
5661
}
5762

58-
for (module in FlutterModuleUtils.getModules(newProject)) {
63+
configureFlutterProject(newProject)
64+
65+
return newProject
66+
}
67+
68+
@org.jetbrains.annotations.VisibleForTesting
69+
protected open suspend fun configureFlutterProject(project: Project) {
70+
for (module in FlutterModuleUtils.getModules(project)) {
5971
if (FlutterModuleUtils.declaresFlutter(module) && !FlutterModuleUtils.isFlutterModule(module)) {
6072
writeAction {
6173
FlutterModuleUtils.setFlutterModuleType(module)
6274
}
6375
FlutterModuleUtils.enableDartSDK(module)
6476
}
6577
}
66-
newProject.save()
67-
EditorNotifications.getInstance(newProject).updateAllNotifications()
68-
69-
return newProject
78+
project.save()
79+
EditorNotifications.getInstance(project).updateAllNotifications()
80+
}
81+
82+
@org.jetbrains.annotations.VisibleForTesting
83+
protected open fun findProject(path: String): Project? {
84+
return FlutterUtils.findProject(path)
7085
}
7186

7287
override fun doOpenProject(
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package io.flutter.editor
2+
3+
import com.intellij.openapi.project.Project
4+
import com.intellij.openapi.vfs.VirtualFile
5+
import com.intellij.projectImport.ProjectOpenProcessor
6+
import kotlinx.coroutines.runBlocking
7+
import org.junit.Assert
8+
import org.junit.Test
9+
import org.mockito.Mockito
10+
11+
class FlutterStudioProjectOpenProcessorTest {
12+
@Test
13+
fun testOpenProjectAsyncFallsBackWhenFindProjectFails() = runBlocking {
14+
// Setup
15+
val mockProject = Mockito.mock(Project::class.java)
16+
val mockVirtualFile = Mockito.mock(VirtualFile::class.java)
17+
Mockito.`when`(mockVirtualFile.path).thenReturn("/path/to/project")
18+
Mockito.`when`(mockProject.isDisposed).thenReturn(false)
19+
20+
val fakeDelegate = object : ProjectOpenProcessor() {
21+
override val name: String = "Fake"
22+
override fun canOpenProject(file: VirtualFile): Boolean = true
23+
override fun doOpenProject(
24+
virtualFile: VirtualFile,
25+
projectToClose: Project?,
26+
forceOpenInNewFrame: Boolean
27+
): Project? = mockProject
28+
29+
override suspend fun openProjectAsync(
30+
virtualFile: VirtualFile,
31+
projectToClose: Project?,
32+
forceOpenInNewFrame: Boolean
33+
): Project? {
34+
return mockProject
35+
}
36+
}
37+
38+
val processor = object : FlutterStudioProjectOpenProcessor() {
39+
override fun getDelegateImportProvider(file: VirtualFile): ProjectOpenProcessor? {
40+
return fakeDelegate
41+
}
42+
43+
override fun findProject(path: String): Project? {
44+
return null // Simulate failure to find project
45+
}
46+
47+
override suspend fun configureFlutterProject(project: Project) {
48+
// No-op for test to avoid dependency on static FlutterModuleUtils
49+
}
50+
}
51+
52+
// Execution
53+
val result = processor.openProjectAsync(mockVirtualFile, null, false)
54+
55+
// Verification
56+
// If the fix works, it should fallback to mockProject
57+
// If the fix is absent, it would return null (because findProject returned null)
58+
Assert.assertEquals(mockProject, result)
59+
}
60+
}

0 commit comments

Comments
 (0)