private void setupTestState(boolean addTtlUpdates, boolean addUndelete) throws InterruptedException, StoreException {
    long segmentCapacity = isLogSegmented ? SEGMENT_CAPACITY : LOG_CAPACITY;
    properties.put("store.index.max.number.of.inmem.elements", Integer.toString(MAX_IN_MEM_ELEMENTS));
    properties.put("store.segment.size.in.bytes", Long.toString(segmentCapacity));
    properties.put("store.validate.authorization", "true");
    properties.put("store.deleted.message.retention.minutes",
        Integer.toString(CuratedLogIndexState.deleteRetentionHour * 60));
    store = createBlobStore(getMockReplicaId(tempDirStr));
    store.start();
    // advance time by a second in order to be able to add expired keys and to avoid keys that are expired from
    // being picked for delete.
    time.sleep(Time.MsPerSec);
    long expectedStoreSize;
    assertTrue("Expected empty store", store.isEmpty());
    if (!isLogSegmented) {
      // log is filled about ~50%.
      expectedStoreSize = segmentCapacity / 2;
      addCuratedData(expectedStoreSize, addTtlUpdates);
    } else {
      expectedStoreSize = segmentCapacity;
      // first log segment is filled to capacity.
      addCuratedData(segmentCapacity, addTtlUpdates);
      assertEquals("Store size not as expected", expectedStoreSize, store.getSizeInBytes());
      assertFalse("Expected nonempty store", store.isEmpty());

      // second log segment is filled but has some space at the end (free space has to be less than the lesser of the
      // standard delete and put record sizes so that the next write causes a roll over of log segments).
      long sizeToWrite = segmentCapacity - (Math.min(PUT_RECORD_SIZE, DELETE_RECORD_SIZE) - 1);
      expectedStoreSize += sizeToWrite;
      addCuratedData(sizeToWrite, addTtlUpdates);
      assertEquals("Store size not as expected", expectedStoreSize, store.getSizeInBytes());

      // third log segment is partially filled and is left as the "active" segment
      sizeToWrite = segmentCapacity / 3;
      // First Index Segment
      // 1 PUT entry
      int puts = 0;
      int deletes = 0;
      int ttlUpdates = 0;
      MockId addedId = put(1, PUT_RECORD_SIZE, Utils.Infinite_Time).get(0);
      idsByLogSegment.add(new HashSet<>());
      idsByLogSegment.get(2).add(addedId);
      puts++;
      // DELETE for a key in the first log segment
      MockId idToDelete = getIdToDelete(idsByLogSegment.get(0), false);
      delete(idToDelete);
      deletes++;
      idsByLogSegment.get(2).add(idToDelete);
      // DELETE for a key in the second segment
      idToDelete = getIdToDelete(idsByLogSegment.get(1), false);
      delete(idToDelete);
      deletes++;
      idsByLogSegment.get(2).add(idToDelete);
      // 1 DELETE for the PUT in the same segment
      deletedKeys.add(addedId);
      liveKeys.remove(addedId);
      delete(addedId);
      deletes++;

      if (addTtlUpdates) {
        // 1 TTL update for a key in first log segment
        MockId idToUpdate = getIdToTtlUpdate(idsByLogSegment.get(0));
        updateTtl(idToUpdate);
        ttlUpdates++;

        // Second Index Segment
        // 1 TTL update for a key in the second log segment
        idToUpdate = getIdToTtlUpdate(idsByLogSegment.get(1));
        updateTtl(idToUpdate);
        ttlUpdates++;
        // 1 TTL update for a key in first log segment
        idToUpdate = getIdToTtlUpdate(idsByLogSegment.get(0));
        updateTtl(idToUpdate);
        ttlUpdates++;
        // 1 DELETE for the key above
        delete(idToUpdate);
        deletes++;
        // 1 TTL update for a key in second log segment
        idToUpdate = getIdToTtlUpdate(idsByLogSegment.get(1));
        updateTtl(idToUpdate);
        ttlUpdates++;
        // 1 DELETE for the key above
        delete(idToUpdate);
        deletes++;

        // Third Index Segment
        // 1 DELETE for a key that's already ttl updated in the first log segment
        idToDelete = getIdToDelete(idsByLogSegment.get(0), true);
        delete(idToDelete);
        deletes++;
        // 1 DELETE for a key that's already ttl updated in the first second segment
        idToDelete = getIdToDelete(idsByLogSegment.get(1), true);
        delete(idToDelete);
        deletes++;
      }
      long sizeAdded = 0;
      if (addUndelete) {
        sizeAdded += addDeleteAndShouldCompactEntry();
        sizeAdded += addCuratedUndeleteToLogSegment();
      }

      // 1 PUT entry that spans the rest of the data in the segment (upto a third of the segment size)
      long size = sizeToWrite - (LogSegment.HEADER_SIZE + puts * PUT_RECORD_SIZE + deletes * DELETE_RECORD_SIZE
          + ttlUpdates * TTL_UPDATE_RECORD_SIZE) - sizeAdded;
      addedId = put(1, size, Utils.Infinite_Time).get(0);
      idsByLogSegment.get(2).add(addedId);
      // the store counts the wasted space at the end of the second segment as "used capacity".
      expectedStoreSize = 2 * segmentCapacity + sizeToWrite;
      assertEquals("Store size not as expected", expectedStoreSize, store.getSizeInBytes());
      // fourth and fifth log segment are free.
    }
    // make sure all indexes are written to disk and mapped as required (forcing IndexPersistor to run).
    reloadStore();
    assertEquals("Store size not as expected", expectedStoreSize, store.getSizeInBytes());
  }
Sets up some state in order to make sure all cases are represented and the tests don't need to do any setup
individually. For understanding the created store, please read the source code which is annotated with comments.
@param addTtlUpdates if {@code true}, adds ttl update entries (temporary until all components can handle TTL
                     updates)
@param addUndelete if {@code true}, adds undelete entries (temporary until all components can handle UNDELETE)
@throws InterruptedException
@throws StoreException
ambry-store/src/test/java/com/github/ambry/store/BlobStoreTest.java