public void mergeRuntimeUpdate(
      List<TimelineEvent> pendingTimeline, Map<String, Artifact> pendingArtifacts) {
    if (timeline.addAll(pendingTimeline)) {
      synced = false;
    }
    if (pendingArtifacts != null && !pendingArtifacts.isEmpty()) {
      for (Map.Entry<String, Artifact> entry : pendingArtifacts.entrySet()) {
        String key = entry.getKey();
        if (!entry.getValue().equals(artifacts.get(key))) {
          if (artifacts.containsKey(key)
              && artifacts.get(key).getType() == Artifact.Type.DEFAULT
              && entry.getValue().getType() == Artifact.Type.DEFAULT) {
            artifacts.get(key).asDefault().getData().putAll(entry.getValue().asDefault().getData());
          } else {
            artifacts.put(entry.getKey(), entry.getValue());
          }
          synced = false;
        }
      }
    }
    if (!synced) {
      runtimeState.setModifyTime(System.currentTimeMillis());
    }
  }
@Test
  public void testNoChangeMerge() throws Exception {
    StepRuntimeSummary summary =
        loadObject(
            "fixtures/execution/sample-step-runtime-summary-1.json", StepRuntimeSummary.class);
    assertTrue(summary.isSynced());
    summary.mergeRuntimeUpdate(null, null);
    assertTrue(summary.isSynced());

    Map<String, Artifact> artifacts = new LinkedHashMap<>();
    DefaultArtifact artifact1 = new DefaultArtifact();
    artifact1.add("value", 1L);
    artifact1.add("foo", "bar");
    artifacts.put("artifact1", artifact1);
    summary.mergeRuntimeUpdate(null, artifacts);
    assertTrue(summary.isSynced());

    SubworkflowArtifact artifact2 = new SubworkflowArtifact();
    artifact2.setSubworkflowId("test-dag");
    artifact2.setSubworkflowVersionId(1L);
    artifact2.setSubworkflowInstanceId(1);
    artifact2.setSubworkflowRunId(1);
    artifact2.setSubworkflowUuid("foo-bar");
    artifact2.setSubworkflowOverview(
        WorkflowRuntimeOverview.of(
            1L,
            singletonEnumMap(StepInstance.Status.SUCCEEDED, WorkflowStepStatusSummary.of(1L)),
            null));
    artifacts.put(artifact2.getType().key(), artifact2);
    summary.mergeRuntimeUpdate(null, artifacts);
    assertTrue(summary.isSynced());

    summary =
        loadObject(
            "fixtures/execution/sample-step-runtime-summary-3.json", StepRuntimeSummary.class);
    artifacts.clear();
    ForeachArtifact artifact3 = new ForeachArtifact();
    artifact3.setForeachWorkflowId("inline-wf");
    artifact3.setForeachIdentity("foo");
    artifact3.setTotalLoopCount(10);
    artifact3.setNextLoopIndex(0);
    artifact3.setForeachOverview(new ForeachStepOverview());
    artifact3.getForeachOverview().setStats(new EnumMap<>(WorkflowInstance.Status.class));
    artifact3.getForeachOverview().setCheckpoint(6L);
    artifact3.getForeachOverview().getStats().put(WorkflowInstance.Status.CREATED, 5L);
    artifact3.getForeachOverview().getStats().put(WorkflowInstance.Status.SUCCEEDED, 1L);
    artifacts.put(artifact3.getType().key(), artifact3);
    summary.mergeRuntimeUpdate(null, artifacts);
    assertTrue(summary.isSynced());
  }