content: /*
   * Copyright 2010-2025 Hyland Software, Inc. and its affiliates.
   *
   * Licensed 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.activiti.standalone.history;
  
  import static java.util.Arrays.asList;
  import static java.util.Collections.singletonMap;
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
  
  import jakarta.persistence.EntityManager;
  import jakarta.persistence.EntityManagerFactory;
  import java.text.SimpleDateFormat;
  import java.util.Calendar;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import org.activiti.engine.ActivitiException;
  import org.activiti.engine.ActivitiIllegalArgumentException;
  import org.activiti.engine.history.HistoricActivityInstance;
  import org.activiti.engine.history.HistoricDetail;
  import org.activiti.engine.history.HistoricProcessInstance;
  import org.activiti.engine.history.HistoricTaskInstance;
  import org.activiti.engine.history.HistoricVariableInstance;
  import org.activiti.engine.history.HistoricVariableInstanceQuery;
  import org.activiti.engine.history.HistoricVariableUpdate;
  import org.activiti.engine.impl.test.ResourceActivitiTestCase;
  import org.activiti.engine.impl.variable.EntityManagerSession;
  import org.activiti.engine.impl.variable.EntityManagerSessionFactory;
  import org.activiti.engine.runtime.ProcessInstance;
  import org.activiti.engine.task.Task;
  import org.activiti.engine.test.Deployment;
  import org.activiti.engine.test.api.runtime.DummySerializable;
  import org.activiti.engine.test.history.SerializableVariable;
  import org.activiti.standalone.jpa.FieldAccessJPAEntity;
  
  public class FullHistoryTest extends ResourceActivitiTestCase {
  
      public FullHistoryTest() {
          super("org/activiti/standalone/history/fullhistory.activiti.cfg.xml");
      }
  
      @Deployment
      public void testVariableUpdates() {
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("number", "one");
          variables.put("character", "a");
          variables.put("bytes", ":-(".getBytes());
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("receiveTask", variables);
          runtimeService.setVariable(processInstance.getId(), "number", "two");
          runtimeService.setVariable(processInstance.getId(), "bytes", ":-)".getBytes());
  
          // Start-task should be added to history
          HistoricActivityInstance historicStartEvent = historyService
              .createHistoricActivityInstanceQuery()
              .processInstanceId(processInstance.getId())
              .activityId("theStart")
              .singleResult();
          assertThat(historicStartEvent).isNotNull();
  
          HistoricActivityInstance waitStateActivity = historyService
              .createHistoricActivityInstanceQuery()
              .processInstanceId(processInstance.getId())
              .activityId("waitState")
              .singleResult();
          assertThat(waitStateActivity).isNotNull();
  
          HistoricActivityInstance serviceTaskActivity = historyService
              .createHistoricActivityInstanceQuery()
              .processInstanceId(processInstance.getId())
              .activityId("serviceTask")
              .singleResult();
          assertThat(serviceTaskActivity).isNotNull();
  
          List<HistoricDetail> historicDetails = historyService
              .createHistoricDetailQuery()
              .orderByVariableName()
              .asc()
              .orderByVariableRevision()
              .asc()
              .list();
  
          assertThat(historicDetails).hasSize(10);
  
          HistoricVariableUpdate historicVariableUpdate = (HistoricVariableUpdate) historicDetails.get(0);
          assertThat(historicVariableUpdate.getVariableName()).isEqualTo("bytes");
          assertThat(new String((byte[]) historicVariableUpdate.getValue())).isEqualTo(":-(");
          assertThat(historicVariableUpdate.getRevision()).isEqualTo(0);
  
          // Activiti 6: we don't store the start event activityId anymore!
          //    assertThat(historicVariableUpdate.getActivityInstanceId()).isEqualTo(historicStartEvent.getId());
          assertThat(historicVariableUpdate.getActivityInstanceId()).isNull();
  
          // Variable is updated when process was in waitstate
          historicVariableUpdate = (HistoricVariableUpdate) historicDetails.get(1);
          assertThat(historicVariableUpdate.getVariableName()).isEqualTo("bytes");
          assertThat(new String((byte[]) historicVariableUpdate.getValue())).isEqualTo(":-)");
          assertThat(historicVariableUpdate.getRevision()).isEqualTo(1);
  
          //    assertThat(historicVariableUpdate.getActivityInstanceId()).isEqualTo(waitStateActivity.getId());
          assertThat(historicVariableUpdate.getActivityInstanceId()).isNull();
  
          historicVariableUpdate = (HistoricVariableUpdate) historicDetails.get(2);
          assertThat(historicVariableUpdate.getVariableName()).isEqualTo("character");
          assertThat(historicVariableUpdate.getValue()).isEqualTo("a");
          assertThat(historicVariableUpdate.getRevision()).isEqualTo(0);
  
          //    assertThat(historicVariableUpdate.getActivityInstanceId()).isEqualTo(historicStartEvent.getId());
          assertThat(historicVariableUpdate.getActivityInstanceId()).isNull();
  
          historicVariableUpdate = (HistoricVariableUpdate) historicDetails.get(3);
          assertThat(historicVariableUpdate.getVariableName()).isEqualTo("number");
          assertThat(historicVariableUpdate.getValue()).isEqualTo("one");
          assertThat(historicVariableUpdate.getRevision()).isEqualTo(0);
  
          //    assertThat(historicVariableUpdate.getActivityInstanceId()).isEqualTo(historicStartEvent.getId());
          assertThat(historicVariableUpdate.getActivityInstanceId()).isNull();
  
          // Variable is updated when process was in waitstate
          historicVariableUpdate = (HistoricVariableUpdate) historicDetails.get(4);
          assertThat(historicVariableUpdate.getVariableName()).isEqualTo("number");
          assertThat(historicVariableUpdate.getValue()).isEqualTo("two");
          assertThat(historicVariableUpdate.getRevision()).isEqualTo(1);
  
          //    assertThat(historicVariableUpdate.getActivityInstanceId()).isEqualTo(waitStateActivity.getId());
          assertThat(historicVariableUpdate.getActivityInstanceId()).isNull();
  
          // Variable set from process-start execution listener
          historicVariableUpdate = (HistoricVariableUpdate) historicDetails.get(5);
          assertThat(historicVariableUpdate.getVariableName()).isEqualTo("zVar1");
          assertThat(historicVariableUpdate.getValue()).isEqualTo("Event: start");
          assertThat(historicVariableUpdate.getRevision()).isEqualTo(0);
          assertThat(historicVariableUpdate.getActivityInstanceId()).isNull();
  
          // Variable set from transition take execution listener
          historicVariableUpdate = (HistoricVariableUpdate) historicDetails.get(6);
          assertThat(historicVariableUpdate.getVariableName()).isEqualTo("zVar2");
          assertThat(historicVariableUpdate.getValue()).isEqualTo("Event: take");
          assertThat(historicVariableUpdate.getRevision()).isEqualTo(0);
          assertThat(historicVariableUpdate.getActivityInstanceId()).isNull();
  
          // Variable set from activity start execution listener on the servicetask
          historicVariableUpdate = (HistoricVariableUpdate) historicDetails.get(7);
          assertThat(historicVariableUpdate.getVariableName()).isEqualTo("zVar3");
          assertThat(historicVariableUpdate.getValue()).isEqualTo("Event: start");
          assertThat(historicVariableUpdate.getRevision()).isEqualTo(0);
          assertThat(historicVariableUpdate.getActivityInstanceId()).isEqualTo(serviceTaskActivity.getId());
  
          // Variable set from activity end execution listener on the servicetask
          historicVariableUpdate = (HistoricVariableUpdate) historicDetails.get(8);
          assertThat(historicVariableUpdate.getVariableName()).isEqualTo("zVar4");
          assertThat(historicVariableUpdate.getValue()).isEqualTo("Event: end");
          assertThat(historicVariableUpdate.getRevision()).isEqualTo(0);
          assertThat(historicVariableUpdate.getActivityInstanceId()).isEqualTo(serviceTaskActivity.getId());
  
          // Variable set from service-task
          historicVariableUpdate = (HistoricVariableUpdate) historicDetails.get(9);
          assertThat(historicVariableUpdate.getVariableName()).isEqualTo("zzz");
          assertThat(historicVariableUpdate.getValue()).isEqualTo(123456789L);
          assertThat(historicVariableUpdate.getRevision()).isEqualTo(0);
          assertThat(historicVariableUpdate.getActivityInstanceId()).isEqualTo(serviceTaskActivity.getId());
  
          // trigger receive task
          runtimeService.trigger(runtimeService.createExecutionQuery().activityId("waitState").singleResult().getId());
          assertProcessEnded(processInstance.getId());
  
          // check for historic process variables set
          HistoricVariableInstanceQuery historicProcessVariableQuery = historyService
              .createHistoricVariableInstanceQuery()
              .orderByVariableName()
              .asc();
  
          assertThat(historicProcessVariableQuery.count()).isEqualTo(8);
  
          List<HistoricVariableInstance> historicVariables = historicProcessVariableQuery.list();
  
          // Variable status when process is finished
          HistoricVariableInstance historicVariable = historicVariables.get(0);
          assertThat(historicVariable.getVariableName()).isEqualTo("bytes");
          assertThat(new String((byte[]) historicVariable.getValue())).isEqualTo(":-)");
          assertThat(historicVariable.getCreateTime()).isNotNull();
          assertThat(historicVariable.getLastUpdatedTime()).isNotNull();
  
          historicVariable = historicVariables.get(1);
          assertThat(historicVariable.getVariableName()).isEqualTo("character");
          assertThat(historicVariable.getValue()).isEqualTo("a");
          assertThat(historicVariable.getCreateTime()).isNotNull();
          assertThat(historicVariable.getLastUpdatedTime()).isNotNull();
  
          historicVariable = historicVariables.get(2);
          assertThat(historicVariable.getVariableName()).isEqualTo("number");
          assertThat(historicVariable.getValue()).isEqualTo("two");
          assertThat(historicVariable.getCreateTime()).isNotNull();
          assertThat(historicVariable.getLastUpdatedTime()).isNotNull();
          assertThat(historicVariable.getLastUpdatedTime()).isNotSameAs(historicVariable.getCreateTime());
  
          historicVariable = historicVariables.get(3);
          assertThat(historicVariable.getVariableName()).isEqualTo("zVar1");
          assertThat(historicVariable.getValue()).isEqualTo("Event: start");
          assertThat(historicVariable.getCreateTime()).isNotNull();
          assertThat(historicVariable.getLastUpdatedTime()).isNotNull();
  
          historicVariable = historicVariables.get(4);
          assertThat(historicVariable.getVariableName()).isEqualTo("zVar2");
          assertThat(historicVariable.getValue()).isEqualTo("Event: take");
          assertThat(historicVariable.getCreateTime()).isNotNull();
          assertThat(historicVariable.getLastUpdatedTime()).isNotNull();
  
          historicVariable = historicVariables.get(5);
          assertThat(historicVariable.getVariableName()).isEqualTo("zVar3");
          assertThat(historicVariable.getValue()).isEqualTo("Event: start");
          assertThat(historicVariable.getCreateTime()).isNotNull();
          assertThat(historicVariable.getLastUpdatedTime()).isNotNull();
  
          historicVariable = historicVariables.get(6);
          assertThat(historicVariable.getVariableName()).isEqualTo("zVar4");
          assertThat(historicVariable.getValue()).isEqualTo("Event: end");
          assertThat(historicVariable.getCreateTime()).isNotNull();
          assertThat(historicVariable.getLastUpdatedTime()).isNotNull();
  
          historicVariable = historicVariables.get(7);
          assertThat(historicVariable.getVariableName()).isEqualTo("zzz");
          assertThat(historicVariable.getValue()).isEqualTo(123456789L);
          assertThat(historicVariable.getCreateTime()).isNotNull();
          assertThat(historicVariable.getLastUpdatedTime()).isNotNull();
  
          historicVariable = historyService
              .createHistoricVariableInstanceQuery()
              .variableValueLike("number", "tw%")
              .singleResult();
          assertThat(historicVariable).isNotNull();
          assertThat(historicVariable.getVariableName()).isEqualTo("number");
          assertThat(historicVariable.getValue()).isEqualTo("two");
  
          historicVariable = historyService
              .createHistoricVariableInstanceQuery()
              .variableValueLikeIgnoreCase("number", "TW%")
              .singleResult();
          assertThat(historicVariable).isNotNull();
          assertThat(historicVariable.getVariableName()).isEqualTo("number");
          assertThat(historicVariable.getValue()).isEqualTo("two");
  
          historicVariable = historyService
              .createHistoricVariableInstanceQuery()
              .variableValueLikeIgnoreCase("number", "TW2%")
              .singleResult();
          assertThat(historicVariable).isNull();
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricVariableInstanceQueryTaskVariables() {
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("variable", "setFromProcess");
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess", variables);
  
          assertThat(historyService.createHistoricVariableInstanceQuery().count()).isEqualTo(1);
  
          Task activeTask = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(activeTask).isNotNull();
          taskService.setVariableLocal(activeTask.getId(), "variable", "setFromTask");
  
          // Check if additional variable is available in history, task-local
          assertThat(historyService.createHistoricVariableInstanceQuery().count()).isEqualTo(2);
          assertThat(historyService.createHistoricVariableInstanceQuery().taskId(activeTask.getId()).count()).isEqualTo(
              1
          );
          assertThat(
              historyService.createHistoricVariableInstanceQuery().taskId(activeTask.getId()).singleResult().getValue()
          ).isEqualTo("setFromTask");
          assertThat(
              historyService.createHistoricVariableInstanceQuery().taskId(activeTask.getId()).singleResult().getTaskId()
          ).isEqualTo(activeTask.getId());
          assertThat(historyService.createHistoricVariableInstanceQuery().excludeTaskVariables().count()).isEqualTo(1);
  
          // Test null task-id
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class)
              .isThrownBy(() -> historyService.createHistoricVariableInstanceQuery().taskId(null).singleResult())
              .withMessage("taskId is null");
  
          // Test invalid usage of taskId together with excludeTaskVariables
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class)
              .isThrownBy(() ->
                  historyService.createHistoricVariableInstanceQuery().taskId("123").excludeTaskVariables().singleResult()
              )
              .withMessage("Cannot use taskId together with excludeTaskVariables");
  
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class)
              .isThrownBy(() ->
                  historyService.createHistoricVariableInstanceQuery().excludeTaskVariables().taskId("123").singleResult()
              )
              .withMessage("Cannot use taskId together with excludeTaskVariables");
      }
  
      @Deployment(resources = "org/activiti/standalone/history/FullHistoryTest.testVariableUpdates.bpmn20.xml")
      public void testHistoricVariableInstanceQuery() {
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("process", "one");
          runtimeService.startProcessInstanceByKey("receiveTask", variables);
          runtimeService.trigger(runtimeService.createExecutionQuery().activityId("waitState").singleResult().getId());
  
          assertThat(historyService.createHistoricVariableInstanceQuery().variableName("process").count()).isEqualTo(1);
          assertThat(
              historyService.createHistoricVariableInstanceQuery().variableValueEquals("process", "one").count()
          ).isEqualTo(1);
  
          Map<String, Object> variables2 = new HashMap<String, Object>();
          variables2.put("process", "two");
          ProcessInstance processInstance2 = runtimeService.startProcessInstanceByKey("receiveTask", variables2);
          runtimeService.trigger(runtimeService.createExecutionQuery().activityId("waitState").singleResult().getId());
  
          assertThat(historyService.createHistoricVariableInstanceQuery().variableName("process").count()).isEqualTo(2);
          assertThat(
              historyService.createHistoricVariableInstanceQuery().variableValueEquals("process", "one").count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricVariableInstanceQuery().variableValueEquals("process", "two").count()
          ).isEqualTo(1);
  
          HistoricVariableInstance historicProcessVariable = historyService
              .createHistoricVariableInstanceQuery()
              .variableValueEquals("process", "one")
              .singleResult();
          assertThat(historicProcessVariable.getVariableName()).isEqualTo("process");
          assertThat(historicProcessVariable.getValue()).isEqualTo("one");
  
          Map<String, Object> variables3 = new HashMap<String, Object>();
          variables3.put("long", 1000l);
          variables3.put("double", 25.43d);
          ProcessInstance processInstance3 = runtimeService.startProcessInstanceByKey("receiveTask", variables3);
          runtimeService.trigger(runtimeService.createExecutionQuery().activityId("waitState").singleResult().getId());
  
          assertThat(historyService.createHistoricVariableInstanceQuery().variableName("long").count()).isEqualTo(1);
          assertThat(
              historyService.createHistoricVariableInstanceQuery().variableValueEquals("long", 1000l).count()
          ).isEqualTo(1);
          assertThat(historyService.createHistoricVariableInstanceQuery().variableName("double").count()).isEqualTo(1);
          assertThat(
              historyService.createHistoricVariableInstanceQuery().variableValueEquals("double", 25.43d).count()
          ).isEqualTo(1);
      }
  
      @Deployment
      public void testHistoricVariableUpdatesAllTypes() throws Exception {
          SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss SSS");
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("aVariable", "initial value");
  
          Date startedDate = sdf.parse("01/01/2001 01:23:45 000");
  
          // In the javaDelegate, the current time is manipulated
          Date updatedDate = sdf.parse("01/01/2001 01:23:46 000");
  
          processEngineConfiguration.getClock().setCurrentTime(startedDate);
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
              "HistoricVariableUpdateProcess",
              variables
          );
  
          List<HistoricDetail> details = historyService
              .createHistoricDetailQuery()
              .variableUpdates()
              .processInstanceId(processInstance.getId())
              .orderByVariableName()
              .asc()
              .orderByTime()
              .asc()
              .list();
  
          // 8 variable updates should be present, one performed when starting process the other 7 are set in VariableSetter serviceTask
          assertThat(details).hasSize(9);
  
          // Since we order by varName, first entry should be aVariable update from startTask
          HistoricVariableUpdate startVarUpdate = (HistoricVariableUpdate) details.get(0);
          assertThat(startVarUpdate.getVariableName()).isEqualTo("aVariable");
          assertThat(startVarUpdate.getValue()).isEqualTo("initial value");
          assertThat(startVarUpdate.getRevision()).isEqualTo(0);
          assertThat(startVarUpdate.getProcessInstanceId()).isEqualTo(processInstance.getId());
          // Date should the one set when starting
          assertThat(startVarUpdate.getTime()).isEqualTo(startedDate);
  
          HistoricVariableUpdate updatedStringVariable = (HistoricVariableUpdate) details.get(1);
          assertThat(updatedStringVariable.getVariableName()).isEqualTo("aVariable");
          assertThat(updatedStringVariable.getValue()).isEqualTo("updated value");
          assertThat(updatedStringVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
          // Date should be the updated date
          assertThat(updatedStringVariable.getTime()).isEqualTo(updatedDate);
  
          HistoricVariableUpdate intVariable = (HistoricVariableUpdate) details.get(2);
          assertThat(intVariable.getVariableName()).isEqualTo("bVariable");
          assertThat(intVariable.getValue()).isEqualTo(123);
          assertThat(intVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
          assertThat(intVariable.getTime()).isEqualTo(updatedDate);
  
          HistoricVariableUpdate longVariable = (HistoricVariableUpdate) details.get(3);
          assertThat(longVariable.getVariableName()).isEqualTo("cVariable");
          assertThat(longVariable.getValue()).isEqualTo(12345L);
          assertThat(longVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
          assertThat(longVariable.getTime()).isEqualTo(updatedDate);
  
          HistoricVariableUpdate doubleVariable = (HistoricVariableUpdate) details.get(4);
          assertThat(doubleVariable.getVariableName()).isEqualTo("dVariable");
          assertThat(doubleVariable.getValue()).isEqualTo(1234.567);
          assertThat(doubleVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
          assertThat(doubleVariable.getTime()).isEqualTo(updatedDate);
  
          HistoricVariableUpdate shortVariable = (HistoricVariableUpdate) details.get(5);
          assertThat(shortVariable.getVariableName()).isEqualTo("eVariable");
          assertThat(shortVariable.getValue()).isEqualTo((short) 12);
          assertThat(shortVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
          assertThat(shortVariable.getTime()).isEqualTo(updatedDate);
  
          HistoricVariableUpdate dateVariable = (HistoricVariableUpdate) details.get(6);
          assertThat(dateVariable.getVariableName()).isEqualTo("fVariable");
          assertThat(dateVariable.getValue()).isEqualTo(sdf.parse("01/01/2001 01:23:45 678"));
          assertThat(dateVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
          assertThat(dateVariable.getTime()).isEqualTo(updatedDate);
  
          HistoricVariableUpdate serializableVariable = (HistoricVariableUpdate) details.get(7);
          assertThat(serializableVariable.getVariableName()).isEqualTo("gVariable");
          assertThat(serializableVariable.getValue()).isEqualTo(new SerializableVariable("hello hello"));
          assertThat(serializableVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
          assertThat(serializableVariable.getTime()).isEqualTo(updatedDate);
  
          HistoricVariableUpdate byteArrayVariable = (HistoricVariableUpdate) details.get(8);
          assertThat(byteArrayVariable.getVariableName()).isEqualTo("hVariable");
          assertThat(new String((byte[]) byteArrayVariable.getValue())).isEqualTo(";-)");
          assertThat(byteArrayVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
          assertThat(byteArrayVariable.getTime()).isEqualTo(updatedDate);
  
          // end process instance
          List<Task> tasks = taskService.createTaskQuery().list();
          assertThat(tasks).hasSize(1);
          taskService.complete(tasks.get(0).getId());
          assertProcessEnded(processInstance.getId());
  
          // check for historic process variables set
          HistoricVariableInstanceQuery historicProcessVariableQuery = historyService
              .createHistoricVariableInstanceQuery()
              .orderByVariableName()
              .asc();
  
          assertThat(historicProcessVariableQuery.count()).isEqualTo(8);
  
          List<HistoricVariableInstance> historicVariables = historicProcessVariableQuery.list();
  
          // Variable status when process is finished
          HistoricVariableInstance historicVariable = historicVariables.get(0);
          assertThat(historicVariable.getVariableName()).isEqualTo("aVariable");
          assertThat(historicVariable.getValue()).isEqualTo("updated value");
          assertThat(historicVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
  
          historicVariable = historicVariables.get(1);
          assertThat(historicVariable.getVariableName()).isEqualTo("bVariable");
          assertThat(historicVariable.getValue()).isEqualTo(123);
          assertThat(historicVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
  
          historicVariable = historicVariables.get(2);
          assertThat(historicVariable.getVariableName()).isEqualTo("cVariable");
          assertThat(historicVariable.getValue()).isEqualTo(12345L);
          assertThat(historicVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
  
          historicVariable = historicVariables.get(3);
          assertThat(historicVariable.getVariableName()).isEqualTo("dVariable");
          assertThat(historicVariable.getValue()).isEqualTo(1234.567);
          assertThat(historicVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
  
          historicVariable = historicVariables.get(4);
          assertThat(historicVariable.getVariableName()).isEqualTo("eVariable");
          assertThat(historicVariable.getValue()).isEqualTo((short) 12);
          assertThat(historicVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
  
          historicVariable = historicVariables.get(5);
          assertThat(historicVariable.getVariableName()).isEqualTo("fVariable");
          assertThat(historicVariable.getValue()).isEqualTo(sdf.parse("01/01/2001 01:23:45 678"));
          assertThat(historicVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
  
          historicVariable = historicVariables.get(6);
          assertThat(historicVariable.getVariableName()).isEqualTo("gVariable");
          assertThat(historicVariable.getValue()).isEqualTo(new SerializableVariable("hello hello"));
          assertThat(historicVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
  
          historicVariable = historicVariables.get(7);
          assertThat(historicVariable.getVariableName()).isEqualTo("hVariable");
          assertThat(new String((byte[]) historicVariable.getValue())).isEqualTo(";-)");
          assertThat(historicVariable.getProcessInstanceId()).isEqualTo(processInstance.getId());
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricVariableQuery() throws Exception {
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("stringVar", "activiti rocks!");
          variables.put("longVar", 12345L);
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess", variables);
  
          // Query on activity-instance, activity instance null will return all
          // vars set when starting process
          assertThat(
              historyService.createHistoricDetailQuery().variableUpdates().activityInstanceId(null).count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricDetailQuery().variableUpdates().activityInstanceId("unexisting").count()
          ).isEqualTo(0);
  
          // Query on process-instance
          assertThat(
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricDetailQuery().variableUpdates().processInstanceId("unexisting").count()
          ).isEqualTo(0);
  
          // Query both process-instance and activity-instance
          assertThat(
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .activityInstanceId(null)
                  .processInstanceId(processInstance.getId())
                  .count()
          ).isEqualTo(2);
  
          // end process instance
          List<Task> tasks = taskService.createTaskQuery().list();
          assertThat(tasks.size()).isEqualTo(1);
          taskService.complete(tasks.get(0).getId());
          assertProcessEnded(processInstance.getId());
  
          assertThat(historyService.createHistoricVariableInstanceQuery().count()).isEqualTo(2);
  
          // Query on process-instance
          assertThat(
              historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(2);
          assertThat(
              historyService.createHistoricVariableInstanceQuery().processInstanceId("unexisting").count()
          ).isEqualTo(0);
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricVariableQueryExcludeTaskRelatedDetails() throws Exception {
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("stringVar", "activiti rocks!");
          variables.put("longVar", 12345L);
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess", variables);
  
          // Set a local task-variable
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task).isNotNull();
          taskService.setVariableLocal(task.getId(), "taskVar", "It is I, le Variable");
  
          // Query on process-instance
          assertEquals(
              3,
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .count()
          );
  
          // Query on process-instance, excluding task-details
          assertEquals(
              2,
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .excludeTaskDetails()
                  .count()
          );
  
          // Check task-id precedence on excluding task-details
          assertEquals(
              1,
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .excludeTaskDetails()
                  .taskId(task.getId())
                  .count()
          );
      }
  
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testHistoricVariableQuerySorting() throws Exception {
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("stringVar", "activiti rocks!");
          variables.put("longVar", 12345L);
  
          runtimeService.startProcessInstanceByKey("oneTaskProcess", variables);
  
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByProcessInstanceId().asc().count()
          );
          assertEquals(2, historyService.createHistoricDetailQuery().variableUpdates().orderByTime().asc().count());
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableName().asc().count()
          );
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableRevision().asc().count()
          );
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableType().asc().count()
          );
  
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByProcessInstanceId().desc().count()
          );
          assertEquals(2, historyService.createHistoricDetailQuery().variableUpdates().orderByTime().desc().count());
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableName().desc().count()
          );
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableRevision().desc().count()
          );
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableType().desc().count()
          );
  
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByProcessInstanceId().asc().list().size()
          );
          assertEquals(2, historyService.createHistoricDetailQuery().variableUpdates().orderByTime().asc().list().size());
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableName().asc().list().size()
          );
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableRevision().asc().list().size()
          );
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableType().asc().list().size()
          );
  
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByProcessInstanceId().desc().list().size()
          );
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByTime().desc().list().size()
          );
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableName().desc().list().size()
          );
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableRevision().desc().list().size()
          );
          assertEquals(
              2,
              historyService.createHistoricDetailQuery().variableUpdates().orderByVariableType().desc().list().size()
          );
      }
  
      public void testHistoricDetailQueryInvalidSorting() throws Exception {
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class).isThrownBy(() ->
              historyService.createHistoricDetailQuery().asc().list()
          );
  
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class).isThrownBy(() ->
              historyService.createHistoricDetailQuery().desc().list()
          );
  
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class).isThrownBy(() ->
              historyService.createHistoricDetailQuery().orderByProcessInstanceId().list()
          );
  
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class).isThrownBy(() ->
              historyService.createHistoricDetailQuery().orderByTime().list()
          );
  
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class).isThrownBy(() ->
              historyService.createHistoricDetailQuery().orderByVariableName().list()
          );
  
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class).isThrownBy(() ->
              historyService.createHistoricDetailQuery().orderByVariableRevision().list()
          );
  
          assertThatExceptionOfType(ActivitiIllegalArgumentException.class).isThrownBy(() ->
              historyService.createHistoricDetailQuery().orderByVariableType().list()
          );
      }
  
      @Deployment
      public void testHistoricTaskInstanceVariableUpdates() {
          String processInstanceId = runtimeService.startProcessInstanceByKey("HistoricTaskInstanceTest").getId();
  
          String taskId = taskService.createTaskQuery().singleResult().getId();
  
          runtimeService.setVariable(processInstanceId, "deadline", "yesterday");
  
          taskService.setVariableLocal(taskId, "bucket", "23c");
          taskService.setVariableLocal(taskId, "mop", "37i");
  
          taskService.complete(taskId);
  
          assertEquals(1, historyService.createHistoricTaskInstanceQuery().count());
  
          List<HistoricDetail> historicTaskVariableUpdates = historyService
              .createHistoricDetailQuery()
              .taskId(taskId)
              .variableUpdates()
              .orderByVariableName()
              .asc()
              .list();
  
          assertEquals(2, historicTaskVariableUpdates.size());
  
          historyService.deleteHistoricTaskInstance(taskId);
  
          // Check if the variable updates have been removed as well
          historicTaskVariableUpdates = historyService
              .createHistoricDetailQuery()
              .taskId(taskId)
              .variableUpdates()
              .orderByVariableName()
              .asc()
              .list();
  
          assertThat(historicTaskVariableUpdates.size()).isEqualTo(0);
      }
  
      // ACT-592
      @Deployment
      public void testSetVariableOnProcessInstanceWithTimer() {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("timerVariablesProcess");
          runtimeService.setVariable(processInstance.getId(), "myVar", 123456L);
          assertThat(runtimeService.getVariable(processInstance.getId(), "myVar")).isEqualTo(123456L);
      }
  
      @Deployment
      public void testDeleteHistoricProcessInstance() {
          // Start process-instance with some variables set
          Map<String, Object> vars = new HashMap<String, Object>();
          vars.put("processVar", 123L);
          vars.put("anotherProcessVar", new DummySerializable());
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("HistoricTaskInstanceTest", vars);
          assertThat(processInstance).isNotNull();
  
          // Set 2 task properties
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          taskService.setVariableLocal(task.getId(), "taskVar", 45678);
          taskService.setVariableLocal(task.getId(), "anotherTaskVar", "value");
  
          // Finish the task, this end the process-instance
          taskService.complete(task.getId());
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(3);
          assertThat(
              historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(4);
          assertThat(
              historyService.createHistoricDetailQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(4);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(1);
  
          // Delete the historic process-instance
          historyService.deleteHistoricProcessInstance(processInstance.getId());
  
          // Verify no traces are left in the history tables
          assertThat(
              historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricDetailQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstance.getId()).count()
          ).isEqualTo(0);
  
          assertThatExceptionOfType(ActivitiException.class)
              .as("Exception expected when deleting process-instance that is still running")
              .isThrownBy(() -> historyService.deleteHistoricProcessInstance("unexisting"))
              .withMessageContaining("No historic process instance found with id: unexisting");
      }
  
      @Deployment
      public void testDeleteRunningHistoricProcessInstance() {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("HistoricTaskInstanceTest");
          assertThat(processInstance).isNotNull();
  
          assertThatExceptionOfType(ActivitiException.class)
              .as("Exception expected when deleting process-instance that is still running")
              .isThrownBy(() -> historyService.deleteHistoricProcessInstance(processInstance.getId()))
              .withMessageContaining("Process instance is still running, cannot delete historic process instance");
      }
  
      @Deployment
      public void testHistoricTaskInstanceQueryTaskVariableValueEquals() throws Exception {
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("HistoricTaskInstanceTest");
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
  
          // Set some variables on the task
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("longVar", 12345L);
          variables.put("shortVar", (short) 123);
          variables.put("integerVar", 1234);
          variables.put("stringVar", "stringValue");
          variables.put("booleanVar", true);
          Date date = Calendar.getInstance().getTime();
          variables.put("dateVar", date);
          variables.put("nullVar", null);
  
          taskService.setVariablesLocal(task.getId(), variables);
  
          // Validate all variable-updates are present in DB
          assertThat(historyService.createHistoricDetailQuery().variableUpdates().taskId(task.getId()).count()).isEqualTo(
              7
          );
  
          // Query Historic task instances based on variable
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("longVar", 12345L).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("shortVar", (short) 123).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("integerVar", 1234).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("stringVar", "stringValue").count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("booleanVar", true).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("dateVar", date).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("nullVar", null).count()
          ).isEqualTo(1);
  
          // Update the variables
          variables.put("longVar", 67890L);
          variables.put("shortVar", (short) 456);
          variables.put("integerVar", 5678);
          variables.put("stringVar", "updatedStringValue");
          variables.put("booleanVar", false);
          Calendar otherCal = Calendar.getInstance();
          otherCal.add(Calendar.DAY_OF_MONTH, 1);
          Date otherDate = otherCal.getTime();
          variables.put("dateVar", otherDate);
          variables.put("nullVar", null);
  
          taskService.setVariablesLocal(task.getId(), variables);
  
          // Validate all variable-updates are present in DB
          assertThat(historyService.createHistoricDetailQuery().variableUpdates().taskId(task.getId()).count()).isEqualTo(
              14
          );
  
          // Previous values should NOT match
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("longVar", 12345L).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("shortVar", (short) 123).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("integerVar", 1234).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("stringVar", "stringValue").count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("booleanVar", true).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("dateVar", date).count()
          ).isEqualTo(0);
  
          // New values should match
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("longVar", 67890L).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("shortVar", (short) 456).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("integerVar", 5678).count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricTaskInstanceQuery()
                  .taskVariableValueEquals("stringVar", "updatedStringValue")
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("booleanVar", false).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("dateVar", otherDate).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().taskVariableValueEquals("nullVar", null).count()
          ).isEqualTo(1);
      }
  
      @Deployment
      public void testHistoricTaskInstanceQueryProcessVariableValueEquals() throws Exception {
          // Set some variables on the process instance
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("longVar", 12345L);
          variables.put("shortVar", (short) 123);
          variables.put("integerVar", 1234);
          variables.put("stringVar", "stringValue");
          variables.put("booleanVar", true);
          Date date = Calendar.getInstance().getTime();
          variables.put("dateVar", date);
          variables.put("nullVar", null);
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
              "HistoricTaskInstanceTest",
              variables
          );
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
  
          // Validate all variable-updates are present in DB
          assertThat(
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .count()
          ).isEqualTo(7);
  
          // Query Historic task instances based on process variable
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("longVar", 12345L).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("shortVar", (short) 123).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("integerVar", 1234).count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricTaskInstanceQuery()
                  .processVariableValueEquals("stringVar", "stringValue")
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("booleanVar", true).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("dateVar", date).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("nullVar", null).count()
          ).isEqualTo(1);
  
          // Update the variables
          variables.put("longVar", 67890L);
          variables.put("shortVar", (short) 456);
          variables.put("integerVar", 5678);
          variables.put("stringVar", "updatedStringValue");
          variables.put("booleanVar", false);
          Calendar otherCal = Calendar.getInstance();
          otherCal.add(Calendar.DAY_OF_MONTH, 1);
          Date otherDate = otherCal.getTime();
          variables.put("dateVar", otherDate);
          variables.put("nullVar", null);
  
          runtimeService.setVariables(processInstance.getId(), variables);
  
          // Validate all variable-updates are present in DB
          assertThat(
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .count()
          ).isEqualTo(14);
  
          // Previous values should NOT match
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("longVar", 12345L).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("shortVar", (short) 123).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("integerVar", 1234).count()
          ).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricTaskInstanceQuery()
                  .processVariableValueEquals("stringVar", "stringValue")
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("booleanVar", true).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("dateVar", date).count()
          ).isEqualTo(0);
  
          // New values should match
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("longVar", 67890L).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("shortVar", (short) 456).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("integerVar", 5678).count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricTaskInstanceQuery()
                  .processVariableValueEquals("stringVar", "updatedStringValue")
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("booleanVar", false).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("dateVar", otherDate).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("nullVar", null).count()
          ).isEqualTo(1);
  
          // Set a task-variables, shouldn't affect the process-variable matches
          taskService.setVariableLocal(task.getId(), "longVar", 9999L);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("longVar", 9999L).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricTaskInstanceQuery().processVariableValueEquals("longVar", 67890L).count()
          ).isEqualTo(1);
      }
  
      @Deployment
      public void testHistoricProcessInstanceVariableValueEquals() throws Exception {
          // Set some variables on the process instance
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("longVar", 12345L);
          variables.put("shortVar", (short) 123);
          variables.put("integerVar", 1234);
          variables.put("stringVar", "stringValue");
          variables.put("booleanVar", true);
          Date date = Calendar.getInstance().getTime();
          variables.put("dateVar", date);
          variables.put("nullVar", null);
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
              "HistoricProcessInstanceTest",
              variables
          );
  
          // Validate all variable-updates are present in DB
          assertThat(
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .count()
          ).isEqualTo(7);
  
          // Query Historic process instances based on process variable
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("longVar", 12345L).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("shortVar", (short) 123).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("integerVar", 1234).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("stringVar", "stringValue").count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("booleanVar", true).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("dateVar", date).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("nullVar", null).count()
          ).isEqualTo(1);
  
          // Update the variables
          variables.put("longVar", 67890L);
          variables.put("shortVar", (short) 456);
          variables.put("integerVar", 5678);
          variables.put("stringVar", "updatedStringValue");
          variables.put("booleanVar", false);
          Calendar otherCal = Calendar.getInstance();
          otherCal.add(Calendar.DAY_OF_MONTH, 1);
          Date otherDate = otherCal.getTime();
          variables.put("dateVar", otherDate);
          variables.put("nullVar", null);
  
          runtimeService.setVariables(processInstance.getId(), variables);
  
          // Validate all variable-updates are present in DB
          assertThat(
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .count()
          ).isEqualTo(14);
  
          // Previous values should NOT match
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("longVar", 12345L).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("shortVar", (short) 123).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("integerVar", 1234).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("stringVar", "stringValue").count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("booleanVar", true).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("dateVar", date).count()
          ).isEqualTo(0);
  
          // New values should match
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("longVar", 67890L).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("shortVar", (short) 456).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("integerVar", 5678).count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .variableValueEquals("stringVar", "updatedStringValue")
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("booleanVar", false).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("dateVar", otherDate).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueEquals("nullVar", null).count()
          ).isEqualTo(1);
      }
  
      @Deployment(
          resources = {
              "org/activiti/standalone/history/FullHistoryTest.testHistoricProcessInstanceVariableValueEquals.bpmn20.xml",
          }
      )
      public void testHistoricProcessInstanceVariableValueNotEquals() throws Exception {
          // Set some variables on the process instance
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("longVar", 12345L);
          variables.put("shortVar", (short) 123);
          variables.put("integerVar", 1234);
          variables.put("stringVar", "stringValue");
          variables.put("booleanVar", true);
          Date date = Calendar.getInstance().getTime();
          Calendar otherCal = Calendar.getInstance();
          otherCal.add(Calendar.DAY_OF_MONTH, 1);
          Date otherDate = otherCal.getTime();
          variables.put("dateVar", date);
          variables.put("nullVar", null);
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
              "HistoricProcessInstanceTest",
              variables
          );
  
          // Validate all variable-updates are present in DB
          assertThat(
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .count()
          ).isEqualTo(7);
  
          // Query Historic process instances based on process variable, shouldn't match
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("longVar", 12345L).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("shortVar", (short) 123).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("integerVar", 1234).count()
          ).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .variableValueNotEquals("stringVar", "stringValue")
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("booleanVar", true).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("dateVar", date).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("nullVar", null).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("nullVar", null).count()
          ).isEqualTo(0);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("longVar", 67890L).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("shortVar", (short) 456).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("integerVar", 5678).count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .variableValueNotEquals("stringVar", "updatedStringValue")
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("booleanVar", false).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("dateVar", otherDate).count()
          ).isEqualTo(1);
  
          // Update the variables
          variables.put("longVar", 67890L);
          variables.put("shortVar", (short) 456);
          variables.put("integerVar", 5678);
          variables.put("stringVar", "updatedStringValue");
          variables.put("booleanVar", false);
          variables.put("dateVar", otherDate);
          variables.put("nullVar", null);
  
          runtimeService.setVariables(processInstance.getId(), variables);
  
          // Validate all variable-updates are present in DB
          assertThat(
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .count()
          ).isEqualTo(14);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("longVar", 12345L).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("shortVar", (short) 123).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("integerVar", 1234).count()
          ).isEqualTo(1);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .variableValueNotEquals("stringVar", "stringValue")
                  .count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("booleanVar", true).count()
          ).isEqualTo(1);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("dateVar", date).count()
          ).isEqualTo(1);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("longVar", 67890L).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("shortVar", (short) 456).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("integerVar", 5678).count()
          ).isEqualTo(0);
          assertThat(
              historyService
                  .createHistoricProcessInstanceQuery()
                  .variableValueNotEquals("stringVar", "updatedStringValue")
                  .count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("booleanVar", false).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("dateVar", otherDate).count()
          ).isEqualTo(0);
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueNotEquals("nullVar", null).count()
          ).isEqualTo(0);
      }
  
      @Deployment(
          resources = {
              "org/activiti/standalone/history/FullHistoryTest.testHistoricProcessInstanceVariableValueEquals.bpmn20.xml",
          }
      )
      public void testHistoricProcessInstanceVariableValueLessThanAndGreaterThan() throws Exception {
          // Set some variables on the process instance
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("longVar", 12345L);
  
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
              "HistoricProcessInstanceTest",
              variables
          );
  
          // Validate all variable-updates are present in DB
          assertThat(
              historyService
                  .createHistoricDetailQuery()
                  .variableUpdates()
                  .processInstanceId(processInstance.getId())
                  .count()
          ).isEqualTo(1);
  
          assertThat(
              historyService.createHistoricProcessInstanceQuery().variableValueGreaterThan("longVar", 12345L).count()
          ).isEqualTo(0);
      }
  
      @Deployment(
          resources = {
              "org/activiti/standalone/history/FullHistoryTest.testVariableUpdatesAreLinkedToActivity.bpmn20.xml",
          }
      )
      public void testVariableUpdatesLinkedToActivity() throws Exception {
          ProcessInstance pi = runtimeService.startProcessInstanceByKey("ProcessWithSubProcess");
  
          Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("test", "1");
          taskService.complete(task.getId(), variables);
  
          // now we are in the subprocess
          task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
          variables.clear();
          variables.put("test", "2");
          taskService.complete(task.getId(), variables);
  
          // now we are ended
          assertProcessEnded(pi.getId());
  
          // check history
          List<HistoricDetail> updates = historyService.createHistoricDetailQuery().variableUpdates().list();
          assertThat(updates.size()).isEqualTo(2);
  
          Map<String, HistoricVariableUpdate> updatesMap = new HashMap<String, HistoricVariableUpdate>();
          HistoricVariableUpdate update = (HistoricVariableUpdate) updates.get(0);
          updatesMap.put((String) update.getValue(), update);
          update = (HistoricVariableUpdate) updates.get(1);
          updatesMap.put((String) update.getValue(), update);
  
          HistoricVariableUpdate update1 = updatesMap.get("1");
          HistoricVariableUpdate update2 = updatesMap.get("2");
  
          assertThat(update1.getActivityInstanceId()).isNotNull();
          assertThat(update1.getExecutionId()).isNotNull();
          HistoricActivityInstance historicActivityInstance1 = historyService
              .createHistoricActivityInstanceQuery()
              .activityInstanceId(update1.getActivityInstanceId())
              .singleResult();
          assertThat(historicActivityInstance1.getActivityId()).isEqualTo("usertask1");
  
          assertThat(update2.getActivityInstanceId()).isNotNull();
          HistoricActivityInstance historicActivityInstance2 = historyService
              .createHistoricActivityInstanceQuery()
              .activityInstanceId(update2.getActivityInstanceId())
              .singleResult();
          assertThat(historicActivityInstance2.getActivityId()).isEqualTo("usertask2");
  
          /*
           * This is OK! The variable is set on the root execution, on a execution never run through the activity, where the process instances stands when calling the set Variable. But the ActivityId of
           * this flow node is used. So the execution id's doesn't have to be equal.
           *
           * execution id: On which execution it was set activity id: in which activity was the process instance when setting the variable
           */
          assertThat(historicActivityInstance2.getExecutionId().equals(update2.getExecutionId())).isFalse();
      }
  
      @Deployment(resources = { "org/activiti/standalone/jpa/JPAVariableTest.testQueryJPAVariable.bpmn20.xml" })
      public void testReadJpaVariableValueFromHistoricVariableUpdate() {
          EntityManagerSessionFactory entityManagerSessionFactory =
              (EntityManagerSessionFactory) processEngineConfiguration
                  .getSessionFactories()
                  .get(EntityManagerSession.class);
  
          EntityManagerFactory entityManagerFactory = entityManagerSessionFactory.getEntityManagerFactory();
  
          String executionId = runtimeService.startProcessInstanceByKey("JPAVariableProcess").getProcessInstanceId();
          String variableName = "name";
  
          FieldAccessJPAEntity entity = new FieldAccessJPAEntity();
          entity.setId(1L);
          entity.setValue("Test");
  
          EntityManager manager = entityManagerFactory.createEntityManager();
          manager.getTransaction().begin();
          manager.persist(entity);
          manager.flush();
          manager.getTransaction().commit();
          manager.close();
  
          Task task = taskService.createTaskQuery().processInstanceId(executionId).taskName("my task").singleResult();
  
          runtimeService.setVariable(executionId, variableName, entity);
          taskService.complete(task.getId());
  
          List<HistoricDetail> variableUpdates = historyService
              .createHistoricDetailQuery()
              .processInstanceId(executionId)
              .variableUpdates()
              .list();
  
          assertThat(variableUpdates.size()).isEqualTo(1);
          HistoricVariableUpdate update = (HistoricVariableUpdate) variableUpdates.get(0);
          assertThat(update.getValue()).isNotNull();
          assertThat(update.getValue()).isInstanceOf(FieldAccessJPAEntity.class);
  
          assertThat(((FieldAccessJPAEntity) update.getValue()).getId()).isEqualTo(entity.getId());
      }
  
      /**
       * Test confirming fix for ACT-1731
       */
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testQueryHistoricTaskIncludeBinaryVariable() throws Exception {
          // Start process with a binary variable
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
              "oneTaskProcess",
              singletonMap("binaryVariable", (Object) "It is I, le binary".getBytes())
          );
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task).isNotNull();
          taskService.setVariableLocal(task.getId(), "binaryTaskVariable", (Object) "It is I, le binary".getBytes());
  
          // Complete task
          taskService.complete(task.getId());
  
          // Query task, including processVariables
          HistoricTaskInstance historicTask = historyService
              .createHistoricTaskInstanceQuery()
              .taskId(task.getId())
              .includeProcessVariables()
              .singleResult();
          assertThat(historicTask).isNotNull();
          assertThat(historicTask.getProcessVariables()).isNotNull();
          byte[] bytes = (byte[]) historicTask.getProcessVariables().get("binaryVariable");
          assertThat(new String(bytes)).isEqualTo("It is I, le binary");
  
          // Query task, including taskVariables
          historicTask = historyService
              .createHistoricTaskInstanceQuery()
              .taskId(task.getId())
              .includeTaskLocalVariables()
              .singleResult();
          assertThat(historicTask).isNotNull();
          assertThat(historicTask.getTaskLocalVariables()).isNotNull();
          bytes = (byte[]) historicTask.getTaskLocalVariables().get("binaryTaskVariable");
          assertThat(new String(bytes)).isEqualTo("It is I, le binary");
      }
  
      /**
       * Test confirming fix for ACT-1731
       */
      @Deployment(resources = { "org/activiti/engine/test/history/oneTaskProcess.bpmn20.xml" })
      public void testQueryHistoricProcessInstanceIncludeBinaryVariable() throws Exception {
          // Start process with a binary variable
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
              "oneTaskProcess",
              singletonMap("binaryVariable", "It is I, le binary".getBytes())
          );
          Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
          assertThat(task).isNotNull();
  
          // Complete task to end process
          taskService.complete(task.getId());
  
          // Query task, including processVariables
          HistoricProcessInstance historicProcess = historyService
              .createHistoricProcessInstanceQuery()
              .processInstanceId(processInstance.getId())
              .includeProcessVariables()
              .singleResult();
          assertThat(historicProcess).isNotNull();
          assertThat(historicProcess.getProcessVariables()).isNotNull();
          byte[] bytes = (byte[]) historicProcess.getProcessVariables().get("binaryVariable");
          assertThat(new String(bytes)).isEqualTo("It is I, le binary");
      }
  
      // Test for https://activiti.atlassian.net/browse/ACT-2186
      @Deployment(resources = { "org/activiti/engine/test/api/oneTaskProcess.bpmn20.xml" })
      public void testHistoricVariableRemovedWhenRuntimeVariableIsRemoved() throws InterruptedException {
          Map<String, Object> vars = new HashMap<String, Object>();
          vars.put("var1", "Hello");
          vars.put("var2", "World");
          vars.put("var3", "!");
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess", vars);
  
          // Verify runtime
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(3);
          assertThat(runtimeService.getVariables(processInstance.getId(), asList("var1", "var2", "var3"))).hasSize(3);
          assertThat(runtimeService.getVariable(processInstance.getId(), "var2")).isNotNull();
  
          // Verify history
          assertThat(historyService.createHistoricVariableInstanceQuery().list()).hasSize(3);
          assertThat(
              historyService
                  .createHistoricVariableInstanceQuery()
                  .processInstanceId(processInstance.getId())
                  .variableName("var2")
                  .singleResult()
          ).isNotNull();
  
          // Verify historic details
          List<HistoricDetail> details = historyService
              .createHistoricDetailQuery()
              .processInstanceId(processInstance.getId())
              .variableUpdates()
              .orderByTime()
              .asc()
              .list();
          assertThat(details).hasSize(3); // 3 vars
          for (HistoricDetail historicDetail : details) {
              assertThat(((HistoricVariableUpdate) historicDetail).getValue()).isNotNull();
          }
  
          // Remove one variable
          Thread.sleep(800);
          runtimeService.removeVariable(processInstance.getId(), "var2");
  
          // Verify runtime
          assertThat(runtimeService.getVariables(processInstance.getId())).hasSize(2);
          assertThat(runtimeService.getVariables(processInstance.getId(), asList("var1", "var2", "var3"))).hasSize(2);
          assertThat(runtimeService.getVariable(processInstance.getId(), "var2")).isNull();
  
          // Verify history
          assertThat(historyService.createHistoricVariableInstanceQuery().list()).hasSize(2);
          assertThat(
              historyService
                  .createHistoricVariableInstanceQuery()
                  .processInstanceId(processInstance.getId())
                  .variableName("var2")
                  .singleResult()
          ).isNull();
  
          // Verify historic details
          details = historyService
              .createHistoricDetailQuery()
              .processInstanceId(processInstance.getId())
              .variableUpdates()
              .orderByTime()
              .asc()
              .list();
          assertThat(details).hasSize(4); // 3 vars + 1 delete
  
          // The last entry should be the delete
          for (int i = 0; i < details.size(); i++) {
              if (i != 3) {
                  assertThat(((HistoricVariableUpdate) details.get(i)).getValue()).isNotNull();
              } else if (i == 3) {
                  assertThat(((HistoricVariableUpdate) details.get(i)).getValue()).isNull();
              }
          }
      }
  }
  