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 151481e4dc Improve Abort transform: refactor processRow logic and add
comprehensive unit tests (#5885)
151481e4dc is described below
commit 151481e4dc1f56f6fff30707ad6eb814f5ae851e
Author: lance <[email protected]>
AuthorDate: Fri Oct 24 18:31:23 2025 +0800
Improve Abort transform: refactor processRow logic and add comprehensive
unit tests (#5885)
Signed-off-by: lance <[email protected]>
---
.../hop/pipeline/transforms/abort/Abort.java | 110 +++++++++--------
.../hop/pipeline/transforms/abort/AbortDialog.java | 21 ++--
.../hop/pipeline/transforms/abort/AbortMeta.java | 90 ++------------
.../pipeline/transforms/abort/AbortDataTests.java | 42 +++++++
.../pipeline/transforms/abort/AbortMetaTest.java | 123 ++++++++++++++++++-
.../hop/pipeline/transforms/abort/AbortTest.java | 134 +++++++++++++++++----
.../org/apache/hop/ui/core/FormDataBuilder.java | 30 ++++-
7 files changed, 374 insertions(+), 176 deletions(-)
diff --git
a/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/Abort.java
b/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/Abort.java
index 810198299e..ccba6c61a8 100644
---
a/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/Abort.java
+++
b/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/Abort.java
@@ -19,6 +19,7 @@ package org.apache.hop.pipeline.transforms.abort;
import org.apache.hop.core.Const;
import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.exception.HopValueException;
import org.apache.hop.core.util.Utils;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.pipeline.Pipeline;
@@ -62,68 +63,71 @@ public class Abort extends BaseTransform<AbortMeta,
AbortData> {
@Override
public boolean processRow() throws HopException {
-
- Object[] r = getRow(); // Get row from input rowset & set row busy!
+ // Get row from input rowset & set row busy!
+ Object[] r = getRow();
// no more input to be expected...
if (r == null) {
setOutputDone();
return false;
- } else {
- putRow(getInputRowMeta(), r);
- nrInputRows++;
- if (nrInputRows > nrThresholdRows) {
- //
- // Here we abort!!
- //
- String abortOptionMessage = BaseMessages.getString(PKG,
"AbortDialog.Options.Abort.Label");
- if (meta.isAbortWithError()) {
- abortOptionMessage =
- BaseMessages.getString(PKG,
"AbortDialog.Options.AbortWithError.Label");
- } else if (meta.isSafeStop()) {
- abortOptionMessage = BaseMessages.getString(PKG,
"AbortDialog.Options.SafeStop.Label");
- }
- logError(
- BaseMessages.getString(
- PKG,
- "Abort.Log.Wrote.AbortRow",
- Long.toString(nrInputRows),
- abortOptionMessage,
- getInputRowMeta().getString(r)));
-
- String message = resolve(meta.getMessage());
- if (Utils.isEmpty(message)) {
- logError(BaseMessages.getString(PKG,
"Abort.Log.DefaultAbortMessage", "" + nrInputRows));
- } else {
- logError(message);
- }
-
- if (meta.isAbortWithError()) {
- setErrors(1);
- }
- stopAll();
+ }
+ putRow(getInputRowMeta(), r);
+ nrInputRows++;
+ if (nrInputRows > nrThresholdRows) {
+ //
+ // Here we abort!!
+ //
+ String abortOptionMessage = BaseMessages.getString(PKG,
"AbortDialog.Options.Abort.Label");
+ if (meta.isAbortWithError()) {
+ abortOptionMessage =
+ BaseMessages.getString(PKG,
"AbortDialog.Options.AbortWithError.Label");
+ } else if (meta.isSafeStop()) {
+ abortOptionMessage = BaseMessages.getString(PKG,
"AbortDialog.Options.SafeStop.Label");
+ }
+ logError(
+ BaseMessages.getString(
+ PKG,
+ "Abort.Log.Wrote.AbortRow",
+ Long.toString(nrInputRows),
+ abortOptionMessage,
+ getInputRowMeta().getString(r)));
+ String message = resolve(meta.getMessage());
+ if (Utils.isEmpty(message)) {
+ logError(BaseMessages.getString(PKG, "Abort.Log.DefaultAbortMessage",
"" + nrInputRows));
} else {
- // seen a row but not yet reached the threshold
- if (meta.isAlwaysLogRows()) {
- logMinimal(
- BaseMessages.getString(
- PKG,
- "Abort.Log.Wrote.Row",
- Long.toString(nrInputRows),
- getInputRowMeta().getString(r)));
- } else {
- if (isRowLevel()) {
- logRowlevel(
- BaseMessages.getString(
- PKG,
- "Abort.Log.Wrote.Row",
- Long.toString(nrInputRows),
- getInputRowMeta().getString(r)));
- }
- }
+ logError(message);
+ }
+
+ if (meta.isAbortWithError()) {
+ setErrors(1);
}
+ stopAll();
+ } else {
+ logRowIfNecessary(r);
}
return true;
}
+
+ /** Logs a single row depending on the logging configuration. */
+ private void logRowIfNecessary(Object[] row) throws HopValueException {
+ // seen a row but not yet reached the threshold
+ if (meta.isAlwaysLogRows()) {
+ logMinimal(
+ BaseMessages.getString(
+ PKG,
+ "Abort.Log.Wrote.Row",
+ Long.toString(nrInputRows),
+ getInputRowMeta().getString(row)));
+ } else {
+ if (isRowLevel()) {
+ logRowlevel(
+ BaseMessages.getString(
+ PKG,
+ "Abort.Log.Wrote.Row",
+ Long.toString(nrInputRows),
+ getInputRowMeta().getString(row)));
+ }
+ }
+ }
}
diff --git
a/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/AbortDialog.java
b/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/AbortDialog.java
index 94e8745ad5..85e849913d 100644
---
a/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/AbortDialog.java
+++
b/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/AbortDialog.java
@@ -22,6 +22,7 @@ import org.apache.hop.core.variables.IVariables;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.pipeline.PipelineMeta;
import org.apache.hop.ui.core.ConstUi;
+import org.apache.hop.ui.core.FormDataBuilder;
import org.apache.hop.ui.core.PropsUi;
import org.apache.hop.ui.core.dialog.BaseDialog;
import org.apache.hop.ui.core.widget.TextVar;
@@ -232,12 +233,13 @@ public class AbortDialog extends BaseTransformDialog {
flLoggingGroup.marginWidth = 15;
wLoggingGroup.setLayout(flLoggingGroup);
- FormData fdLoggingGroup = new FormData();
- fdLoggingGroup.left = new FormAttachment(0, 0);
- fdLoggingGroup.top = new FormAttachment(widgetAbove, 15);
- fdLoggingGroup.right = new FormAttachment(100, 0);
- fdLoggingGroup.bottom = new FormAttachment(hSpacer, -15);
- wLoggingGroup.setLayoutData(fdLoggingGroup);
+ wLoggingGroup.setLayoutData(
+ FormDataBuilder.builder()
+ .left()
+ .top(widgetAbove, 15)
+ .right(100, 0)
+ .bottom(hSpacer, -15)
+ .build());
Label wlMessage = new Label(wLoggingGroup, SWT.RIGHT);
wlMessage.setText(BaseMessages.getString(PKG,
"AbortDialog.Logging.AbortMessage.Label"));
@@ -299,7 +301,7 @@ public class AbortDialog extends BaseTransformDialog {
wTransformName.setFocus();
}
- private void getInfo(AbortMeta in) {
+ private void getInfo() {
input.setRowThreshold(wRowThreshold.getText());
input.setMessage(wMessage.getText());
input.setAlwaysLogRows(wAlwaysLogRows.getSelection());
@@ -325,8 +327,9 @@ public class AbortDialog extends BaseTransformDialog {
return;
}
- getInfo(input);
- transformName = wTransformName.getText(); // return value
+ getInfo();
+ // return value
+ transformName = wTransformName.getText();
dispose();
}
}
diff --git
a/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/AbortMeta.java
b/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/AbortMeta.java
index 269de5afe2..0b5f6c8fb6 100644
---
a/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/AbortMeta.java
+++
b/plugins/transforms/abort/src/main/java/org/apache/hop/pipeline/transforms/abort/AbortMeta.java
@@ -18,10 +18,11 @@
package org.apache.hop.pipeline.transforms.abort;
import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.hop.core.CheckResult;
import org.apache.hop.core.ICheckResult;
import org.apache.hop.core.annotations.Transform;
-import org.apache.hop.core.exception.HopTransformException;
import org.apache.hop.core.exception.HopXmlException;
import org.apache.hop.core.row.IRowMeta;
import org.apache.hop.core.variables.IVariables;
@@ -35,6 +36,8 @@ import org.apache.hop.pipeline.transform.TransformMeta;
import org.w3c.dom.Node;
/** Meta data for the abort transform. */
+@Setter
+@Getter
@Transform(
id = "Abort",
name = "i18n::Abort.Name",
@@ -69,6 +72,7 @@ public class AbortMeta extends BaseTransformMeta<Abort,
AbortData> {
injectionKeyDescription = "AbortDialog.Logging.AlwaysLogRows.Label")
private boolean alwaysLogRows;
+ /** value of abortOption */
@HopMetadataProperty(
key = "abort_option",
injectionKeyDescription = "AbortMeta.Injection.AbortOption")
@@ -78,23 +82,11 @@ public class AbortMeta extends BaseTransformMeta<Abort,
AbortData> {
abortOption = AbortOption.ABORT;
}
- @Override
- public void getFields(
- IRowMeta inputRowMeta,
- String name,
- IRowMeta[] info,
- TransformMeta nextTransform,
- IVariables variables,
- IHopMetadataProvider metadataProvider)
- throws HopTransformException {
- // Default: no values are added to the row in the transform
- }
-
@Override
public void check(
List<ICheckResult> remarks,
PipelineMeta pipelineMeta,
- TransformMeta transforminfo,
+ TransformMeta transform,
IRowMeta prev,
String[] input,
String[] output,
@@ -107,7 +99,7 @@ public class AbortMeta extends BaseTransformMeta<Abort,
AbortData> {
new CheckResult(
ICheckResult.TYPE_RESULT_WARNING,
BaseMessages.getString(PKG,
"AbortMeta.CheckResult.NoInputReceivedError"),
- transforminfo);
+ transform);
remarks.add(cr);
}
}
@@ -126,11 +118,11 @@ public class AbortMeta extends BaseTransformMeta<Abort,
AbortData> {
super.loadXml(transformNode, metadataProvider);
// Backward compatible code
- //
if (abortOption == null) {
String awe = XmlHandler.getTagValue(transformNode, "abort_with_error");
if (awe == null) {
- awe = "Y"; // existing pipelines will have to maintain backward
compatibility with yes
+ // existing pipelines will have to maintain backward compatibility
with yes
+ awe = "Y";
}
abortOption = "Y".equalsIgnoreCase(awe) ? AbortOption.ABORT_WITH_ERROR :
AbortOption.ABORT;
}
@@ -148,70 +140,6 @@ public class AbortMeta extends BaseTransformMeta<Abort,
AbortData> {
return abortOption == AbortOption.SAFE_STOP;
}
- /**
- * Gets rowThreshold
- *
- * @return value of rowThreshold
- */
- public String getRowThreshold() {
- return rowThreshold;
- }
-
- /**
- * @param rowThreshold The rowThreshold to set
- */
- public void setRowThreshold(String rowThreshold) {
- this.rowThreshold = rowThreshold;
- }
-
- /**
- * Gets message
- *
- * @return value of message
- */
- public String getMessage() {
- return message;
- }
-
- /**
- * @param message The message to set
- */
- public void setMessage(String message) {
- this.message = message;
- }
-
- /**
- * Gets alwaysLogRows
- *
- * @return value of alwaysLogRows
- */
- public boolean isAlwaysLogRows() {
- return alwaysLogRows;
- }
-
- /**
- * @param alwaysLogRows The alwaysLogRows to set
- */
- public void setAlwaysLogRows(boolean alwaysLogRows) {
- this.alwaysLogRows = alwaysLogRows;
- }
-
- /**
- * Gets abortOption
- *
- * @return value of abortOption
- */
- public AbortOption getAbortOption() {
- return abortOption;
- }
-
- /**
- * @param abortOption The abortOption to set
- */
- public void setAbortOption(AbortOption abortOption) {
- this.abortOption = abortOption;
- }
-
@Override
public boolean supportsMultiCopyExecution() {
return false;
diff --git
a/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortDataTests.java
b/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortDataTests.java
new file mode 100644
index 0000000000..04172e97cf
--- /dev/null
+++
b/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortDataTests.java
@@ -0,0 +1,42 @@
+/*
+ * 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.pipeline.transforms.abort;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.apache.hop.pipeline.transform.BaseTransformData;
+import org.apache.hop.pipeline.transform.ITransformData;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class AbortDataTests {
+
+ @Test
+ void testAbortDataInitialization() {
+ // Create instance
+ AbortData data = new AbortData();
+
+ // Verify object is not null
+ assertNotNull(data, "AbortData instance should be created successfully.");
+
+ Assertions.assertInstanceOf(
+ BaseTransformData.class, data, "AbortData should extend
BaseTransformData.");
+ Assertions.assertInstanceOf(
+ ITransformData.class, data, "AbortData should implement
ITransformData.");
+ }
+}
diff --git
a/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortMetaTest.java
b/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortMetaTest.java
index 5ee65b8c9a..5909477d6d 100644
---
a/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortMetaTest.java
+++
b/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortMetaTest.java
@@ -17,25 +17,43 @@
package org.apache.hop.pipeline.transforms.abort;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.apache.hop.core.CheckResult;
+import org.apache.hop.core.ICheckResult;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopXmlException;
import org.apache.hop.core.xml.XmlHandler;
import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
import org.apache.hop.pipeline.transforms.loadsave.LoadSaveTester;
import
org.apache.hop.pipeline.transforms.loadsave.validator.EnumLoadSaveValidator;
import
org.apache.hop.pipeline.transforms.loadsave.validator.IFieldLoadSaveValidator;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
import org.w3c.dom.Node;
+/** AbortMeta test */
class AbortMetaTest {
+ private AbortMeta meta;
+
+ @BeforeEach
+ void setUp() {
+ meta = new AbortMeta();
+ }
@Test
void testRoundTrip() throws HopException {
@@ -70,18 +88,111 @@ class AbortMetaTest {
@Test
void testBackwardsCompatibilityAbortWithError() throws HopXmlException {
IHopMetadataProvider metadataProvider = mock(IHopMetadataProvider.class);
- AbortMeta meta = new AbortMeta();
// No abort option specified: leave the default: Abort
String inputXml =
"""
- <transform>
- <name>Abort</name>
- <type>Abort</type>
- </transform>\
- """;
+ <transform>
+ <name>Abort</name>
+ <type>Abort</type>
+ </transform>\
+ """;
Node node = XmlHandler.loadXmlString(inputXml).getFirstChild();
meta.loadXml(node, metadataProvider);
assertTrue(meta.isAbort());
}
+
+ @Test
+ void testDefaultValuesAfterConstruction() {
+ assertEquals(AbortMeta.AbortOption.ABORT, meta.getAbortOption());
+ }
+
+ @Test
+ void testSetDefaultValues() {
+ meta.setDefault();
+
+ assertEquals("0", meta.getRowThreshold());
+ assertEquals("", meta.getMessage());
+ assertTrue(meta.isAlwaysLogRows());
+ assertEquals(AbortMeta.AbortOption.ABORT_WITH_ERROR,
meta.getAbortOption());
+ }
+
+ @Test
+ void testCheckWithNoInputAddsWarning() {
+ List<ICheckResult> remarks = new ArrayList<>();
+ PipelineMeta pipelineMeta = mock(PipelineMeta.class);
+ TransformMeta transformMeta = mock(TransformMeta.class);
+
+ meta.check(remarks, pipelineMeta, transformMeta, null, new String[0],
null, null, null, null);
+
+ assertFalse(remarks.isEmpty());
+ CheckResult result = (CheckResult) remarks.get(0);
+ assertEquals(ICheckResult.TYPE_RESULT_WARNING, result.getType());
+ }
+
+ @Test
+ void testCheckWithInputDoesNotAddWarning() {
+ List<ICheckResult> remarks = new ArrayList<>();
+ PipelineMeta pipelineMeta = mock(PipelineMeta.class);
+ TransformMeta transformMeta = mock(TransformMeta.class);
+
+ meta.check(
+ remarks, pipelineMeta, transformMeta, null, new String[] {"input"},
null, null, null, null);
+
+ assertTrue(remarks.isEmpty());
+ }
+
+ @Test
+ void testAbortOptionHelperMethods() {
+ meta.setAbortOption(AbortMeta.AbortOption.ABORT);
+ assertTrue(meta.isAbort());
+ assertFalse(meta.isAbortWithError());
+ assertFalse(meta.isSafeStop());
+
+ meta.setAbortOption(AbortMeta.AbortOption.ABORT_WITH_ERROR);
+ assertTrue(meta.isAbortWithError());
+ assertFalse(meta.isAbort());
+ assertFalse(meta.isSafeStop());
+
+ meta.setAbortOption(AbortMeta.AbortOption.SAFE_STOP);
+ assertTrue(meta.isSafeStop());
+ }
+
+ @Test
+ void testLoadXmlBackwardCompatibility() throws Exception {
+ meta.setAbortOption(null);
+ Document doc = XmlTestUtil.createDocument();
+ Element transformNode = doc.createElement("transform");
+ // Simulate legacy XML tag
+ transformNode.appendChild(XmlTestUtil.createElement(doc,
"abort_with_error", "Y"));
+
+ meta.loadXml(transformNode, mock(IHopMetadataProvider.class));
+ assertEquals(AbortMeta.AbortOption.ABORT_WITH_ERROR,
meta.getAbortOption());
+
+ // Test with value N
+ meta.setAbortOption(null);
+ transformNode = doc.createElement("transform");
+ transformNode.appendChild(XmlTestUtil.createElement(doc,
"abort_with_error", "N"));
+ transformNode.appendChild(XmlTestUtil.createElement(doc,
"abort_with_success", "Y"));
+ meta.loadXml(transformNode, mock(IHopMetadataProvider.class));
+ assertEquals(AbortMeta.AbortOption.ABORT, meta.getAbortOption());
+ }
+
+ @Test
+ void testSupportsMultiCopyExecution() {
+ assertFalse(meta.supportsMultiCopyExecution());
+ }
+
+ static class XmlTestUtil {
+
+ static Document createDocument() throws Exception {
+ return
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ }
+
+ static Element createElement(Document doc, String name, String value) {
+ Element element = doc.createElement(name);
+ element.setTextContent(value);
+ return element;
+ }
+ }
}
diff --git
a/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortTest.java
b/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortTest.java
index 880bfb9fd7..2287996677 100644
---
a/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortTest.java
+++
b/plugins/transforms/abort/src/test/java/org/apache/hop/pipeline/transforms/abort/AbortTest.java
@@ -21,27 +21,45 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import org.apache.hop.core.Const;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.logging.ILoggingObject;
+import org.apache.hop.core.row.RowMeta;
+import org.apache.hop.core.row.value.ValueMetaInteger;
import org.apache.hop.pipeline.transforms.mock.TransformMockHelper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
class AbortTest {
private TransformMockHelper<AbortMeta, AbortData> transformMockHelper;
+ private Abort abort;
@BeforeEach
void setup() {
- transformMockHelper = new TransformMockHelper("ABORT TEST",
AbortMeta.class, AbortData.class);
+ transformMockHelper = new TransformMockHelper<>("ABORT TEST",
AbortMeta.class, AbortData.class);
when(transformMockHelper.logChannelFactory.create(any(),
any(ILoggingObject.class)))
.thenReturn(transformMockHelper.iLogChannel);
when(transformMockHelper.pipeline.isRunning()).thenReturn(true);
+
+ abort =
+ new Abort(
+ transformMockHelper.transformMeta,
+ transformMockHelper.iTransformMeta,
+ transformMockHelper.iTransformData,
+ 0,
+ transformMockHelper.pipelineMeta,
+ transformMockHelper.pipeline);
}
@AfterEach
@@ -51,14 +69,6 @@ class AbortTest {
@Test
void testAbortDoesntAbortWithoutInputRow() throws HopException {
- Abort abort =
- new Abort(
- transformMockHelper.transformMeta,
- transformMockHelper.iTransformMeta,
- transformMockHelper.iTransformData,
- 0,
- transformMockHelper.pipelineMeta,
- transformMockHelper.pipeline);
abort.processRow();
abort.addRowSetToInputRowSets(transformMockHelper.getMockInputRowSet());
assertFalse(abort.isStopped());
@@ -69,14 +79,6 @@ class AbortTest {
@Test
void testAbortAbortsWithInputRow() throws HopException {
- Abort abort =
- new Abort(
- transformMockHelper.transformMeta,
- transformMockHelper.iTransformMeta,
- transformMockHelper.iTransformData,
- 0,
- transformMockHelper.pipelineMeta,
- transformMockHelper.pipeline);
abort.processRow();
abort.addRowSetToInputRowSets(transformMockHelper.getMockInputRowSet(new
Object[] {}));
assertFalse(abort.isStopped());
@@ -87,14 +89,6 @@ class AbortTest {
@Test
void testAbortWithError() throws HopException {
- Abort abort =
- new Abort(
- transformMockHelper.transformMeta,
- transformMockHelper.iTransformMeta,
- transformMockHelper.iTransformData,
- 0,
- transformMockHelper.pipelineMeta,
- transformMockHelper.pipeline);
when(transformMockHelper.iTransformMeta.isSafeStop()).thenReturn(false);
when(transformMockHelper.iTransformMeta.isAbortWithError()).thenReturn(true);
abort.processRow();
@@ -103,4 +97,94 @@ class AbortTest {
assertEquals(1L, abort.getErrors());
verify(transformMockHelper.pipeline).stopAll();
}
+
+ @Test
+ void testInitWithValidThreshold() {
+
when(transformMockHelper.iTransformMeta.getRowThreshold()).thenReturn("10");
+ boolean result = abort.init();
+
+ assertTrue(result);
+ }
+
+ @Test
+ void testInitWithInvalidThreshold() {
+ try (MockedStatic<Const> constMock = mockStatic(Const.class)) {
+
when(transformMockHelper.iTransformMeta.getRowThreshold()).thenReturn("abc");
+ constMock.when(() -> Const.toInt("abc", -1)).thenReturn(-1);
+
+ boolean result = abort.init();
+ // init() returns true even if threshold invalid
+ assertTrue(result);
+ }
+ }
+
+ @Test
+ void testProcessRowStopsAfterThreshold() throws Exception {
+ when(transformMockHelper.iTransformMeta.getRowThreshold()).thenReturn("1");
+
when(transformMockHelper.iTransformMeta.isAbortWithError()).thenReturn(true);
+
when(transformMockHelper.iTransformMeta.isAlwaysLogRows()).thenReturn(false);
+
+ // Prepare transform
+ boolean result = abort.init();
+ assertTrue(result);
+
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaInteger("No"));
+ abort.setInputRowMeta(inputRowMeta);
+
+ // First row — should continue
+ abort = spy(abort);
+ doReturn(new Object[] {1000L}).doReturn(null).when(abort).getRow();
+
+ boolean firstResult = abort.processRow();
+ assertTrue(firstResult);
+
+ // Second call — should detect no more rows
+ boolean secondResult = abort.processRow();
+ assertFalse(secondResult);
+ }
+
+ @Test
+ void testProcessRowTriggersAbortCondition() throws Exception {
+ when(transformMockHelper.iTransformMeta.getRowThreshold()).thenReturn("0");
+
when(transformMockHelper.iTransformMeta.isAbortWithError()).thenReturn(true);
+ when(transformMockHelper.iTransformMeta.getMessage()).thenReturn("Abort
now!");
+
when(transformMockHelper.iTransformMeta.isAlwaysLogRows()).thenReturn(false);
+
+ abort.init();
+
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaInteger("No"));
+ abort.setInputRowMeta(inputRowMeta);
+
+ // First row — should continue
+ abort = spy(abort);
+ doReturn(new Object[] {1000L}).doReturn(null).when(abort).getRow();
+
+ abort.processRow();
+
+ // Verify abort behavior
+ verify(transformMockHelper.iTransformMeta,
atLeastOnce()).isAbortWithError();
+ verify(abort, atLeastOnce()).stopAll();
+ }
+
+ @Test
+ void testProcessRowLogsRowWhenAlwaysLogRowsEnabled() throws Exception {
+
when(transformMockHelper.iTransformMeta.getRowThreshold()).thenReturn("10");
+
when(transformMockHelper.iTransformMeta.isAlwaysLogRows()).thenReturn(true);
+
+ abort.init();
+
+ RowMeta inputRowMeta = new RowMeta();
+ inputRowMeta.addValueMeta(new ValueMetaInteger("No"));
+ abort.setInputRowMeta(inputRowMeta);
+
+ // First row — should continue
+ abort = spy(abort);
+ doReturn(new Object[] {1000L}).doReturn(null).when(abort).getRow();
+
+ // Should log at minimal level
+ abort.processRow();
+ verify(transformMockHelper.iTransformMeta).isAlwaysLogRows();
+ }
}
diff --git a/ui/src/main/java/org/apache/hop/ui/core/FormDataBuilder.java
b/ui/src/main/java/org/apache/hop/ui/core/FormDataBuilder.java
index 7efb9af40e..4f685bef5a 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/FormDataBuilder.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/FormDataBuilder.java
@@ -21,13 +21,39 @@ import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.Control;
+/**
+ * Builder class for creating and configuring {@link FormData} instances using
a fluent API.
+ *
+ * <p>Example usage:
+ *
+ * <pre>{@code
+ * FormData fd = FormDataBuilder.builder()
+ * .left()
+ * .top()
+ * .fullWidth()
+ * .build();
+ * }</pre>
+ */
public class FormDataBuilder implements Cloneable {
- private final FormAttachment MIN = new FormAttachment(0, 0);
- private final FormAttachment MAX = new FormAttachment(100, 0);
+ private static final FormAttachment MIN = new FormAttachment(0, 0);
+ private static final FormAttachment MAX = new FormAttachment(100, 0);
protected FormData fd = new FormData();
+ /**
+ * Static factory method for creating a new builder instance.
+ *
+ * @return create FormDataBuilder
+ */
+ public static FormDataBuilder builder() {
+ return new FormDataBuilder();
+ }
+
+ public FormData build() {
+ return fd;
+ }
+
public FormDataBuilder width(int width) {
fd.width = width;
return this;