content: /*
   * This library is part of OpenCms -
   * the Open Source Content Management System
   *
   * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   * Lesser General Public License for more details.
   *
   * For further information about Alkacon Software GmbH & Co. KG, please see the
   * company website: https://www.alkacon.com
   *
   * For further information about OpenCms, please see the
   * project website: https://www.opencms.org
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  package org.opencms.file;
  
  import org.opencms.db.CmsResourceState;
  import org.opencms.file.history.CmsHistoryFile;
  import org.opencms.file.history.I_CmsHistoryResource;
  import org.opencms.file.types.CmsResourceTypeFolder;
  import org.opencms.file.types.CmsResourceTypePlain;
  import org.opencms.main.OpenCms;
  import org.opencms.publish.CmsPublishManager;
  import org.opencms.relations.CmsRelation;
  import org.opencms.relations.CmsRelationFilter;
  import org.opencms.relations.CmsRelationType;
  import org.opencms.report.CmsShellReport;
  import org.opencms.test.OpenCmsTestCase;
  import org.opencms.test.OpenCmsTestProperties;
  import org.opencms.test.OpenCmsTestResourceConfigurableFilter;
  import org.opencms.util.CmsUUID;
  
  import java.io.UnsupportedEncodingException;
  import java.util.List;
  import java.util.Locale;
  
  import junit.extensions.TestSetup;
  import junit.framework.Test;
  import junit.framework.TestSuite;
  
  /**
   * Unit tests for history operation.<p>
   *
   * @since 6.9.1
   */
  public class TestHistory extends OpenCmsTestCase {
  
      /**
       * Default JUnit constructor.<p>
       *
       * @param arg0 JUnit parameters
       */
      public TestHistory(String arg0) {
  
          super(arg0);
      }
  
      /**
       * Test suite for this test class.<p>
       *
       * @return the test suite
       */
      public static Test suite() {
  
          OpenCmsTestProperties.initialize(org.opencms.test.AllTests.TEST_PROPERTIES_PATH);
  
          TestSuite suite = new TestSuite();
          suite.setName(TestHistory.class.getName());
  
          suite.addTest(new TestHistory("testFileRestore"));
          suite.addTest(new TestHistory("testReadDeleted"));
          suite.addTest(new TestHistory("testDeleteDate"));
          suite.addTest(new TestHistory("testFileRestoreIteration"));
          suite.addTest(new TestHistory("testSiblingsRestoration"));
          suite.addTest(new TestHistory("testSiblingsEdition"));
          suite.addTest(new TestHistory("testSiblingsV7HistoryIssue2"));
          suite.addTest(new TestHistory("testSiblingVersions"));
          suite.addTest(new TestHistory("testSiblingRestoreIteration"));
          suite.addTest(new TestHistory("testCreateAndDeleteFile"));
          suite.addTest(new TestHistory("testCreateAndDeleteFolder"));
          suite.addTest(new TestHistory("testMoveFile"));
          suite.addTest(new TestHistory("testPathHistory"));
          suite.addTest(new TestHistory("testFileHistory"));
          suite.addTest(new TestHistory("testFileHistoryFileWithSibling"));
          suite.addTest(new TestHistory("testFileVersions"));
          suite.addTest(new TestHistory("testVersioningLimit"));
          suite.addTest(new TestHistory("testSiblingsV7HistoryIssue"));
          suite.addTest(new TestHistory("testHistoryRemoveDeleted"));
          suite.addTest(new TestHistory("testRestoreFileInRenamedFolder"));
  
          TestSetup wrapper = new TestSetup(suite) {
  
              @Override
              protected void setUp() {
  
                  setupOpenCms("simpletest", "/");
              }
  
              @Override
              protected void tearDown() {
  
                  removeOpenCms();
              }
          };
  
          return wrapper;
      }
  
      /**
       * Creates and deletes a file n-times and tests if the historical data
       * are correct and if the content can be properly restored.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testCreateAndDeleteFile() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing history creating, modifying and deleting a file");
  
          String filename = "/dummy1.txt";
          int counter = 2;
  
          // create a plain text file
          CmsResource res = cms.createResource(
              filename,
              CmsResourceTypePlain.getStaticTypeId(),
              "content version 0".getBytes(),
              null);
          cms.unlockResource(filename);
          OpenCms.getPublishManager().publishResource(cms, filename);
          OpenCms.getPublishManager().waitWhileRunning();
  
          for (int i = 1; i <= counter; i++) {
              // create a plain text file
              String contentStr = "content version " + i;
              CmsFile file = cms.readFile(filename);
              file.setContents(contentStr.getBytes());
              cms.lockResource(filename);
              cms.writeFile(file);
              OpenCms.getPublishManager().publishResource(cms, filename);
              OpenCms.getPublishManager().waitWhileRunning();
          }
          CmsFile file = cms.readFile(filename);
          file.setContents(("content version " + (counter + 1)).getBytes());
          cms.lockResource(filename);
          cms.writeFile(file);
          // delete the file
          cms.deleteResource(filename, CmsResource.DELETE_PRESERVE_SIBLINGS);
          cms.unlockResource(filename);
  
          OpenCms.getPublishManager().publishResource(cms, filename);
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.createResource(filename, CmsResourceTypePlain.getStaticTypeId(), null, null);
          // the history works only by ID so the expected number of versions is ZERO
          List historyResources = cms.readAllAvailableVersions(filename);
          assertTrue(historyResources.isEmpty());
  
          // re-create the resource to be able to read all versions from the history
          // assert that we have the expected number of versions in the history
          String importFile = "import.txt";
          cms.importResource(importFile, res, "blah-blah".getBytes(), null);
          historyResources = cms.readAllAvailableVersions(importFile);
          assertEquals(counter + 2, historyResources.size()); // counter + created + deleted
  
          for (int i = 0; i < (counter + 2); i++) {
              // the list of historical resources contains at index 0 the
              // resource with the highest version and tag ID
              int version = (counter + 2) - i;
              String contentStr = "content version " + (version - 1);
  
              // assert that the historical resource has the correct version
              I_CmsHistoryResource historyResource = (I_CmsHistoryResource)historyResources.get(i);
              assertEquals(version, historyResource.getVersion());
  
              cms.restoreResourceVersion(historyResource.getStructureId(), historyResource.getVersion());
              file = cms.readFile(importFile);
  
              // assert that the content and version fit together
              String restoredContent = getContentString(cms, file.getContents());
              assertEquals(contentStr, restoredContent);
          }
      }
  
      /**
       * Creates and deletes a folder n-times and tests if the historical data
       * are correct and if the properties can be properly restored.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testCreateAndDeleteFolder() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing history creating, modifying and deleting a folder");
  
          String folderName = "/dummy1/";
          int counter = 2;
  
          // create a folder
          CmsResource res = cms.createResource(folderName, CmsResourceTypeFolder.getStaticTypeId(), null, null);
          cms.writePropertyObject(
              folderName,
              new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "title version \u00E4\u00F6\u00DF\u20AC 0", null));
          cms.unlockResource(folderName);
          OpenCms.getPublishManager().publishResource(cms, folderName);
          OpenCms.getPublishManager().waitWhileRunning();
          // check
          assertHistory(cms, folderName, 1);
  
          for (int i = 1; i <= counter; i++) {
              cms.lockResource(folderName);
              cms.writePropertyObject(
                  folderName,
                  new CmsProperty(
                      CmsPropertyDefinition.PROPERTY_TITLE,
                      "title version \u00E4\u00F6\u00DF\u20AC " + i,
                      null));
              // check
              assertHistory(cms, folderName, 1 + i);
              OpenCms.getPublishManager().publishResource(cms, folderName);
              OpenCms.getPublishManager().waitWhileRunning();
              // check
              assertHistory(cms, folderName, 1 + i);
          }
          cms.lockResource(folderName);
          cms.writePropertyObject(
              folderName,
              new CmsProperty(
                  CmsPropertyDefinition.PROPERTY_TITLE,
                  "title version \u00E4\u00F6\u00DF\u20AC " + (counter + 1),
                  null));
          // delete the folder
          cms.deleteResource(folderName, CmsResource.DELETE_PRESERVE_SIBLINGS);
          cms.unlockResource(folderName);
          OpenCms.getPublishManager().publishResource(cms, folderName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the history works only by ID so the expected number of versions is ZERO
          cms.createResource(folderName, CmsResourceTypeFolder.getStaticTypeId(), null, null);
          List historyResources = cms.readAllAvailableVersions(folderName);
          assertTrue(historyResources.isEmpty());
  
          // re-create the folder to be able to read all versions from the history
          // assert that we have the expected number of versions in the history
          String importFolder = "import/";
          cms.importResource(importFolder, res, null, null);
          historyResources = cms.readAllAvailableVersions(importFolder);
          assertEquals(counter + 2, historyResources.size()); // counter + created + deleted
  
          for (int i = 0; i < (counter + 2); i++) {
              // the list of historical resources contains at index 0 the
              // folder with the highest version and tag ID
              int version = (counter + 2) - i;
              String title = "title version \u00E4\u00F6\u00DF\u20AC " + (version - 1);
  
              // assert that the historical resource has the correct version
              I_CmsHistoryResource historyResource = (I_CmsHistoryResource)historyResources.get(i);
              assertEquals(version, historyResource.getVersion());
  
              cms.restoreResourceVersion(historyResource.getStructureId(), historyResource.getVersion());
              CmsProperty property = cms.readPropertyObject(
                  cms.readResource(importFolder),
                  CmsPropertyDefinition.PROPERTY_TITLE,
                  false);
  
              // assert that the title and version fit together
              assertEquals(title, property.getStructureValue());
          }
      }
  
      /**
       * Tests the delete date of deleted resources.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testDeleteDate() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing the delete date of deleted resources");
  
          String folderName = "/folderReadDeletedDate";
          String fileName = "/test.txt";
  
          cms.createResource(folderName, CmsResourceTypeFolder.getStaticTypeId());
          cms.createResource(folderName + fileName, CmsResourceTypePlain.getStaticTypeId(), "hallo".getBytes(), null);
          OpenCms.getPublishManager().publishResource(cms, folderName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          long time = cms.readResource(folderName + fileName).getDateLastModified();
          cms.lockResource(folderName + fileName);
          cms.deleteResource(folderName + fileName, CmsResource.DELETE_REMOVE_SIBLINGS);
          echo("time: " + time);
          echo("dlm: " + cms.readResource(folderName + fileName, CmsResourceFilter.ALL).getDateLastModified());
  
          OpenCms.getPublishManager().publishResource(cms, folderName + fileName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // get the deleted resources in the new folder
          List deleted = cms.readDeletedResources(folderName, false);
          assertEquals(1, deleted.size());
          echo("dlm2: " + ((I_CmsHistoryResource)deleted.get(0)).getDateLastModified());
          if (((I_CmsHistoryResource)deleted.get(0)).getDateLastModified() < time) {
              fail("the date last modified after deletion is wrong.");
          }
      }
  
      /**
       * Creates a file, modifies and publishes it n-times, create a sibling,
       * publishes both and compares the histories.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testFileHistory() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing history with one sibling");
  
          String filename = "/testFileHistory1.txt";
          String siblingname = "/testFileHistory2.txt";
          int counter = 2;
  
          // create a plain text file
          String contentStr = "content version " + 0;
          cms.createResource(filename, CmsResourceTypePlain.getStaticTypeId(), contentStr.getBytes(), null);
          OpenCms.getPublishManager().publishResource(cms, filename);
          OpenCms.getPublishManager().waitWhileRunning();
  
          for (int i = 1; i <= counter; i++) {
              // modify the plain text file
              contentStr = "content version " + i;
              CmsFile file = cms.readFile(filename);
              file.setContents(contentStr.getBytes());
              cms.lockResource(filename);
              cms.writeFile(file);
              cms.unlockResource(filename);
              OpenCms.getPublishManager().publishResource(cms, filename);
              OpenCms.getPublishManager().waitWhileRunning();
          }
  
          // create a sibling
          cms.copyResource(filename, siblingname, CmsResource.COPY_AS_SIBLING);
          cms.unlockResource(siblingname);
          OpenCms.getPublishManager().publishResource(cms, siblingname);
          OpenCms.getPublishManager().waitWhileRunning();
  
          for (int i = 1; i <= counter; i++) {
              // modify the sibling text file
              contentStr = "sibling content version " + (counter + i);
              CmsFile file = cms.readFile(siblingname);
              file.setContents(contentStr.getBytes());
              cms.lockResource(siblingname);
              cms.writeFile(file);
              cms.unlockResource(siblingname);
              OpenCms.getPublishManager().publishResource(cms, siblingname);
              OpenCms.getPublishManager().waitWhileRunning();
          }
  
          List historyResourcesForFile = cms.readAllAvailableVersions(filename);
          List historyResourcesForSibling = cms.readAllAvailableVersions(siblingname);
  
          // 1 creation
          // counter modifications of original file
          // 0 sibling creation, does not affects the history for the original file
          // counter modifications of sibling
          assertEquals(1 + counter + 0 + counter, historyResourcesForFile.size());
          // 1 creation
          // counter modifications of original file
          // 1 sibling creation
          // counter modifications of sibling
          assertEquals(1 + counter + 1 + counter, historyResourcesForSibling.size());
      }
  
      /**
       * creates a file, modifies and publishes it n-times, create 2 siblings,
       * delete file and sibling N2, delete some versions from history, restore
       * file and sibling from history and compare contents.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testFileHistoryFileWithSibling() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing history with several siblings");
  
          String filename = "/testFileRoot.txt";
          String siblingname = "/testFileSibling1.txt";
          String siblingname2 = "/testFileSibling2.txt";
  
          int counter = 3;
          int counterSibl = 4;
          int counterSibl2 = 5;
  
          // create a plain text file
          String contentStr = "content version " + 0;
          cms.createResource(filename, CmsResourceTypePlain.getStaticTypeId(), contentStr.getBytes(), null);
  
          for (int i = 1; i <= counter; i++) {
              // modify the plain text file
              contentStr = "content version " + i;
              CmsFile file = cms.readFile(filename);
              file.setContents(contentStr.getBytes());
              cms.lockResource(filename);
              cms.writeFile(file);
              CmsProperty property = new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "RootTitle" + i, null);
              cms.writePropertyObject(filename, property);
              cms.unlockResource(filename);
              OpenCms.getPublishManager().publishResource(cms, filename);
              OpenCms.getPublishManager().waitWhileRunning();
          }
  
          // create a sibling
          cms.copyResource(filename, siblingname, CmsResource.COPY_AS_SIBLING);
          cms.unlockResource(siblingname);
          OpenCms.getPublishManager().publishResource(cms, siblingname);
          OpenCms.getPublishManager().waitWhileRunning();
  
          for (int i = 1; i <= counterSibl; i++) {
              // modify the sibling text file
              contentStr = "sibling content version " + (counter + i);
              CmsFile file = cms.readFile(siblingname);
              file.setContents(contentStr.getBytes());
              cms.lockResource(siblingname);
              cms.writeFile(file);
              CmsProperty property = new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "SiblingTitle" + i, null);
              cms.writePropertyObject(siblingname, property);
              cms.unlockResource(siblingname);
              OpenCms.getPublishManager().publishResource(cms, siblingname);
              OpenCms.getPublishManager().waitWhileRunning();
          }
  
          // create a sibling2
          cms.copyResource(filename, siblingname2, CmsResource.COPY_AS_SIBLING);
          cms.unlockResource(siblingname2);
          OpenCms.getPublishManager().publishResource(cms, siblingname2);
          OpenCms.getPublishManager().waitWhileRunning();
  
          for (int i = 1; i <= counterSibl2; i++) {
              // modify the sibling text file
              contentStr = "sibling2 content version " + (counter + counterSibl2 + i);
              CmsFile file = cms.readFile(siblingname2);
              file.setContents(contentStr.getBytes());
              cms.lockResource(siblingname2);
              cms.writeFile(file);
              CmsProperty property = new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "Sibling2Title" + i, null);
              cms.writePropertyObject(siblingname2, property);
              cms.unlockResource(siblingname2);
              OpenCms.getPublishManager().publishResource(cms, siblingname2);
              OpenCms.getPublishManager().waitWhileRunning();
          }
  
          // now delete and publish the root
          cms.lockResource(filename);
          cms.deleteResource(filename, CmsResource.DELETE_PRESERVE_SIBLINGS);
          cms.unlockResource(filename);
          OpenCms.getPublishManager().publishResource(cms, filename);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // now delete and publish second sibling
          cms.lockResource(siblingname2);
          cms.deleteResource(siblingname2, CmsResource.DELETE_PRESERVE_SIBLINGS);
          cms.unlockResource(siblingname2);
          OpenCms.getPublishManager().publishResource(cms, siblingname2);
          OpenCms.getPublishManager().waitWhileRunning();
  
          List allFiles = cms.readAllAvailableVersions(siblingname);
          // 0 creation is not published
          // 'counter' modifications to the original file
          // '1' first sibling creation
          // 'counterSibl' first sibling modifications
          // '0' second sibling creation, affects just the given sibling
          // 'counterSibl2' second sibling modifications
          // '0' original file deletion, affects just the given sibling
          // '0' second sibling deletion, affects just the given sibling
          // -3 versions deleted due to history overflow, while deleting original file
          assertEquals((0 + counter + 1 + counterSibl + 0 + counterSibl2 + 0 + 0) - 3, allFiles.size());
  
          //Delete historical entries, keep only 3 latest versions.
          cms.deleteHistoricalVersions(3, 3, -1, new CmsShellReport(cms.getRequestContext().getLocale()));
  
          allFiles = cms.readAllAvailableVersions(siblingname);
          // 3 the number of versions that should remain for the sibling
          // 3 the number of versions that should remain for the second sibling
          // 2 additional remaining versions of the original file (one overlap)
          assertEquals(3 + counterSibl2, allFiles.size()); // it is not 3 since there are more versions coming from the siblings!
  
          I_CmsHistoryResource history = (I_CmsHistoryResource)allFiles.get(1);
          cms.lockResource(siblingname);
          //and restore it from history
          cms.restoreResourceVersion(history.getStructureId(), history.getVersion());
          cms.unlockResource(siblingname);
          CmsFile file = cms.readFile(siblingname);
  
          // assert that the content and version fit together
          String restoredContent = getContentString(cms, file.getContents());
  
          // the content is coming from a sibling2 modification
          assertEquals("sibling2 content version 12", restoredContent);
          CmsProperty prop = cms.readPropertyObject(siblingname, CmsPropertyDefinition.PROPERTY_TITLE, false);
          // the property is coming from the sibling's last set title
          assertEquals("SiblingTitle4", prop.getValue());
  
          // create a new empty resource
          cms.createResource(siblingname2, CmsResourceTypePlain.getStaticTypeId(), null, null);
  
          allFiles = cms.readAllAvailableVersions(siblingname2);
          assertTrue(allFiles.isEmpty());
      }
  
      /**
       * Test restoring a file also possible missing folders are restored.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testFileRestore() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing restoring a file also possible missing folders are restored");
  
          // initial check
          List deletedResources = cms.readDeletedResources("/", true);
          assertTrue(deletedResources.isEmpty());
  
          // create a new folder and resource
          CmsResource folder = cms.createResource("testFolder", CmsResourceTypeFolder.RESOURCE_TYPE_ID);
          // create a relation
          cms.lockResource("index.html");
          cms.addRelationToResource("index.html", "testFolder", CmsRelationType.CATEGORY.getName());
          // write props
          cms.writePropertyObject(
              "testFolder",
              new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "strFolder", "resFolder"));
          CmsResource res = cms.createResource(
              "testFolder/test.txt",
              CmsResourceTypePlain.getStaticTypeId(),
              "test".getBytes(),
              null);
          // create a relation
          cms.addRelationToResource("index.html", "testFolder/test.txt", CmsRelationType.CATEGORY.getName());
          // write props
          cms.writePropertyObject(
              "testFolder/test.txt",
              new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "strFile", "resFile"));
  
          storeResources(cms, "testFolder", true);
  
          // publish
          OpenCms.getPublishManager().publishResource(cms, "testFolder");
          OpenCms.getPublishManager().waitWhileRunning();
  
          // delete the folder and file
          cms.lockResource("testFolder");
          cms.deleteResource("testFolder/test.txt", CmsResource.DELETE_PRESERVE_SIBLINGS);
          cms.deleteResource("testFolder", CmsResource.DELETE_PRESERVE_SIBLINGS);
  
          // publish again
          OpenCms.getPublishManager().publishResource(cms, "testFolder");
          OpenCms.getPublishManager().waitWhileRunning();
  
          // be sure the files were deleted, this could fail with enabled cache
          assertFalse(cms.existsResource("testFolder", CmsResourceFilter.ALL));
          assertFalse(cms.existsResource("testFolder/test.txt", CmsResourceFilter.ALL));
  
          // be sure the files were deleted
          CmsProject offline = cms.getRequestContext().getCurrentProject();
          cms.getRequestContext().setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID));
          assertFalse(cms.existsResource("testFolder", CmsResourceFilter.ALL));
          assertFalse(cms.existsResource("testFolder/test.txt", CmsResourceFilter.ALL));
          cms.getRequestContext().setCurrentProject(offline);
  
          // check the deleted resources
          deletedResources = cms.readDeletedResources("/", false);
          assertEquals(1, deletedResources.size());
          assertEquals("/testFolder/", cms.getSitePath((CmsResource)deletedResources.get(0)));
  
          deletedResources = cms.readDeletedResources("/", true);
          assertEquals(2, deletedResources.size());
          assertEquals("/testFolder/", cms.getSitePath((CmsResource)deletedResources.get(0)));
          assertEquals("/testFolder/test.txt", cms.getSitePath((CmsResource)deletedResources.get(1)));
  
          // restore the deleted file
          cms.restoreDeletedResource(res.getStructureId());
  
          // check the deleted resources
          deletedResources = cms.readDeletedResources("/", true);
          assertTrue(deletedResources.isEmpty());
  
          // assert the resources
          OpenCmsTestResourceConfigurableFilter filter = new OpenCmsTestResourceConfigurableFilter();
          filter.enableDateCreatedSecTest();
          filter.disableDateContentTest();
          assertFilter(cms, "testFolder/", filter);
          assertFilter(cms, cms.getSitePath(res), filter);
  
          // assert the relations
          List relations = cms.getRelationsForResource("/testFolder", CmsRelationFilter.SOURCES);
          assertEquals(1, relations.size());
          assertRelation(
              new CmsRelation(cms.readResource("index.html"), folder, CmsRelationType.CATEGORY),
              (CmsRelation)relations.get(0));
          relations = cms.getRelationsForResource("/testFolder/test.txt", CmsRelationFilter.SOURCES);
          assertEquals(1, relations.size());
          assertRelation(
              new CmsRelation(cms.readResource("index.html"), res, CmsRelationType.CATEGORY),
              (CmsRelation)relations.get(0));
  
          // delete again
          cms.lockResource("testFolder");
          cms.deleteResource("testFolder/test.txt", CmsResource.DELETE_PRESERVE_SIBLINGS);
          cms.deleteResource("testFolder", CmsResource.DELETE_PRESERVE_SIBLINGS);
  
          // create new ones
          cms.createResource("testFolder", CmsResourceTypeFolder.RESOURCE_TYPE_ID);
          cms.createResource("testFolder/test.txt", CmsResourceTypePlain.getStaticTypeId(), "test".getBytes(), null);
  
          // check the deleted resources
          deletedResources = cms.readDeletedResources("/", false);
          assertEquals(1, deletedResources.size());
          assertEquals("/testFolder/", cms.getSitePath((CmsResource)deletedResources.get(0)));
  
          deletedResources = cms.readDeletedResources("/", true);
          assertEquals(2, deletedResources.size());
          assertEquals("/testFolder/", cms.getSitePath((CmsResource)deletedResources.get(0)));
          assertEquals("/testFolder/test.txt", cms.getSitePath((CmsResource)deletedResources.get(1)));
  
          // restore the deleted file
          cms.restoreDeletedResource(res.getStructureId());
  
          // assert again, checking for the new name!
          assertFalse(cms.readFolder("testFolder").getStructureId().equals(folder.getStructureId()));
          setMapping("/testFolder/test_1.txt", "/testFolder/test.txt");
          filter.disableNameTest();
          assertFilter(cms, "/testFolder/test_1.txt", filter);
  
          // restore the deleted folder
          cms.restoreDeletedResource(folder.getStructureId());
          setMapping("/testFolder_1/", "testFolder/");
          assertFilter(cms, "/testFolder_1/", filter);
      }
  
      /**
       * Test restoring and deleting a file several times.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testFileRestoreIteration() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing restoring and deleting a file several times");
  
          String resName = "fileRestoreIteration.txt";
  
          int vers = OpenCms.getSystemInfo().getHistoryVersions();
          int delVers = OpenCms.getSystemInfo().getHistoryVersionsAfterDeletion();
          try {
              OpenCms.getSystemInfo().setVersionHistorySettings(true, 10, 10);
  
              // create new resource
              CmsResource res = cms.createResource(resName, CmsResourceTypePlain.getStaticTypeId());
              // check offline
              assertHistory(cms, resName, 1);
  
              // publish
              OpenCms.getPublishManager().publishResource(cms, resName);
              OpenCms.getPublishManager().waitWhileRunning();
              // check after publish
              assertHistory(cms, resName, 1);
  
              for (int i = 0; i < 3; i++) {
                  cms.lockResource(resName);
                  cms.deleteResource(resName, CmsResource.DELETE_PRESERVE_SIBLINGS);
                  // check offline
                  assertHistory(cms, resName, 2 + (i * 2));
                  // publish
                  OpenCms.getPublishManager().publishResource(cms, resName);
                  OpenCms.getPublishManager().waitWhileRunning();
                  // restore
                  cms.restoreDeletedResource(res.getStructureId());
                  // check offline
                  assertHistoryForRestored(cms, resName, 3 + (i * 2));
                  // publish
                  OpenCms.getPublishManager().publishResource(cms, resName);
                  OpenCms.getPublishManager().waitWhileRunning();
                  // check after publish
                  assertHistory(cms, resName, 3 + (i * 2));
              }
          } finally {
              OpenCms.getSystemInfo().setVersionHistorySettings(true, vers, delVers);
          }
      }
  
      /**
       * Test the version numbers of a file after several modifications.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testFileVersions() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing the version numbers of a file after several modifications");
  
          String resName = "fileVersionTest.txt";
  
          CmsProject offline = cms.getRequestContext().getCurrentProject();
          CmsProject online = cms.readProject(CmsProject.ONLINE_PROJECT_ID);
  
          // 1. create new resource
          cms.createResource(resName, CmsResourceTypePlain.getStaticTypeId());
          // check offline
          assertHistory(cms, resName, 1);
  
          // publish
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check after publish
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 1);
  
          // 2. modify resource
          cms.getRequestContext().setCurrentProject(offline);
          cms.lockResource(resName);
          cms.setDateLastModified(resName, System.currentTimeMillis(), false);
  
          // check offline
          assertHistory(cms, resName, 2);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 1);
  
          // publish
          cms.getRequestContext().setCurrentProject(offline);
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 2);
  
          // check offline
          cms.getRequestContext().setCurrentProject(offline);
          assertHistory(cms, resName, 2);
  
          // 3. modify structure
          cms.lockResource(resName);
          cms.writePropertyObject(resName, new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "test", null));
  
          // check offline
          assertHistory(cms, resName, 3);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 2);
  
          // publish
          cms.getRequestContext().setCurrentProject(offline);
          assertHistory(cms, resName, 3);
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check offline
          assertHistory(cms, resName, 3);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 3);
  
          String sibName = "fileVersionTest_sib.txt";
  
          // 4. create sibling
          cms.getRequestContext().setCurrentProject(offline);
          cms.createSibling(resName, sibName, null);
  
          // check offline
          assertHistory(cms, resName, 3);
          assertHistory(cms, sibName, 3);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 3);
  
          // publish sibling
          cms.getRequestContext().setCurrentProject(offline);
          OpenCms.getPublishManager().publishResource(cms, sibName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check offline
          assertHistory(cms, resName, 3);
          assertHistory(cms, sibName, 3);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 3);
          assertHistory(cms, sibName, 3);
  
          // 5. modify sibling resource
          cms.getRequestContext().setCurrentProject(offline);
          cms.lockResource(sibName);
          cms.writePropertyObject(
              sibName,
              new CmsProperty(CmsPropertyDefinition.PROPERTY_DESCRIPTION, null, "test description"));
  
          // check offline
          assertHistory(cms, resName, 4);
          assertHistory(cms, sibName, 4);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 3);
          assertHistory(cms, sibName, 3);
  
          // publish
          cms.getRequestContext().setCurrentProject(offline);
          OpenCms.getPublishManager().publishResource(cms, sibName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check offline
          assertHistory(cms, resName, 4);
          assertHistory(cms, sibName, 4);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 4);
          assertHistory(cms, sibName, 4);
  
          // 6. modify structure
          cms.getRequestContext().setCurrentProject(offline);
          cms.lockResource(resName);
          cms.setDateExpired(resName, System.currentTimeMillis() + 100000, false);
  
          // check offline
          assertHistory(cms, resName, 5);
          assertHistory(cms, sibName, 4);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 4);
          assertHistory(cms, sibName, 4);
  
          // publish
          cms.getRequestContext().setCurrentProject(offline);
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check offline
          assertHistory(cms, resName, 5);
          assertHistory(cms, sibName, 4);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 5);
          assertHistory(cms, sibName, 4);
  
          // 7. modify resource
          cms.getRequestContext().setCurrentProject(offline);
          cms.lockResource(resName);
          cms.writePropertyObject(resName, new CmsProperty(CmsPropertyDefinition.PROPERTY_NAVPOS, null, "1.5"));
  
          // check offline
          assertHistory(cms, resName, 6);
          assertHistory(cms, sibName, 5);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 5);
          assertHistory(cms, sibName, 4);
  
          // publish
          cms.getRequestContext().setCurrentProject(offline);
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check offline
          assertHistory(cms, resName, 6);
          assertHistory(cms, sibName, 5);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 6);
          assertHistory(cms, sibName, 5);
  
          // 8. modify sibling structure
          cms.getRequestContext().setCurrentProject(offline);
          cms.lockResource(sibName);
          cms.setDateReleased(sibName, System.currentTimeMillis() - 1000, false);
  
          // check offline
          assertHistory(cms, resName, 6);
          assertHistory(cms, sibName, 6);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 6);
          assertHistory(cms, sibName, 5);
  
          // publish
          cms.getRequestContext().setCurrentProject(offline);
          OpenCms.getPublishManager().publishResource(cms, sibName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check offline
          assertHistory(cms, resName, 6);
          assertHistory(cms, sibName, 6);
  
          // check online
          cms.getRequestContext().setCurrentProject(online);
          assertHistory(cms, resName, 6);
          assertHistory(cms, sibName, 6);
  
          /*
          
           restore res version 2
           assert res version == 6 (res = 4, str = 2)
           assert sib version == 6 (res = 4, sib = 2)
          
           move
          
           */
      }
  
      /**
       * Tests that that the history.removedeleted property works.
       *
       * @throws Exception
       */
      public void testHistoryRemoveDeleted() throws Exception {
  
          CmsObject cms = getCmsObject();
          String folder = "/testHistoryRemoveDeleted";
          String path = folder + "/cant-undelete.txt";
  
          cms.createResource(folder, 0);
          CmsUUID structureId = cms.createResource(path, 1).getStructureId();
          cms.writePropertyObject(
              folder,
              new CmsProperty(CmsPropertyDefinition.PROPERTY_HISTORY_REMOVE_DELETED, "true", "true"));
  
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
          cms.lockResourceTemporary(path);
          cms.deleteResource(path, CmsResource.DELETE_PRESERVE_SIBLINGS);
          OpenCms.getPublishManager().publishProject(cms);
          OpenCms.getPublishManager().waitWhileRunning();
          assertThrows(
              "Should not be able to restore a resource in a folder with history.removedeleted=true",
              () -> cms.restoreDeletedResource(structureId));
  
      }
  
      /**
       * Moves a resource n-times and tests if the version ID of the history resources
       * are correct and if the content could be restored for a specified version ID.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testMoveFile() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing history after moving a resource n-times");
  
          String filename = "/dummyMove";
          String ext = ".txt";
          String resName = filename + 0 + ext;
          int counter = 2;
  
          // create a plain text file
          CmsResource res = cms.createResource(
              resName,
              CmsResourceTypePlain.getStaticTypeId(),
              "content version 0".getBytes(),
              null);
          cms.unlockResource(resName);
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          for (int i = 1; i <= counter; i++) {
              // create a plain text file
              String contentStr = "content version " + i;
              CmsFile file = cms.readFile(resName);
              file.setContents(contentStr.getBytes());
              cms.lockResource(resName);
              cms.writeFile(file);
              String newName = filename + i + ext;
              cms.moveResource(resName, newName);
              OpenCms.getPublishManager().publishResource(cms, newName);
              OpenCms.getPublishManager().waitWhileRunning();
              resName = newName;
          }
          CmsFile file = cms.readFile(resName);
          file.setContents("content version 3".getBytes());
          cms.lockResource(resName);
          cms.writeFile(file);
          // delete the resource again
          cms.deleteResource(resName, CmsResource.DELETE_PRESERVE_SIBLINGS);
          cms.unlockResource(resName);
          OpenCms.getPublishManager().publishResource(cms, resName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // the history works only by ID so the expected number of versions is ZERO
          cms.createResource(resName, CmsResourceTypePlain.getStaticTypeId(), null, null);
          List historyResources = cms.readAllAvailableVersions(resName);
          assertTrue(historyResources.isEmpty());
  
          // re-create the resource to be able to read all versions from the history
          // assert that we have the expected number of versions in the history
          String importFile = "import.txt";
          cms.importResource(importFile, res, "blah-blah".getBytes(), null);
          historyResources = cms.readAllAvailableVersions(importFile);
          assertEquals(counter + 2, historyResources.size()); // counter + created + deleted
  
          for (int i = 0; i < (counter + 2); i++) {
              // the list of historical resources contains at index 0 the
              // resource with the highest version and tag ID
              int version = (counter + 2) - i;
              String contentStr = "content version " + (version - 1);
  
              // assert that the historical resource has the correct version
              I_CmsHistoryResource historyResource = (I_CmsHistoryResource)historyResources.get(i);
              assertEquals(version, historyResource.getVersion());
  
              cms.restoreResourceVersion(historyResource.getStructureId(), historyResource.getVersion());
              file = cms.readFile(importFile);
  
              // assert that the content and version fit together
              String restoredContent = getContentString(cms, file.getContents());
              assertEquals(contentStr, restoredContent);
          }
      }
  
      /**
       * Tests reconstructing the resource path for the history.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testPathHistory() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing the reconstruction of the resource path for the history");
  
          String foldername = "/folderMove";
          String filename = "/dummyMove";
          String ext = ".txt";
          String path = foldername + 0;
          String resName = path + filename + 0 + ext;
          int counter = 2;
  
          // create folder
          cms.createResource(path, CmsResourceTypeFolder.getStaticTypeId());
          cms.unlockResource(path);
  
          // create a plain text file
          CmsResource res = cms.createResource(
              resName,
              CmsResourceTypePlain.getStaticTypeId(),
              "content version 0".getBytes(),
              null);
          cms.unlockResource(resName);
          OpenCms.getPublishManager().publishResource(cms, path);
          OpenCms.getPublishManager().waitWhileRunning();
  
          for (int i = 1; i <= counter; i++) {
              // modify the file
              String contentStr = "content version " + i;
              CmsFile file = cms.readFile(resName);
              file.setContents(contentStr.getBytes());
              cms.lockResource(resName);
              cms.writeFile(file);
              // move the folder
              String newPath = foldername + i;
              cms.lockResource(path);
              cms.moveResource(path, newPath);
              // move the file
              String newName = newPath + filename + i + ext;
              cms.moveResource(newPath + filename + (i - 1) + ext, newName);
              // publish
              OpenCms.getPublishManager().publishResource(cms, newPath);
              OpenCms.getPublishManager().waitWhileRunning();
              resName = newName;
              path = newPath;
          }
          // modify the file
          CmsFile file = cms.readFile(resName);
          file.setContents(("content version " + (counter + 1)).getBytes());
          cms.lockResource(resName);
          cms.writeFile(file);
          // move the folder
          String newPath = foldername + (counter + 1);
          cms.lockResource(path);
          cms.moveResource(path, newPath);
          // move the file
          String newName = newPath + filename + (counter + 1) + ext;
          cms.moveResource(newPath + filename + counter + ext, newName);
  
          CmsFolder folder = cms.readFolder(newPath);
          // delete the folder
          cms.deleteResource(newPath, CmsResource.DELETE_PRESERVE_SIBLINGS);
          // publish
          OpenCms.getPublishManager().publishResource(cms, newPath);
          OpenCms.getPublishManager().waitWhileRunning();
  
          resName = filename + ext;
          // the history works only by ID so the expected number of versions is ZERO
          cms.createResource(resName, CmsResourceTypePlain.getStaticTypeId(), null, null);
          List historyResources = cms.readAllAvailableVersions(resName);
          assertTrue(historyResources.isEmpty());
  
          // re-create the resource to be able to read all versions from the history
          // assert that we have the expected number of versions in the history
          String importFile = "import2.txt";
          cms.importResource(importFile, res, "blah-blah".getBytes(), null);
          historyResources = cms.readAllAvailableVersions(importFile);
          assertEquals(counter + 2, historyResources.size()); // 1 (created) + counter (modified/moved) + 1 (deleted)
  
          for (int i = 0; i < (counter + 2); i++) {
              // the list of historical resources contains at index 0 the
              // resource with the highest version and tag ID
              int version = (counter + 2) - i;
              String contentStr = "content version " + (version - 1);
              String histResName = foldername + (version - 1) + filename + (version - 1) + ext;
  
              // assert that the historical resource has the correct version and path
              I_CmsHistoryResource historyResource = (I_CmsHistoryResource)historyResources.get(i);
              assertEquals(version, historyResource.getVersion());
              assertEquals(histResName, cms.getSitePath((CmsResource)historyResource));
  
              // restore the version
              cms.restoreResourceVersion(historyResource.getStructureId(), historyResource.getVersion());
              file = cms.readFile(importFile);
  
              // assert that the content and version fit together
              String restoredContent = getContentString(cms, file.getContents());
              assertEquals(contentStr, restoredContent);
          }
  
          // now recreate the folder
          String importFolder = "/import";
          cms.importResource(importFolder, folder, null, null);
          historyResources = cms.readAllAvailableVersions(importFile);
          assertEquals(counter + 2, historyResources.size()); // 1 (created) + counter (modified/moved) + 1 (deleted)
  
          cms.lockResource(importFile);
  
          for (int i = 0; i < (counter + 2); i++) {
              // the list of historical resources contains at index 0 the
              // resource with the highest version and tag ID
              int version = (counter + 2) - i;
              String contentStr = "content version " + (version - 1);
              String histResName = foldername + (version - 1) + filename + (version - 1) + ext;
  
              // assert that the historical resource has the correct version and path
              I_CmsHistoryResource historyResource = (I_CmsHistoryResource)historyResources.get(i);
              assertEquals(version, historyResource.getVersion());
              assertEquals(histResName, cms.getSitePath((CmsResource)historyResource));
  
              // restore the version
              cms.restoreResourceVersion(historyResource.getStructureId(), historyResource.getVersion());
              file = cms.readFile(importFile);
  
              // assert that the content and version fit together
              String restoredContent = getContentString(cms, file.getContents());
              assertEquals(contentStr, restoredContent);
          }
      }
  
      /**
       * Tests the retrieval of deleted resources by name in a folder.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testReadDeleted() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing the retrieval of deleted resources by name in a folder");
  
          String folderName = "/folderReadDeleted";
          String fileName = "/test.txt";
  
          cms.createResource(folderName, CmsResourceTypeFolder.getStaticTypeId());
          cms.createResource(
              folderName + fileName,
              CmsResourceTypePlain.getStaticTypeId(),
              "test content".getBytes(),
              null);
  
          OpenCms.getPublishManager().publishResource(cms, folderName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          cms.lockResource(folderName);
          cms.deleteResource(folderName, CmsResource.DELETE_PRESERVE_SIBLINGS);
  
          OpenCms.getPublishManager().publishResource(cms, folderName);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // create with new id
          cms.createResource(folderName, CmsResourceTypeFolder.getStaticTypeId());
          // get the deleted resources in the new folder
          List deleted = cms.readDeletedResources(folderName, false);
          assertEquals(1, deleted.size());
  
          cms.restoreDeletedResource(((I_CmsHistoryResource)deleted.get(0)).getStructureId());
          cms.readResource(((I_CmsHistoryResource)deleted.get(0)).getStructureId());
          cms.readResource(cms.getRequestContext().removeSiteRoot(((I_CmsHistoryResource)deleted.get(0)).getRootPath()));
      }
  
      /**
       * Test restoring a deleted file in a renamed folder.
       *
       * @throws Exception if something goes wrong
       */
      public void testRestoreFileInRenamedFolder() throws Exception {
  
          CmsObject cms = getCmsObject();
          String base = "/testRestoreInRenamedFolder";
          cms.createResource(base, 0);
          cms.createResource(base + "/a", 0);
          CmsResource fileToDelete = cms.createResource(base + "/a/1.txt", 1);
          CmsPublishManager pm = OpenCms.getPublishManager();
          pm.publishProject(cms);
          pm.waitWhileRunning();
  
          cms.lockResourceTemporary(base + "/a/1.txt");
          cms.deleteResource(base + "/a/1.txt", CmsResource.DELETE_PRESERVE_SIBLINGS);
          pm.publishProject(cms);
          pm.waitWhileRunning();
  
          cms.lockResourceTemporary(base + "/a");
          cms.moveResource(base + "/a", base + "/b");
          pm.publishProject(cms);
          pm.waitWhileRunning();
  
          cms.restoreDeletedResource(fileToDelete.getStructureId());
          assertEquals(fileToDelete.getStructureId(), cms.readResource(base + "/b/1.txt").getStructureId());
      }
  
      /**
       * Test restoring and deleting a sibling several times.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testSiblingRestoreIteration() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing restoring and deleting a sibling several times");
  
          String resName = "siblingRestoreIteration.txt";
          String sibName = "sibling2RestoreIteration.txt";
  
          int vers = OpenCms.getSystemInfo().getHistoryVersions();
          int delVers = OpenCms.getSystemInfo().getHistoryVersionsAfterDeletion();
          try {
              OpenCms.getSystemInfo().setVersionHistorySettings(true, 10, 10);
  
              // create new resource
              cms.createResource(resName, CmsResourceTypePlain.getStaticTypeId());
              // check offline
              assertHistory(cms, resName, 1);
  
              // publish
              OpenCms.getPublishManager().publishResource(cms, resName);
              OpenCms.getPublishManager().waitWhileRunning();
              // check after publish
              assertHistory(cms, resName, 1);
  
              // create sibling
              cms.copyResource(resName, sibName, CmsResource.COPY_AS_SIBLING);
              CmsResource sib = cms.readResource(sibName);
              // check offline
              assertHistory(cms, resName, 1);
              // new siblings have no history until they get first published
              assertVersion(cms, sibName, 2);
  
              // publish
              OpenCms.getPublishManager().publishResource(cms, sibName);
              OpenCms.getPublishManager().waitWhileRunning();
              // check after publish
              assertHistory(cms, resName, 1);
              // after publishing the sibling gets the history of the resource
              assertHistory(cms, sibName, 2);
  
              for (int i = 0; i < 3; i++) {
                  cms.lockResource(sibName);
                  cms.deleteResource(sibName, CmsResource.DELETE_PRESERVE_SIBLINGS);
                  // check offline
                  assertHistory(cms, resName, 1 + i);
                  assertHistory(cms, sibName, 3 + (i * 2));
                  // publish
                  OpenCms.getPublishManager().publishResource(cms, sibName);
                  OpenCms.getPublishManager().waitWhileRunning();
                  // check after publish
                  assertHistory(cms, resName, 1 + i);
                  // restore
                  cms.restoreDeletedResource(sib.getStructureId());
                  // check offline
                  assertVersion(cms, resName, 2 + i);
                  assertHistoryForRestored(cms, sibName, 4 + (i * 2));
                  // publish
                  OpenCms.getPublishManager().publishResource(cms, sibName);
                  OpenCms.getPublishManager().waitWhileRunning();
                  // check after publish
                  assertVersion(cms, resName, 2 + i);
                  assertHistory(cms, sibName, 4 + (i * 2));
              }
          } finally {
              OpenCms.getSystemInfo().setVersionHistorySettings(true, vers, delVers);
          }
      }
  
      /**
       * Test a create and edit siblings scenario.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testSiblingsEdition() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing history with siblings edition");
  
          // first we create a complete new folder as base for the test
          String folder = "/siblings_edition/";
          cms.createResource(folder, CmsResourceTypeFolder.getStaticTypeId());
          OpenCms.getPublishManager().publishResource(cms, folder);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // 1. create first sibling
          String s1name = folder + "s1.txt";
          CmsResource s1 = cms.createResource(
              s1name,
              CmsResourceTypePlain.getStaticTypeId(),
              "first sibling".getBytes(),
              null);
  
          // check the history
          assertHistory(cms, s1name, 1);
  
          // 2. publish s1
          OpenCms.getPublishManager().publishResource(cms, s1name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history
          assertHistory(cms, s1name, 1);
          I_CmsHistoryResource histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
  
          // remember pub tag to be able to check the next operations
          int basePubTag = histRes.getPublishTag();
  
          // 3. create second sibling
          String s2name = folder + "s2.txt";
          CmsResource s2 = cms.createSibling(s1name, s2name, null);
  
          // check the history
          assertHistory(cms, s1name, 1);
          assertHistory(cms, s2name, 2);
  
          // 4. publish s2
          OpenCms.getPublishManager().publishResource(cms, s2name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 1);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
  
          // check the history for s2
          assertHistory(cms, s2name, 2);
          histRes = cms.readResource(s2.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
  
          // 5. make a resource change
          cms.lockResource(s1name);
          cms.writePropertyObject(s1name, new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, null, "resource change"));
  
          // check history
          assertHistory(cms, s1name, 2);
          assertHistory(cms, s2name, 3);
  
          // 6. make a structure change on s2
          cms.changeLock(s2name);
          cms.writePropertyObject(
              s2name,
              new CmsProperty(CmsPropertyDefinition.PROPERTY_NAVTEXT, "structure change", null));
  
          // check history
          assertHistory(cms, s1name, 2);
          assertHistory(cms, s2name, 3);
  
          // 7. publish s1
          OpenCms.getPublishManager().publishResource(cms, s1name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 2);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
  
          // check the history for s2
          assertHistory(cms, s2name, 4);
          histRes = cms.readResource(s2.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
  
          // 8. publish s2
          OpenCms.getPublishManager().publishResource(cms, s2name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 2);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
  
          // check the history for s2
          assertHistory(cms, s2name, 4);
          histRes = cms.readResource(s2.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 4);
          assertEquals(2, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 3, histRes.getPublishTag());
  
          // 9. change structure on s1
          cms.lockResource(s1name);
          cms.setDateExpired(s1name, System.currentTimeMillis(), false);
  
          // check history
          assertHistory(cms, s1name, 3);
          assertHistory(cms, s2name, 4);
  
          // 10. change structure on s2
          cms.changeLock(s2name);
          cms.setDateReleased(s2name, System.currentTimeMillis(), false);
  
          // check history
          assertHistory(cms, s1name, 3);
          assertHistory(cms, s2name, 5);
  
          // 11. publish s1+s2
          OpenCms.getPublishManager().publishResource(cms, s2name, true, null);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 3);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
  
          // check the history for s2
          assertHistory(cms, s2name, 5);
          histRes = cms.readResource(s2.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 4);
          assertEquals(2, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 3, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 5);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
  
          // 12. make a resource change
          CmsFile file = cms.readFile(s1name);
          file.setContents("resource changed".getBytes());
          cms.lockResource(s1name);
          cms.writeFile(file);
  
          // check history
          assertHistory(cms, s1name, 4);
          assertHistory(cms, s2name, 6);
  
          // 13. make a structure change
          cms.changeLock(s2name);
          cms.writePropertyObject(
              s2name,
              new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "structure change", null));
  
          // check history
          assertHistory(cms, s1name, 4);
          assertHistory(cms, s2name, 6);
  
          // 14. publish s2 (s1 will be unchanged after that)
          OpenCms.getPublishManager().publishResource(cms, s2name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 4);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 4);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
  
          // check the history for s2
          assertHistory(cms, s2name, 6);
          histRes = cms.readResource(s2.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 4);
          assertEquals(2, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 3, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 5);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 6);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
  
          // 15. make a resource change
          cms.lockResource(s1name);
          cms.writePropertyObject(
              s1name,
              new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, null, "new resource change"));
  
          // check history
          assertHistory(cms, s1name, 5);
          assertHistory(cms, s2name, 7);
  
          // 16. make a structure change on s1
          cms.writePropertyObject(
              s1name,
              new CmsProperty(CmsPropertyDefinition.PROPERTY_NAVTEXT, "new structure change", null));
  
          // check history
          assertHistory(cms, s1name, 5);
          assertHistory(cms, s2name, 7);
  
          // 17. make a structure change on s2
          cms.changeLock(s2name);
          cms.writePropertyObject(
              s2name,
              new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, "new structure change", null));
  
          // check history
          assertHistory(cms, s1name, 5);
          assertHistory(cms, s2name, 7);
  
          // 18. publish s2
          OpenCms.getPublishManager().publishResource(cms, s2name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 6);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 4);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 5);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertEquals(basePubTag + 6, histRes.getPublishTag());
  
          // check the history for s2
          assertHistory(cms, s2name, 7);
          histRes = cms.readResource(s2.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 4);
          assertEquals(2, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 3, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 5);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 6);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 7);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertEquals(basePubTag + 6, histRes.getPublishTag());
  
          // 19. publish s1
          OpenCms.getPublishManager().publishResource(cms, s1name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 6);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 4);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 5);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertEquals(basePubTag + 6, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 6);
          assertEquals(2, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertEquals(basePubTag + 7, histRes.getPublishTag());
  
          // check the history for s2
          assertHistory(cms, s2name, 7);
          histRes = cms.readResource(s2.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 4);
          assertEquals(2, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 3, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 5);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 6);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 7);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertEquals(basePubTag + 6, histRes.getPublishTag());
  
          // 20. create a third sibling
          String s3name = folder + "s3.txt";
          CmsResource s3 = cms.createSibling(s1name, s3name, null);
  
          // check history
          assertHistory(cms, s1name, 6);
          assertHistory(cms, s2name, 7);
          assertHistory(cms, s3name, 5);
  
          // 21. publish s3
          OpenCms.getPublishManager().publishResource(cms, s3name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 6);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 4);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 5);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertEquals(basePubTag + 6, histRes.getPublishTag());
          histRes = cms.readResource(s1.getStructureId(), 6);
          assertEquals(2, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertEquals(basePubTag + 7, histRes.getPublishTag());
  
          // check the history for s2
          assertHistory(cms, s2name, 7);
          histRes = cms.readResource(s2.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 4);
          assertEquals(2, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 3, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 5);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 6);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
          histRes = cms.readResource(s2.getStructureId(), 7);
          assertEquals(3, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertEquals(basePubTag + 6, histRes.getPublishTag());
  
          // check the history for s3
          assertHistory(cms, s3name, 5);
          histRes = cms.readResource(s3.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          histRes = cms.readResource(s3.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 2, histRes.getPublishTag());
          histRes = cms.readResource(s3.getStructureId(), 3);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
          histRes = cms.readResource(s3.getStructureId(), 4);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertEquals(basePubTag + 6, histRes.getPublishTag());
          histRes = cms.readResource(s3.getStructureId(), 5);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertEquals(basePubTag + 8, histRes.getPublishTag());
      }
  
      /**
       * Test a sibling restoration scenario.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testSiblingsRestoration() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing history with siblings restoration");
  
          // first we create a complete new folder as base for the test
          String folder = "/siblings_restoration/";
          cms.createResource(folder, CmsResourceTypeFolder.getStaticTypeId());
          OpenCms.getPublishManager().publishResource(cms, folder);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // 1. create first sibling
          String s1name = folder + "s1.txt";
          String txt1 = "first sibling";
          CmsResource s1 = cms.createResource(s1name, CmsResourceTypePlain.getStaticTypeId(), txt1.getBytes(), null);
  
          // check the history
          assertHistory(cms, s1name, 1);
  
          // 2. create second sibling
          String s2name = folder + "s2.txt";
          CmsResource s2 = cms.createSibling(s1name, s2name, null);
  
          // check the history
          assertHistory(cms, s1name, 1);
          assertHistory(cms, s2name, 1);
  
          // 3. publish both
          OpenCms.getPublishManager().publishResource(cms, folder);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 1);
          I_CmsHistoryResource histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
  
          // check the history for s2
          assertHistory(cms, s2name, 1);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
  
          // remember pub tag to be able to check the next operations
          int basePubTag = histRes.getPublishTag();
  
          // 4. create third sibling
          String s3name = folder + "s3.txt";
          CmsResource s3 = cms.createSibling(s1name, s3name, null);
  
          // check the history
          assertHistory(cms, s1name, 1);
          assertHistory(cms, s2name, 1);
          assertHistory(cms, s3name, 2);
  
          // 5. make a resource change
          cms.changeLock(s1name);
          cms.writePropertyObject(s1name, new CmsProperty(CmsPropertyDefinition.PROPERTY_TITLE, null, "resource change"));
  
          // check the history
          assertHistory(cms, s1name, 2);
          assertHistory(cms, s2name, 2);
          assertHistory(cms, s3name, 2);
  
          // 6. publish s3
          OpenCms.getPublishManager().publishResource(cms, s3name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 2);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
  
          // check the history for s2
          assertHistory(cms, s2name, 2);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
  
          // check the history for s3
          assertHistory(cms, s3name, 2);
          histRes = cms.readResource(s3.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s3.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
  
          // 7. delete s3
          cms.lockResource(s3name);
          cms.deleteResource(s3name, CmsResource.DELETE_PRESERVE_SIBLINGS);
  
          // check history
          assertHistory(cms, s1name, 2);
          assertHistory(cms, s2name, 2);
          assertEquals(3, cms.readResource(s3name, CmsResourceFilter.ALL).getVersion());
  
          // 8. publish s3
          OpenCms.getPublishManager().publishResource(cms, s3name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 2);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
  
          // check the history for s2
          assertHistory(cms, s2name, 2);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
  
          // 9. delete s2
          cms.lockResource(s2name);
          cms.deleteResource(s2name, CmsResource.DELETE_PRESERVE_SIBLINGS);
  
          // check history
          assertHistory(cms, s1name, 2);
          assertEquals(3, cms.readResource(s2name, CmsResourceFilter.ALL).getVersion());
  
          // 10. make a resource change
          CmsFile file = cms.readFile(s1name);
          String txt2 = "resource changed";
          file.setContents(txt2.getBytes());
          cms.changeLock(s1name);
          cms.writeFile(file);
  
          // check history
          assertHistory(cms, s1name, 3);
  
          // 11. publish s2
          OpenCms.getPublishManager().publishResource(cms, s2name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check online contents
          cms.getRequestContext().setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID));
          assertContent(cms, s1name, txt1.getBytes());
          // go back to offline
          cms = getCmsObject();
  
          // check the history for s1
          assertState(cms, s1name, CmsResource.STATE_CHANGED);
          assertHistory(cms, s1name, 3);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
  
          // 12. delete s1
          cms.lockResource(s1name);
          cms.deleteResource(s1name, CmsResource.DELETE_PRESERVE_SIBLINGS);
  
          // assert version
          assertEquals(3, cms.readResource(s1name, CmsResourceFilter.ALL).getVersion());
  
          // 13. publish s1
          OpenCms.getPublishManager().publishResource(cms, s1name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // 14. restore s1
          cms.restoreDeletedResource(s1.getStructureId());
  
          // check history
          assertHistoryForRestored(cms, s1name, 4);
  
          // 15. publish s1
          OpenCms.getPublishManager().publishResource(cms, s1name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 4);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s1.getStructureId(), 3);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertTrue(histRes.getState().isDeleted());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          assertEquals(txt2, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s1.getStructureId(), 4);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
          assertEquals(txt2, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
  
          // 16. restore s2
          cms.restoreDeletedResource(s2.getStructureId());
  
          // check history
          assertHistory(cms, s1name, 5);
          assertHistoryForRestored(cms, s2name, 6);
  
          // 17. publish s2
          OpenCms.getPublishManager().publishResource(cms, s2name);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check the history for s1
          assertHistory(cms, s1name, 5);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s1.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s1.getStructureId(), 3);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertTrue(histRes.getState().isDeleted());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          assertEquals(txt2, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s1.getStructureId(), 4);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
          assertEquals(txt2, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s1.getStructureId(), 5);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(5, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 6, histRes.getPublishTag());
          // we deleted s2 before publishing the contents, so the restored version has the old content
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
  
          // check the history for s2
          assertHistory(cms, s2name, 6);
          histRes = cms.readResource(s1.getStructureId(), 1);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(1, histRes.getResourceVersion());
          assertEquals(basePubTag, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s2.getStructureId(), 2);
          assertEquals(0, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 1, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s2.getStructureId(), 3);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(2, histRes.getResourceVersion());
          assertEquals(basePubTag + 3, histRes.getPublishTag());
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s2.getStructureId(), 4);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(3, histRes.getResourceVersion());
          assertTrue(histRes.getState().isDeleted());
          assertEquals(basePubTag + 4, histRes.getPublishTag());
          assertEquals(txt2, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s2.getStructureId(), 5);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(4, histRes.getResourceVersion());
          assertTrue(histRes.getState().isDeleted());
          assertEquals(basePubTag + 5, histRes.getPublishTag());
          assertEquals(txt2, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
          histRes = cms.readResource(s2.getStructureId(), 6);
          assertEquals(1, histRes.getStructureVersion());
          assertEquals(5, histRes.getResourceVersion());
          assertFalse(histRes.getState().isDeleted());
          assertEquals(basePubTag + 6, histRes.getPublishTag());
          // we deleted s2 before publishing the contents, so the restored version has the old content
          assertEquals(txt1, new String(cms.readFile((CmsHistoryFile)histRes).getContents()));
      }
  
      /**
       * Tests an issue present in OpenCms 7 siblings have a wrong history.<p>
       *
       * @throws Exception if the test fails
       */
      public void testSiblingsV7HistoryIssue() throws Exception {
  
          echo("Tests OpenCms v7 history issue with siblings");
          CmsObject cms = getCmsObject();
  
          // first we create a complete new folder as base for the test
          String folderA = "/history_v7issue_a/";
          cms.createResource(folderA, CmsResourceTypeFolder.getStaticTypeId());
  
          String firstContent = "This is the first content";
          byte[] firstContentBytes = firstContent.getBytes();
  
          String source = folderA + "1.txt";
          cms.createResource(source, CmsResourceTypePlain.getStaticTypeId(), firstContentBytes, null);
  
          assertState(cms, folderA, CmsResourceState.STATE_NEW);
          assertState(cms, source, CmsResourceState.STATE_NEW);
  
          // publish the folder
          OpenCms.getPublishManager().publishResource(cms, folderA);
          OpenCms.getPublishManager().waitWhileRunning();
  
          assertHistory(cms, source, 1);
  
          String secondContent = "This is the second content";
          byte[] secondContentBytes = secondContent.getBytes();
  
          CmsFile sourceFile = cms.readFile(source);
          cms.lockResource(source);
          sourceFile.setContents(secondContentBytes);
          cms.writeFile(sourceFile);
  
          // publish the folder again
          OpenCms.getPublishManager().publishResource(cms, folderA);
          OpenCms.getPublishManager().waitWhileRunning();
  
          assertHistory(cms, source, 2);
  
          // create a another new folder as base for the test
          String folderB = "/history_v7issue_b/";
          cms.createResource(folderB, CmsResourceTypeFolder.getStaticTypeId());
  
          // publish the new folder
          OpenCms.getPublishManager().publishResource(cms, folderB);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // copy the source to the new folder
          String destination = folderB + "2.txt";
          cms.copyResource(source, destination, CmsResource.COPY_AS_SIBLING);
  
          // now publish the sibling
          OpenCms.getPublishManager().publishResource(cms, destination, false, null);
          OpenCms.getPublishManager().waitWhileRunning();
  
          assertHistory(cms, source, 2);
          assertHistory(cms, destination, 3);
      }
  
      /**
       * Tests an issue present in OpenCms 7 siblings have a wrong history.<p>
       *
       * @throws Exception if the test fails
       */
      public void testSiblingsV7HistoryIssue2() throws Exception {
  
          echo("Tests OpenCms v7 history issue with siblings");
          CmsObject cms = getCmsObject();
  
          // first we create a complete new folder as base for the test
          String folderA = "/history_v7issue2_a/";
          cms.createResource(folderA, CmsResourceTypeFolder.getStaticTypeId());
  
          // create a new resource
          String firstContent = "This is the first content";
          byte[] firstContentBytes = firstContent.getBytes();
  
          String source = folderA + "1.txt";
          cms.createResource(source, CmsResourceTypePlain.getStaticTypeId(), firstContentBytes, null);
  
          // check
          assertState(cms, folderA, CmsResourceState.STATE_NEW);
          assertState(cms, source, CmsResourceState.STATE_NEW);
  
          assertHistory(cms, source, 1);
  
          // publish the folder and resource
          OpenCms.getPublishManager().publishResource(cms, folderA);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check
          assertHistory(cms, source, 1);
  
          // modify the resource (resource entry)
          String secondContent = "This is the second content";
          byte[] secondContentBytes = secondContent.getBytes();
  
          CmsFile sourceFile = cms.readFile(source);
          cms.lockResource(source);
          sourceFile.setContents(secondContentBytes);
          cms.writeFile(sourceFile);
  
          // check
          assertHistory(cms, source, 2);
  
          // create a another new folder as base for the test
          String folderB = "/history_v7issue2_b/";
          cms.createResource(folderB, CmsResourceTypeFolder.getStaticTypeId());
  
          // publish the new folder
          OpenCms.getPublishManager().publishResource(cms, folderB);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check
          assertHistory(cms, source, 2);
  
          // copy the source to the new folder as sibling
          String destination = folderB + "2.txt";
          cms.copyResource(source, destination, CmsResource.COPY_AS_SIBLING);
  
          // check
          assertHistory(cms, source, 2);
          assertHistory(cms, destination, 2);
  
          // now publish the sibling
          OpenCms.getPublishManager().publishResource(cms, destination, false, null);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check
          assertHistory(cms, source, 2);
          assertHistory(cms, destination, 2);
      }
  
      /**
       * Test the version number of siblings after different operations.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testSiblingVersions() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing sibling version numbers");
  
          String sib1 = "/sibVer1.txt";
          String sib2 = "/sibVer2.txt";
  
          cms.createResource(sib1, CmsResourceTypePlain.getStaticTypeId());
          cms.createSibling(sib1, sib2, null);
  
          // check offline
          assertHistory(cms, sib1, 1);
          assertHistory(cms, sib2, 1);
  
          OpenCms.getPublishManager().publishResource(cms, sib1, true, new CmsShellReport(Locale.ENGLISH));
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check after publish
          assertHistory(cms, sib1, 1);
          // siblings where published together
          assertHistory(cms, sib2, 1);
  
          // check online
          cms.getRequestContext().setCurrentProject(cms.readProject(CmsProject.ONLINE_PROJECT_ID));
          assertHistory(cms, sib2, 1);
  
          // back to offline
          cms = getCmsObject();
  
          // now the same but in a different sequence
          String sib3 = "/sibVer3.txt";
          String sib4 = "/sibVer4.txt";
  
          cms.createResource(sib3, CmsResourceTypePlain.getStaticTypeId());
          cms.createSibling(sib3, sib4, null);
  
          // check offline
          assertHistory(cms, sib3, 1);
          assertHistory(cms, sib4, 1);
  
          OpenCms.getPublishManager().publishResource(cms, sib3);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check after publish of sib3
          assertHistory(cms, sib3, 1);
          assertHistory(cms, sib4, 2);
  
          OpenCms.getPublishManager().publishResource(cms, sib4);
          OpenCms.getPublishManager().waitWhileRunning();
  
          // check after publish of sib3
          assertHistory(cms, sib3, 1);
          assertHistory(cms, sib4, 2);
      }
  
      /**
       * Test that the versions are properly updated
       * after reaching the limit of stored versions.<p>
       *
       * @throws Throwable if something goes wrong
       */
      public void testVersioningLimit() throws Throwable {
  
          CmsObject cms = getCmsObject();
          echo("Testing versioning limit");
  
          String source = "/index.html";
          cms.getRequestContext().setCurrentProject(cms.readProject("Offline"));
  
          // set the history version settings
          OpenCms.getSystemInfo().setVersionHistorySettings(true, 3, 3);
  
          // make 5 versions
          for (int i = 0; i < 5; i++) {
              if (i < 3) {
                  assertEquals(i + 1, cms.readAllAvailableVersions(source).size());
              } else {
                  assertEquals(3, cms.readAllAvailableVersions(source).size());
              }
              cms.lockResource(source);
              cms.setDateLastModified(source, System.currentTimeMillis(), false);
              cms.setDateExpired(source, System.currentTimeMillis(), false);
              cms.setDateReleased(source, System.currentTimeMillis(), false);
              cms.unlockResource(source);
              OpenCms.getPublishManager().publishResource(cms, source);
              OpenCms.getPublishManager().waitWhileRunning();
          }
      }
  
      /**
       * Turns the byte content of a resource into a string.<p>
       *
       * @param cms the current user's Cms object
       * @param content the byte content of a resource
       * @return the content as a string
       */
      protected String getContentString(CmsObject cms, byte[] content) {
  
          try {
              return new String(content, cms.getRequestContext().getEncoding());
          } catch (UnsupportedEncodingException e) {
              return new String(content);
          }
      }
  }
  