This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new e734a122f0 Issue #2742 : multi-transform unit test tweaks and editor 
support (#7573)
e734a122f0 is described below

commit e734a122f0badcdf20bb510e7e5bdf893ec78487
Author: Matt Casters <[email protected]>
AuthorDate: Mon Jul 20 12:45:53 2026 +0200

    Issue #2742 : multi-transform unit test tweaks and editor support (#7573)
    
    Apply Bypass/Remove/Include unit-test tweak actions to all selected
    transforms, not only the right-clicked one. Rework PipelineUnitTestEditor
    into General, Database, Parameters & Variables, and Tweaks tabs so stored
    tweaks are visible and editable in metadata.
---
 .../apache/hop/testing/gui/TestingGuiPlugin.java   |  62 ++++-
 .../org/apache/hop/testing/util/DataSetConst.java  |  42 ++++
 .../hop/ui/testing/PipelineUnitTestEditor.java     | 249 +++++++++++++++++----
 .../ui/testing/messages/messages_en_US.properties  |   7 +
 .../hop/testing/gui/TestingGuiPluginTweakTest.java | 140 ++++++++++++
 5 files changed, 443 insertions(+), 57 deletions(-)

diff --git 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
index 28ee451809..7d89ad5f8f 100644
--- 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
+++ 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/gui/TestingGuiPlugin.java
@@ -1407,7 +1407,11 @@ public class TestingGuiPlugin {
     TransformMeta transformMeta = context.getTransformMeta();
     IVariables variables = context.getPipelineGraph().getVariables();
 
-    if (transformMeta == null || pipelineMeta == null) {
+    if (pipelineMeta == null) {
+      return;
+    }
+    List<TransformMeta> targets = resolveTweakTargetTransforms(pipelineMeta, 
transformMeta);
+    if (targets.isEmpty()) {
       return;
     }
     if (checkTestPresent(hopGui, context)) {
@@ -1419,12 +1423,10 @@ public class TestingGuiPlugin {
       if (unitTest == null) {
         return;
       }
-      PipelineUnitTestTweak unitTestTweak = 
unitTest.findTweak(transformMeta.getName());
-      if (unitTestTweak != null) {
-        unitTest.getTweaks().remove(unitTestTweak);
-      }
-      if (enable) {
-        unitTest.getTweaks().add(new PipelineUnitTestTweak(tweak, 
transformMeta.getName()));
+
+      // Apply to all selected transforms (issue #2742), not only the one 
right-clicked
+      for (TransformMeta target : targets) {
+        applyTweakToTransform(unitTest, target.getName(), tweak, enable);
       }
 
       saveUnitTest(variables, metadataProvider, unitTest, pipelineMeta);
@@ -1439,12 +1441,56 @@ public class TestingGuiPlugin {
           BaseMessages.getString(
               PKG,
               "TestingGuiPlugin.TweakUnitTestTransform.Error.Message",
-              transformMeta.getName(),
+              targets.get(0).getName(),
               tweak.name()),
           exception);
     }
   }
 
+  /**
+   * Resolve which transforms a unit-test tweak action should affect. Prefer 
the current selection;
+   * if nothing is selected, fall back to the transform that was right-clicked.
+   *
+   * @param pipelineMeta the pipeline containing the selection
+   * @param clickedTransform the transform under the context menu, may be null
+   * @return the transforms to tweak (never null)
+   */
+  static List<TransformMeta> resolveTweakTargetTransforms(
+      PipelineMeta pipelineMeta, TransformMeta clickedTransform) {
+    List<TransformMeta> selected = pipelineMeta.getSelectedTransforms();
+    if (selected != null && !selected.isEmpty()) {
+      return selected;
+    }
+    if (clickedTransform == null) {
+      return Collections.emptyList();
+    }
+    return Collections.singletonList(clickedTransform);
+  }
+
+  /**
+   * Apply or clear a unit-test tweak on a single transform. When enabling, 
any existing tweak on
+   * the transform is replaced. When disabling, only a tweak of the requested 
type is removed so a
+   * multi-select action does not wipe unrelated tweaks.
+   *
+   * @param unitTest the active unit test
+   * @param transformName name of the transform to tweak
+   * @param tweak the tweak type
+   * @param enable true to set the tweak, false to clear a matching tweak
+   */
+  static void applyTweakToTransform(
+      PipelineUnitTest unitTest, String transformName, PipelineTweak tweak, 
boolean enable) {
+    PipelineUnitTestTweak existing = unitTest.findTweak(transformName);
+    if (existing != null) {
+      if (!enable && existing.getTweak() != tweak) {
+        return;
+      }
+      unitTest.getTweaks().remove(existing);
+    }
+    if (enable) {
+      unitTest.getTweaks().add(new PipelineUnitTestTweak(tweak, 
transformName));
+    }
+  }
+
   /** List all unit tests which are defined And allow the user to select one */
   public RowMetaAndData selectUnitTestFromAllTests() {
     HopGui hopGui = HopGui.getInstance();
diff --git 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/util/DataSetConst.java
 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/util/DataSetConst.java
index 4340b18954..e2901c36ef 100644
--- 
a/plugins/misc/testing/src/main/java/org/apache/hop/testing/util/DataSetConst.java
+++ 
b/plugins/misc/testing/src/main/java/org/apache/hop/testing/util/DataSetConst.java
@@ -37,6 +37,7 @@ import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.engine.IPipelineEngine;
 import org.apache.hop.testing.DataSet;
+import org.apache.hop.testing.PipelineTweak;
 import org.apache.hop.testing.PipelineUnitTest;
 import org.apache.hop.testing.PipelineUnitTestFieldMapping;
 import org.apache.hop.testing.PipelineUnitTestSetLocation;
@@ -522,4 +523,45 @@ public class DataSetConst {
   public static String[] getTestTypeDescriptions() {
     return testTypeDesc;
   }
+
+  /**
+   * Get the localized description for a pipeline unit-test tweak.
+   *
+   * @param tweak the tweak type
+   * @return description for dialogs/editors
+   */
+  public static String getTweakDescription(PipelineTweak tweak) {
+    int index = 0; // NONE
+    if (tweak != null) {
+      PipelineTweak[] tweaks = PipelineTweak.values();
+      for (int i = 0; i < tweaks.length; i++) {
+        if (tweaks[i] == tweak) {
+          index = i;
+          break;
+        }
+      }
+    }
+    return tweakDesc[index];
+  }
+
+  /**
+   * Get the PipelineTweak for a description (from the dialog).
+   *
+   * @param tweakDescription the description to look for
+   * @return the tweak type or NONE if nothing matched
+   */
+  public static PipelineTweak getTweakForDescription(String tweakDescription) {
+    if (StringUtils.isEmpty(tweakDescription)) {
+      return PipelineTweak.NONE;
+    }
+    int index = Const.indexOfString(tweakDescription, tweakDesc);
+    if (index < 0) {
+      return PipelineTweak.NONE;
+    }
+    return PipelineTweak.values()[index];
+  }
+
+  public static String[] getTweakDescriptions() {
+    return tweakDesc;
+  }
 }
diff --git 
a/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/PipelineUnitTestEditor.java
 
b/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/PipelineUnitTestEditor.java
index f39d206009..4fcb94271d 100644
--- 
a/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/PipelineUnitTestEditor.java
+++ 
b/plugins/misc/testing/src/main/java/org/apache/hop/ui/testing/PipelineUnitTestEditor.java
@@ -21,17 +21,21 @@ import java.util.Collections;
 import java.util.List;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hop.core.Const;
+import org.apache.hop.core.Props;
 import org.apache.hop.core.database.DatabaseMeta;
 import org.apache.hop.core.exception.HopException;
 import org.apache.hop.core.logging.LogChannel;
 import org.apache.hop.i18n.BaseMessages;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.testing.PipelineTweak;
 import org.apache.hop.testing.PipelineUnitTest;
 import org.apache.hop.testing.PipelineUnitTestDatabaseReplacement;
+import org.apache.hop.testing.PipelineUnitTestTweak;
 import org.apache.hop.testing.VariableValue;
 import org.apache.hop.testing.util.DataSetConst;
 import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.dialog.BaseDialog;
+import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.metadata.MetadataEditor;
 import org.apache.hop.ui.core.metadata.MetadataManager;
 import org.apache.hop.ui.core.widget.ColumnInfo;
@@ -39,9 +43,12 @@ import org.apache.hop.ui.core.widget.TableView;
 import org.apache.hop.ui.core.widget.TextVar;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
 import org.eclipse.swt.events.ModifyListener;
 import org.eclipse.swt.layout.FormAttachment;
 import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Composite;
@@ -64,6 +71,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
   private Button wAutoOpen;
   private TableView wDbReplacements;
   private TableView wVariableValues;
+  private TableView wTweaks;
 
   private final PropsUi props;
 
@@ -82,12 +90,59 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
 
     PipelineUnitTest pipelineUnitTest = this.getMetadata();
 
+    int margin = PropsUi.getMargin();
+
+    CTabFolder wTabFolder = new CTabFolder(parent, SWT.BORDER);
+    PropsUi.setLook(wTabFolder, Props.WIDGET_STYLE_TAB);
+    FormData fdTabFolder = new FormData();
+    fdTabFolder.left = new FormAttachment(0, 0);
+    fdTabFolder.top = new FormAttachment(0, 0);
+    fdTabFolder.right = new FormAttachment(100, 0);
+    fdTabFolder.bottom = new FormAttachment(100, -margin);
+    wTabFolder.setLayoutData(fdTabFolder);
+
+    createGeneralTab(wTabFolder);
+    createDatabaseTab(wTabFolder, pipelineUnitTest);
+    createVariablesTab(wTabFolder, pipelineUnitTest);
+    createTweaksTab(wTabFolder, pipelineUnitTest);
+
+    wTabFolder.setSelection(0);
+
+    setWidgetsContent();
+
+    // Add listener to detect change after loading data
+    Listener listener = e -> setChanged();
+    ModifyListener modifyListener = e -> setChanged();
+    wName.addListener(SWT.Modify, listener);
+    wDescription.addListener(SWT.Modify, listener);
+    wTestType.addListener(SWT.Modify, listener);
+    wPipelineFilename.addListener(SWT.Modify, listener);
+    wFilename.addListener(SWT.Modify, listener);
+    wBasePath.addListener(SWT.Modify, listener);
+    wAutoOpen.addListener(SWT.Selection, listener);
+    wDbReplacements.addModifyListener(modifyListener);
+    wVariableValues.addModifyListener(modifyListener);
+    wTweaks.addModifyListener(modifyListener);
+  }
+
+  private void createGeneralTab(CTabFolder wTabFolder) {
     int middle = props.getMiddlePct();
     int margin = PropsUi.getMargin();
 
+    CTabItem wGeneralTab = new CTabItem(wTabFolder, SWT.NONE);
+    wGeneralTab.setFont(GuiResource.getInstance().getFontDefault());
+    wGeneralTab.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Tab.General"));
+
+    Composite wGeneralComp = new Composite(wTabFolder, SWT.NONE);
+    PropsUi.setLook(wGeneralComp);
+    FormLayout generalLayout = new FormLayout();
+    generalLayout.marginWidth = PropsUi.getFormMargin();
+    generalLayout.marginHeight = PropsUi.getFormMargin();
+    wGeneralComp.setLayout(generalLayout);
+
     // The name of the unit test...
     //
-    Label wlName = new Label(parent, SWT.RIGHT);
+    Label wlName = new Label(wGeneralComp, SWT.RIGHT);
     PropsUi.setLook(wlName);
     wlName.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Name.Label"));
     FormData fdlName = new FormData();
@@ -95,7 +150,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     fdlName.left = new FormAttachment(0, 0);
     fdlName.right = new FormAttachment(middle, -margin);
     wlName.setLayoutData(fdlName);
-    wName = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    wName = new Text(wGeneralComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
     PropsUi.setLook(wName);
     FormData fdName = new FormData();
     fdName.top = new FormAttachment(0, margin);
@@ -106,7 +161,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
 
     // The description of the test...
     //
-    Label wlDescription = new Label(parent, SWT.RIGHT);
+    Label wlDescription = new Label(wGeneralComp, SWT.RIGHT);
     PropsUi.setLook(wlDescription);
     wlDescription.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Description.Label"));
     FormData fdlDescription = new FormData();
@@ -114,7 +169,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     fdlDescription.left = new FormAttachment(0, 0);
     fdlDescription.right = new FormAttachment(middle, -margin);
     wlDescription.setLayoutData(fdlDescription);
-    wDescription = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    wDescription = new Text(wGeneralComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
     PropsUi.setLook(wDescription);
     FormData fdDescription = new FormData();
     fdDescription.top = new FormAttachment(lastControl, margin);
@@ -125,7 +180,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
 
     // The type of test...
     //
-    Label wlTestType = new Label(parent, SWT.RIGHT);
+    Label wlTestType = new Label(wGeneralComp, SWT.RIGHT);
     PropsUi.setLook(wlTestType);
     wlTestType.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.TestType.Label"));
     FormData fdlTestType = new FormData();
@@ -133,7 +188,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     fdlTestType.left = new FormAttachment(0, 0);
     fdlTestType.right = new FormAttachment(middle, -margin);
     wlTestType.setLayoutData(fdlTestType);
-    wTestType = new Combo(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    wTestType = new Combo(wGeneralComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
     PropsUi.setLook(wTestType);
     FormData fdTestType = new FormData();
     fdTestType.top = new FormAttachment(lastControl, margin);
@@ -145,7 +200,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
 
     // The filename of the pipeline to test
     //
-    Label wlPipelineFilename = new Label(parent, SWT.RIGHT);
+    Label wlPipelineFilename = new Label(wGeneralComp, SWT.RIGHT);
     PropsUi.setLook(wlPipelineFilename);
     wlPipelineFilename.setText(
         BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.PipelineFilename.Label"));
@@ -155,7 +210,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     fdlPipelineFilename.right = new FormAttachment(middle, -margin);
     wlPipelineFilename.setLayoutData(fdlPipelineFilename);
 
-    Button wbPipelineFilename = new Button(parent, SWT.PUSH);
+    Button wbPipelineFilename = new Button(wGeneralComp, SWT.PUSH);
     PropsUi.setLook(wbPipelineFilename);
     wbPipelineFilename.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Button.Browse"));
     FormData fdbPipelineFilename = new FormData();
@@ -163,7 +218,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     fdbPipelineFilename.top = new FormAttachment(lastControl, margin);
     wbPipelineFilename.setLayoutData(fdbPipelineFilename);
     wbPipelineFilename.addListener(SWT.Selection, 
this::browsePipelineFilename);
-    wPipelineFilename = new Text(parent, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    wPipelineFilename = new Text(wGeneralComp, SWT.SINGLE | SWT.LEFT | 
SWT.BORDER);
     PropsUi.setLook(wPipelineFilename);
     FormData fdPipelineFilename = new FormData();
     fdPipelineFilename.top = new FormAttachment(lastControl, margin);
@@ -174,7 +229,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
 
     // The optional filename of the test result...
     //
-    Label wlFilename = new Label(parent, SWT.RIGHT);
+    Label wlFilename = new Label(wGeneralComp, SWT.RIGHT);
     PropsUi.setLook(wlFilename);
     wlFilename.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Filename.Label"));
     FormData fdlFilename = new FormData();
@@ -182,7 +237,8 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     fdlFilename.left = new FormAttachment(0, 0);
     fdlFilename.right = new FormAttachment(middle, -margin);
     wlFilename.setLayoutData(fdlFilename);
-    wFilename = new TextVar(manager.getVariables(), parent, SWT.SINGLE | 
SWT.LEFT | SWT.BORDER);
+    wFilename =
+        new TextVar(manager.getVariables(), wGeneralComp, SWT.SINGLE | 
SWT.LEFT | SWT.BORDER);
     PropsUi.setLook(wFilename);
     FormData fdFilename = new FormData();
     fdFilename.top = new FormAttachment(lastControl, margin);
@@ -193,7 +249,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
 
     // The base path for relative test path resolution
     //
-    Label wlBasePath = new Label(parent, SWT.RIGHT);
+    Label wlBasePath = new Label(wGeneralComp, SWT.RIGHT);
     PropsUi.setLook(wlBasePath);
     wlBasePath.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.BasePath.Label"));
     FormData fdlBasePath = new FormData();
@@ -202,7 +258,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     fdlBasePath.right = new FormAttachment(middle, -margin);
     wlBasePath.setLayoutData(fdlBasePath);
 
-    Button wbBasePath = new Button(parent, SWT.PUSH);
+    Button wbBasePath = new Button(wGeneralComp, SWT.PUSH);
     PropsUi.setLook(wbBasePath);
     wbBasePath.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Button.Browse"));
     FormData fdbBasePath = new FormData();
@@ -211,7 +267,8 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     wbBasePath.setLayoutData(fdbBasePath);
     wbBasePath.addListener(SWT.Selection, this::browseTestPathDir);
 
-    wBasePath = new TextVar(manager.getVariables(), parent, SWT.SINGLE | 
SWT.LEFT | SWT.BORDER);
+    wBasePath =
+        new TextVar(manager.getVariables(), wGeneralComp, SWT.SINGLE | 
SWT.LEFT | SWT.BORDER);
     PropsUi.setLook(wBasePath);
     FormData fdBasePath = new FormData();
     fdBasePath.top = new FormAttachment(lastControl, margin);
@@ -222,7 +279,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
 
     // Auto-open checkbox
     //
-    Label wlAutoOpen = new Label(parent, SWT.RIGHT);
+    Label wlAutoOpen = new Label(wGeneralComp, SWT.RIGHT);
     PropsUi.setLook(wlAutoOpen);
     wlAutoOpen.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.AutoOpen.Label"));
     FormData fdlAutoOpen = new FormData();
@@ -230,28 +287,40 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     fdlAutoOpen.left = new FormAttachment(0, 0);
     fdlAutoOpen.right = new FormAttachment(middle, -margin);
     wlAutoOpen.setLayoutData(fdlAutoOpen);
-    wAutoOpen = new Button(parent, SWT.CHECK);
+    wAutoOpen = new Button(wGeneralComp, SWT.CHECK);
     PropsUi.setLook(wAutoOpen);
     FormData fdAutoOpen = new FormData();
     fdAutoOpen.top = new FormAttachment(wlAutoOpen, 0, SWT.CENTER);
     fdAutoOpen.left = new FormAttachment(middle, 0);
     wAutoOpen.setLayoutData(fdAutoOpen);
-    lastControl = wAutoOpen;
 
-    // The list of database replacements in the unit test pipeline
-    //
-    Label wlDbReplacements = new Label(parent, SWT.NONE);
+    wGeneralComp.layout();
+    wGeneralTab.setControl(wGeneralComp);
+  }
+
+  private void createDatabaseTab(CTabFolder wTabFolder, PipelineUnitTest 
pipelineUnitTest) {
+    int margin = PropsUi.getMargin();
+
+    CTabItem wDatabaseTab = new CTabItem(wTabFolder, SWT.NONE);
+    wDatabaseTab.setFont(GuiResource.getInstance().getFontDefault());
+    wDatabaseTab.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Tab.Database"));
+
+    Composite wDatabaseComp = new Composite(wTabFolder, SWT.NONE);
+    PropsUi.setLook(wDatabaseComp);
+    FormLayout databaseLayout = new FormLayout();
+    databaseLayout.marginWidth = PropsUi.getFormMargin();
+    databaseLayout.marginHeight = PropsUi.getFormMargin();
+    wDatabaseComp.setLayout(databaseLayout);
+
+    Label wlDbReplacements = new Label(wDatabaseComp, SWT.NONE);
     wlDbReplacements.setText(
         BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.DbReplacements.Label"));
     PropsUi.setLook(wlDbReplacements);
     FormData fdlDbReplacements = new FormData();
     fdlDbReplacements.left = new FormAttachment(0, 0);
-    fdlDbReplacements.top = new FormAttachment(lastControl, 2 * margin);
+    fdlDbReplacements.top = new FormAttachment(0, 0);
     wlDbReplacements.setLayoutData(fdlDbReplacements);
-    lastControl = wlDbReplacements;
 
-    // the database replacements
-    //
     List<String> dbNames;
     try {
       dbNames = 
metadataProvider.getSerializer(DatabaseMeta.class).listObjectNames();
@@ -281,30 +350,46 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     wDbReplacements =
         new TableView(
             manager.getVariables(),
-            parent,
+            wDatabaseComp,
             SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL,
             columns,
-            pipelineUnitTest.getTweaks().size(),
+            pipelineUnitTest.getDatabaseReplacements().size(),
             null,
             props);
 
     FormData fdDbReplacements = new FormData();
     fdDbReplacements.left = new FormAttachment(0, 0);
-    fdDbReplacements.top = new FormAttachment(lastControl, margin);
+    fdDbReplacements.top = new FormAttachment(wlDbReplacements, margin);
     fdDbReplacements.right = new FormAttachment(100, 0);
-    fdDbReplacements.bottom = new FormAttachment(50, -margin);
+    fdDbReplacements.bottom = new FormAttachment(100, 0);
     wDbReplacements.setLayoutData(fdDbReplacements);
-    lastControl = wDbReplacements;
 
-    Label wlVariableValues = new Label(parent, SWT.NONE);
+    wDatabaseComp.layout();
+    wDatabaseTab.setControl(wDatabaseComp);
+  }
+
+  private void createVariablesTab(CTabFolder wTabFolder, PipelineUnitTest 
pipelineUnitTest) {
+    int margin = PropsUi.getMargin();
+
+    CTabItem wVariablesTab = new CTabItem(wTabFolder, SWT.NONE);
+    wVariablesTab.setFont(GuiResource.getInstance().getFontDefault());
+    wVariablesTab.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Tab.Variables"));
+
+    Composite wVariablesComp = new Composite(wTabFolder, SWT.NONE);
+    PropsUi.setLook(wVariablesComp);
+    FormLayout variablesLayout = new FormLayout();
+    variablesLayout.marginWidth = PropsUi.getFormMargin();
+    variablesLayout.marginHeight = PropsUi.getFormMargin();
+    wVariablesComp.setLayout(variablesLayout);
+
+    Label wlVariableValues = new Label(wVariablesComp, SWT.NONE);
     wlVariableValues.setText(
         BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.VariableValues.Label"));
     PropsUi.setLook(wlVariableValues);
     FormData fdlVariableValues = new FormData();
     fdlVariableValues.left = new FormAttachment(0, 0);
-    fdlVariableValues.top = new FormAttachment(lastControl, 2 * margin);
+    fdlVariableValues.top = new FormAttachment(0, 0);
     wlVariableValues.setLayoutData(fdlVariableValues);
-    lastControl = wlVariableValues;
 
     ColumnInfo[] varValColumns =
         new ColumnInfo[] {
@@ -325,7 +410,7 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
     wVariableValues =
         new TableView(
             manager.getVariables(),
-            parent,
+            wVariablesComp,
             SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL,
             varValColumns,
             pipelineUnitTest.getVariableValues().size(),
@@ -334,26 +419,70 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
 
     FormData fdVariableValues = new FormData();
     fdVariableValues.left = new FormAttachment(0, 0);
-    fdVariableValues.top = new FormAttachment(lastControl, margin);
+    fdVariableValues.top = new FormAttachment(wlVariableValues, margin);
     fdVariableValues.right = new FormAttachment(100, 0);
-    fdVariableValues.bottom = new FormAttachment(100, -2 * margin);
+    fdVariableValues.bottom = new FormAttachment(100, 0);
     wVariableValues.setLayoutData(fdVariableValues);
-    lastControl = wVariableValues;
 
-    setWidgetsContent();
+    wVariablesComp.layout();
+    wVariablesTab.setControl(wVariablesComp);
+  }
 
-    // Add listener to detect change after loading data
-    Listener listener = e -> setChanged();
-    ModifyListener modifyListener = e -> setChanged();
-    wName.addListener(SWT.Modify, listener);
-    wDescription.addListener(SWT.Modify, listener);
-    wTestType.addListener(SWT.Modify, listener);
-    wPipelineFilename.addListener(SWT.Modify, listener);
-    wFilename.addListener(SWT.Modify, listener);
-    wBasePath.addListener(SWT.Modify, listener);
-    wAutoOpen.addListener(SWT.Selection, listener);
-    wDbReplacements.addModifyListener(modifyListener);
-    wVariableValues.addModifyListener(modifyListener);
+  private void createTweaksTab(CTabFolder wTabFolder, PipelineUnitTest 
pipelineUnitTest) {
+    int margin = PropsUi.getMargin();
+
+    CTabItem wTweaksTab = new CTabItem(wTabFolder, SWT.NONE);
+    wTweaksTab.setFont(GuiResource.getInstance().getFontDefault());
+    wTweaksTab.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Tab.Tweaks"));
+
+    Composite wTweaksComp = new Composite(wTabFolder, SWT.NONE);
+    PropsUi.setLook(wTweaksComp);
+    FormLayout tweaksLayout = new FormLayout();
+    tweaksLayout.marginWidth = PropsUi.getFormMargin();
+    tweaksLayout.marginHeight = PropsUi.getFormMargin();
+    wTweaksComp.setLayout(tweaksLayout);
+
+    Label wlTweaks = new Label(wTweaksComp, SWT.NONE);
+    wlTweaks.setText(BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Tweaks.Label"));
+    PropsUi.setLook(wlTweaks);
+    FormData fdlTweaks = new FormData();
+    fdlTweaks.left = new FormAttachment(0, 0);
+    fdlTweaks.top = new FormAttachment(0, 0);
+    wlTweaks.setLayoutData(fdlTweaks);
+
+    ColumnInfo[] tweakColumns =
+        new ColumnInfo[] {
+          new ColumnInfo(
+              BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Tweaks.ColumnInfo.Tweak"),
+              ColumnInfo.COLUMN_TYPE_CCOMBO,
+              DataSetConst.getTweakDescriptions(),
+              false),
+          new ColumnInfo(
+              BaseMessages.getString(PKG, 
"PipelineUnitTestDialog.Tweaks.ColumnInfo.TransformName"),
+              ColumnInfo.COLUMN_TYPE_TEXT,
+              false),
+        };
+    tweakColumns[1].setUsingVariables(true);
+
+    wTweaks =
+        new TableView(
+            manager.getVariables(),
+            wTweaksComp,
+            SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL,
+            tweakColumns,
+            pipelineUnitTest.getTweaks().size(),
+            null,
+            props);
+
+    FormData fdTweaks = new FormData();
+    fdTweaks.left = new FormAttachment(0, 0);
+    fdTweaks.top = new FormAttachment(wlTweaks, margin);
+    fdTweaks.right = new FormAttachment(100, 0);
+    fdTweaks.bottom = new FormAttachment(100, 0);
+    wTweaks.setLayoutData(fdTweaks);
+
+    wTweaksComp.layout();
+    wTweaksTab.setControl(wTweaksComp);
   }
 
   private void browsePipelineFilename(Event event) {
@@ -416,8 +545,18 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
       wVariableValues.setText(Const.NVL(variableValue.getValue(), ""), 2, i);
     }
 
+    for (int i = 0; i < pipelineUnitTest.getTweaks().size(); i++) {
+      PipelineUnitTestTweak tweak = pipelineUnitTest.getTweaks().get(i);
+      wTweaks.setText(DataSetConst.getTweakDescription(tweak.getTweak()), 1, 
i);
+      wTweaks.setText(Const.NVL(tweak.getTransformName(), ""), 2, i);
+    }
+
     wDbReplacements.removeEmptyRows();
     wDbReplacements.setRowNums();
+    wVariableValues.removeEmptyRows();
+    wVariableValues.setRowNums();
+    wTweaks.removeEmptyRows();
+    wTweaks.setRowNums();
   }
 
   @Override
@@ -450,5 +589,17 @@ public class PipelineUnitTestEditor extends 
MetadataEditor<PipelineUnitTest> {
       VariableValue variableValue = new VariableValue(key, value);
       test.getVariableValues().add(variableValue);
     }
+
+    test.getTweaks().clear();
+    int nrTweaks = wTweaks.nrNonEmpty();
+    for (int i = 0; i < nrTweaks; i++) {
+      TableItem item = wTweaks.getNonEmpty(i);
+      PipelineTweak tweakType = 
DataSetConst.getTweakForDescription(item.getText(1));
+      String transformName = item.getText(2);
+      if (StringUtils.isBlank(transformName) || tweakType == 
PipelineTweak.NONE) {
+        continue;
+      }
+      test.getTweaks().add(new PipelineUnitTestTweak(tweakType, 
transformName));
+    }
   }
 }
diff --git 
a/plugins/misc/testing/src/main/resources/org/apache/hop/ui/testing/messages/messages_en_US.properties
 
b/plugins/misc/testing/src/main/resources/org/apache/hop/ui/testing/messages/messages_en_US.properties
index a47835e20f..7304fbe298 100644
--- 
a/plugins/misc/testing/src/main/resources/org/apache/hop/ui/testing/messages/messages_en_US.properties
+++ 
b/plugins/misc/testing/src/main/resources/org/apache/hop/ui/testing/messages/messages_en_US.properties
@@ -50,7 +50,14 @@ PipelineUnitTestDialog.Description.Label=Description
 PipelineUnitTestDialog.Filename.Label=Test pipeline filename (Optional)
 PipelineUnitTestDialog.Name.Label=Name
 PipelineUnitTestDialog.PipelineFilename.Label=The pipeline to test
+PipelineUnitTestDialog.Tab.Database=Database
+PipelineUnitTestDialog.Tab.General=General
+PipelineUnitTestDialog.Tab.Tweaks=Tweaks
+PipelineUnitTestDialog.Tab.Variables=Parameters & Variables
 PipelineUnitTestDialog.TestType.Label=Type of test
+PipelineUnitTestDialog.Tweaks.ColumnInfo.TransformName=Transform name
+PipelineUnitTestDialog.Tweaks.ColumnInfo.Tweak=Tweak
+PipelineUnitTestDialog.Tweaks.Label=Transform tweaks applied when this unit 
test runs:
 PipelineUnitTestDialog.VariableValues.ColumnInfo.VariableName=Name
 PipelineUnitTestDialog.VariableValues.ColumnInfo.VariableValue=Value
 PipelineUnitTestDialog.VariableValues.Label=Parameters and variables
diff --git 
a/plugins/misc/testing/src/test/java/org/apache/hop/testing/gui/TestingGuiPluginTweakTest.java
 
b/plugins/misc/testing/src/test/java/org/apache/hop/testing/gui/TestingGuiPluginTweakTest.java
new file mode 100644
index 0000000000..33f0136f88
--- /dev/null
+++ 
b/plugins/misc/testing/src/test/java/org/apache/hop/testing/gui/TestingGuiPluginTweakTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.testing.gui;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
+import org.apache.hop.testing.PipelineTweak;
+import org.apache.hop.testing.PipelineUnitTest;
+import org.apache.hop.testing.PipelineUnitTestTweak;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for multi-transform unit-test tweak application (issue #2742). 
Covers pure helpers in
+ * {@link TestingGuiPlugin} that do not require a HopGui instance.
+ */
+class TestingGuiPluginTweakTest {
+
+  @Test
+  void resolveTargetsFallsBackToClickedWhenNothingSelected() {
+    PipelineMeta pipelineMeta = new PipelineMeta();
+    TransformMeta a = transform("A", false);
+    TransformMeta b = transform("B", false);
+    pipelineMeta.addTransform(a);
+    pipelineMeta.addTransform(b);
+
+    List<TransformMeta> targets = 
TestingGuiPlugin.resolveTweakTargetTransforms(pipelineMeta, b);
+
+    assertEquals(1, targets.size());
+    assertSame(b, targets.get(0));
+  }
+
+  @Test
+  void resolveTargetsUsesSelectionWhenPresent() {
+    PipelineMeta pipelineMeta = new PipelineMeta();
+    TransformMeta a = transform("A", true);
+    TransformMeta b = transform("B", true);
+    TransformMeta c = transform("C", false);
+    pipelineMeta.addTransform(a);
+    pipelineMeta.addTransform(b);
+    pipelineMeta.addTransform(c);
+
+    List<TransformMeta> targets = 
TestingGuiPlugin.resolveTweakTargetTransforms(pipelineMeta, c);
+
+    assertEquals(2, targets.size());
+    assertTrue(targets.contains(a));
+    assertTrue(targets.contains(b));
+  }
+
+  @Test
+  void resolveTargetsEmptyWhenNoSelectionAndNoClick() {
+    PipelineMeta pipelineMeta = new PipelineMeta();
+    pipelineMeta.addTransform(transform("A", false));
+
+    List<TransformMeta> targets = 
TestingGuiPlugin.resolveTweakTargetTransforms(pipelineMeta, null);
+
+    assertTrue(targets.isEmpty());
+  }
+
+  @Test
+  void applyTweakEnableAddsBypass() {
+    PipelineUnitTest unitTest = new PipelineUnitTest();
+
+    TestingGuiPlugin.applyTweakToTransform(unitTest, "A", 
PipelineTweak.BYPASS_TRANSFORM, true);
+
+    PipelineUnitTestTweak tweak = unitTest.findTweak("A");
+    assertEquals(PipelineTweak.BYPASS_TRANSFORM, tweak.getTweak());
+    assertEquals("A", tweak.getTransformName());
+  }
+
+  @Test
+  void applyTweakEnableReplacesExistingDifferentTweak() {
+    PipelineUnitTest unitTest = new PipelineUnitTest();
+    unitTest.getTweaks().add(new 
PipelineUnitTestTweak(PipelineTweak.REMOVE_TRANSFORM, "A"));
+
+    TestingGuiPlugin.applyTweakToTransform(unitTest, "A", 
PipelineTweak.BYPASS_TRANSFORM, true);
+
+    assertEquals(1, unitTest.getTweaks().size());
+    assertEquals(PipelineTweak.BYPASS_TRANSFORM, 
unitTest.findTweak("A").getTweak());
+  }
+
+  @Test
+  void applyTweakDisableRemovesOnlyMatchingType() {
+    PipelineUnitTest unitTest = new PipelineUnitTest();
+    unitTest.getTweaks().add(new 
PipelineUnitTestTweak(PipelineTweak.BYPASS_TRANSFORM, "A"));
+    unitTest.getTweaks().add(new 
PipelineUnitTestTweak(PipelineTweak.REMOVE_TRANSFORM, "B"));
+
+    // Disable REMOVE on A (which is bypassed) — leave A alone
+    TestingGuiPlugin.applyTweakToTransform(unitTest, "A", 
PipelineTweak.REMOVE_TRANSFORM, false);
+    // Disable BYPASS on A — remove it
+    TestingGuiPlugin.applyTweakToTransform(unitTest, "A", 
PipelineTweak.BYPASS_TRANSFORM, false);
+    // Disable REMOVE on B — remove it
+    TestingGuiPlugin.applyTweakToTransform(unitTest, "B", 
PipelineTweak.REMOVE_TRANSFORM, false);
+
+    assertNull(unitTest.findTweak("A"));
+    assertNull(unitTest.findTweak("B"));
+    assertTrue(unitTest.getTweaks().isEmpty());
+  }
+
+  @Test
+  void applyTweakEnableOnMultipleTransforms() {
+    PipelineUnitTest unitTest = new PipelineUnitTest();
+
+    TestingGuiPlugin.applyTweakToTransform(unitTest, "A", 
PipelineTweak.REMOVE_TRANSFORM, true);
+    TestingGuiPlugin.applyTweakToTransform(unitTest, "B", 
PipelineTweak.REMOVE_TRANSFORM, true);
+    TestingGuiPlugin.applyTweakToTransform(unitTest, "C", 
PipelineTweak.REMOVE_TRANSFORM, true);
+
+    assertEquals(3, unitTest.getTweaks().size());
+    assertEquals(PipelineTweak.REMOVE_TRANSFORM, 
unitTest.findTweak("A").getTweak());
+    assertEquals(PipelineTweak.REMOVE_TRANSFORM, 
unitTest.findTweak("B").getTweak());
+    assertEquals(PipelineTweak.REMOVE_TRANSFORM, 
unitTest.findTweak("C").getTweak());
+  }
+
+  private static TransformMeta transform(String name, boolean selected) {
+    TransformMeta transformMeta = new TransformMeta();
+    transformMeta.setName(name);
+    transformMeta.setSelected(selected);
+    return transformMeta;
+  }
+}


Reply via email to