From 8452ff1e9f10b87c23d21fd1be7363ddeea3ed83 Mon Sep 17 00:00:00 2001 From: Hans Van Akelyen Date: Mon, 20 Jul 2026 10:05:23 +0200 Subject: [PATCH] harden unit tests, fixes #7578 --- .../SerializableMetadataProviderTest.java | 5 +++-- .../refactor/MetadataReferenceFinderTest.java | 5 +++-- .../apache/hop/pipeline/PipelineMetaTest.java | 10 +++------- .../TransformWithMappingMetaTest.java | 14 ++------------ .../remote/RemotePipelineEngineTest.java | 10 +++------- .../transform/TransformOptionTest.java | 10 ++-------- .../org/apache/hop/workflow/WorkflowTest.java | 5 +++-- .../WorkflowActionLoadSaveTestSupport.java | 10 +++------- .../engine/WorkflowEngineFactoryTest.java | 19 ++++++++++++------- .../RegisterPipelineServletLogFileTest.java | 10 +++------- .../apache/hop/ui/testing/SwtBotTestBase.java | 4 ++++ 11 files changed, 41 insertions(+), 61 deletions(-) diff --git a/engine/src/test/java/org/apache/hop/core/metadata/SerializableMetadataProviderTest.java b/engine/src/test/java/org/apache/hop/core/metadata/SerializableMetadataProviderTest.java index 0b6f9c6f70a..3f036580139 100644 --- a/engine/src/test/java/org/apache/hop/core/metadata/SerializableMetadataProviderTest.java +++ b/engine/src/test/java/org/apache/hop/core/metadata/SerializableMetadataProviderTest.java @@ -20,19 +20,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import org.apache.hop.core.HopEnvironment; import org.apache.hop.core.util.TestUtil; +import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension; import org.apache.hop.metadata.api.IHopMetadataSerializer; import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider; import org.apache.hop.server.HopServerMeta; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +@ExtendWith(RestoreHopEngineEnvironmentExtension.class) class SerializableMetadataProviderTest { @BeforeEach void before() throws Exception { - HopEnvironment.init(); TestUtil.registerTestPluginTypes(); } diff --git a/engine/src/test/java/org/apache/hop/metadata/refactor/MetadataReferenceFinderTest.java b/engine/src/test/java/org/apache/hop/metadata/refactor/MetadataReferenceFinderTest.java index 6de500691ff..3c850bb4ff6 100644 --- a/engine/src/test/java/org/apache/hop/metadata/refactor/MetadataReferenceFinderTest.java +++ b/engine/src/test/java/org/apache/hop/metadata/refactor/MetadataReferenceFinderTest.java @@ -26,13 +26,14 @@ import java.nio.file.Path; import java.util.Collections; import java.util.List; -import org.apache.hop.core.HopEnvironment; import org.apache.hop.core.variables.Variables; +import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension; import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; /** * Unit tests for {@link MetadataReferenceFinder}. @@ -46,6 +47,7 @@ *

