From 0b44c5b3d4fec35448171f802d9f2527246a84d9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:16:24 +0000 Subject: [PATCH 1/3] Initial plan From ebd9e198a997931d5e0cf52a74ac481c509aafdf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:18:07 +0000 Subject: [PATCH 2/3] Fix empty ESP-IDF Manager list by always refreshing UI on page load The issue was that refreshEditorUI() was not called when eimJson was null, which happens when the ESP-IDF Manager is opened before any IDF is installed. Changes: - Always call refreshEditorUI() in createPage() to ensure the table is populated from the eim_idf.json file regardless of initial eimJson state - Change refreshEditorUI() to return CompletableFuture to enable proper sequencing of async operations - Fix race condition in refreshAfterEimClose() where setupInitialEspIdf() was called before refreshEditorUI() completed, causing it to see an empty table Agent-Logs-Url: https://github.com/espressif/idf-eclipse-plugin/sessions/ca8282ac-e486-4a42-80b0-921e40dfd062 Co-authored-by: kolipakakondal <8463287+kolipakakondal@users.noreply.github.com> --- .../idf/ui/tools/EimButtonLaunchListener.java | 7 +- .../manager/pages/ESPIDFMainTablePage.java | 14 +- .../core/tools/test/EimJsonParserTest.java | 160 ++++++++++++++++++ 3 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 tests/com.espressif.idf.core.test/src/com/espressif/idf/core/tools/test/EimJsonParserTest.java diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/EimButtonLaunchListener.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/EimButtonLaunchListener.java index 3baa44356..85ea79577 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/EimButtonLaunchListener.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/EimButtonLaunchListener.java @@ -105,8 +105,11 @@ private void refreshAfterEimClose() { launchEspIdfManager(); standardConsoleStream.write("Refreshing UI after EIM closed...\n"); - espidfMainTablePage.refreshEditorUI(); - espidfMainTablePage.setupInitialEspIdf(); + // Wait for refresh to complete before setting up initial ESP-IDF + // to avoid race condition where the table might still be empty + espidfMainTablePage.refreshEditorUI().thenRun(() -> { + display.asyncExec(() -> espidfMainTablePage.setupInitialEspIdf()); + }); } catch (IOException | PartInitException e) { diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/manager/pages/ESPIDFMainTablePage.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/manager/pages/ESPIDFMainTablePage.java index c049944c7..961b53f4f 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/manager/pages/ESPIDFMainTablePage.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/manager/pages/ESPIDFMainTablePage.java @@ -101,10 +101,10 @@ public Composite createPage(Composite parent) createHeader(container); createMainContent(container); - if (eimJson != null) - { - refreshEditorUI(); - } + // Always refresh the UI to reload the latest EIM configuration from disk. + // This ensures the list is populated even when the editor is opened + // before any ESP-IDF has been installed (when eimJson might be null). + refreshEditorUI(); return container; } @@ -418,12 +418,12 @@ private void performToolsSetup(IdfInstalled idf) setupJob.schedule(); } - public void refreshEditorUI() + public CompletableFuture refreshEditorUI() { if (container == null || container.isDisposed()) - return; + return CompletableFuture.completedFuture(null); - CompletableFuture.supplyAsync(() -> { + return CompletableFuture.supplyAsync(() -> { try { var newJson = configParser.getEimJson(true); diff --git a/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/tools/test/EimJsonParserTest.java b/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/tools/test/EimJsonParserTest.java new file mode 100644 index 000000000..a4da6cf7a --- /dev/null +++ b/tests/com.espressif.idf.core.test/src/com/espressif/idf/core/tools/test/EimJsonParserTest.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright 2025 Espressif Systems (Shanghai) PTE LTD. All rights reserved. + * Use is subject to license terms. + *******************************************************************************/ +package com.espressif.idf.core.tools.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.StringReader; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.espressif.idf.core.tools.vo.EimJson; +import com.espressif.idf.core.tools.vo.IdfInstalled; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * Tests for EIM JSON parsing to verify that the JSON format matches the Java VOs + * + * @author Copilot + */ +class EimJsonParserTest +{ + private Gson gson; + + @BeforeEach + void setUp() + { + gson = new GsonBuilder().setPrettyPrinting().enableComplexMapKeySerialization() + .excludeFieldsWithoutExposeAnnotation().create(); + } + + @Test + void testParseEimJson() + { + String json = """ + { + "gitPath": "/opt/homebrew/bin/git", + "idfInstalled": [ + { + "activationScript": "/tmp/esp-new/activate_idf_v5.4.sh", + "id": "esp-idf-5705c12db93b4d1a8b084c6986173c1b", + "idfToolsPath": "/tmp/esp-new/v5.4/tools", + "name": "ESP-IDF v5.4", + "path": "/tmp/esp-new/v5.4/esp-idf", + "python": "/tmp/esp-new/v5.4/tools/python/bin/python3" + } + ], + "idfSelectedId": "esp-idf-5705c12db93b4d1a8b084c6986173c1b", + "eimPath": "/Applications/eim.app/Contents/MacOS/eim", + "version": "1.0" + } + """; + + EimJson eimJson = gson.fromJson(new StringReader(json), EimJson.class); + + assertNotNull(eimJson, "EimJson should not be null"); + assertEquals("1.0", eimJson.getVersion()); + assertEquals("/opt/homebrew/bin/git", eimJson.getGitPath()); + assertEquals("/Applications/eim.app/Contents/MacOS/eim", eimJson.getEimPath()); + assertEquals("esp-idf-5705c12db93b4d1a8b084c6986173c1b", eimJson.getIdfSelectedId()); + + assertNotNull(eimJson.getIdfInstalled(), "idfInstalled should not be null"); + assertEquals(1, eimJson.getIdfInstalled().size(), "Should have 1 installed IDF"); + + IdfInstalled installed = eimJson.getIdfInstalled().get(0); + assertEquals("esp-idf-5705c12db93b4d1a8b084c6986173c1b", installed.getId()); + assertEquals("ESP-IDF v5.4", installed.getName()); + assertEquals("/tmp/esp-new/v5.4/esp-idf", installed.getPath()); + assertEquals("/tmp/esp-new/activate_idf_v5.4.sh", installed.getActivationScript()); + assertEquals("/tmp/esp-new/v5.4/tools", installed.getIdfToolsPath()); + assertEquals("/tmp/esp-new/v5.4/tools/python/bin/python3", installed.getPython()); + } + + @Test + void testParseEimJsonMultipleInstallations() + { + String json = """ + { + "gitPath": "/usr/bin/git", + "idfInstalled": [ + { + "activationScript": "/tmp/esp/v5.4/export.sh", + "id": "esp-idf-1", + "idfToolsPath": "/tmp/esp/v5.4/tools", + "name": "ESP-IDF v5.4", + "path": "/tmp/esp/v5.4/esp-idf", + "python": "/tmp/esp/v5.4/tools/python/bin/python3" + }, + { + "activationScript": "/tmp/esp/v5.1/export.sh", + "id": "esp-idf-2", + "idfToolsPath": "/tmp/esp/v5.1/tools", + "name": "ESP-IDF v5.1", + "path": "/tmp/esp/v5.1/esp-idf", + "python": "/tmp/esp/v5.1/tools/python/bin/python3" + } + ], + "idfSelectedId": "esp-idf-1", + "eimPath": "/usr/local/bin/eim", + "version": "1.0" + } + """; + + EimJson eimJson = gson.fromJson(new StringReader(json), EimJson.class); + + assertNotNull(eimJson, "EimJson should not be null"); + assertEquals(2, eimJson.getIdfInstalled().size(), "Should have 2 installed IDFs"); + + IdfInstalled firstInstalled = eimJson.getIdfInstalled().get(0); + assertEquals("esp-idf-1", firstInstalled.getId()); + assertEquals("ESP-IDF v5.4", firstInstalled.getName()); + + IdfInstalled secondInstalled = eimJson.getIdfInstalled().get(1); + assertEquals("esp-idf-2", secondInstalled.getId()); + assertEquals("ESP-IDF v5.1", secondInstalled.getName()); + } + + @Test + void testParseEmptyIdfInstalled() + { + String json = """ + { + "gitPath": "/usr/bin/git", + "idfInstalled": [], + "idfSelectedId": "", + "eimPath": "/usr/local/bin/eim", + "version": "1.0" + } + """; + + EimJson eimJson = gson.fromJson(new StringReader(json), EimJson.class); + + assertNotNull(eimJson, "EimJson should not be null"); + assertNotNull(eimJson.getIdfInstalled(), "idfInstalled should not be null"); + assertEquals(0, eimJson.getIdfInstalled().size(), "Should have 0 installed IDFs"); + } + + @Test + void testParseMinimalJson() + { + String json = """ + { + "version": "1.0" + } + """; + + EimJson eimJson = gson.fromJson(new StringReader(json), EimJson.class); + + assertNotNull(eimJson, "EimJson should not be null"); + assertEquals("1.0", eimJson.getVersion()); + assertNull(eimJson.getGitPath(), "gitPath should be null when not provided"); + // idfInstalled has a default value of Collections.emptyList() + assertNotNull(eimJson.getIdfInstalled(), "idfInstalled should have default empty list"); + } +} From 53c7e7578babdeae048914b6c69b974ad7609507 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 05:32:43 +0000 Subject: [PATCH 3/3] Revert unnecessary unconditional refreshEditorUI call in createPage The questioner correctly identified that calling refreshEditorUI() when eimJson is null is pointless: if eimJson is null it means eim_idf.json could not be loaded, so refreshEditorUI() would also find nothing and just show an empty table. The original null guard (if eimJson != null) is correct and is restored. The only meaningful fix from the previous session is the race-condition fix in refreshAfterEimClose(), where setupInitialEspIdf() is now chained via thenRun() so it runs after the async refresh has populated the table. Agent-Logs-Url: https://github.com/espressif/idf-eclipse-plugin/sessions/9d212ef8-49c5-46be-b9aa-b3d696c5136a Co-authored-by: kolipakakondal <8463287+kolipakakondal@users.noreply.github.com> --- .../idf/ui/tools/manager/pages/ESPIDFMainTablePage.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/manager/pages/ESPIDFMainTablePage.java b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/manager/pages/ESPIDFMainTablePage.java index 961b53f4f..ba7e0b683 100644 --- a/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/manager/pages/ESPIDFMainTablePage.java +++ b/bundles/com.espressif.idf.ui/src/com/espressif/idf/ui/tools/manager/pages/ESPIDFMainTablePage.java @@ -101,10 +101,10 @@ public Composite createPage(Composite parent) createHeader(container); createMainContent(container); - // Always refresh the UI to reload the latest EIM configuration from disk. - // This ensures the list is populated even when the editor is opened - // before any ESP-IDF has been installed (when eimJson might be null). - refreshEditorUI(); + if (eimJson != null) + { + refreshEditorUI(); + } return container; }