@Test
  public void testCheckInCheckOut() throws Exception {
    DocumentModel doc = new DocumentModelImpl("/", "file#789", "File");
    assertTrue(doc.isCheckedOut());
    doc = session.createDocument(doc);
    assertTrue(session.isCheckedOut(doc.getRef()));
    assertTrue(doc.isCheckedOut());
    session.save();
    assertTrue(session.isCheckedOut(doc.getRef()));
    assertTrue(doc.isCheckedOut());

    DocumentRef verRef = session.checkIn(doc.getRef(), null, null);
    DocumentModel ver = session.getDocument(verRef);
    assertTrue(ver.isVersion());
    doc.refresh();
    assertFalse(session.isCheckedOut(doc.getRef()));
    assertFalse(doc.isCheckedOut());

    session.checkOut(doc.getRef());
    assertTrue(session.isCheckedOut(doc.getRef()));

    // using DocumentModel API
    DocumentRef verRef2 = doc.checkIn(null, null);
    DocumentModel ver2 = session.getDocument(verRef2);
    assertTrue(ver2.isVersion());
    assertFalse(doc.isCheckedOut());
    doc.checkOut();
    assertTrue(doc.isCheckedOut());
  }
  @Test
  public void testSaveRestoredVersionWithVersionAutoIncrement() {
    // check-in version 1.0, 2.0 and restore version 1.0
    DocumentModel doc = new DocumentModelImpl("/", "myfile", "File");
    doc = session.createDocument(doc);
    doc = session.saveDocument(doc);
    DocumentRef co = doc.getRef();
    DocumentRef ci1 = session.checkIn(co, VersioningOption.MAJOR, "first check-in");
    session.checkOut(co);
    maybeSleepToNextSecond();
    DocumentRef ci2 = session.checkIn(co, VersioningOption.MAJOR, "second check-in");
    waitForFulltextIndexing();
    maybeSleepToNextSecond();
    session.restoreToVersion(co, ci1);

    // save document with auto-increment should produce version 3.0
    doc = session.getDocument(co);
    assertEquals(doc.getVersionLabel(), "1.0");
    doc.getContextData()
        .putScopedValue(
            ScopeType.DEFAULT, VersioningService.VERSIONING_OPTION, VersioningOption.MAJOR);
    // mark as dirty - must change the value
    doc.setPropertyValue("dc:title", doc.getPropertyValue("dc:title") + " dirty");
    doc = session.saveDocument(doc);
    assertEquals(doc.getVersionLabel(), "3.0");
  }
  @Test
  public void testRestoreToVersion() throws Exception {
    String name2 = "file#456";
    DocumentModel doc = new DocumentModelImpl("/", name2, "File");
    doc = session.createDocument(doc);
    DocumentRef docRef = doc.getRef();

    session.save();
    DocumentRef v1Ref = session.checkIn(docRef, null, null);
    assertFalse(session.isCheckedOut(docRef));
    session.checkOut(docRef);
    assertTrue(session.isCheckedOut(docRef));

    doc.setProperty("file", "filename", "second name");
    doc.setProperty("dc", "title", "f1");
    doc.setProperty("dc", "description", "desc 1");
    session.saveDocument(doc);
    session.save();

    maybeSleepToNextSecond();
    DocumentRef v2Ref = session.checkIn(docRef, null, null);
    session.checkOut(docRef);

    DocumentModel newDoc = session.getDocument(docRef);
    assertNotNull(newDoc);
    assertNotNull(newDoc.getRef());
    assertEquals("second name", newDoc.getProperty("file", "filename"));

    waitForFulltextIndexing();
    maybeSleepToNextSecond();
    DocumentModel restoredDoc = session.restoreToVersion(docRef, v1Ref);

    assertNotNull(restoredDoc);
    assertNotNull(restoredDoc.getRef());
    assertNull(restoredDoc.getProperty("file", "filename"));

    waitForFulltextIndexing();
    maybeSleepToNextSecond();
    restoredDoc = session.restoreToVersion(docRef, v2Ref);

    assertNotNull(restoredDoc);
    assertNotNull(restoredDoc.getRef());
    String pr = (String) restoredDoc.getProperty("file", "filename");
    assertEquals("second name", pr);
  }
  @Test
  public void testGetVersions() {
    DocumentModel doc = session.createDocumentModel("/", "doc", "File");
    doc = session.createDocument(doc);
    session.save();
    DocumentRef v1ref = session.checkIn(doc.getRef(), VersioningOption.MAJOR, null);
    session.checkOut(doc.getRef());
    session.checkIn(doc.getRef(), VersioningOption.MINOR, null);

    // versions on the doc
    List<DocumentModel> vers = session.getVersions(doc.getRef());
    assertEquals(2, vers.size());
    List<DocumentRef> verRefs = session.getVersionsRefs(doc.getRef());
    assertEquals(2, verRefs.size());

    // versions on any version
    vers = session.getVersions(v1ref);
    assertEquals(2, vers.size());
    verRefs = session.getVersionsRefs(v1ref);
    assertEquals(2, verRefs.size());
  }
  @Test
  public void testGetLastVersion() {
    DocumentModel doc = session.createDocumentModel("/", "doc", "File");
    doc = session.createDocument(doc);
    session.save();
    DocumentRef v1ref = session.checkIn(doc.getRef(), VersioningOption.MAJOR, null);
    session.checkOut(doc.getRef());
    DocumentRef v2ref = session.checkIn(doc.getRef(), VersioningOption.MINOR, null);

    // last version on the doc
    DocumentModel last = session.getLastDocumentVersion(doc.getRef());
    assertEquals(v2ref.reference(), last.getId());
    DocumentRef lastRef = session.getLastDocumentVersionRef(doc.getRef());
    assertEquals(v2ref.reference(), lastRef.reference());

    // last version on any version
    last = session.getLastDocumentVersion(v2ref);
    assertEquals(v2ref.reference(), last.getId());
    lastRef = session.getLastDocumentVersionRef(v2ref);
    assertEquals(v2ref.reference(), lastRef.reference());
  }
  @Test
  public void testGetDocumentWithVersion() throws Exception {
    String name2 = "file#248";
    DocumentModel childFile = new DocumentModelImpl("/", name2, "File");
    childFile = session.createDocument(childFile);
    session.save();
    DocumentRef v1Ref = session.checkIn(childFile.getRef(), null, null);
    session.checkOut(childFile.getRef());

    childFile.setProperty("file", "filename", "second name");
    childFile.setProperty("dc", "title", "f1");
    childFile.setProperty("dc", "description", "desc 1");
    session.saveDocument(childFile);
    session.save();
    maybeSleepToNextSecond();
    DocumentRef v2Ref = session.checkIn(childFile.getRef(), null, null);

    DocumentModel newDoc = session.getDocument(childFile.getRef());
    assertNotNull(newDoc);
    assertNotNull(newDoc.getRef());
    assertEquals("second name", newDoc.getProperty("file", "filename"));

    // restore, no snapshot as already pristine
    waitForFulltextIndexing();
    maybeSleepToNextSecond();
    DocumentModel restoredDoc = session.restoreToVersion(childFile.getRef(), v1Ref);

    assertNotNull(restoredDoc);
    assertNotNull(restoredDoc.getRef());
    assertNull(restoredDoc.getProperty("file", "filename"));

    DocumentModel last = session.getLastDocumentVersion(childFile.getRef());
    assertNotNull(last);
    assertNotNull(last.getRef());
    assertEquals(v2Ref.reference(), last.getId());
    assertEquals("second name", last.getProperty("file", "filename"));
  }
  @SuppressWarnings("deprecation")
  @Test
  @LocalDeploy("org.nuxeo.ecm.core.test.tests:test-versioningservice-contrib.xml")
  public void testOldNuxeoVersioning() throws Exception {
    ((VersioningComponent) service).service = new CompatVersioningService();

    DocumentModel folder = session.createDocumentModel("/", "folder", "Folder");
    folder = session.createDocument(folder);
    DocumentModel doc = session.createDocumentModel("/", "testfile1", "File");
    doc = session.createDocument(doc);
    doc.setPropertyValue("dc:title", "A");
    maybeSleepToNextSecond();
    doc = session.saveDocument(doc);
    DocumentRef docRef = doc.getRef();
    assertTrue(doc.isCheckedOut());
    assertVersion("1.0", doc);
    assertLatestVersion(null, doc);

    // snapshot A=1.0 and save B
    doc.setPropertyValue("dc:title", "B");
    doc.putContextData(VersioningService.VERSIONING_OPTION, VersioningOption.MINOR);
    maybeSleepToNextSecond();
    doc = session.saveDocument(doc);
    assertTrue(doc.isCheckedOut());
    assertVersion("1.1", doc);
    assertLatestVersion("1.0", doc);

    // another snapshot for B=1.1, using major inc
    doc.putContextData(VersioningService.VERSIONING_OPTION, VersioningOption.MAJOR);
    maybeSleepToNextSecond();
    doc = session.saveDocument(doc);
    assertTrue(doc.isCheckedOut());
    assertVersion("2.0", doc);
    assertLatestVersion("1.1", doc);
    DocumentModel v11 = session.getLastDocumentVersion(docRef);
    assertVersion("1.1", v11);

    // another snapshot but no increment doesn't change anything, doc is
    // clean
    doc.putContextData(
        ScopeType.REQUEST, VersioningDocument.CREATE_SNAPSHOT_ON_SAVE_KEY, Boolean.TRUE);
    doc = session.saveDocument(doc);
    assertTrue(doc.isCheckedOut());
    assertVersion("2.0", doc);
    assertLatestVersion("1.1", doc);

    // now dirty doc and snapshot+inc
    doc.setPropertyValue("dc:title", "C");
    maybeSleepToNextSecond();
    doc = session.saveDocument(doc);
    doc.putContextData(VersioningService.VERSIONING_OPTION, VersioningOption.MINOR);
    maybeSleepToNextSecond();
    doc = session.saveDocument(doc);
    assertTrue(doc.isCheckedOut());
    assertVersion("2.1", doc);
    assertLatestVersion("2.0", doc);

    // another save+inc, no snapshot
    doc.setPropertyValue("dc:title", "D");
    doc.putContextData(VersioningService.VERSIONING_OPTION, VersioningOption.MAJOR);
    maybeSleepToNextSecond();
    doc = session.saveDocument(doc);
    assertTrue(doc.isCheckedOut());
    assertVersion("3.0", doc);
    assertLatestVersion("2.1", doc);

    // checkin/checkout (old style)
    maybeSleepToNextSecond();
    session.checkIn(docRef, null);
    session.checkOut(docRef);
    doc = session.getDocument(docRef);
    assertTrue(doc.isCheckedOut());
    assertVersion("3.1", doc);
    assertLatestVersion("3.0", doc);

    // wait before doing a restore
    session.save();
    waitForAsyncCompletion();

    // restore 1.1 -> 3.2 (snapshots 3.1)
    maybeSleepToNextSecond();
    doc = session.restoreToVersion(docRef, v11.getRef());
    assertFalse(doc.isCheckedOut());
    assertVersion("1.1", doc);
    assertVersionLabel("1.1", doc);
    assertLatestVersion("3.1", doc);

    // checkout restored version
    doc.checkOut();
    assertTrue(doc.isCheckedOut());
    assertVersion("3.2", doc);
    assertVersionLabel("3.2", doc);
    assertLatestVersion("3.1", doc);
  }
  @Test
  public void testRestoreInvalidations() throws Exception {
    DocumentModel doc = new DocumentModelImpl("/", "myfile", "File");
    doc.setPropertyValue("dc:title", "t1");
    doc = session.createDocument(doc);
    final DocumentRef docRef = doc.getRef();
    DocumentRef v1 = session.checkIn(docRef, null, null);
    session.checkOut(docRef);
    doc.setPropertyValue("dc:title", "t2");
    session.saveDocument(doc);
    session.save();

    waitForFulltextIndexing();

    // we need 2 threads to get 2 different sessions that send each other invalidations
    final CyclicBarrier barrier = new CyclicBarrier(2);
    Throwable[] throwables = new Throwable[2];
    Thread t1 =
        new Thread() {
          @Override
          public void run() {
            TransactionHelper.startTransaction();
            try (CoreSession session = openSessionAs(SecurityConstants.ADMINISTRATOR)) {
              DocumentModel doc = session.getDocument(docRef);
              assertEquals("t2", doc.getPropertyValue("dc:title"));
              // 1. sync
              barrier.await(30, TimeUnit.SECONDS); // (throws on timeout)
              // 2. restore and next tx to send invalidations
              DocumentModel restored = session.restoreToVersion(docRef, v1);
              assertEquals("t1", restored.getPropertyValue("dc:title"));
              session.save();
              nextTransaction();
              // 3. sync
              barrier.await(30, TimeUnit.SECONDS); // (throws on timeout)
              // 4. wait
            } catch (InterruptedException
                | BrokenBarrierException
                | TimeoutException
                | RuntimeException
                | AssertionError t) {
              throwables[0] = t;
            } finally {
              TransactionHelper.commitOrRollbackTransaction();
            }
          }
        };
    Thread t2 =
        new Thread() {
          @Override
          public void run() {
            TransactionHelper.startTransaction();
            try (CoreSession session = openSessionAs(SecurityConstants.ADMINISTRATOR)) {
              DocumentModel doc = session.getDocument(docRef);
              assertEquals("t2", doc.getPropertyValue("dc:title"));
              // 1. sync
              barrier.await(30, TimeUnit.SECONDS); // (throws on timeout)
              // 2. nop
              // 3. sync
              barrier.await(30, TimeUnit.SECONDS); // (throws on timeout)
              // 4. next tx to get invalidations and check
              nextTransaction();
              DocumentModel restored = session.getDocument(docRef);
              assertEquals("t1", restored.getPropertyValue("dc:title"));
            } catch (InterruptedException
                | BrokenBarrierException
                | TimeoutException
                | RuntimeException
                | AssertionError t) {
              throwables[1] = t;
            } finally {
              TransactionHelper.commitOrRollbackTransaction();
            }
          }
        };
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    AssertionError assertionError = null;
    for (Throwable t : throwables) {
      if (t != null) {
        if (assertionError == null) {
          assertionError = new AssertionError("Exceptions in threads");
        }
        assertionError.addSuppressed(t);
      }
    }
    if (assertionError != null) {
      throw assertionError;
    }
  }