The tests here focus on guard conditions (null/empty inputs) and behaviors that are * independent of plugin registration. */ +@ExtendWith(RestoreHopEngineEnvironmentExtension.class) class MetadataReferenceFinderTest { private static MemoryMetadataProvider provider; @@ -54,7 +56,6 @@ class MetadataReferenceFinderTest { @BeforeAll static void setupEnvironment() throws Exception { - HopEnvironment.init(); provider = new MemoryMetadataProvider(); } diff --git a/engine/src/test/java/org/apache/hop/pipeline/PipelineMetaTest.java b/engine/src/test/java/org/apache/hop/pipeline/PipelineMetaTest.java index 85a68b264ab..1ee4349d05b 100644 --- a/engine/src/test/java/org/apache/hop/pipeline/PipelineMetaTest.java +++ b/engine/src/test/java/org/apache/hop/pipeline/PipelineMetaTest.java @@ -42,7 +42,6 @@ import java.util.List; import java.util.Map; import org.apache.hop.core.Const; -import org.apache.hop.core.HopEnvironment; import org.apache.hop.core.IProgressMonitor; import org.apache.hop.core.NotePadMeta; import org.apache.hop.core.annotations.Transform; @@ -59,6 +58,7 @@ import org.apache.hop.core.variables.IVariables; import org.apache.hop.core.variables.Variables; import org.apache.hop.core.xml.XmlHandler; +import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension; import org.apache.hop.metadata.api.IHopMetadataProvider; import org.apache.hop.metadata.api.IHopMetadataSerializer; import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider; @@ -72,23 +72,19 @@ import org.apache.hop.pipeline.transform.TransformPartitioningMeta; import org.apache.hop.pipeline.transform.transforms.FakeMeta; import org.apache.hop.pipeline.transforms.dummy.DummyMeta; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** Unit test for {@link PipelineMeta} */ +@ExtendWith(RestoreHopEngineEnvironmentExtension.class) class PipelineMetaTest { public static final String TRANSFORM_NAME = "Any transform name"; - @BeforeAll - static void initHop() throws Exception { - HopEnvironment.init(); - } - private PipelineMeta pipelineMeta; private IVariables variables; private IHopMetadataProvider metadataProvider; diff --git a/engine/src/test/java/org/apache/hop/pipeline/TransformWithMappingMetaTest.java b/engine/src/test/java/org/apache/hop/pipeline/TransformWithMappingMetaTest.java index 8358f3102ca..e1fedc061d4 100644 --- a/engine/src/test/java/org/apache/hop/pipeline/TransformWithMappingMetaTest.java +++ b/engine/src/test/java/org/apache/hop/pipeline/TransformWithMappingMetaTest.java @@ -23,32 +23,22 @@ import static org.mockito.Mockito.when; import org.apache.hop.core.Const; -import org.apache.hop.core.HopEnvironment; import org.apache.hop.core.logging.LoggingObject; import org.apache.hop.core.variables.IVariables; import org.apache.hop.core.variables.Variables; +import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension; import org.apache.hop.pipeline.engines.local.LocalPipelineEngine; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +@ExtendWith(RestoreHopEngineEnvironmentExtension.class) @ExtendWith(MockitoExtension.class) class TransformWithMappingMetaTest { @Mock PipelineMeta pipelineMeta; - @BeforeEach - void setupBefore() throws Exception { - // Without initialization of the Hop Environment, the load of the pipeline fails - // when run in Windows (saying it cannot find the Database plugin ID for Oracle). Digging into - // it I discovered that it's during the read of the shared objects xml which doesn't reference - // Oracle - // at all. Initializing the environment fixed everything. - HopEnvironment.init(); - } - @Test void activateParamsTest() throws Exception { String childParam = "childParam"; diff --git a/engine/src/test/java/org/apache/hop/pipeline/engines/remote/RemotePipelineEngineTest.java b/engine/src/test/java/org/apache/hop/pipeline/engines/remote/RemotePipelineEngineTest.java index 0bc094afe25..a0ed6f326d1 100644 --- a/engine/src/test/java/org/apache/hop/pipeline/engines/remote/RemotePipelineEngineTest.java +++ b/engine/src/test/java/org/apache/hop/pipeline/engines/remote/RemotePipelineEngineTest.java @@ -21,31 +21,27 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; -import org.apache.hop.core.HopEnvironment; import org.apache.hop.core.exception.HopException; import org.apache.hop.core.logging.LogLevel; +import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension; import org.apache.hop.metadata.api.IHopMetadataProvider; import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider; import org.apache.hop.pipeline.PipelineMeta; import org.apache.hop.pipeline.config.PipelineRunConfiguration; import org.apache.hop.pipeline.engines.local.LocalPipelineRunConfiguration; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; /** * The run configuration a remote run configuration hands the pipeline to is used on the server. * When that leads back to a remote run configuration the pipeline keeps being handed on and * registered again and again. See issue #4086. */ +@ExtendWith(RestoreHopEngineEnvironmentExtension.class) class RemotePipelineEngineTest { private static final String SERVER_NAME = "a-server"; - @BeforeAll - static void setUpBeforeClass() throws HopException { - HopEnvironment.init(); - } - /** A remote run configuration that names itself never reaches a server that would run it. */ @Test void runConfigurationThatRefersToItselfIsRejected() throws Exception { diff --git a/engine/src/test/java/org/apache/hop/pipeline/transform/TransformOptionTest.java b/engine/src/test/java/org/apache/hop/pipeline/transform/TransformOptionTest.java index c5747b67668..dd8f200ab2f 100644 --- a/engine/src/test/java/org/apache/hop/pipeline/transform/TransformOptionTest.java +++ b/engine/src/test/java/org/apache/hop/pipeline/transform/TransformOptionTest.java @@ -24,27 +24,21 @@ import java.util.ArrayList; import java.util.List; -import org.apache.hop.core.HopEnvironment; import org.apache.hop.core.ICheckResult; -import org.apache.hop.core.exception.HopException; import org.apache.hop.core.variables.IVariables; -import org.junit.jupiter.api.BeforeAll; +import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +@ExtendWith(RestoreHopEngineEnvironmentExtension.class) @ExtendWith(MockitoExtension.class) class TransformOptionTest { @Mock TransformMeta transformMeta; @Mock IVariables variables; - @BeforeAll - static void setUpBeforeClass() throws HopException { - HopEnvironment.init(); - } - @BeforeEach void setup() { when(variables.resolve(anyString())) diff --git a/engine/src/test/java/org/apache/hop/workflow/WorkflowTest.java b/engine/src/test/java/org/apache/hop/workflow/WorkflowTest.java index 1fae0e3faae..3b1a49aaff7 100644 --- a/engine/src/test/java/org/apache/hop/workflow/WorkflowTest.java +++ b/engine/src/test/java/org/apache/hop/workflow/WorkflowTest.java @@ -23,7 +23,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import org.apache.hop.core.HopEnvironment; import org.apache.hop.core.Result; import org.apache.hop.core.annotations.Action; import org.apache.hop.core.exception.HopException; @@ -33,6 +32,7 @@ import org.apache.hop.core.logging.LogLevel; import org.apache.hop.core.plugins.ActionPluginType; import org.apache.hop.core.plugins.PluginRegistry; +import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension; import org.apache.hop.workflow.action.ActionBase; import org.apache.hop.workflow.action.ActionMeta; import org.apache.hop.workflow.action.IAction; @@ -43,7 +43,9 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +@ExtendWith(RestoreHopEngineEnvironmentExtension.class) class WorkflowTest { int count = 10000; @@ -141,7 +143,6 @@ public Result execute(Result prevResult, int nr) throws HopException { @BeforeAll static void beforeClass() throws HopException, HopPluginException { - HopEnvironment.init(); PluginRegistry.getInstance() .registerPluginClass( ActionBlockingForWorkflowStopTest.class.getName(), diff --git a/engine/src/test/java/org/apache/hop/workflow/action/loadsave/WorkflowActionLoadSaveTestSupport.java b/engine/src/test/java/org/apache/hop/workflow/action/loadsave/WorkflowActionLoadSaveTestSupport.java index 3f545267696..d80da6a0a87 100644 --- a/engine/src/test/java/org/apache/hop/workflow/action/loadsave/WorkflowActionLoadSaveTestSupport.java +++ b/engine/src/test/java/org/apache/hop/workflow/action/loadsave/WorkflowActionLoadSaveTestSupport.java @@ -21,23 +21,19 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.hop.core.HopEnvironment; import org.apache.hop.core.exception.HopException; +import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension; import org.apache.hop.pipeline.transforms.loadsave.validator.IFieldLoadSaveValidator; import org.apache.hop.workflow.action.IAction; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +@ExtendWith(RestoreHopEngineEnvironmentExtension.class) public abstract class WorkflowActionLoadSaveTestSupport { protected LoadSaveTester tester; - @BeforeAll - static void setUpBeforeClass() throws HopException { - HopEnvironment.init(); - } - @BeforeEach void setUp() throws Exception { List attributes = listAttributes(); diff --git a/engine/src/test/java/org/apache/hop/workflow/engine/WorkflowEngineFactoryTest.java b/engine/src/test/java/org/apache/hop/workflow/engine/WorkflowEngineFactoryTest.java index fbc52080afc..c21beffb608 100644 --- a/engine/src/test/java/org/apache/hop/workflow/engine/WorkflowEngineFactoryTest.java +++ b/engine/src/test/java/org/apache/hop/workflow/engine/WorkflowEngineFactoryTest.java @@ -21,12 +21,12 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertSame; -import org.apache.hop.core.HopEnvironment; import org.apache.hop.core.exception.HopException; import org.apache.hop.core.logging.LogLevel; import org.apache.hop.core.logging.LoggingObjectType; import org.apache.hop.core.logging.SimpleLoggingObject; import org.apache.hop.core.variables.Variables; +import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension; import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider; import org.apache.hop.workflow.WorkflowMeta; import org.apache.hop.workflow.config.IWorkflowEngineRunConfiguration; @@ -35,16 +35,21 @@ import org.apache.hop.workflow.engines.local.LocalWorkflowRunConfiguration; import org.apache.hop.workflow.engines.remote.RemoteWorkflowEngine; import org.apache.hop.workflow.engines.remote.RemoteWorkflowRunConfiguration; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +/** + * Uses {@link RestoreHopEngineEnvironmentExtension} rather than a plain {@code + * HopEnvironment.init()} in a {@code @BeforeAll}. The bare init is a no-op when a previous test + * already flipped {@code HopEnvironment.initialized} to true, and a base {@code + * RestoreHopEnvironmentExtension} test running in between wipes the PluginRegistry without nulling + * that flag. That combination left the workflow engine plugins ("Local"/"Remote") unregistered and + * made this class fail depending on test execution order. The engine extension forces a full reset + * + re-init, so the registry is always populated here. + */ +@ExtendWith(RestoreHopEngineEnvironmentExtension.class) class WorkflowEngineFactoryTest { - @BeforeAll - static void setUpBeforeClass() throws HopException { - HopEnvironment.init(); - } - /** * The parent logging object is how the log level is pushed down into the engine. A workflow that * runs on a server needs it as much as one that runs locally, or the server ends up logging at diff --git a/engine/src/test/java/org/apache/hop/www/RegisterPipelineServletLogFileTest.java b/engine/src/test/java/org/apache/hop/www/RegisterPipelineServletLogFileTest.java index 7e3a25bc5d6..1df7ec40a3b 100644 --- a/engine/src/test/java/org/apache/hop/www/RegisterPipelineServletLogFileTest.java +++ b/engine/src/test/java/org/apache/hop/www/RegisterPipelineServletLogFileTest.java @@ -23,11 +23,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import org.apache.hop.core.HopEnvironment; import org.apache.hop.core.exception.HopException; import org.apache.hop.core.logging.LoggingRegistry; import org.apache.hop.core.metadata.SerializableMetadataProvider; import org.apache.hop.core.variables.Variables; +import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension; import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider; import org.apache.hop.metadata.serializer.multi.MultiMetadataProvider; import org.apache.hop.pipeline.PipelineConfiguration; @@ -37,25 +37,21 @@ import org.apache.hop.pipeline.engine.IPipelineEngine; import org.apache.hop.pipeline.engines.local.LocalPipelineRunConfiguration; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; /** * Registering a pipeline with a log file used to fail with a NullPointerException, because the * logging object of the servlet had no log channel to hang the log file writer on. See issue #4677. */ +@ExtendWith(RestoreHopEngineEnvironmentExtension.class) class RegisterPipelineServletLogFileTest { private static final String RUN_CONFIGURATION_NAME = "local"; private Path logFile; - @BeforeAll - static void setUpBeforeClass() throws HopException { - HopEnvironment.init(); - } - @BeforeEach void setUp() throws Exception { logFile = Files.createTempFile("register-pipeline-servlet", ".log"); diff --git a/ui/src/test/java/org/apache/hop/ui/testing/SwtBotTestBase.java b/ui/src/test/java/org/apache/hop/ui/testing/SwtBotTestBase.java index c535e9eeba9..5fdc60d3575 100644 --- a/ui/src/test/java/org/apache/hop/ui/testing/SwtBotTestBase.java +++ b/ui/src/test/java/org/apache/hop/ui/testing/SwtBotTestBase.java @@ -60,6 +60,10 @@ static void initHopUiEnvironment() throws Exception { GraphicsEnvironment.isHeadless(), "No display available (headless); skipping SWTBot UI tests. Run on a desktop or under Xvfb."); // Registers the transform/plugin metadata (e.g. the Abort transform) the dialogs look up. + // reset() first so we always get a full re-registration: a bare init() is a no-op when an + // earlier test already flipped HopEnvironment.initialized, and a test that wiped the + // PluginRegistry in the meantime would otherwise leave the dialogs without their plugins. + HopEnvironment.reset(); HopEnvironment.init(); keepAuditStateOutOfSourceTree(); ensureDisplay();