<commit_before>/*
 * Copyright (C) 2014  <NAME>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package c5db.log;

import c5db.C5CommonTestUtil;
import c5db.util.KeySerializingExecutor;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import java.nio.file.Path;
import java.util.List;

import static c5db.FutureMatchers.resultsIn;
import static c5db.log.LogTestUtil.aSeqNum;
import static c5db.log.LogTestUtil.anOLogEntry;
import static c5db.log.LogTestUtil.emptyEntryList;
import static c5db.log.LogTestUtil.makeSingleEntryList;
import static c5db.log.LogTestUtil.seqNum;
import static c5db.log.LogTestUtil.someConsecutiveEntries;
import static c5db.log.LogTestUtil.someData;
import static c5db.log.LogTestUtil.term;
import static c5db.log.SequentialLog.LogEntryNotInSequence;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class QuorumDelegatingLogTest {
  private static Path testDirectory;
  private LogFileService logPersistenceService;
  private OLog log;
  private final String quorumId = "quorumId";

  @BeforeClass
  public static void setTestDirectory() {
    if (testDirectory == null) {
      testDirectory = (new C5CommonTestUtil()).getDataTestDir("olog");
    }
  }

  @Before
  public final void setUp() throws Exception {
    logPersistenceService = new LogFileService(testDirectory);
    logPersistenceService.moveLogsToArchive();

    log = new QuorumDelegatingLog(
        logPersistenceService,
        new KeySerializingExecutor(MoreExecutors.sameThreadExecutor()),
        NavigableMapTermOracle::new,
        InMemoryPersistenceNavigator::new);
  }

  @After
  public final void tearDown() throws Exception {
    log.close();
    logPersistenceService.moveLogsToArchive();
  }

  @Test(expected = Exception.class, timeout = 1000)
  public void throwsExceptionFromGetLogEntryMethodWhenTheLogIsEmpty() throws Exception {
    log.getLogEntry(1, quorumId).get();
  }

  @Test(expected = Exception.class, timeout = 1000)
  public void throwsExceptionFromGetLogEntriesMethodWhenTheLogIsEmpty() throws Exception {
    log.getLogEntries(1, 2, quorumId).get();
  }

  @Test
  public void canLogAndRetrieveASingleEntry() throws Exception {
    OLogEntry entryToLog = anOLogEntry();
    long seqNum = entryToLog.getSeqNum();

    log.logEntry(Lists.newArrayList(entryToLog), quorumId);

    assertThat(log.getLogEntry(seqNum, quorumId),
        resultsIn(equalTo(entryToLog)));
  }

  @Test
  public void retrievesListsOfEntriesItHasLogged() throws Exception {
    List<OLogEntry> entries = someConsecutiveEntries(1, 5);

    log.logEntry(entries, quorumId);

    assertThat(log.getLogEntries(1, 5, quorumId),
        resultsIn(equalTo(entries)));
  }

  @Test(expected = IllegalArgumentException.class)
  public void throwsAnExceptionWhenGettingEntriesIfEndIsLessThanStart() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    log.getLogEntries(3, 2, quorumId);
  }

  @Test
  public void returnsAnEmptyListWhenGettingEntriesIfStartEqualsEnd() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    assertThat(log.getLogEntries(3, 3, quorumId), resultsIn(equalTo(emptyEntryList())));
  }

  @Test
  public void logsAndRetrievesDifferentQuorumsWithTheSameSequenceNumbers() throws Exception {
    String quorumA = "A";
    String quorumB = "B";

    List<OLogEntry> entriesA = someConsecutiveEntries(1, 5);
    List<OLogEntry> entriesB = someConsecutiveEntries(1, 5);

    log.logEntry(entriesA, quorumA);
    log.logEntry(entriesB, quorumB);

    assertThat(log.getLogEntries(1, 5, quorumA), resultsIn(equalTo(entriesA)));
    assertThat(log.getLogEntries(1, 5, quorumB), resultsIn(equalTo(entriesB)));
  }

  @Test
  public void retrievesEntriesFromTheMiddleOfTheLog() throws Exception {
    List<OLogEntry> entries = someConsecutiveEntries(1, 10);

    log.logEntry(entries, quorumId);

    assertThat(log.getLogEntries(4, 6, quorumId), resultsIn(equalTo(entries.subList(3, 5))));
  }

  @Test
  public void truncatesEntriesFromTheEndOfTheLog() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    log.truncateLog(3, quorumId);
    log.logEntry(someConsecutiveEntries(3, 5), quorumId);
  }

  @Test(expected = LogEntryNotInSequence.class)
  public void throwsAnExceptionIfAttemptingToLogEntriesWithASequenceGap() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 3), quorumId);
    log.logEntry(someConsecutiveEntries(4, 5), quorumId);
  }

  @Test(expected = LogEntryNotInSequence.class)
  public void throwsAnExceptionIfAttemptingToLogEntriesWithoutAscendingSequenceNumber() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 2), quorumId);
    log.logEntry(someConsecutiveEntries(1, 2), quorumId);
  }

  @Test(expected = Exception.class, timeout = 1000)
  public void throwsAnExceptionIfTryingToRetrieveAnEntryThatIsNotInTheLog() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    log.truncateLog(3, quorumId);
    log.getLogEntry(4, quorumId).get();
  }

  @Test(expected = Exception.class, timeout = 1000)
  public void throwsAnExceptionIfTryingToRetrieveEntriesAndAtLeastOneIsNotInTheLog() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    log.truncateLog(3, quorumId);
    log.getLogEntries(2, 4, quorumId).get();
  }

  @Test
  public void maintainsCorrectSequenceAfterATruncation() {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    log.truncateLog(3, quorumId);
    log.logEntry(someConsecutiveEntries(3, 6), quorumId);
  }

  @Test
  public void storesAndRetrievesElectionTermForEntriesItHasLogged() {
    log.logEntry(makeSingleEntryList(nextSeqNum(), term(1), someData()), quorumId);<commit_msg>change QuorumDelegatingLogTest to use 'resultsInException' matcher<commit_after>/*
 * Copyright (C) 2014  <NAME>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package c5db.log;

import c5db.C5CommonTestUtil;
import c5db.util.KeySerializingExecutor;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import java.nio.file.Path;
import java.util.List;

import static c5db.FutureMatchers.resultsIn;
import static c5db.FutureMatchers.resultsInException;
import static c5db.log.LogTestUtil.aSeqNum;
import static c5db.log.LogTestUtil.anOLogEntry;
import static c5db.log.LogTestUtil.emptyEntryList;
import static c5db.log.LogTestUtil.makeSingleEntryList;
import static c5db.log.LogTestUtil.seqNum;
import static c5db.log.LogTestUtil.someConsecutiveEntries;
import static c5db.log.LogTestUtil.someData;
import static c5db.log.LogTestUtil.term;
import static c5db.log.SequentialLog.LogEntryNotFound;
import static c5db.log.SequentialLog.LogEntryNotInSequence;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class QuorumDelegatingLogTest {
  private static Path testDirectory;
  private LogFileService logPersistenceService;
  private OLog log;
  private final String quorumId = "quorumId";

  @BeforeClass
  public static void setTestDirectory() {
    if (testDirectory == null) {
      testDirectory = (new C5CommonTestUtil()).getDataTestDir("olog");
    }
  }

  @Before
  public final void setUp() throws Exception {
    logPersistenceService = new LogFileService(testDirectory);
    logPersistenceService.moveLogsToArchive();

    log = new QuorumDelegatingLog(
        logPersistenceService,
        new KeySerializingExecutor(MoreExecutors.sameThreadExecutor()),
        NavigableMapTermOracle::new,
        InMemoryPersistenceNavigator::new);
  }

  @After
  public final void tearDown() throws Exception {
    log.close();
    logPersistenceService.moveLogsToArchive();
  }

  @Test(timeout = 1000)
  public void throwsExceptionFromGetLogEntryMethodWhenTheLogIsEmpty() throws Exception {
    assertThat(log.getLogEntry(1, quorumId), resultsInException(LogEntryNotFound.class));
  }

  @Test(timeout = 1000)
  public void throwsExceptionFromGetLogEntriesMethodWhenTheLogIsEmpty() throws Exception {
    assertThat(log.getLogEntries(1, 2, quorumId), resultsInException(LogEntryNotFound.class));
  }

  @Test
  public void canLogAndRetrieveASingleEntry() throws Exception {
    OLogEntry entryToLog = anOLogEntry();
    long seqNum = entryToLog.getSeqNum();

    log.logEntry(Lists.newArrayList(entryToLog), quorumId);

    assertThat(log.getLogEntry(seqNum, quorumId),
        resultsIn(equalTo(entryToLog)));
  }

  @Test
  public void retrievesListsOfEntriesItHasLogged() throws Exception {
    List<OLogEntry> entries = someConsecutiveEntries(1, 5);

    log.logEntry(entries, quorumId);

    assertThat(log.getLogEntries(1, 5, quorumId),
        resultsIn(equalTo(entries)));
  }

  @Test(expected = IllegalArgumentException.class)
  public void throwsAnExceptionWhenGettingEntriesIfEndIsLessThanStart() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    log.getLogEntries(3, 2, quorumId);
  }

  @Test
  public void returnsAnEmptyListWhenGettingEntriesIfStartEqualsEnd() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    assertThat(log.getLogEntries(3, 3, quorumId), resultsIn(equalTo(emptyEntryList())));
  }

  @Test
  public void logsAndRetrievesDifferentQuorumsWithTheSameSequenceNumbers() throws Exception {
    String quorumA = "A";
    String quorumB = "B";

    List<OLogEntry> entriesA = someConsecutiveEntries(1, 5);
    List<OLogEntry> entriesB = someConsecutiveEntries(1, 5);

    log.logEntry(entriesA, quorumA);
    log.logEntry(entriesB, quorumB);

    assertThat(log.getLogEntries(1, 5, quorumA), resultsIn(equalTo(entriesA)));
    assertThat(log.getLogEntries(1, 5, quorumB), resultsIn(equalTo(entriesB)));
  }

  @Test
  public void retrievesEntriesFromTheMiddleOfTheLog() throws Exception {
    List<OLogEntry> entries = someConsecutiveEntries(1, 10);

    log.logEntry(entries, quorumId);

    assertThat(log.getLogEntries(4, 6, quorumId), resultsIn(equalTo(entries.subList(3, 5))));
  }

  @Test
  public void truncatesEntriesFromTheEndOfTheLog() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    log.truncateLog(3, quorumId);
    log.logEntry(someConsecutiveEntries(3, 5), quorumId);
  }

  @Test(expected = LogEntryNotInSequence.class)
  public void throwsAnExceptionIfAttemptingToLogEntriesWithASequenceGap() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 3), quorumId);
    log.logEntry(someConsecutiveEntries(4, 5), quorumId);
  }

  @Test(expected = LogEntryNotInSequence.class)
  public void throwsAnExceptionIfAttemptingToLogEntriesWithoutAscendingSequenceNumber() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 2), quorumId);
    log.logEntry(someConsecutiveEntries(1, 2), quorumId);
  }

  @Test(timeout = 1000)
  public void throwsAnExceptionIfTryingToRetrieveAnEntryThatIsNotInTheLog() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    log.truncateLog(3, quorumId);

    assertThat(log.getLogEntry(4, quorumId), resultsInException(LogEntryNotFound.class));
  }

  @Test(timeout = 1000)
  public void throwsAnExceptionIfTryingToRetrieveEntriesAndAtLeastOneIsNotInTheLog() throws Exception {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    log.truncateLog(3, quorumId);

    assertThat(log.getLogEntries(2, 4, quorumId), resultsInException(LogEntryNotFound.class));
  }

  @Test
  public void maintainsCorrectSequenceAfterATruncation() {
    log.logEntry(someConsecutiveEntries(1, 5), quorumId);
    log.truncateLog(3, quorumId);
    log.logEntry(someConsecutiveEntries(3, 6), quorumId);
  }

  @Test
  public void storesAndRetrievesElectionTermForEntriesItHasLogged() {
    log.logEntry(makeSingleEntryList(nextSeqNum(), term(1), someData()), quorumId);
{'fraction_non_alphanumeric': 0.09703162291169451, 'fraction_numerical': 0.014170644391408114, 'mean_word_length': 5.2049976862563625, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3746539', 'n_tokens_mistral': 4319, 'n_tokens_neox': 4146, 'n_words': 904}