public static RuntimeOpts mergeRuntimeOpts(RuntimeOpts oriOpts, RuntimeOpts newOpts) {
        RuntimeOpts mergedOpts = oriOpts.partialDeepClone();
        if (mergedOpts.getExtraLabels() == null) {
            mergedOpts.setExtraLabels(new HashMap<>());
        }
        if (mergedOpts.getExtraAnnotations() == null) {
            mergedOpts.setExtraAnnotations(new HashMap<>());
        }
        if (mergedOpts.getNodeSelectorLabels() == null) {
            mergedOpts.setNodeSelectorLabels(new HashMap<>());
        }
        if (mergedOpts.getTolerations() == null) {
            mergedOpts.setTolerations(new ArrayList<>());
        }
        if (mergedOpts.getResourceRequirements() == null) {
            mergedOpts.setResourceRequirements(new V1ResourceRequirements());
        }

        if (!StringUtils.isEmpty(newOpts.getJobName())) {
            mergedOpts.setJobName(newOpts.getJobName());
        }
        if (!StringUtils.isEmpty(newOpts.getJobNamespace())) {
            mergedOpts.setJobNamespace(newOpts.getJobNamespace());
        }
        if (newOpts.getExtraLabels() != null && !newOpts.getExtraLabels().isEmpty()) {
            newOpts.getExtraLabels().forEach((key, labelsItem) -> {
                if (!mergedOpts.getExtraLabels().containsKey(key)) {
                    log.debug("extra label {} has been changed to {}", key, labelsItem);
                }
                mergedOpts.getExtraLabels().put(key, labelsItem);
            });
        }
        if (newOpts.getExtraAnnotations() != null && !newOpts.getExtraAnnotations().isEmpty()) {
            newOpts.getExtraAnnotations().forEach((key, annotationsItem) -> {
                if (!mergedOpts.getExtraAnnotations().containsKey(key)) {
                    log.debug("extra annotation {} has been changed to {}", key, annotationsItem);
                }
                mergedOpts.getExtraAnnotations().put(key, annotationsItem);
            });
        }
        if (newOpts.getNodeSelectorLabels() != null && !newOpts.getNodeSelectorLabels().isEmpty()) {
            newOpts.getNodeSelectorLabels().forEach((key, nodeSelectorItem) -> {
                if (!mergedOpts.getNodeSelectorLabels().containsKey(key)) {
                    log.debug("node selector label {} has been changed to {}", key, nodeSelectorItem);
                }
                mergedOpts.getNodeSelectorLabels().put(key, nodeSelectorItem);
            });
        }

        if (newOpts.getResourceRequirements() != null) {
            V1ResourceRequirements mergedResourcesRequirements = mergedOpts.getResourceRequirements();
            V1ResourceRequirements newResourcesRequirements = newOpts.getResourceRequirements();

            Map<String, Quantity> limits = newResourcesRequirements.getLimits();
            Map<String, Quantity> requests = newResourcesRequirements.getRequests();
            for (String resource : RESOURCES) {
                if (limits != null && limits.containsKey(resource)) {
                    mergedResourcesRequirements.putLimitsItem(resource, limits.get(resource));
                }
                if (requests != null && requests.containsKey(resource)) {
                    mergedResourcesRequirements.putRequestsItem(resource, requests.get(resource));
                }
            }
            mergedOpts.setResourceRequirements(mergedResourcesRequirements);
        }

        if (newOpts.getTolerations() != null && !newOpts.getTolerations().isEmpty()) {
            mergedOpts.getTolerations().addAll(newOpts.getTolerations());
        }
        return mergedOpts;
    }
@Test
    public void testMergeRuntimeOptsDoesNotModifyArguments() {
        BasicKubernetesManifestCustomizer.RuntimeOpts opts1 = new BasicKubernetesManifestCustomizer.RuntimeOpts(
                "namespace1", "job1", new HashMap<>(), new HashMap<>(), new HashMap<>(), new V1ResourceRequirements(),
                new ArrayList<>());

        HashMap<String, String> testMap = new HashMap<>();
        testMap.put("testKey", "testValue");

        List<V1Toleration> testList = new ArrayList<>();
        testList.add(new V1Toleration());

        V1ResourceRequirements requirements = new V1ResourceRequirements();
        requirements.setLimits(new HashMap<>());
        BasicKubernetesManifestCustomizer.RuntimeOpts opts2 = new BasicKubernetesManifestCustomizer.RuntimeOpts(
                "namespace2", "job2", testMap, testMap, testMap,requirements, testList);

        // Merge the runtime opts
        BasicKubernetesManifestCustomizer.RuntimeOpts result =
                BasicKubernetesManifestCustomizer.mergeRuntimeOpts(opts1, opts2);

        // Assert opts1 is same
        assertEquals("namespace1", opts1.getJobNamespace());
        assertEquals("job1", opts1.getJobName());
        assertEquals(new HashMap<>(), opts1.getNodeSelectorLabels());
        assertEquals(new HashMap<>(), opts1.getExtraAnnotations());
        assertEquals(new HashMap<>(), opts1.getExtraLabels());
        assertEquals(new ArrayList<>(), opts1.getTolerations());
        assertEquals(new V1ResourceRequirements(), opts1.getResourceRequirements());

        // Assert opts2 is same
        HashMap<String, String> expectedTestMap = new HashMap<>();
        expectedTestMap.put("testKey", "testValue");

        List<V1Toleration> expectedTestList = new ArrayList<>();
        expectedTestList.add(new V1Toleration());

        V1ResourceRequirements expectedRequirements = new V1ResourceRequirements();
        expectedRequirements.setLimits(new HashMap<>());

        assertEquals("namespace2", opts2.getJobNamespace());
        assertEquals("job2", opts2.getJobName());
        assertEquals(expectedTestMap, opts2.getNodeSelectorLabels());
        assertEquals(expectedTestMap, opts2.getExtraAnnotations());
        assertEquals(expectedTestMap, opts2.getExtraLabels());
        assertEquals(expectedTestList, opts2.getTolerations());
        assertEquals(expectedRequirements, opts2.getResourceRequirements());
    }